From 404be3d6a54ad1460bdf8f8eb826758a4ceabcba Mon Sep 17 00:00:00 2001 From: Rekushi <170186554+RekuNote@users.noreply.github.com> Date: Thu, 29 Oct 2026 11:59:16 +0000 Subject: [PATCH] Add files via upload --- assets/flipnote-editor/Draw.png | Bin 0 -> 4370 bytes assets/flipnote-editor/Draw_selected.png | Bin 0 -> 4472 bytes assets/flipnote-editor/Erase.png | Bin 0 -> 4453 bytes assets/flipnote-editor/Erase_selected.png | Bin 0 -> 4437 bytes assets/flipnote-editor/ImportImage.png | Bin 0 -> 17394 bytes assets/flipnote-editor/Paint.png | Bin 0 -> 4440 bytes assets/flipnote-editor/Paint_selected.png | Bin 0 -> 4433 bytes assets/flipnote-editor/arabic-text.js | 213 + assets/flipnote-editor/arabic-text.json | 1 + assets/flipnote-editor/base.png | Bin 0 -> 38262 bytes assets/flipnote-editor/bidi-LICENSE.txt | 22 + assets/flipnote-editor/bidi.min.js | 1 + assets/flipnote-editor/checker.png | Bin 0 -> 11022 bytes assets/flipnote-editor/clear.png | Bin 0 -> 16729 bytes assets/flipnote-editor/clone-placeholder.png | Bin 0 -> 2446 bytes assets/flipnote-editor/closepages.png | Bin 0 -> 7712 bytes assets/flipnote-editor/colour.png | Bin 0 -> 48625 bytes assets/flipnote-editor/copy.png | Bin 0 -> 18513 bytes assets/flipnote-editor/copy_pressed.png | Bin 0 -> 20096 bytes assets/flipnote-editor/cut.png | Bin 0 -> 5977 bytes assets/flipnote-editor/delete-frame.png | Bin 0 -> 2795 bytes assets/flipnote-editor/downloadwebm.png | Bin 0 -> 18554 bytes assets/flipnote-editor/drawing-engine.js | 661 ++ assets/flipnote-editor/earsHurt.png | Bin 0 -> 160 bytes assets/flipnote-editor/editor.js | 7265 +++++++++++++++++ assets/flipnote-editor/editpattern.png | Bin 0 -> 19090 bytes assets/flipnote-editor/erase1.png | Bin 0 -> 773 bytes assets/flipnote-editor/erase1_Adv.png | Bin 0 -> 773 bytes assets/flipnote-editor/erase2.png | Bin 0 -> 793 bytes assets/flipnote-editor/erase2_Adv.png | Bin 0 -> 789 bytes assets/flipnote-editor/erase3.png | Bin 0 -> 818 bytes assets/flipnote-editor/erase3_Adv.png | Bin 0 -> 828 bytes assets/flipnote-editor/erase4.png | Bin 0 -> 876 bytes assets/flipnote-editor/erase4_Adv.png | Bin 0 -> 907 bytes assets/flipnote-editor/erase5.png | Bin 0 -> 793 bytes assets/flipnote-editor/erase5_Adv.png | Bin 0 -> 832 bytes assets/flipnote-editor/erase6.png | Bin 0 -> 797 bytes assets/flipnote-editor/erase6_Adv.png | Bin 0 -> 896 bytes assets/flipnote-editor/erase7.png | Bin 0 -> 775 bytes assets/flipnote-editor/erase7_Adv.png | Bin 0 -> 824 bytes assets/flipnote-editor/erase8.png | Bin 0 -> 771 bytes assets/flipnote-editor/erase8_Adv.png | Bin 0 -> 793 bytes assets/flipnote-editor/eraseaudio.png | Bin 0 -> 3893 bytes assets/flipnote-editor/erasebig.png | Bin 0 -> 8397 bytes assets/flipnote-editor/eraser.png | Bin 0 -> 9937 bytes assets/flipnote-editor/fill.png | Bin 0 -> 9821 bytes assets/flipnote-editor/fill1.png | Bin 0 -> 1265 bytes assets/flipnote-editor/fill2.png | Bin 0 -> 1135 bytes assets/flipnote-editor/fill3.png | Bin 0 -> 1036 bytes assets/flipnote-editor/fill4.png | Bin 0 -> 1094 bytes assets/flipnote-editor/fill5.png | Bin 0 -> 1175 bytes assets/flipnote-editor/fill6.png | Bin 0 -> 1139 bytes assets/flipnote-editor/fill7.png | Bin 0 -> 1297 bytes assets/flipnote-editor/fill8.png | Bin 0 -> 1024 bytes assets/flipnote-editor/fill_selected.png | Bin 0 -> 10014 bytes assets/flipnote-editor/filmstrip-dots.png | Bin 0 -> 122 bytes assets/flipnote-editor/first-frame.png | Bin 0 -> 1145 bytes assets/flipnote-editor/flip_x.png | Bin 0 -> 12906 bytes assets/flipnote-editor/flip_y.png | Bin 0 -> 13640 bytes assets/flipnote-editor/goodVolume.png | Bin 0 -> 161 bytes assets/flipnote-editor/invert.png | Bin 0 -> 15443 bytes assets/flipnote-editor/last-frame.png | Bin 0 -> 1144 bytes assets/flipnote-editor/layer1.png | Bin 0 -> 3176 bytes assets/flipnote-editor/layer1_inactive.png | Bin 0 -> 2457 bytes assets/flipnote-editor/layer2.png | Bin 0 -> 3264 bytes assets/flipnote-editor/layer2_inactive.png | Bin 0 -> 2715 bytes .../flipnote-editor/lightbox-placeholder.png | Bin 0 -> 2570 bytes assets/flipnote-editor/lightbox.png | Bin 0 -> 16443 bytes assets/flipnote-editor/lightbox_pressed.png | Bin 0 -> 18245 bytes assets/flipnote-editor/loop_disabled.png | Bin 0 -> 7568 bytes assets/flipnote-editor/loop_enabled.png | Bin 0 -> 7638 bytes assets/flipnote-editor/musicBarEmpty.png | Bin 0 -> 3102 bytes assets/flipnote-editor/musicImportalt.png | Bin 0 -> 17468 bytes assets/flipnote-editor/musicPlay.png | Bin 0 -> 9908 bytes assets/flipnote-editor/musicRec.png | Bin 0 -> 6652 bytes assets/flipnote-editor/musicStop.png | Bin 0 -> 9854 bytes assets/flipnote-editor/new-frame.png | Bin 0 -> 9660 bytes assets/flipnote-editor/new-frame.wav | Bin 0 -> 196400 bytes assets/flipnote-editor/next-frame.png | Bin 0 -> 1122 bytes assets/flipnote-editor/openpages.png | Bin 0 -> 4213 bytes assets/flipnote-editor/openppm.png | Bin 0 -> 18601 bytes assets/flipnote-editor/pageCopy.png | Bin 0 -> 13518 bytes assets/flipnote-editor/pageErase.png | Bin 0 -> 12322 bytes assets/flipnote-editor/pageInsert.png | Bin 0 -> 13215 bytes assets/flipnote-editor/pagePaste.png | Bin 0 -> 13672 bytes assets/flipnote-editor/paint1.png | Bin 0 -> 807 bytes assets/flipnote-editor/paint1_Adv.png | Bin 0 -> 813 bytes assets/flipnote-editor/paint2.png | Bin 0 -> 811 bytes assets/flipnote-editor/paint2_Adv.png | Bin 0 -> 816 bytes assets/flipnote-editor/paint3.png | Bin 0 -> 781 bytes assets/flipnote-editor/paint3_Adv.png | Bin 0 -> 781 bytes assets/flipnote-editor/paint4.png | Bin 0 -> 800 bytes assets/flipnote-editor/paint4_Adv.png | Bin 0 -> 799 bytes assets/flipnote-editor/paint5.png | Bin 0 -> 836 bytes assets/flipnote-editor/paint5_Adv.png | Bin 0 -> 860 bytes 95 files changed, 8163 insertions(+) create mode 100644 assets/flipnote-editor/Draw.png create mode 100644 assets/flipnote-editor/Draw_selected.png create mode 100644 assets/flipnote-editor/Erase.png create mode 100644 assets/flipnote-editor/Erase_selected.png create mode 100644 assets/flipnote-editor/ImportImage.png create mode 100644 assets/flipnote-editor/Paint.png create mode 100644 assets/flipnote-editor/Paint_selected.png create mode 100644 assets/flipnote-editor/arabic-text.js create mode 100644 assets/flipnote-editor/arabic-text.json create mode 100644 assets/flipnote-editor/base.png create mode 100644 assets/flipnote-editor/bidi-LICENSE.txt create mode 100644 assets/flipnote-editor/bidi.min.js create mode 100644 assets/flipnote-editor/checker.png create mode 100644 assets/flipnote-editor/clear.png create mode 100644 assets/flipnote-editor/clone-placeholder.png create mode 100644 assets/flipnote-editor/closepages.png create mode 100644 assets/flipnote-editor/colour.png create mode 100644 assets/flipnote-editor/copy.png create mode 100644 assets/flipnote-editor/copy_pressed.png create mode 100644 assets/flipnote-editor/cut.png create mode 100644 assets/flipnote-editor/delete-frame.png create mode 100644 assets/flipnote-editor/downloadwebm.png create mode 100644 assets/flipnote-editor/drawing-engine.js create mode 100644 assets/flipnote-editor/earsHurt.png create mode 100644 assets/flipnote-editor/editor.js create mode 100644 assets/flipnote-editor/editpattern.png create mode 100644 assets/flipnote-editor/erase1.png create mode 100644 assets/flipnote-editor/erase1_Adv.png create mode 100644 assets/flipnote-editor/erase2.png create mode 100644 assets/flipnote-editor/erase2_Adv.png create mode 100644 assets/flipnote-editor/erase3.png create mode 100644 assets/flipnote-editor/erase3_Adv.png create mode 100644 assets/flipnote-editor/erase4.png create mode 100644 assets/flipnote-editor/erase4_Adv.png create mode 100644 assets/flipnote-editor/erase5.png create mode 100644 assets/flipnote-editor/erase5_Adv.png create mode 100644 assets/flipnote-editor/erase6.png create mode 100644 assets/flipnote-editor/erase6_Adv.png create mode 100644 assets/flipnote-editor/erase7.png create mode 100644 assets/flipnote-editor/erase7_Adv.png create mode 100644 assets/flipnote-editor/erase8.png create mode 100644 assets/flipnote-editor/erase8_Adv.png create mode 100644 assets/flipnote-editor/eraseaudio.png create mode 100644 assets/flipnote-editor/erasebig.png create mode 100644 assets/flipnote-editor/eraser.png create mode 100644 assets/flipnote-editor/fill.png create mode 100644 assets/flipnote-editor/fill1.png create mode 100644 assets/flipnote-editor/fill2.png create mode 100644 assets/flipnote-editor/fill3.png create mode 100644 assets/flipnote-editor/fill4.png create mode 100644 assets/flipnote-editor/fill5.png create mode 100644 assets/flipnote-editor/fill6.png create mode 100644 assets/flipnote-editor/fill7.png create mode 100644 assets/flipnote-editor/fill8.png create mode 100644 assets/flipnote-editor/fill_selected.png create mode 100644 assets/flipnote-editor/filmstrip-dots.png create mode 100644 assets/flipnote-editor/first-frame.png create mode 100644 assets/flipnote-editor/flip_x.png create mode 100644 assets/flipnote-editor/flip_y.png create mode 100644 assets/flipnote-editor/goodVolume.png create mode 100644 assets/flipnote-editor/invert.png create mode 100644 assets/flipnote-editor/last-frame.png create mode 100644 assets/flipnote-editor/layer1.png create mode 100644 assets/flipnote-editor/layer1_inactive.png create mode 100644 assets/flipnote-editor/layer2.png create mode 100644 assets/flipnote-editor/layer2_inactive.png create mode 100644 assets/flipnote-editor/lightbox-placeholder.png create mode 100644 assets/flipnote-editor/lightbox.png create mode 100644 assets/flipnote-editor/lightbox_pressed.png create mode 100644 assets/flipnote-editor/loop_disabled.png create mode 100644 assets/flipnote-editor/loop_enabled.png create mode 100644 assets/flipnote-editor/musicBarEmpty.png create mode 100644 assets/flipnote-editor/musicImportalt.png create mode 100644 assets/flipnote-editor/musicPlay.png create mode 100644 assets/flipnote-editor/musicRec.png create mode 100644 assets/flipnote-editor/musicStop.png create mode 100644 assets/flipnote-editor/new-frame.png create mode 100644 assets/flipnote-editor/new-frame.wav create mode 100644 assets/flipnote-editor/next-frame.png create mode 100644 assets/flipnote-editor/openpages.png create mode 100644 assets/flipnote-editor/openppm.png create mode 100644 assets/flipnote-editor/pageCopy.png create mode 100644 assets/flipnote-editor/pageErase.png create mode 100644 assets/flipnote-editor/pageInsert.png create mode 100644 assets/flipnote-editor/pagePaste.png create mode 100644 assets/flipnote-editor/paint1.png create mode 100644 assets/flipnote-editor/paint1_Adv.png create mode 100644 assets/flipnote-editor/paint2.png create mode 100644 assets/flipnote-editor/paint2_Adv.png create mode 100644 assets/flipnote-editor/paint3.png create mode 100644 assets/flipnote-editor/paint3_Adv.png create mode 100644 assets/flipnote-editor/paint4.png create mode 100644 assets/flipnote-editor/paint4_Adv.png create mode 100644 assets/flipnote-editor/paint5.png create mode 100644 assets/flipnote-editor/paint5_Adv.png diff --git a/assets/flipnote-editor/Draw.png b/assets/flipnote-editor/Draw.png new file mode 100644 index 0000000000000000000000000000000000000000..7e46e92d9d29c5c3d384529a3d052150bc73bb61 GIT binary patch literal 4370 zcmbVPc}&w+9RC&=MHmHjfhkCdz_QJ%fJTdq77?jz2An$1LCcVl%%c@5hqX{ds0N)* z5kyECVUA^tqY}WQg#sQ!px`o8Cbpnpu>zG-(hBSWb1p_5umAq?e(!z1pW}NR+vdkG zH(hE90Or11yte}w+5;E}OpMW&T{gC^0G1uT-Wzu&iN#$70$Ph$6W|!ca%=4<8*lwzlIImZ!9vMDvUlO zr6&(Xjz=2+?0zviqG7C|YshQp?*nwLGB5)8-NX{W-oX<%oZ1XtIF4{Eac0PKf|j z*Dw(iADP+`%3Mkg<0`e!5mC;#8DzCtboSL>zRm?O-RJpkYF~%Ii5vIL_(g4>>O@Fo zA5F(=UZG9#oBE(L?{o#k0&dn@)r-CCd8NsA3idk zw`OM(3-S`0Yo9$HP&;gOxnfDz^7(eqPhW38U-8Z|dt=dtBULp5{>NthAd9<xorYm=;sh3jXwlQCk@wA9=z(BQw=~S1pb5 zv*GhzZ0!(PMU*`9x{N1!y`5lbrs@=4=XUvHf_>bH3sV_su>lv)k%ai3BR%sDqDEin z&0QAaZf*?-zZ*lh?bgC^^W!yS{o;t5l>7Oj)AGEjCfzd1oc-S%G@| z>td$*tw%~I)`R(v#2oqO3Y+rPdOSNVMBooA1e@^74N*jyi`oAywo8{$saEpfM&9nZ z7A7F#+K5tM$ZJ5HC9=)URGbO?%w;ii0iez|)q`)BxEn7Y*T*m^yowphLQw|gEATDT zwff)K17b1j52c`CF>6Mq)#+)mEGB-xjW`)hjds$MM0DFy*W$;1??+To+ZpHTw~r?c zE0$WxQ;Ke3_gaz-mmljsHsa_1y-?)N8;emZD>J1lt>iXnb{l+*O{cJ}{d(`|T6bP| z-d-_BQO+Sb3-LAWg{sF6x@NQ|L9G)16HJI62t`Jhg_qI{ELhzV1TrfZU);`hC?B=M zoj;*ikF~sPcF>r?Rz};8o!y9IOL!H{!%d`3 zd?7u_tSO<@e^6MPjoRYCgd?=+!+c?rI=T!o&d~PWRi=w_rH-`+vYH4iJAAl^y{Ji# zEgT#jYN{?!`D9nGJU#7^(WBR(OuAJ-No7y+f*grpr$_zZH7|FAayGuR`qO5~x^!l9 z39_r99N|8w%?ez*k9rB}3Q z?=4?LR!G8-)-jRGNe%WQqWute#Cv_+eEaVG>SRvgZK5*2c0$~gigtG0)1v%j@^@yK qdi^$G0Y7tQtEAc%nyCtL)1^bpiAwSA(hDUh1n}ML=UwW>I{Y_@n+Zn% literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/Draw_selected.png b/assets/flipnote-editor/Draw_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..7738f0a2452798cdf5e52db54b0d653513577d67 GIT binary patch literal 4472 zcmbVQYfO`86h5Vfixnu-&?yy|4vnseph%0cb$|kafI3}T5#-Jk2WUkUEUgYiBMvtw zNT@L57BjWu7GYeZ2ueYql^H4s<+6gsQHEMtE~RB3FgHV{;ibQtFW-CKbDrmU&vV}I z!Y)5g)5R+m1AwWwm&YCeye)tM&)5)q5^hCy0WxKR5dg>qcE~7Hbfqj7{3hp5~h1UlQ+OJ)@nn?Ig7ZI<@4Q%;zy}|1p zE2bKpy5!dMr2yCD;eBbk=)N=@fO6G#0A4y^f1L~#0n{540c=BE;car%VZXSrg6EE{g-l0Jsc{E8@s^kt6e*xU@6XS2z4bhkSTMmCWQ3zvv&$ha~jk3NjMXeCuBwj%Ms(e2xn!A+SA(l zwa*fYGoauQmd~NV;pT1&^4V!lM5X$q-w_d=p(MGfhPLTcY;_g-p~i(ygOY@gMAaF_ z#*$DBlS-GKT?^P&Z%$A&_1(vb>JSSqOfjou&gLAn9+3d7C&wM9@Hbr`r)Yi~8E+F?H_&4{o8g$%^{>#1aD01(pHIyBoH(|{%v&bCdzaY-6a&OG` z`Ob8+6NcUKwjWh@V%-_T6Mv|0E9jLr+Iahj*o&#EbE#~e4Bga;AH6tguuWG0^=*KX26T$>oiiA(2|QRC zG;L=`DdhpwSZpe!9zjf}siBCHQEXm#@lk-;gF7w>?u^)>vtwzaqC(qb3Wd+e=pL>1 ziRW(B44)TFar+QY0$)srS3(M(?MvZfHi?*9I;Ml(7jHr|7Qw~S>pw#$zMvNK`Wtx( znAP`R2@`I(bZ z0q9U567c?y1Mgf_=X^$JYFL`f>FijCS(1C{ZF^J|-lE*dk8oUx!7O?biE1Kx)7{;_ VY)wuYu>n_e454 z)b!;26k)T`1o;L`7V^$|?akU_z5Mx#@2n*cQ!^ZY>9Q4}50UoXYqWpRK9O^oo|ja< zqxy#F-ANm9g-lu+a#iVU$OAxvB^ouI?H5NTxa*Zm0C?_-HUZ#~`7!_;)_DWK$^<9? z+}^n;D>-l7fC8);a-6He^u4#uos~;+Hv?cJ9)aq;hlL>h0EI^2nwSIdz14ra82p66 zDhgC68W>23?pJa$ei#&OqW>so3ljOZw*68(tY zMQjg3q|k)3m)ZsuG?BW zV}cy!wDysuhJT{g7~LQ5%uHhTeLNq<0iLu>IpZ=`bwE5r9<@{Bay>|ip< zu5B@Nj_g>@f@$g$w$P$iN!M5U5GNGu?2Ov{$oSArf!_aY=YR2l%=K}s6=9;3pzhk! zHT`P^|!s`ub0nt9Zn<4I$V_NStoJ9`x6FxK}8MSG%ny7sZ9q`Ur?a&pLvP| zfQyJD(O*3nLps;>)*yufuM>@~gy$mWl8VauHs|&w%w~9RTqX#_H0so1qvu#v(Tu__ z$en$t53*Ee?F@nQYm^gLpRjy8Jc8-BJA^%@R-@Mux{y8=eDxq6uiWK76g0{SyCy1g5$_h^Jv%P72Wj7 zJ4pS1dOk}<-QUC$C%2*D>HjiO|5DrdniHI77}K|*s~<#;-=`9ak2%X#M-nP4>`}%a zq06hGG!yU0oEU0D%!zD)W{1%Y0=nI?IDxBZmwwHNczX9;ZyM^5l5WKI-DIpqf6 z*=ibe)vi5CqUX%UgQTmj7e0#fLy;uIP)_TvvVe|i%ZR7j9#aXMN%(mguvb75i9QtN z#FzE(^HrLIf3odAkD&%E%I z)Dv|n$m=?^nlY`aye@>DdSrh6g}56FfP|5W&&`IF$BgN%f-Ixsm9*CY`Fwr$?=IaL GdiD<~KSDYH literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/Erase_selected.png b/assets/flipnote-editor/Erase_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..91c2fbc3222760ac1efe53f900b65385371c1be6 GIT binary patch literal 4437 zcmbtXdrXs86u$)lwGnX3M1)cnqmtQD!5J(Ji->@k^OcEwwmhUzl!r)*!sKCH1ja;B zrY%OaQxdlX1X{GLPTCG4pru7eC?H@3gdz_S3w+S>*e9Auh$xr+dvotO=XcNf{mwZV zdv^IOTe@l~0ASe;U$1=t7~2D2#9eHPorK!hIs>rWc8Ay29}{x>Tb+*DAgjEew9Cz} zQ7_~*c^RwLqNewKW8{&R8$60`tadwgYY%SS%1kHR?UXyeqWiz9%`g#F`{f(jEu?iK zscu=|mjL8+-03bFJ3Kb4;gRCR) zR-e8*+W_YvDHrp$9P-)R!OV6r<7NztCO>#xV8@~v0pK>$(Tg@^y}oH72&ety%9s<~ zM-A2-L3p~p^n{F54ge#E)gryxsxAQ1#?>Z^;{7oiB~qCCp#3`_&q457pEe^bxX?w5 z0l2W-9smz-E&#w9H|HVghAi787H|!3ChHofrjVVo8k2eCYz8WSPcy)^Db7i`f}gk7 z>G*_aRQsPvZRkBoDXZv{w#j9C^9KGF-=$SOO``CX`ao}`eo{PH28v@G=Y!+}=E^Tz zU4$bc`U3RYFjd`^DCu*M{Sn!$*WngmO27V`qiIa*)3cQ&E3&iRP!G2>myiAVMCb=D9UWP@N6aP#`G6cuyb^*@zPPDD?PfkqcdS~8bRTxOB? z7AbEIhDB27MbTKIeSc;OC;w9g&>6Uwa_(d*65(REj8n2#GQlPSf|310W-_dNO-9gb)yE|V>ZaD+Z z3iI($iES_;Ptg-exg|?>8HpbsdwHlHO^t+)rkKf7;6|d=A+9wqp!INDHJj?uYY1ZE z+asr12?3ka6XA_i;7NSrJ1jEa;I@PSO_6Kcgbee?B8E9b0iDV4Zxo$?tmu?S!3+3- z_ik;^L8nh8A~gqXF0ID*rFOEpdSOVaIYu93fS7``VL>hSZ zooE}1jT%e1w4K?F(a=t*FH3ia7o?_CT}R`-LA|1cSri_7Wwnr@a(8{i9@EuT^!adQ z@V%MikcWS$tr9l8W#!wz2w{?>Ax$1CAmwsw;S+pcE^;jmmgFXWt52jwsLLgt>;!k{ z@+5fj>d}X6DvB}X*n0RV`Wms>pf-!cyT6;*5O7k=Q0?R?7SaGt3)F#rntKSoub5PY zb%Noy8Zt_O2Lw!8n%QHe6ebOw{4JI9+!Y>_ORRpgcx6(`ct_>#@{6No?JK;kRAjz! z0sQzAj_?LeTJQ3?KK$E0h&-*MgkipbInh-i%B1wC7~UjzYguGVx?g%f#&7u)2fEQp z9(9)w=;|7KCq^R>g1C}R1*Pzsxbbq>aD%WwCBj-oa1cC4%^!&Lz5aeN*z@PtAqD12 zort7uvKY+PuEXE6ty1&r8%|sbJB~?Z%fH=I#LTDqQ(2G^O;1K>Xxt(F*pa?rv9urs zD**c~5phT>_CHa4XbClUV)ujk(FX3bsivm#8331ZQ(>90kVURxpvhFv|nHemA`kdHvt z#3y#pS#i#|Nevh*(Oz{o0iZa3TQux$TZniP!<+~JoM+avVEfAy&Y^!#@zzvj4z@sp N9ou(#6?z8${2xnTLQ4Pu literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/ImportImage.png b/assets/flipnote-editor/ImportImage.png new file mode 100644 index 0000000000000000000000000000000000000000..17bde144203045f51bd6a25e881f560594a939b4 GIT binary patch literal 17394 zcmeHPX;f3!7EWUD0Tvll5DEe!LsfN^p*X=p29*l647rGiEu&Rb zM2JOEAEU1n1QgOzv_7kVP*f0!QjtL>kwK=s9U!?WIm`E_t|e<-{&303J?Eaizwg_7 zpK~rpnGQBG(kjvv3Pr}w*4mLmk-#Vvny2Jk_=F*I>k#~+?_X!C764eXPy+bS1;n2=PNC>nU|PxI59j!pSG{P!2J4=$8_GBL$4ze zPiK44GoDtO`#JAge=XSH++2f>*4rm|-KoNs8-)%~g0)+5@;`~GRW;AiSjr+51| zZ>{PLo8ikT`|pt&8+`7uQ7zMh<$rl3R_G?;0@tQ5WHWyAmO|ZM2yL#**J>e9;3-9w zXjAxbYb4Q?5Kg#0v?YI6^*3-$1Hxq;MlG?9O%{Qj5GtMwXuIQ*R0a!i(ie1saNwmU{`@Nc7|9z|ynbM{YgaEX4Q0-?_Q=m#}LcX^$tgwk0nG#SJDU&|DRD6mj|ovqTzmvrK3&|HBF;E0ibY$s zbQeQGoI1ZRWU_X(`uieaClC0KIi+tW4pxoL7&YZ|X$}JZ)==$gc@-`bClvk}fPc15 zDs^dL!StMZdGxd%b^GrN?(&9591rrHI=;0$qU*J5Mfu>^{lS0%-?QZd6ZK=cVG$)B zS>-)^<-{@}rjmB%Yw0@z9?l0|j449km2^H;KR#F%;XN|(b5DTEAa)?fh+NtGJ?5s!-*`~E2}+a*5ZU` zVG=;z!^i)yMYM5_z{99}^=l(EguWc~V8!_z8&GX)m$511a%T+X#brr@X$fhErnWIc zvNU=%A%8gXAoQrX(ot|}+C^!rlq5kD*MbI_(HGA`Lc@wX#tn%4An+(Ka@&v3+)+x(ho3&rk zP;FZvs*?AK`}^(K<&jxaQ`z;=`ri!A%E~@7J6nSgu{y<&vIxEFK;Pu}s$TIYqf4p+ zeG`JrP+OGj0#Mwb&}c#k2!krjg=7EE2PbuEwZmBpZ!h}__^?O-KlAx;;UgRIw(%n&r-zi0~o!YY9py~86_hb}J3F@VTwk(K9dF)bAwM_M?w#3?mdlh8(? zTe&<4mgD9~f05@0_-6X2PegE}h@~q$iB=vS>RZy?Ih4M!d1$9V-0_vva>||mHWniI z@FR%*=X+g~Md>s3H7ps{1y%$!B9iPF2u>R+{k{|oAi|d~fhO74{1`$=2ro43F{;X< zqMe9nw*x}*PcIAgMf#b3R9p{YoztUaZE>(AYO`nUr0~skg~Xh^6^92}Tx*p*%6WT; z8gp9eqUQ}uv<2={LbyT02ux~f-(ZgLtK@sIRytodSX11lR9Xf&J>z_nmC!fD1t$=~ zF*zJzkV#^^*_aL)2K!_McE;3k4~w$ZYOe-HtFpp*4Y0n~b6e=3XAlVs+;+@GEZ$*U zH!yv%<6~rInhKdId7qd{vEPba@Y-U-g9(Dc*a@6G_2!iMBcnqWDP_E@_C}Ag(D8|% z+N0`wOx(K(=kA5bsG?MS7@=@~LDsSVKZPF%P$KA?MZwpA!nysWh(JW57AV}UhD;%P zh>NGd;Jrz%r-*(8J!A18+q%AI5PA`_fXTYC-v29vY<~i>oto`uKLhvt6*&ix?b$l1 zi>X_Lb86G-?Yvo(d1zu{JR!5ED7<)J!Xs|Zlp~#a^`mE^MIz=CaHVPLqr-&7iCQxj zqF?;61471&`k(?T!HQe%$dxb%Wo>#sL#X7)$PI*?9FEBDC&T?4jfvM@`u>q1g1M~c z_B8WccAk*!swv^+$u^Co8@i89+~^BCEAWnIKCB7MpBtO=brM8m%`Oq zI)DGmv&|VLm`mA|jdl%bGe+fmng+zg&$*r!_4?G;ue0)MD5k~$Q)oeF$5!XuE1{ZgPWVhtPNj~hKbKRvO5UX77sJHxi$@vM`u*UXYHvX#fR|U{cxcek#rnv-Ti7BBVpJmc?pJEU-tE#2$d{Az zVe6*Ju@8Bcf&t~k$(R}i6q)zHK2ecVDC$rIM**0p?Nxw$-`N$~^cT(Y>6zSr{1lkk z)nI%Td2YT+ez>cuJh#DeM568aUmZ>;;_JXsLFDez;r-oZL|ooD3#0IeSl;{X5v literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/Paint.png b/assets/flipnote-editor/Paint.png new file mode 100644 index 0000000000000000000000000000000000000000..69c110ab97c0249c4e3a8dc0d37a176299ac2c61 GIT binary patch literal 4440 zcmbVPc}x>l7=Hyp4yl4#1S(b0u+4IAi~>?noIr3WY7kT?$0$)y3ME5mON)rip)zGd z&_ZR#BNJx<2Y66w5!uwX9Ez9$1!rv;M;UiH+C4E#j8Vtq-CjbTjXPpHA{BH2UM|xB@9)PRWm^u8NyVdvp9Pa4TCCXonQO8UH zh;TU@tEkB38@SFn%-Psqy%axE5U7L~bRt(52&=CuSGmpaFtX0|%dH1j-q$31!(g`ym?=;{IUlXQzDoJ7(%0+Nx6=ial4R4U9}4 zkmms{4Uyu&&bv%a5?vv-FQ%LMB-e}SHPPNSmCl$NFukH?t0*a}zP5(tI%Pon0zKCN-o;ok^<`Z5bbZB@ zon&)t209E*SUF9QP=S;97KKy9WS#hVgu=;;>SoF+1+?@;#^*4U!FhxdeI$>sERTI|=+>YMw88iby;OpuyYXbP z$c=0?I37-`UdY>Kco*fuTO8JdbY+10Ye+iQt?pFTe_| z(F-mTV_xOzBMmDqy1_I%b-F$oV2>lqxw6YIWUDrXJ*|G$X9Mud(aZIy{h@?o#ZX0x zdhOu;hRTBU6s@D2PJt6fPRCH^G5Q9-t`roI(*&V`H*Qb~m z3#=1*>PN0SaC0?NdZb`)kEz!^iMjKW!7j$y&#$B>}L-6|&%(86uWH zSNGWvQAg-=v6(vnd=@JCb2;AM!aIBBb5D>bUde|S$5}(bqW|Wx?@$rb+yq| z({emfvw!Lm05Uh{aDKUsINgGJ;D-s&_c{y~b+*J2fOD%n0N5V-y2Yn{#p!rC1_=FR(JHBy|8(+b}0T6Ov!GA2=_N!mk=UN4Y^?{m4d^9Qc z*oZdXw3SyoR>T)vPfxGXHOBFbO1=MMz=kq(Ia@61tdH#C_O8fQA{d* zO59Is|1}WEDp+0;z0m3H#hw?$OIXU&=0a(77x!-xG#1UkvDqH^`FvLy`G6+N%IjfT z6t%BI>{TQ4univ$*jiT;+n7j8&d2o6Mn~ATK|*?X+2!k@&fV;bf9$5lT;z`g^Hd6+ z%6wGSpC! z<`)=5oY9Y)_?_O>6iC33IFlU^$V-GsDP3eeooco6hq?LGRGL9Su+BwCWYFQ$$1ogB z6&&ibfsAq?(rNDPYtWK|Ck&fRNR6~IM?O&lO*v3@D2ztQA5sMOtxS2Y)RiwabsjZ} zb>)i2yhT{s)Yyrahlv}*ysYM_sY9_^w^IR-bvwL_0PqR7gj_)vJjE~!fe@)yqk8m0 zT>B}o;_jIMaP3Xg()O<@W<I?Lykt15S90eq^qjoyTTT6u8@V=8nGyBRoI0@{afHq`K zoT_QM%lm5nt-om z*K9riHU)kP9s&jJci31sBL$Ix#J6<_r(C0?rxY#Rdi;JIp7$|7IxLgiDI4ey=d zhgX{0o3;U4I=u8nu*oSjbSo?S7{0S9u6U)U6?IHoM8>x_*G*Xso2qj7Nj6bei2^mF zy9?jSxKT@7?2g%?#IeyVQgOCRiaR_>xiy}K&O)ZsU0KYu-X6?uhAc&8F8aR^iKedv zfb(ML-uXX=<9YM3bCGgnW-N@T9Tls+(ZjW|r%%Ce^nvVxM{%MK^qf8pD~=0~^S>{- z6K55C`uH03UZK_%`d$FJ /[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb50-\ufdff\ufe70-\ufeff]/u.test(value); + function configure(value, factory) { data = value; bidi = factory(); } + function load() { + if (data && bidi) return Promise.resolve(); + if (!loading) loading = Promise.all([ + fetch('./assets/flipnote-editor/arabic-text.json?v=1.1').then(response => { + if (!response.ok) throw new Error('The Arabic text font could not load.'); + return response.json(); + }), + new Promise((resolve, reject) => { + if (global.bidi_js) { resolve(global.bidi_js); return; } + const script = document.createElement('script'); + script.src = './assets/flipnote-editor/bidi.min.js?v=1.0.3'; + script.onload = () => resolve(global.bidi_js); + script.onerror = reject; + document.head.append(script); + }), + ]).then(([value, factory]) => configure(value, factory)); + return loading; + } + + // Joining forms follow arabic-reshaper's MIT-licensed algorithm. + // Keep the same forms, ligatures and mark handling as the PPM renderer. + function reshape(text) { + text = text.normalize('NFC'); + const output = [], marks = new Map(), letters = data.letters; + for (const char of text) { + if (data.marks.includes(char)) { + const position = output.length - 1; + if (!marks.has(position)) marks.set(position, []); + marks.get(position).push(char); + } else if (!letters[char]) output.push([char, -1]); + else if (!output.length) output.push([char, 0]); + else { + const previous = output[output.length - 1]; + const a = letters[char], b = letters[previous[0]]; + if (previous[1] === -1 || !(a[3] || a[2]) || !(b[1] || b[2]) || (previous[1] === 3 && !b[2])) output.push([char, 0]); + else { previous[1] = previous[1] === 0 ? 1 : 2; output.push([char, 3]); } + } + if (output.length > 1 && output[output.length - 2][0] === '\u200d') output.splice(output.length - 2, 1); + } + if (output.length && output[output.length - 1][0] === '\u200d') output.pop(); + const clean = Array.from(text).filter(char => !data.marks.includes(char)).join(''); + for (const [word, forms] of data.ligatures) { + let start = clean.indexOf(word); + while (start !== -1) { + const end = start + word.length, first = output[start]?.[1], last = output[end - 1]?.[1]; + const form = first === 0 || first === 1 ? (last === 0 || last === 3 ? 0 : 1) : (last === 0 || last === 3 ? 3 : 2); + if (forms[form] && output[start]) { + output[start] = [forms[form], -1]; + for (let i = start + 1; i < end; i++) output[i] = ['', -1]; + } + start = clean.indexOf(word, end); + } + } + return (marks.get(-1) || []).join('') + output.map(([char, form], index) => (char && form !== -1 && form !== 0 ? letters[char][form] : char) + (marks.get(index) || []).join('')).join(''); + } + function visualClusters(text) { + const clusters = []; + for (const char of reshape(text)) { + if (data.marks.includes(char) && clusters.length) clusters[clusters.length - 1].marks += char; + else clusters.push({char, marks: ''}); + } + const base = clusters.map(cluster => cluster.char).join(''); + const levels = bidi.getEmbeddingLevels(base); + const order = bidi.getReorderedIndices(base, levels); + const display = bidi.getReorderedString(base, levels); + return order.map((index, position) => ({char: display[position], marks: clusters[index].marks})); + } + function visual(text) { + return text.split('\n').map(line => visualClusters(line).map(cluster => cluster.char + cluster.marks).join('')).join('\n'); + } + function markGlyphs(marks) { + const glyphs = []; + for (let index = 0; index < marks.length;) { + const match = data.mark_ligatures.find(([text]) => marks.startsWith(text, index)); + if (match) { glyphs.push(data.glyphs[match[1]]); index += match[0].length; } + else glyphs.push(data.glyphs[marks[index++]]); + } + return glyphs; + } + function placeMark(bitmap, base, previous) { + let position; + for (const lookup of data.positioning) { + const reference = lookup.marks[bitmap.name]; + if (!reference) continue; + const targets = lookup.kind === 'mark' ? previous.slice().reverse() : [base]; + for (const target of targets) { + let anchors = lookup.bases[target.bitmap.name]; + if (!anchors) continue; + if (lookup.kind === 'ligature') anchors = anchors[0]; + const anchor = anchors[reference[0]]; + if (!anchor) continue; + position = {bitmap, + x: target.x + Math.round((anchor[0] - reference[1]) * data.pixel_scale), + y: target.y - Math.round((anchor[1] - reference[2]) * data.pixel_scale)}; + break; + } + } + return position || {bitmap, x: base.x + Math.floor(base.bitmap.advance / 2), y: base.y}; + } + function line(text) { + const glyphs = []; + let pen = 0; + for (const cluster of visualClusters(text)) { + if (cluster.char === '\u200c' || cluster.char === '\u200d') continue; + const bitmap = data.glyphs[cluster.char]; + if (!bitmap) throw new Error(`The text fonts do not include “${cluster.char}”. Replace it to save this subtitle.`); + const base = {bitmap, x: pen, y: 0}; + glyphs.push(base); + const marks = []; + for (const mark of markGlyphs(cluster.marks)) { + const placed = placeMark(mark, base, marks); + glyphs.push(placed); marks.push(placed); + } + pen += bitmap.advance; + } + let left = 0, right = pen, above = 11, below = 1; + for (const glyph of glyphs) { + for (const [_dy, dx, count] of glyph.bitmap.spans) { + left = Math.min(left, glyph.x + dx); right = Math.max(right, glyph.x + dx + count); + } + above = Math.max(above, -glyph.bitmap.top - glyph.y); + below = Math.max(below, glyph.bitmap.bottom + glyph.y); + } + if (left) glyphs.forEach(glyph => { glyph.x -= left; }); + return {glyphs, advance: pen, width: right - left, above, height: Math.max(14, above + below + 2)}; + } + function paint(mask, width, height, glyph, x, baseline) { + for (const [dy, dx, count] of glyph.spans) { + const y = baseline + dy; + if (y < 0 || y >= height) continue; + const start = Math.max(0, x + dx), end = Math.min(width, x + dx + count); + if (end > start) mask.fill(1, y * width + start, y * width + end); + } + } + function rasterize(glyphs, width, height) { + const mask = new Uint8Array(width * height); + for (const glyph of glyphs) paint(mask, width, height, glyph.bitmap, glyph.x, glyph.baseline); + return mask; + } + function wrap(text, width) { + const lines = []; + for (const paragraph of text.split('\n')) { + let current = ''; + for (const word of paragraph.split(' ')) { + if (current && line((current + ' ' + word).trim()).width > width) { lines.push(current); current = ''; } + for (const char of graphemes((current ? ' ' : '') + word)) { + if (current && line(current + char).width > width) { lines.push(current.trimEnd()); current = ''; } + current += char; + } + } + lines.push(current.trim()); + } + return lines; + } + function graphemes(text) { + const groups = []; + for (const char of text) { + if (groups.length && (/\p{M}/u.test(char) || char === '\u200c' || char === '\u200d')) groups[groups.length - 1] += char; + else groups.push(char); + } + return groups; + } + function layoutText(value, anchor, width, caretIndex = value.length) { + const glyphs = []; + const rtl = bidi.getEmbeddingLevels(value).paragraphs[0]?.level % 2 === 1; + const available = Math.max(1, rtl ? anchor.x : width - anchor.x); + function editorLines(text) { + const result = []; + for (const paragraph of text.replace(/\t/g, ' ').split('\n')) { + let current = ''; + for (const char of graphemes(paragraph)) { + if (current && line(current + char).width > available) { result.push(current); current = ''; } + current += char; + } + result.push(current); + } + return result; + } + const lines = editorLines(value); + let top = anchor.y, caret = {x: anchor.x, y: top, height: 12}; + const prefix = value.slice(0, caretIndex); + const before = editorLines(prefix); + lines.forEach((text, index) => { + const shaped = line(text); + let x = rtl ? anchor.x - shaped.width : anchor.x; + for (const placed of shaped.glyphs) glyphs.push({bitmap: placed.bitmap, x: x + placed.x, baseline: top + shaped.above + placed.y}); + if (index === before.length - 1) { + const advance = line(before[index] || '').advance; + caret = {x: rtl ? anchor.x - advance : anchor.x + advance, y: top, height: shaped.height - 2}; + } + top += shaped.height; + }); + return {glyphs, caret, arabic: true}; + } + function subtitleMask(value) { + const lines = wrap(value, 244); + if (lines.length > 2) throw new Error('Keep each subtitle to two lines. Split longer text into another subtitle.'); + const shaped = lines.map(line), glyphs = []; + let top = 192 - 4 - shaped.reduce((sum, value) => sum + value.height, 0) + 1; + for (const value of shaped) { + let x = Math.floor((256 - value.width) / 2); + for (const placed of value.glyphs) glyphs.push({bitmap: placed.bitmap, x: x + placed.x, baseline: top + value.above + placed.y}); + top += value.height; + } + return {mask: rasterize(glyphs, 256, 192), lines}; + } + global.FlipnoteArabicText = {load, configure, contains, visual, line, layoutText, rasterize, subtitleMask}; +})(typeof window !== 'undefined' ? window : globalThis); diff --git a/assets/flipnote-editor/arabic-text.json b/assets/flipnote-editor/arabic-text.json new file mode 100644 index 0000000..d4bc433 --- /dev/null +++ b/assets/flipnote-editor/arabic-text.json @@ -0,0 +1 @@ +{"version":2,"glyphs":{" ":{"name":"space","advance":5,"spans":[],"top":0,"bottom":0},"!":{"name":"exclam","advance":2,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"\"":{"name":"quotedbl","advance":6,"spans":[[-9,2,1],[-9,4,1],[-8,1,1],[-8,3,1],[-7,0,1],[-7,2,1]],"top":-9,"bottom":-6},"#":{"name":"numbersign","advance":8,"spans":[[-9,3,1],[-9,5,1],[-8,3,1],[-8,5,1],[-7,1,6],[-6,2,1],[-6,4,1],[-5,2,1],[-5,4,1],[-4,2,1],[-4,4,1],[-3,0,6],[-2,1,1],[-2,3,1],[-1,1,1],[-1,3,1]],"top":-9,"bottom":0},"%":{"name":"percent","advance":8,"spans":[[-9,1,1],[-9,5,1],[-8,0,1],[-8,2,1],[-8,5,1],[-7,0,1],[-7,2,1],[-7,4,1],[-6,1,1],[-6,4,1],[-5,3,1],[-4,2,1],[-4,5,1],[-3,2,1],[-3,4,1],[-3,6,1],[-2,1,1],[-2,4,1],[-2,6,1],[-1,1,1],[-1,5,1]],"top":-9,"bottom":0},"'":{"name":"quotesingle","advance":2,"spans":[[-9,0,1],[-8,0,1]],"top":-9,"bottom":-7},"(":{"name":"parenleft","advance":4,"spans":[[-9,2,1],[-8,1,1],[-7,1,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,1,1],[-2,1,1],[-1,2,1]],"top":-9,"bottom":0},")":{"name":"parenright","advance":4,"spans":[[-9,0,1],[-8,1,1],[-7,1,1],[-6,2,1],[-5,2,1],[-4,2,1],[-3,1,1],[-2,1,1],[-1,0,1]],"top":-9,"bottom":0},"*":{"name":"asterisk","advance":8,"spans":[[-8,3,1],[-7,0,1],[-7,3,1],[-7,6,1],[-6,1,1],[-6,3,1],[-6,5,1],[-5,2,3],[-4,1,1],[-4,3,1],[-4,5,1],[-3,0,1],[-3,3,1],[-3,6,1],[-2,3,1]],"top":-8,"bottom":-1},"+":{"name":"plus","advance":6,"spans":[[-7,2,1],[-6,2,1],[-5,0,5],[-4,2,1],[-3,2,1]],"top":-7,"bottom":-2},",":{"name":"comma","advance":3,"spans":[[-1,1,1],[0,0,1]],"top":-1,"bottom":1},"-":{"name":"hyphen","advance":6,"spans":[[-5,0,5]],"top":-5,"bottom":-4},".":{"name":"period","advance":2,"spans":[[-1,0,1]],"top":-1,"bottom":0},"/":{"name":"slash","advance":4,"spans":[[-9,2,1],[-8,2,1],[-7,2,1],[-6,1,1],[-5,1,1],[-4,1,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"0":{"name":"zero","advance":6,"spans":[[-9,1,3],[-8,0,1],[-8,4,1],[-7,0,1],[-7,3,2],[-6,0,1],[-6,3,2],[-5,0,1],[-5,2,1],[-5,4,1],[-4,0,2],[-4,4,1],[-3,0,2],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-9,"bottom":0},"1":{"name":"one","advance":3,"spans":[[-9,0,2],[-8,1,1],[-7,1,1],[-6,1,1],[-5,1,1],[-4,1,1],[-3,1,1],[-2,1,1],[-1,1,1]],"top":-9,"bottom":0},"2":{"name":"two","advance":6,"spans":[[-9,0,4],[-8,4,1],[-7,4,1],[-6,4,1],[-5,1,3],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,5]],"top":-9,"bottom":0},"3":{"name":"three","advance":6,"spans":[[-9,0,4],[-8,4,1],[-7,4,1],[-6,4,1],[-5,1,3],[-4,4,1],[-3,4,1],[-2,4,1],[-1,0,4]],"top":-9,"bottom":0},"4":{"name":"four","advance":7,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,1,5],[-3,4,1],[-2,4,1],[-1,4,1]],"top":-9,"bottom":0},"5":{"name":"five","advance":6,"spans":[[-9,0,5],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,4],[-4,4,1],[-3,4,1],[-2,4,1],[-1,0,4]],"top":-9,"bottom":0},"6":{"name":"six","advance":6,"spans":[[-9,1,3],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,4],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-9,"bottom":0},"7":{"name":"seven","advance":6,"spans":[[-9,0,5],[-8,4,1],[-7,4,1],[-6,3,1],[-5,2,1],[-4,2,1],[-3,2,1],[-2,2,1],[-1,2,1]],"top":-9,"bottom":0},"8":{"name":"eight","advance":6,"spans":[[-9,1,3],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,1,3],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-9,"bottom":0},"9":{"name":"nine","advance":6,"spans":[[-9,1,3],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,1,4],[-4,4,1],[-3,4,1],[-2,4,1],[-1,1,3]],"top":-9,"bottom":0},":":{"name":"colon","advance":2,"spans":[[-7,0,1],[-3,0,1]],"top":-7,"bottom":-2},";":{"name":"semicolon","advance":3,"spans":[[-7,1,1],[-3,1,1],[-2,0,1]],"top":-7,"bottom":-1},"<":{"name":"less","advance":4,"spans":[[-7,2,1],[-6,1,1],[-5,0,1],[-4,1,1],[-3,2,1]],"top":-7,"bottom":-2},"=":{"name":"equal","advance":6,"spans":[[-6,0,5],[-4,0,5]],"top":-6,"bottom":-3},">":{"name":"greater","advance":4,"spans":[[-7,0,1],[-6,1,1],[-5,2,1],[-4,1,1],[-3,0,1]],"top":-7,"bottom":-2},"?":{"name":"question","advance":5,"spans":[[-9,0,3],[-8,3,1],[-7,3,1],[-6,2,1],[-5,1,1],[-4,1,1],[-2,1,1],[-1,1,1]],"top":-9,"bottom":0},"[":{"name":"bracketleft","advance":4,"spans":[[-9,0,3],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,3]],"top":-9,"bottom":0},"\\":{"name":"backslash","advance":4,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,1,1],[-5,1,1],[-4,1,1],[-3,2,1],[-2,2,1],[-1,2,1]],"top":-9,"bottom":0},"]":{"name":"bracketright","advance":4,"spans":[[-9,0,3],[-8,2,1],[-7,2,1],[-6,2,1],[-5,2,1],[-4,2,1],[-3,2,1],[-2,2,1],[-1,0,3]],"top":-9,"bottom":0},"^":{"name":"asciicircum","advance":4,"spans":[[-9,1,1],[-8,0,1],[-8,2,1]],"top":-9,"bottom":-7},"_":{"name":"underscore","advance":6,"spans":[[-1,0,5]],"top":-1,"bottom":0},"{":{"name":"braceleft","advance":5,"spans":[[-9,2,2],[-8,1,1],[-7,1,1],[-6,1,1],[-5,0,1],[-4,1,1],[-3,1,1],[-2,1,1],[-1,2,2]],"top":-9,"bottom":0},"|":{"name":"bar","advance":2,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"}":{"name":"braceright","advance":5,"spans":[[-9,0,2],[-8,2,1],[-7,2,1],[-6,2,1],[-5,3,1],[-4,2,1],[-3,2,1],[-2,2,1],[-1,0,2]],"top":-9,"bottom":0},"¡":{"name":"exclamdown","advance":2,"spans":[[-9,0,1],[-8,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"«":{"name":"guillemotleft","advance":11,"spans":[[-10,3,2],[-10,9,1],[-9,3,2],[-9,9,1],[-8,1,2],[-8,7,1],[-7,1,2],[-7,7,1],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,3,2],[-4,9,1],[-3,3,2],[-3,9,1]],"top":-10,"bottom":-2},"­":{"name":"uni00AD","advance":8,"spans":[[-8,1,5],[-7,1,6],[-6,1,6],[-5,1,6]],"top":-8,"bottom":-4},"·":{"name":"periodcentered","advance":4,"spans":[[-6,1,2],[-5,1,2]],"top":-6,"bottom":-4},"»":{"name":"guillemotright","advance":11,"spans":[[-10,1,2],[-10,7,1],[-9,1,2],[-9,7,1],[-8,3,2],[-8,9,1],[-7,3,2],[-7,9,1],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,1,2],[-4,7,1],[-3,1,2],[-3,7,1]],"top":-10,"bottom":-2},"¿":{"name":"questiondown","advance":5,"spans":[[-9,2,1],[-8,2,1],[-6,2,1],[-5,2,1],[-4,1,1],[-3,0,1],[-2,0,1],[-1,1,3]],"top":-9,"bottom":0},"×":{"name":"multiply","advance":6,"spans":[[-7,0,1],[-7,4,1],[-6,1,1],[-6,3,1],[-5,2,1],[-4,1,1],[-4,3,1],[-3,0,1],[-3,4,1]],"top":-7,"bottom":-2},"÷":{"name":"divide","advance":6,"spans":[[-7,2,1],[-5,0,5],[-3,2,1]],"top":-7,"bottom":-2},"،":{"name":"uni060C","advance":6,"spans":[[-4,1,2],[-3,1,2],[-2,1,4],[-1,1,4]],"top":-4,"bottom":0},"ؕ":{"name":"uni0615","advance":0,"spans":[[-15,-4,2],[-14,-4,2],[-13,-4,2],[-12,-4,2],[-11,-2,4],[-10,-2,4],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-15,"bottom":-6},"؛":{"name":"uni061B","advance":4,"spans":[[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-2,1,2],[-1,1,2]],"top":-11,"bottom":0},"؟":{"name":"uni061F","advance":9,"spans":[[-13,3,4],[-12,3,4],[-11,1,7],[-10,1,7],[-9,1,2],[-9,7,1],[-8,1,4],[-7,1,4],[-6,3,4],[-5,3,4],[-4,5,2],[-3,5,2],[-2,5,2],[-1,5,2],[2,5,2],[3,5,2]],"top":-13,"bottom":4},"ء":{"name":"uni0621","advance":9,"spans":[[-10,5,3],[-9,5,3],[-8,3,5],[-7,3,5],[-6,3,2],[-5,3,2],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7]],"top":-10,"bottom":0},"آ":{"name":"uni0622","advance":6,"spans":[[-19,3,2],[-18,3,2],[-17,1,2],[-16,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-19,"bottom":0},"أ":{"name":"uni0623","advance":6,"spans":[[-21,3,2],[-20,3,2],[-19,1,2],[-18,1,2],[-17,1,4],[-16,1,4],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-21,"bottom":0},"ؤ":{"name":"uni0624","advance":9,"spans":[[-17,5,2],[-16,5,2],[-15,3,2],[-14,3,2],[-13,3,4],[-12,3,4],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7],[0,7,1],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-17,"bottom":6},"إ":{"name":"uni0625","advance":6,"spans":[[-15,1,2],[-14,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2],[0,3,2],[1,3,2],[2,1,2],[3,1,2],[4,1,4],[5,1,4]],"top":-15,"bottom":6},"ئ":{"name":"uni0626","advance":15,"spans":[[-11,3,2],[-10,3,2],[-9,1,2],[-9,9,3],[-8,1,4],[-8,7,7],[-7,1,4],[-7,7,7],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,5],[-3,3,2],[-3,7,5],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9]],"top":-11,"bottom":6},"ا":{"name":"uni0627","advance":6,"spans":[[-15,1,2],[-14,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-15,"bottom":0},"ب":{"name":"uni0628","advance":15,"spans":[[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11],[2,9,1],[3,9,1]],"top":-8,"bottom":4},"ة":{"name":"uni0629","advance":9,"spans":[[-13,1,2],[-13,5,2],[-12,1,2],[-12,5,1],[-9,3,4],[-8,2,5],[-7,1,7],[-6,1,2],[-6,6,2],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7]],"top":-13,"bottom":0},"ت":{"name":"uni062A","advance":15,"spans":[[-11,7,1],[-11,10,2],[-10,7,1],[-10,10,2],[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11]],"top":-11,"bottom":0},"ث":{"name":"uni062B","advance":15,"spans":[[-13,9,1],[-12,9,1],[-11,7,1],[-11,10,2],[-10,7,1],[-10,10,2],[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11]],"top":-13,"bottom":0},"ج":{"name":"uni062C","advance":13,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,7],[2,10,2],[3,1,7],[3,10,2],[4,3,5],[5,3,5]],"top":-10,"bottom":6},"ح":{"name":"uni062D","advance":13,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-10,"bottom":6},"خ":{"name":"uni062E","advance":13,"spans":[[-13,5,2],[-12,5,2],[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"د":{"name":"uni062F","advance":8,"spans":[[-10,3,2],[-9,3,2],[-8,3,4],[-7,3,4],[-6,5,2],[-5,5,2],[-4,1,6],[-3,1,6],[-2,1,4],[-1,1,4]],"top":-10,"bottom":0},"ذ":{"name":"uni0630","advance":8,"spans":[[-13,3,2],[-12,3,2],[-9,3,2],[-8,3,4],[-7,3,4],[-6,5,2],[-5,5,2],[-4,1,6],[-3,1,6],[-2,1,4],[-1,1,4]],"top":-13,"bottom":0},"ر":{"name":"uni0631","advance":6,"spans":[[-7,3,2],[-6,3,2],[-5,3,2],[-4,3,2],[-3,3,2],[-2,3,2],[-1,3,2],[0,3,2],[1,3,2],[2,-1,6],[3,-1,6],[4,1,4],[5,1,4]],"top":-7,"bottom":6},"ز":{"name":"uni0632","advance":6,"spans":[[-11,3,2],[-10,3,2],[-7,3,2],[-6,3,2],[-5,3,2],[-4,3,2],[-3,3,2],[-2,3,2],[-1,3,2],[0,3,2],[1,3,2],[2,-1,6],[3,-1,6],[4,1,4],[5,1,4]],"top":-11,"bottom":6},"س":{"name":"uni0633","advance":17,"spans":[[-10,15,1],[-9,14,2],[-8,7,1],[-8,11,1],[-8,14,2],[-7,7,1],[-7,10,2],[-7,14,2],[-6,7,1],[-6,10,2],[-6,14,2],[-5,7,1],[-5,10,2],[-5,14,2],[-4,3,2],[-4,7,9],[-3,3,2],[-3,7,9],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-10,"bottom":6},"ش":{"name":"uni0634","advance":17,"spans":[[-13,10,2],[-12,10,2],[-11,9,1],[-11,12,2],[-10,9,1],[-10,12,2],[-9,14,2],[-8,14,2],[-7,7,1],[-7,10,2],[-7,14,2],[-6,7,1],[-6,10,2],[-6,14,2],[-5,7,1],[-5,10,2],[-5,14,2],[-4,3,2],[-4,7,9],[-3,3,2],[-3,7,9],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-13,"bottom":6},"ص":{"name":"uni0635","advance":21,"spans":[[-9,7,1],[-9,10,10],[-8,7,13],[-7,7,13],[-6,7,2],[-6,18,2],[-5,7,1],[-5,18,2],[-4,3,2],[-4,7,13],[-3,3,2],[-3,7,13],[-2,1,2],[-2,7,11],[-1,1,2],[-1,7,11],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-9,"bottom":6},"ض":{"name":"uni0636","advance":21,"spans":[[-13,12,2],[-12,13,1],[-9,7,1],[-9,10,10],[-8,7,13],[-7,7,13],[-6,7,2],[-6,18,2],[-5,7,1],[-5,18,2],[-4,3,2],[-4,7,13],[-3,3,2],[-3,7,13],[-2,1,2],[-2,7,11],[-1,1,2],[-1,7,11],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-13,"bottom":6},"ط":{"name":"uni0637","advance":17,"spans":[[-15,3,2],[-14,3,2],[-13,3,4],[-12,3,4],[-11,3,2],[-10,3,2],[-9,3,2],[-9,7,9],[-8,3,13],[-7,3,13],[-6,3,2],[-6,14,2],[-5,3,2],[-5,14,2],[-4,1,15],[-3,1,15],[-2,3,11],[-1,3,11]],"top":-15,"bottom":0},"ظ":{"name":"uni0638","advance":17,"spans":[[-15,3,2],[-14,3,2],[-13,3,4],[-13,9,1],[-12,3,4],[-12,9,1],[-11,3,2],[-10,3,2],[-9,3,2],[-9,7,9],[-8,3,13],[-7,3,13],[-6,3,2],[-6,14,2],[-5,3,2],[-5,14,2],[-4,1,15],[-3,1,15],[-2,3,11],[-1,3,11]],"top":-15,"bottom":0},"ع":{"name":"uni0639","advance":13,"spans":[[-10,5,3],[-9,5,3],[-8,5,5],[-7,5,5],[-6,5,2],[-5,5,2],[-4,3,9],[-3,3,9],[-2,1,9],[-1,1,9],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-10,"bottom":6},"غ":{"name":"uni063A","advance":13,"spans":[[-13,5,2],[-12,5,2],[-9,5,3],[-8,5,5],[-7,5,5],[-6,5,2],[-5,5,2],[-4,3,9],[-3,3,9],[-2,1,9],[-1,1,9],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"ـ":{"name":"uni0640","advance":4,"spans":[[-4,0,5],[-3,0,5],[-2,0,5],[-1,0,5]],"top":-4,"bottom":0},"ف":{"name":"uni0641","advance":19,"spans":[[-13,14,2],[-12,14,2],[-9,12,6],[-8,3,2],[-8,10,8],[-7,3,2],[-7,10,8],[-6,1,2],[-6,10,2],[-6,16,2],[-5,1,2],[-5,10,2],[-5,16,2],[-4,1,17],[-3,1,17],[-2,3,15],[-1,3,15]],"top":-13,"bottom":0},"ق":{"name":"uni0642","advance":13,"spans":[[-13,7,1],[-13,10,2],[-12,7,1],[-12,10,2],[-9,7,5],[-8,5,7],[-7,5,7],[-6,5,2],[-6,10,2],[-5,5,2],[-5,10,2],[-4,1,2],[-4,5,7],[-3,1,2],[-3,5,7],[-2,1,2],[-2,5,7],[-1,1,2],[-1,5,7],[0,1,2],[0,10,2],[1,1,2],[1,10,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"ك":{"name":"uni0643","advance":15,"spans":[[-15,12,2],[-14,12,2],[-13,10,4],[-12,10,4],[-11,7,1],[-11,12,2],[-10,7,1],[-10,12,2],[-9,5,2],[-9,12,2],[-8,3,7],[-8,12,2],[-7,3,7],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,9],[-1,3,9]],"top":-15,"bottom":0},"ل":{"name":"uni0644","advance":9,"spans":[[-15,7,1],[-14,7,1],[-13,5,3],[-12,5,3],[-11,7,1],[-10,7,1],[-9,7,1],[-8,7,1],[-7,7,1],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,1],[-3,3,2],[-3,7,1],[-2,1,2],[-2,7,1],[-1,1,2],[-1,7,1],[0,1,2],[0,7,1],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-15,"bottom":6},"م":{"name":"uni0645","advance":11,"spans":[[-10,5,3],[-9,5,3],[-8,5,5],[-7,5,5],[-6,5,2],[-6,9,1],[-5,5,2],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,9],[-1,1,9],[0,1,2],[1,1,2],[2,1,2],[3,1,2],[4,1,2],[5,1,2]],"top":-10,"bottom":6},"ن":{"name":"uni0646","advance":11,"spans":[[-8,5,2],[-7,5,2],[-6,9,1],[-5,9,1],[-4,3,2],[-4,9,1],[-3,3,2],[-3,9,1],[-2,1,2],[-2,9,1],[-1,1,2],[-1,9,1],[0,1,2],[0,9,1],[1,1,2],[1,9,1],[2,1,9],[3,1,9],[4,3,5],[5,3,5]],"top":-8,"bottom":6},"ه":{"name":"uni0647","advance":9,"spans":[[-10,3,3],[-9,3,4],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7]],"top":-10,"bottom":0},"و":{"name":"uni0648","advance":9,"spans":[[-10,3,5],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7],[0,7,1],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-10,"bottom":6},"ى":{"name":"uni0649","advance":15,"spans":[[-10,9,3],[-9,9,3],[-8,7,7],[-7,7,7],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,5],[-3,3,2],[-3,7,5],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9]],"top":-10,"bottom":6},"ي":{"name":"uni064A","advance":15,"spans":[[-10,9,3],[-9,9,3],[-8,7,7],[-7,7,7],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,5],[-3,3,2],[-3,7,5],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9],[8,5,2],[8,9,1],[9,5,2],[9,9,1]],"top":-10,"bottom":10},"ً":{"name":"uni064B","advance":0,"spans":[[-11,-2,4],[-10,-2,4],[-8,-2,4],[-7,-2,4]],"top":-11,"bottom":-6},"ٌ":{"name":"uni064C","advance":0,"spans":[[-13,0,2],[-12,0,2],[-11,-2,4],[-10,-2,4],[-9,0,2],[-8,-2,4],[-7,-2,4]],"top":-13,"bottom":-6},"ٍ":{"name":"uni064D","advance":0,"spans":[[-2,-2,4],[-1,-2,4],[2,-2,4],[3,-2,4]],"top":-2,"bottom":4},"َ":{"name":"uni064E","advance":0,"spans":[[-8,-2,4],[-7,-2,4]],"top":-8,"bottom":-6},"ُ":{"name":"uni064F","advance":0,"spans":[[-10,0,2],[-9,0,2],[-8,-2,4],[-7,-2,4]],"top":-10,"bottom":-6},"ِ":{"name":"uni0650","advance":0,"spans":[[-2,-2,4],[-1,-2,4]],"top":-2,"bottom":0},"ّ":{"name":"uni0651","advance":0,"spans":[[-10,-4,2],[-10,0,2],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-10,"bottom":-6},"ْ":{"name":"uni0652","advance":0,"spans":[[-11,-2,2],[-10,-4,6],[-9,-4,2],[-9,0,2],[-8,-2,2],[-7,-2,2]],"top":-11,"bottom":-6},"ٓ":{"name":"uni0653","advance":0,"spans":[[-10,0,2],[-9,0,2],[-8,-2,2],[-7,-2,2]],"top":-10,"bottom":-6},"ٔ":{"name":"uni0654","advance":0,"spans":[[-11,0,2],[-10,0,2],[-9,-2,2],[-8,-2,4],[-7,-2,4]],"top":-11,"bottom":-6},"ٖ":{"name":"uni0656","advance":0,"spans":[[-2,0,2],[-1,0,2],[0,0,2],[1,0,2],[2,0,2],[3,0,2]],"top":-2,"bottom":4},"ٗ":{"name":"uni0657","advance":0,"spans":[[-10,-2,4],[-9,-2,4],[-8,-2,2],[-7,-2,2]],"top":-10,"bottom":-6},"٘":{"name":"uni0658","advance":0,"spans":[],"top":0,"bottom":0},"٠":{"name":"uni0660","advance":6,"spans":[[-6,1,4],[-5,1,4],[-4,1,4],[-3,1,4]],"top":-6,"bottom":-2},"١":{"name":"uni0661","advance":4,"spans":[[-13,1,2],[-12,1,2],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-13,"bottom":0},"٢":{"name":"uni0662","advance":8,"spans":[[-14,1,1],[-14,5,1],[-13,1,2],[-13,5,2],[-12,1,2],[-12,5,2],[-11,1,2],[-11,5,2],[-10,1,6],[-9,1,6],[-8,1,6],[-7,1,6],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-14,"bottom":0},"٣":{"name":"uni0663","advance":11,"spans":[[-14,1,1],[-14,5,1],[-14,9,1],[-13,1,2],[-13,5,2],[-13,9,1],[-12,1,2],[-12,5,2],[-12,9,1],[-11,1,2],[-11,5,2],[-11,9,1],[-10,1,9],[-9,1,9],[-8,1,9],[-7,1,9],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-14,"bottom":0},"٤":{"name":"uni0664","advance":8,"spans":[[-13,3,4],[-12,3,4],[-11,1,2],[-10,1,2],[-9,3,4],[-8,1,6],[-7,1,6],[-6,1,2],[-5,1,2],[-4,1,5],[-3,1,6],[-2,1,6],[-1,1,6]],"top":-13,"bottom":0},"٥":{"name":"uni0665","advance":8,"spans":[[-13,3,2],[-12,3,2],[-11,1,6],[-10,1,6],[-9,1,2],[-9,5,2],[-8,1,2],[-8,5,2],[-7,1,2],[-7,5,2],[-6,1,2],[-6,5,2],[-5,1,2],[-5,5,2],[-4,1,6],[-3,1,6],[-2,1,6],[-1,1,6]],"top":-13,"bottom":0},"٦":{"name":"uni0666","advance":8,"spans":[[-13,1,6],[-12,1,6],[-11,1,6],[-10,1,6],[-9,5,2],[-8,5,2],[-7,5,2],[-6,5,2],[-5,5,2],[-4,5,2],[-3,5,2],[-2,5,2],[-1,5,2]],"top":-13,"bottom":0},"٧":{"name":"uni0667","advance":8,"spans":[[-13,1,2],[-13,5,2],[-12,1,2],[-12,5,2],[-11,1,2],[-11,5,2],[-10,1,2],[-10,5,2],[-9,1,2],[-9,5,2],[-8,1,2],[-8,5,2],[-7,1,2],[-7,5,2],[-6,1,6],[-5,1,6],[-4,1,6],[-3,1,6],[-2,3,2],[-1,3,2]],"top":-13,"bottom":0},"٨":{"name":"uni0668","advance":8,"spans":[[-13,3,2],[-12,3,2],[-11,1,6],[-10,1,6],[-9,1,6],[-8,1,6],[-7,1,2],[-7,5,2],[-6,1,2],[-6,5,2],[-5,1,2],[-5,5,2],[-4,1,2],[-4,5,2],[-3,1,2],[-3,5,2],[-2,1,2],[-2,5,2],[-1,1,2],[-1,5,2]],"top":-13,"bottom":0},"٩":{"name":"uni0669","advance":9,"spans":[[-13,3,5],[-12,3,5],[-11,1,2],[-11,7,1],[-10,1,7],[-9,1,7],[-8,1,7],[-7,1,7],[-6,7,1],[-5,7,1],[-4,7,1],[-3,7,1],[-2,7,1],[-1,7,1]],"top":-13,"bottom":0},"٪":{"name":"uni066A","advance":11,"spans":[[-13,7,1],[-12,7,1],[-11,7,1],[-10,1,2],[-10,5,3],[-9,1,2],[-9,5,2],[-8,5,2],[-7,5,2],[-6,5,2],[-6,9,1],[-5,5,2],[-5,8,2],[-4,3,2],[-3,3,2],[-2,3,2],[-1,3,2]],"top":-13,"bottom":0},"٫":{"name":"uni066B","advance":4,"spans":[[-2,1,2],[-1,1,2],[0,1,2],[1,1,2]],"top":-2,"bottom":2},"٬":{"name":"uni066C","advance":4,"spans":[[-2,1,2],[-1,1,2],[0,1,2],[1,1,2]],"top":-2,"bottom":2},"٭":{"name":"uni066D","advance":11,"spans":[[-10,1,2],[-10,5,2],[-10,9,1],[-9,1,2],[-9,5,2],[-9,9,1],[-8,3,5],[-7,3,5],[-6,5,2],[-5,5,2],[-4,3,5],[-3,3,5],[-2,1,2],[-2,5,2],[-2,9,1],[-1,1,2],[-1,5,2],[-1,9,1]],"top":-10,"bottom":0},"ٰ":{"name":"uni0670","advance":0,"spans":[[-12,0,2],[-11,0,2],[-10,0,2],[-9,0,2],[-8,0,2],[-7,0,2]],"top":-12,"bottom":-6},"پ":{"name":"uni067E","advance":15,"spans":[[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11],[2,7,1],[2,10,2],[3,7,1],[3,10,2],[4,9,1],[5,9,1]],"top":-8,"bottom":6},"چ":{"name":"uni0686","advance":13,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,4],[2,7,1],[2,10,2],[3,1,4],[3,7,1],[3,10,2],[4,3,4],[4,9,1],[5,3,4],[5,9,1]],"top":-10,"bottom":6},"ڤ":{"name":"uni06A4","advance":19,"spans":[[-15,14,2],[-14,14,2],[-13,12,2],[-13,16,2],[-12,12,2],[-12,16,2],[-9,12,6],[-8,3,2],[-8,10,8],[-7,3,2],[-7,10,8],[-6,1,2],[-6,10,2],[-6,16,2],[-5,1,2],[-5,10,2],[-5,16,2],[-4,1,17],[-3,1,17],[-2,3,15],[-1,3,15]],"top":-15,"bottom":0},"۔":{"name":"uni06D4","advance":4,"spans":[[-2,1,2],[-1,1,2]],"top":-2,"bottom":0},"۰":{"name":"uni06F0","advance":6,"spans":[],"top":0,"bottom":0},"۱":{"name":"uni06F1","advance":7,"spans":[],"top":0,"bottom":0},"۲":{"name":"uni06F2","advance":13,"spans":[],"top":0,"bottom":0},"۳":{"name":"uni06F3","advance":14,"spans":[],"top":0,"bottom":0},"۴":{"name":"uni06F4","advance":8,"spans":[],"top":0,"bottom":0},"۵":{"name":"uni06F5","advance":14,"spans":[],"top":0,"bottom":0},"۶":{"name":"uni06F6","advance":13,"spans":[],"top":0,"bottom":0},"۷":{"name":"uni06F7","advance":11,"spans":[],"top":0,"bottom":0},"۸":{"name":"uni06F8","advance":13,"spans":[],"top":0,"bottom":0},"۹":{"name":"uni06F9","advance":13,"spans":[],"top":0,"bottom":0},"–":{"name":"endash","advance":9,"spans":[[-8,1,7],[-7,1,7],[-6,1,7],[-5,1,7]],"top":-8,"bottom":-4},"—":{"name":"emdash","advance":11,"spans":[[-8,1,9],[-7,1,9],[-6,1,9],[-5,1,9]],"top":-8,"bottom":-4},"‘":{"name":"quoteleft","advance":3,"spans":[[-9,1,1],[-8,0,1]],"top":-9,"bottom":-7},"’":{"name":"quoteright","advance":3,"spans":[[-9,1,1],[-8,0,1]],"top":-9,"bottom":-7},"‚":{"name":"quotesinglbase","advance":4,"spans":[[-2,1,2],[-1,1,2],[0,1,2],[1,1,2]],"top":-2,"bottom":2},"“":{"name":"quotedblleft","advance":5,"spans":[[-9,1,1],[-9,3,1],[-8,0,1],[-8,2,1]],"top":-9,"bottom":-7},"”":{"name":"quotedblright","advance":5,"spans":[[-9,1,1],[-9,3,1],[-8,0,1],[-8,2,1]],"top":-9,"bottom":-7},"„":{"name":"quotedblbase","advance":8,"spans":[[-2,1,2],[-2,5,2],[-1,1,2],[-1,5,2],[0,1,2],[0,5,2],[1,1,2],[1,5,1]],"top":-2,"bottom":2},"•":{"name":"bullet","advance":5,"spans":[[-6,1,2],[-5,0,4],[-4,0,4],[-3,1,2]],"top":-6,"bottom":-2},"…":{"name":"ellipsis","advance":6,"spans":[[-1,0,1],[-1,2,1],[-1,4,1]],"top":-1,"bottom":0},"‰":{"name":"perthousand","advance":19,"spans":[[-13,3,2],[-13,9,1],[-12,3,2],[-12,8,2],[-11,1,2],[-11,5,2],[-11,8,2],[-10,1,9],[-9,3,2],[-9,7,1],[-8,7,1],[-7,7,1],[-6,7,1],[-6,10,2],[-6,14,2],[-5,7,1],[-5,10,2],[-5,14,2],[-4,5,2],[-4,9,1],[-4,12,2],[-4,16,2],[-3,5,2],[-3,8,2],[-3,12,2],[-3,16,2],[-2,5,2],[-2,10,2],[-2,14,2],[-1,5,2],[-1,10,2],[-1,14,2]],"top":-13,"bottom":0},"‹":{"name":"guilsinglleft","advance":6,"spans":[[-10,3,2],[-9,3,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,3,2],[-3,3,2]],"top":-10,"bottom":-2},"›":{"name":"guilsinglright","advance":6,"spans":[[-10,1,2],[-9,1,2],[-8,3,2],[-7,3,2],[-6,3,2],[-5,3,2],[-4,1,2],[-3,1,2]],"top":-10,"bottom":-2},"−":{"name":"minus","advance":10,"spans":[[-8,1,7],[-7,1,7],[-6,1,7],[-5,1,7]],"top":-8,"bottom":-4},"≠":{"name":"notequal","advance":11,"spans":[[-12,7,1],[-11,7,1],[-10,1,9],[-9,1,9],[-8,1,9],[-7,1,9],[-6,5,2],[-5,5,2],[-4,1,9],[-3,1,9],[-2,1,9],[-1,1,9],[0,3,2],[1,3,2]],"top":-12,"bottom":2},"ﭗ":{"name":"uniFB57","advance":14,"spans":[[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,14],[-3,1,14],[-2,3,12],[-1,3,12],[2,7,1],[2,10,2],[3,7,1],[3,10,2],[4,9,1],[5,9,1]],"top":-8,"bottom":6},"ﭘ":{"name":"uniFB58","advance":9,"spans":[[-8,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,8],[-3,0,8],[-2,0,8],[-1,0,8],[2,2,2],[2,6,2],[3,2,2],[3,6,1],[4,4,2],[5,4,2]],"top":-8,"bottom":6},"ﭙ":{"name":"uniFB59","advance":8,"spans":[[-8,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9],[2,2,2],[2,6,2],[3,2,2],[3,6,1],[4,4,2],[5,4,2]],"top":-8,"bottom":6},"ﭫ":{"name":"uniFB6B","advance":18,"spans":[[-15,14,2],[-14,14,2],[-13,12,2],[-13,16,2],[-12,12,2],[-12,16,2],[-9,12,6],[-8,3,2],[-8,10,8],[-7,3,2],[-7,10,8],[-6,1,2],[-6,10,2],[-6,16,2],[-5,1,2],[-5,10,2],[-5,16,2],[-4,1,18],[-3,1,18],[-2,3,16],[-1,3,16]],"top":-15,"bottom":0},"ﭬ":{"name":"uniFB6C","advance":10,"spans":[[-15,6,2],[-14,6,1],[-13,4,2],[-13,8,1],[-12,4,2],[-12,8,1],[-9,4,5],[-8,2,7],[-7,2,7],[-6,2,2],[-6,8,1],[-5,2,2],[-5,8,1],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9]],"top":-15,"bottom":0},"ﭭ":{"name":"uniFB6D","advance":9,"spans":[[-15,6,2],[-14,6,1],[-13,4,2],[-13,8,1],[-12,4,2],[-12,8,1],[-9,4,5],[-8,2,7],[-7,2,7],[-6,2,2],[-6,8,1],[-5,2,2],[-5,8,1],[-4,0,11],[-3,0,11],[-2,0,11],[-1,0,11]],"top":-15,"bottom":0},"ﭻ":{"name":"uniFB7B","advance":12,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,11],[-3,3,11],[-2,1,7],[-2,10,4],[-1,1,7],[-1,10,4],[0,1,2],[1,1,2],[2,1,4],[2,7,1],[2,10,2],[3,1,4],[3,7,1],[3,10,2],[4,3,4],[4,9,1],[5,3,4],[5,9,1]],"top":-10,"bottom":6},"ﭼ":{"name":"uniFB7C","advance":14,"spans":[[-10,4,5],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,11],[-3,0,11],[-2,0,9],[-1,0,9],[2,4,2],[2,8,1],[3,4,2],[3,8,1],[4,6,2],[5,6,1]],"top":-10,"bottom":6},"ﭽ":{"name":"uniFB7D","advance":13,"spans":[[-10,4,5],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,15],[-3,0,15],[-2,0,9],[-2,11,4],[-1,0,9],[-1,11,4],[2,4,2],[2,8,1],[3,4,2],[3,8,1],[4,6,2],[5,6,1]],"top":-10,"bottom":6},"ﰉ":{"name":"uniFC09","advance":19,"spans":[[-8,16,2],[-7,16,2],[-6,7,1],[-6,16,2],[-5,7,1],[-5,16,2],[-4,3,2],[-4,7,11],[-3,3,2],[-3,7,11],[-2,1,2],[-2,9,9],[-1,1,2],[-1,9,9],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[3,1,13],[3,16,2],[4,3,9],[5,3,9]],"top":-8,"bottom":6},"ﰊ":{"name":"uniFC0A","advance":19,"spans":[[-8,16,2],[-7,16,2],[-6,7,1],[-6,16,2],[-5,7,1],[-5,16,2],[-4,3,2],[-4,7,11],[-3,3,2],[-3,7,11],[-2,1,2],[-2,9,9],[-1,1,2],[-1,9,9],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[3,1,13],[3,16,2],[4,3,9],[5,3,9],[8,5,2],[8,9,1],[9,5,2],[9,9,1]],"top":-8,"bottom":10},"ﱙ":{"name":"uniFC59","advance":23,"spans":[[-8,20,1],[-7,20,2],[-6,7,1],[-6,20,2],[-5,7,1],[-5,20,2],[-4,3,2],[-4,7,15],[-3,3,2],[-3,7,15],[-2,1,2],[-2,9,13],[-1,1,2],[-1,9,13],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[2,20,2],[3,1,13],[3,16,2],[3,20,2],[4,3,9],[5,3,9]],"top":-8,"bottom":6},"ﱚ":{"name":"uniFC5A","advance":23,"spans":[[-8,20,1],[-7,20,2],[-6,7,1],[-6,20,2],[-5,7,1],[-5,20,2],[-4,3,2],[-4,7,15],[-3,3,2],[-3,7,15],[-2,1,2],[-2,9,13],[-1,1,2],[-1,9,13],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[2,20,2],[3,1,13],[3,16,2],[3,20,2],[4,3,9],[5,3,9],[8,5,2],[8,9,1],[9,5,2],[9,9,1]],"top":-8,"bottom":10},"ﱮ":{"name":"uniFC6E","advance":18,"spans":[[-8,16,2],[-7,16,2],[-6,7,1],[-6,16,2],[-5,7,1],[-5,16,2],[-4,3,2],[-4,7,12],[-3,3,2],[-3,7,12],[-2,1,2],[-2,9,10],[-1,1,2],[-1,9,10],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[3,1,13],[3,16,2],[4,3,9],[5,3,9]],"top":-8,"bottom":6},"ﱯ":{"name":"uniFC6F","advance":18,"spans":[[-8,16,2],[-7,16,2],[-6,7,1],[-6,16,2],[-5,7,1],[-5,16,2],[-4,3,2],[-4,7,12],[-3,3,2],[-3,7,12],[-2,1,2],[-2,9,10],[-1,1,2],[-1,9,10],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[3,1,13],[3,16,2],[4,3,9],[5,3,9],[8,5,2],[8,9,1],[9,5,2],[9,9,1]],"top":-8,"bottom":10},"ﲕ":{"name":"uniFC95","advance":22,"spans":[[-8,20,1],[-7,20,2],[-6,7,1],[-6,20,2],[-5,7,1],[-5,20,2],[-4,3,2],[-4,7,16],[-3,3,2],[-3,7,16],[-2,1,2],[-2,9,14],[-1,1,2],[-1,9,14],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[2,20,2],[3,1,13],[3,16,2],[3,20,2],[4,3,9],[5,3,9]],"top":-8,"bottom":6},"ﲖ":{"name":"uniFC96","advance":22,"spans":[[-8,20,1],[-7,20,2],[-6,7,1],[-6,20,2],[-5,7,1],[-5,20,2],[-4,3,2],[-4,7,16],[-3,3,2],[-3,7,16],[-2,1,2],[-2,9,14],[-1,1,2],[-1,9,14],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[2,16,2],[2,20,2],[3,1,13],[3,16,2],[3,20,2],[4,3,9],[5,3,9],[8,5,2],[8,9,1],[9,5,2],[9,9,1]],"top":-8,"bottom":10},"﴾":{"name":"uniFD3E","advance":13,"spans":[[-15,9,3],[-14,9,3],[-13,7,5],[-12,7,5],[-11,7,1],[-10,7,1],[-9,7,1],[-8,3,2],[-8,7,1],[-7,3,2],[-7,7,1],[-6,1,2],[-6,5,2],[-5,1,2],[-5,5,2],[-4,3,2],[-4,7,1],[-3,3,2],[-3,7,1],[-2,7,1],[-1,7,1],[0,7,1],[1,7,1],[2,7,5],[3,7,5],[4,9,3],[5,9,3]],"top":-15,"bottom":6},"﴿":{"name":"uniFD3F","advance":13,"spans":[[-15,1,4],[-14,1,4],[-13,1,6],[-12,1,6],[-11,5,2],[-10,5,2],[-9,5,2],[-8,5,2],[-8,9,1],[-7,5,2],[-7,9,1],[-6,7,1],[-6,10,2],[-5,7,1],[-5,10,2],[-4,5,2],[-4,9,1],[-3,5,2],[-3,9,1],[-2,5,2],[-1,5,2],[0,5,2],[1,5,2],[2,1,6],[3,1,6],[4,1,4],[5,1,4]],"top":-15,"bottom":6},"ﷲ":{"name":"uniFDF2","advance":27,"spans":[[-28,12,2],[-27,12,2],[-26,12,2],[-25,12,2],[-24,12,2],[-21,11,1],[-21,14,2],[-20,10,2],[-20,14,2],[-19,10,6],[-18,10,6],[-15,12,2],[-15,18,2],[-15,22,2],[-14,12,2],[-14,18,2],[-14,22,2],[-13,10,4],[-13,16,4],[-13,22,4],[-12,10,4],[-12,16,4],[-12,22,3],[-11,7,1],[-11,12,2],[-11,18,2],[-11,22,2],[-10,6,2],[-10,12,2],[-10,18,2],[-10,22,2],[-9,3,5],[-9,12,2],[-9,18,2],[-9,22,2],[-8,1,7],[-8,12,2],[-8,18,2],[-8,22,2],[-7,1,7],[-7,12,2],[-7,18,2],[-7,22,2],[-6,1,2],[-6,7,1],[-6,12,2],[-6,18,2],[-6,22,2],[-5,1,2],[-5,7,1],[-5,12,2],[-5,18,2],[-5,22,2],[-4,1,19],[-4,22,2],[-3,1,19],[-3,22,2],[-2,3,4],[-2,9,11],[-2,22,2],[-1,3,4],[-1,9,11],[-1,22,2]],"top":-28,"bottom":0},"ﺂ":{"name":"uniFE82","advance":5,"spans":[[-19,3,2],[-18,3,2],[-17,1,2],[-16,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,5],[-3,1,5],[-2,1,5],[-1,1,5]],"top":-19,"bottom":0},"ﺄ":{"name":"uniFE84","advance":5,"spans":[[-21,3,2],[-20,3,2],[-19,1,2],[-18,1,2],[-17,1,4],[-16,1,4],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,5],[-3,1,5],[-2,1,5],[-1,1,5]],"top":-21,"bottom":0},"ﺆ":{"name":"uniFE86","advance":9,"spans":[[-17,5,2],[-16,5,2],[-15,3,2],[-14,3,2],[-13,3,4],[-12,3,4],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,9],[-3,1,9],[-2,1,9],[-1,1,9],[0,7,1],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-17,"bottom":6},"ﺈ":{"name":"uniFE88","advance":5,"spans":[[-15,1,2],[-14,1,2],[-13,1,4],[-12,1,2],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,5],[-3,1,5],[-2,1,5],[-1,1,5],[0,3,2],[1,3,2],[2,1,2],[3,1,2],[4,1,4]],"top":-15,"bottom":5},"ﺊ":{"name":"uniFE8A","advance":12,"spans":[[-11,3,2],[-10,3,2],[-9,1,2],[-8,1,4],[-7,1,4],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,7],[-3,3,2],[-3,7,7],[-2,1,2],[-2,9,5],[-1,1,2],[-1,9,5],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9]],"top":-11,"bottom":6},"ﺋ":{"name":"uniFE8B","advance":7,"spans":[[-15,4,2],[-14,4,2],[-13,2,2],[-12,2,2],[-11,2,4],[-10,2,4],[-7,4,2],[-6,4,2],[-5,4,2],[-4,0,6],[-3,0,6],[-2,0,6],[-1,0,6]],"top":-15,"bottom":0},"ﺌ":{"name":"uniFE8C","advance":6,"spans":[[-15,4,2],[-14,4,2],[-13,2,2],[-12,2,2],[-11,2,4],[-10,2,4],[-7,4,2],[-6,4,2],[-5,4,2],[-4,0,7],[-3,0,7],[-2,0,7],[-1,0,7]],"top":-15,"bottom":0},"ﺎ":{"name":"uniFE8E","advance":5,"spans":[[-15,1,2],[-14,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,5],[-3,1,5],[-2,1,5],[-1,1,5]],"top":-15,"bottom":0},"ﺐ":{"name":"uniFE90","advance":14,"spans":[[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,14],[-3,1,14],[-2,3,12],[-1,3,12],[2,9,1],[3,9,1]],"top":-8,"bottom":4},"ﺑ":{"name":"uniFE91","advance":5,"spans":[[-8,2,1],[-7,2,2],[-6,2,2],[-5,2,2],[-4,0,4],[-3,0,4],[-2,0,4],[-1,0,4],[2,2,2],[3,2,2]],"top":-8,"bottom":4},"ﺒ":{"name":"uniFE92","advance":4,"spans":[[-8,2,1],[-7,2,2],[-6,2,2],[-5,2,2],[-4,0,5],[-3,0,5],[-2,0,5],[-1,0,5],[2,2,2],[3,2,2]],"top":-8,"bottom":4},"ﺔ":{"name":"uniFE94","advance":9,"spans":[[-13,1,2],[-13,5,2],[-12,1,2],[-12,5,2],[-11,7,1],[-10,6,2],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,9],[-3,1,9],[-2,3,4],[-1,3,4]],"top":-13,"bottom":0},"ﺖ":{"name":"uniFE96","advance":14,"spans":[[-11,7,1],[-11,10,2],[-10,7,1],[-10,10,2],[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,14],[-3,1,14],[-2,3,12],[-1,3,12]],"top":-11,"bottom":0},"ﺗ":{"name":"uniFE97","advance":9,"spans":[[-11,2,2],[-11,6,2],[-10,2,2],[-10,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,8],[-3,0,8],[-2,0,8],[-1,0,8]],"top":-11,"bottom":0},"ﺘ":{"name":"uniFE98","advance":8,"spans":[[-11,2,2],[-11,6,2],[-10,2,2],[-10,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9]],"top":-11,"bottom":0},"ﺚ":{"name":"uniFE9A","advance":14,"spans":[[-13,9,1],[-12,9,1],[-11,7,1],[-11,10,2],[-10,7,1],[-10,10,2],[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,14],[-3,1,14],[-2,3,12],[-1,3,12]],"top":-13,"bottom":0},"ﺛ":{"name":"uniFE9B","advance":9,"spans":[[-13,4,2],[-12,4,2],[-11,2,2],[-11,6,2],[-10,2,2],[-10,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,8],[-3,0,8],[-2,0,8],[-1,0,8]],"top":-13,"bottom":0},"ﺜ":{"name":"uniFE9C","advance":8,"spans":[[-13,4,2],[-12,4,2],[-11,2,2],[-11,6,2],[-10,2,2],[-10,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9]],"top":-13,"bottom":0},"ﺞ":{"name":"uniFE9E","advance":12,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,11],[-3,3,11],[-2,1,7],[-2,10,4],[-1,1,7],[-1,10,4],[0,1,2],[1,1,2],[2,1,7],[2,10,2],[3,1,7],[3,10,2],[4,3,5],[5,3,5]],"top":-10,"bottom":6},"ﺟ":{"name":"uniFE9F","advance":14,"spans":[[-10,4,5],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,11],[-3,0,11],[-2,0,9],[-1,0,9],[2,6,2],[3,6,1]],"top":-10,"bottom":4},"ﺠ":{"name":"uniFEA0","advance":13,"spans":[[-10,4,5],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,15],[-3,0,15],[-2,0,9],[-2,11,4],[-1,0,9],[-1,11,4],[2,6,2],[3,6,1]],"top":-10,"bottom":4},"ﺢ":{"name":"uniFEA2","advance":12,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,11],[-3,3,11],[-2,1,7],[-2,10,4],[-1,1,7],[-1,10,4],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-10,"bottom":6},"ﺣ":{"name":"uniFEA3","advance":14,"spans":[[-10,4,5],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,11],[-3,0,11],[-2,0,9],[-1,0,9]],"top":-10,"bottom":0},"ﺤ":{"name":"uniFEA4","advance":13,"spans":[[-10,4,5],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,15],[-3,0,15],[-2,0,9],[-2,11,4],[-1,0,9],[-1,11,4]],"top":-10,"bottom":0},"ﺦ":{"name":"uniFEA6","advance":12,"spans":[[-13,5,2],[-12,5,2],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,11],[-3,3,11],[-2,1,7],[-2,10,4],[-1,1,7],[-1,10,4],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"ﺧ":{"name":"uniFEA7","advance":14,"spans":[[-13,6,2],[-12,6,1],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,11],[-3,0,11],[-2,0,9],[-1,0,9]],"top":-13,"bottom":0},"ﺨ":{"name":"uniFEA8","advance":13,"spans":[[-13,6,2],[-12,6,1],[-9,4,5],[-8,2,11],[-7,2,11],[-6,9,2],[-5,9,2],[-4,0,15],[-3,0,15],[-2,0,9],[-2,11,4],[-1,0,9],[-1,11,4]],"top":-13,"bottom":0},"ﺪ":{"name":"uniFEAA","advance":7,"spans":[[-10,3,2],[-9,3,2],[-8,3,4],[-7,3,4],[-6,5,2],[-5,5,2],[-4,1,7],[-3,1,7],[-2,1,4],[-1,1,4]],"top":-10,"bottom":0},"ﺬ":{"name":"uniFEAC","advance":7,"spans":[[-13,3,2],[-12,3,2],[-9,3,2],[-8,3,4],[-7,3,4],[-6,5,2],[-5,5,2],[-4,1,7],[-3,1,7],[-2,1,4],[-1,1,4]],"top":-13,"bottom":0},"ﺮ":{"name":"uniFEAE","advance":5,"spans":[[-8,3,1],[-7,3,2],[-6,3,2],[-5,3,2],[-4,3,3],[-3,3,3],[-2,3,3],[-1,3,3],[0,3,2],[1,3,2],[2,-1,6],[3,-1,6],[4,1,4],[5,1,4]],"top":-8,"bottom":6},"ﺰ":{"name":"uniFEB0","advance":5,"spans":[[-11,3,2],[-10,3,2],[-7,3,2],[-6,3,2],[-5,3,2],[-4,3,3],[-3,3,3],[-2,3,3],[-1,3,3],[0,3,2],[1,3,2],[2,-1,6],[3,-1,6],[4,1,4],[5,1,4]],"top":-11,"bottom":6},"ﺲ":{"name":"uniFEB2","advance":16,"spans":[[-10,15,1],[-9,14,2],[-8,7,1],[-8,11,1],[-8,14,2],[-7,7,1],[-7,10,2],[-7,14,2],[-6,7,1],[-6,10,2],[-6,14,2],[-5,7,1],[-5,10,2],[-5,14,2],[-4,3,2],[-4,7,10],[-3,3,2],[-3,7,10],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-10,"bottom":6},"ﺳ":{"name":"uniFEB3","advance":12,"spans":[[-10,10,1],[-9,9,2],[-8,2,2],[-8,6,1],[-8,9,2],[-7,2,2],[-7,6,2],[-7,9,2],[-6,2,2],[-6,6,2],[-6,9,2],[-5,2,2],[-5,6,2],[-5,9,2],[-4,0,11],[-3,0,11],[-2,0,9],[-1,0,9]],"top":-10,"bottom":0},"ﺴ":{"name":"uniFEB4","advance":11,"spans":[[-10,10,1],[-9,9,2],[-8,2,2],[-8,6,1],[-8,9,2],[-7,2,2],[-7,6,2],[-7,9,2],[-6,2,2],[-6,6,2],[-6,9,2],[-5,2,2],[-5,6,2],[-5,9,2],[-4,0,13],[-3,0,13],[-2,0,9],[-1,0,9]],"top":-10,"bottom":0},"ﺶ":{"name":"uniFEB6","advance":16,"spans":[[-13,10,2],[-12,10,2],[-11,9,1],[-11,12,2],[-10,9,1],[-10,12,2],[-9,14,2],[-8,14,2],[-7,7,1],[-7,10,2],[-7,14,2],[-6,7,1],[-6,10,2],[-6,14,2],[-5,7,1],[-5,10,2],[-5,14,2],[-4,3,2],[-4,7,10],[-3,3,2],[-3,7,10],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-13,"bottom":6},"ﺷ":{"name":"uniFEB7","advance":12,"spans":[[-13,6,2],[-12,6,2],[-11,4,2],[-11,8,1],[-10,9,2],[-9,9,2],[-8,2,2],[-8,6,1],[-8,9,2],[-7,2,2],[-7,6,2],[-7,9,2],[-6,2,2],[-6,6,2],[-6,9,2],[-5,2,2],[-5,6,2],[-5,9,2],[-4,0,11],[-3,0,11],[-2,0,9],[-1,0,9]],"top":-13,"bottom":0},"ﺸ":{"name":"uniFEB8","advance":11,"spans":[[-13,6,2],[-12,6,1],[-11,4,2],[-11,8,1],[-10,4,2],[-10,8,1],[-9,9,2],[-8,9,2],[-7,2,2],[-7,6,2],[-7,9,2],[-6,2,2],[-6,6,2],[-6,9,2],[-5,2,2],[-5,6,2],[-5,9,2],[-4,0,13],[-3,0,13],[-2,0,9],[-1,0,9]],"top":-13,"bottom":0},"ﺺ":{"name":"uniFEBA","advance":20,"spans":[[-9,7,1],[-9,10,10],[-8,7,13],[-7,7,13],[-6,7,2],[-6,18,2],[-5,7,1],[-5,18,2],[-4,3,2],[-4,7,14],[-3,3,2],[-3,7,14],[-2,1,2],[-2,7,11],[-1,1,2],[-1,7,11],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-9,"bottom":6},"ﺻ":{"name":"uniFEBB","advance":16,"spans":[[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,15],[-3,0,15],[-2,0,2],[-2,4,9],[-1,0,2],[-1,4,9]],"top":-9,"bottom":0},"ﺼ":{"name":"uniFEBC","advance":15,"spans":[[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,16],[-3,0,16],[-2,0,2],[-2,4,9],[-1,0,2],[-1,4,9]],"top":-9,"bottom":0},"ﺾ":{"name":"uniFEBE","advance":20,"spans":[[-13,12,2],[-12,13,1],[-9,7,1],[-9,10,10],[-8,7,13],[-7,7,13],[-6,7,2],[-6,18,2],[-5,7,1],[-5,18,2],[-4,3,2],[-4,7,14],[-3,3,2],[-3,7,14],[-2,1,2],[-2,7,11],[-1,1,2],[-1,7,11],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-13,"bottom":6},"ﺿ":{"name":"uniFEBF","advance":16,"spans":[[-13,8,1],[-12,8,1],[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,15],[-3,0,15],[-2,0,2],[-2,4,9],[-1,0,2],[-1,4,9]],"top":-13,"bottom":0},"ﻀ":{"name":"uniFEC0","advance":15,"spans":[[-13,8,1],[-12,8,1],[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,16],[-3,0,16],[-2,0,2],[-2,4,9],[-1,0,2],[-1,4,9]],"top":-13,"bottom":0},"ﻂ":{"name":"uniFEC2","advance":16,"spans":[[-15,3,2],[-14,3,2],[-13,3,4],[-12,3,4],[-11,3,2],[-10,3,2],[-9,3,2],[-9,7,9],[-8,3,13],[-7,3,13],[-6,3,2],[-6,14,2],[-5,3,2],[-5,14,2],[-4,1,16],[-3,1,16],[-2,3,11],[-1,3,11]],"top":-15,"bottom":0},"ﻃ":{"name":"uniFEC3","advance":16,"spans":[[-15,2,2],[-14,2,2],[-13,2,4],[-12,2,4],[-11,2,2],[-10,2,2],[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,15],[-3,0,15],[-2,0,13],[-1,0,13]],"top":-15,"bottom":0},"ﻄ":{"name":"uniFEC4","advance":15,"spans":[[-15,2,2],[-14,2,2],[-13,2,4],[-12,2,4],[-11,2,2],[-10,2,2],[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,16],[-3,0,16],[-2,0,13],[-1,0,13]],"top":-15,"bottom":0},"ﻆ":{"name":"uniFEC6","advance":16,"spans":[[-15,3,2],[-14,3,2],[-13,3,4],[-13,9,1],[-12,3,4],[-12,9,1],[-11,3,2],[-10,3,2],[-9,3,2],[-9,7,9],[-8,3,13],[-7,3,13],[-6,3,2],[-6,14,2],[-5,3,2],[-5,14,2],[-4,1,16],[-3,1,16],[-2,3,11],[-1,3,11]],"top":-15,"bottom":0},"ﻇ":{"name":"uniFEC7","advance":16,"spans":[[-15,2,2],[-14,2,2],[-13,2,4],[-13,8,1],[-12,2,4],[-12,8,1],[-11,2,2],[-10,2,2],[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,15],[-3,0,15],[-2,0,13],[-1,0,13]],"top":-15,"bottom":0},"ﻈ":{"name":"uniFEC8","advance":15,"spans":[[-15,2,2],[-14,2,2],[-13,2,4],[-13,8,1],[-12,2,4],[-12,8,1],[-11,2,2],[-10,2,2],[-9,2,2],[-9,6,9],[-8,2,13],[-7,2,13],[-6,2,2],[-6,13,2],[-5,2,2],[-5,13,2],[-4,0,16],[-3,0,16],[-2,0,13],[-1,0,13]],"top":-15,"bottom":0},"ﻊ":{"name":"uniFECA","advance":10,"spans":[[-10,3,7],[-9,3,7],[-8,5,5],[-7,5,5],[-6,7,2],[-5,7,1],[-4,3,9],[-3,3,9],[-2,1,6],[-2,9,3],[-1,1,6],[-1,9,3],[0,1,2],[1,1,2],[2,1,9],[3,1,9],[4,3,5],[5,3,5]],"top":-10,"bottom":6},"ﻋ":{"name":"uniFECB","advance":10,"spans":[[-10,2,4],[-9,2,4],[-8,2,5],[-7,2,6],[-6,2,2],[-5,2,2],[-4,0,9],[-3,0,9],[-2,0,8],[-1,0,7]],"top":-10,"bottom":0},"ﻌ":{"name":"uniFECC","advance":9,"spans":[[-10,2,7],[-9,2,7],[-8,4,5],[-7,4,5],[-6,6,2],[-5,6,2],[-4,0,11],[-3,0,11],[-2,0,6],[-2,8,3],[-1,0,6],[-1,8,3]],"top":-10,"bottom":0},"ﻎ":{"name":"uniFECE","advance":10,"spans":[[-13,7,1],[-12,7,1],[-10,3,7],[-9,3,7],[-8,5,5],[-7,5,5],[-6,7,2],[-5,7,1],[-4,3,9],[-3,3,9],[-2,1,6],[-2,9,3],[-1,1,6],[-1,9,3],[0,1,2],[1,1,2],[2,1,9],[3,1,9],[4,3,5],[5,3,5]],"top":-13,"bottom":6},"ﻏ":{"name":"uniFECF","advance":10,"spans":[[-13,2,2],[-12,2,2],[-9,2,4],[-8,2,5],[-7,2,6],[-6,2,2],[-5,2,2],[-4,0,9],[-3,0,9],[-2,0,8],[-1,0,7]],"top":-13,"bottom":0},"ﻐ":{"name":"uniFED0","advance":9,"spans":[[-13,6,2],[-12,6,1],[-10,2,7],[-9,2,7],[-8,4,5],[-7,4,5],[-6,6,2],[-5,6,2],[-4,0,11],[-3,0,11],[-2,0,6],[-2,8,3],[-1,0,6],[-1,8,3]],"top":-13,"bottom":0},"ﻒ":{"name":"uniFED2","advance":18,"spans":[[-13,14,2],[-12,14,2],[-9,12,6],[-8,3,2],[-8,10,8],[-7,3,2],[-7,10,8],[-6,1,2],[-6,10,2],[-6,16,2],[-5,1,2],[-5,10,2],[-5,16,2],[-4,1,18],[-3,1,18],[-2,3,16],[-1,3,16]],"top":-13,"bottom":0},"ﻓ":{"name":"uniFED3","advance":10,"spans":[[-13,6,2],[-12,6,1],[-9,4,5],[-8,2,7],[-7,2,7],[-6,2,2],[-6,8,1],[-5,2,2],[-5,8,1],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9]],"top":-13,"bottom":0},"ﻔ":{"name":"uniFED4","advance":9,"spans":[[-13,6,2],[-12,6,1],[-9,4,5],[-8,2,7],[-7,2,7],[-6,2,2],[-6,8,1],[-5,2,2],[-5,8,1],[-4,0,11],[-3,0,11],[-2,0,11],[-1,0,11]],"top":-13,"bottom":0},"ﻖ":{"name":"uniFED6","advance":12,"spans":[[-13,7,1],[-13,10,2],[-12,7,1],[-12,10,2],[-9,7,5],[-8,5,7],[-7,5,7],[-6,5,2],[-6,10,2],[-5,5,2],[-5,10,2],[-4,1,2],[-4,5,8],[-3,1,2],[-3,5,9],[-2,1,2],[-2,5,9],[-1,1,2],[-1,5,9],[0,1,2],[0,10,2],[1,1,2],[1,10,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"ﻗ":{"name":"uniFED7","advance":10,"spans":[[-13,4,2],[-13,8,1],[-12,4,2],[-12,8,1],[-9,4,5],[-8,2,7],[-7,2,7],[-6,2,2],[-6,8,1],[-5,2,2],[-5,8,1],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9]],"top":-13,"bottom":0},"ﻘ":{"name":"uniFED8","advance":9,"spans":[[-13,4,2],[-13,8,1],[-12,4,2],[-12,8,1],[-9,4,5],[-8,2,7],[-7,2,7],[-6,2,2],[-6,8,1],[-5,2,2],[-5,8,1],[-4,0,11],[-3,0,11],[-2,0,11],[-1,0,11]],"top":-13,"bottom":0},"ﻚ":{"name":"uniFEDA","advance":14,"spans":[[-15,12,2],[-14,12,2],[-13,10,4],[-12,10,4],[-11,7,1],[-11,12,2],[-10,7,1],[-10,12,2],[-9,5,2],[-9,12,2],[-8,3,7],[-8,12,2],[-7,3,7],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,14],[-3,1,14],[-2,3,9],[-1,3,9]],"top":-15,"bottom":0},"ﻛ":{"name":"uniFEDB","advance":16,"spans":[[-15,4,7],[-14,4,7],[-13,2,9],[-12,2,9],[-11,2,2],[-10,2,2],[-9,2,11],[-8,2,13],[-7,2,13],[-6,13,2],[-5,13,2],[-4,0,15],[-3,0,15],[-2,0,13],[-1,0,13]],"top":-15,"bottom":0},"ﻜ":{"name":"uniFEDC","advance":15,"spans":[[-15,4,7],[-14,4,7],[-13,2,9],[-12,2,9],[-11,2,2],[-10,2,2],[-9,2,11],[-8,2,13],[-7,2,13],[-6,13,2],[-5,13,2],[-4,0,16],[-3,0,16],[-2,0,13],[-1,0,13]],"top":-15,"bottom":0},"ﻞ":{"name":"uniFEDE","advance":9,"spans":[[-15,7,1],[-14,7,1],[-13,5,3],[-12,5,3],[-11,7,1],[-10,7,1],[-9,7,1],[-8,7,1],[-7,7,1],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,3],[-3,3,2],[-3,7,3],[-2,1,2],[-2,7,3],[-1,1,2],[-1,7,3],[0,1,2],[0,7,1],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-15,"bottom":6},"ﻟ":{"name":"uniFEDF","advance":7,"spans":[[-15,4,2],[-14,4,2],[-13,2,4],[-12,2,4],[-11,4,2],[-10,4,2],[-9,4,2],[-8,4,2],[-7,4,2],[-6,4,2],[-5,4,2],[-4,0,6],[-3,0,6],[-2,0,6],[-1,0,6]],"top":-15,"bottom":0},"ﻠ":{"name":"uniFEE0","advance":6,"spans":[[-15,4,2],[-14,4,2],[-13,2,4],[-12,2,4],[-11,4,2],[-10,4,2],[-9,4,2],[-8,4,2],[-7,4,2],[-6,4,2],[-5,4,2],[-4,0,7],[-3,0,7],[-2,0,7],[-1,0,7]],"top":-15,"bottom":0},"ﻢ":{"name":"uniFEE2","advance":10,"spans":[[-10,5,3],[-9,5,3],[-8,5,5],[-7,5,5],[-6,5,2],[-6,9,1],[-5,5,2],[-5,9,1],[-4,3,9],[-3,3,9],[-2,1,11],[-1,1,11],[0,1,2],[1,1,2],[2,1,2],[3,1,2],[4,1,2],[5,1,2]],"top":-10,"bottom":6},"ﻣ":{"name":"uniFEE3","advance":9,"spans":[[-10,2,4],[-9,2,4],[-8,2,5],[-7,2,6],[-6,2,2],[-6,6,2],[-5,2,2],[-5,6,2],[-4,0,8],[-3,0,8],[-2,0,8],[-1,0,8]],"top":-10,"bottom":0},"ﻤ":{"name":"uniFEE4","advance":8,"spans":[[-10,2,4],[-9,2,4],[-8,2,5],[-7,2,6],[-6,2,2],[-6,6,2],[-5,2,2],[-5,6,2],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9]],"top":-10,"bottom":0},"ﻦ":{"name":"uniFEE6","advance":10,"spans":[[-8,5,2],[-7,5,2],[-6,9,1],[-5,9,1],[-4,3,2],[-4,9,3],[-3,3,2],[-3,9,3],[-2,1,2],[-2,9,3],[-1,1,2],[-1,9,3],[0,1,2],[0,9,1],[1,1,2],[1,9,1],[2,1,9],[3,1,9],[4,3,5],[5,3,5]],"top":-8,"bottom":6},"ﻧ":{"name":"uniFEE7","advance":5,"spans":[[-11,2,2],[-10,2,2],[-7,2,2],[-6,2,2],[-5,2,2],[-4,0,4],[-3,0,4],[-2,0,4],[-1,0,4]],"top":-11,"bottom":0},"ﻨ":{"name":"uniFEE8","advance":4,"spans":[[-11,2,2],[-10,2,2],[-7,2,2],[-6,2,2],[-5,2,2],[-4,0,5],[-3,0,5],[-2,0,5],[-1,0,5]],"top":-11,"bottom":0},"ﻪ":{"name":"uniFEEA","advance":9,"spans":[[-12,7,1],[-11,7,1],[-10,3,5],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,9],[-3,1,9],[-2,3,4],[-1,3,4]],"top":-12,"bottom":0},"ﻫ":{"name":"uniFEEB","advance":16,"spans":[[-10,4,9],[-9,4,9],[-8,2,13],[-7,2,13],[-6,4,2],[-6,8,1],[-6,13,2],[-5,4,2],[-5,8,1],[-5,13,2],[-4,0,15],[-3,0,15],[-2,0,13],[-1,0,13]],"top":-10,"bottom":0},"ﻬ":{"name":"uniFEEC","advance":15,"spans":[[-10,4,9],[-9,4,9],[-8,2,13],[-7,2,13],[-6,4,2],[-6,8,1],[-6,13,2],[-5,4,2],[-5,8,1],[-5,13,2],[-4,0,16],[-3,0,16],[-2,0,16],[-1,0,16]],"top":-10,"bottom":0},"ﻮ":{"name":"uniFEEE","advance":9,"spans":[[-10,3,5],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,9],[-3,1,9],[-2,1,9],[-1,1,9],[0,7,1],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-10,"bottom":6},"ﻰ":{"name":"uniFEF0","advance":12,"spans":[[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,7],[-3,3,2],[-3,7,7],[-2,1,2],[-2,9,5],[-1,1,2],[-1,9,5],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9]],"top":-6,"bottom":6},"ﻲ":{"name":"uniFEF2","advance":12,"spans":[[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,7],[-3,3,2],[-3,7,7],[-2,1,2],[-2,9,5],[-1,1,2],[-1,9,5],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9],[8,5,2],[8,9,1],[9,5,2],[9,9,1]],"top":-6,"bottom":10},"ﻳ":{"name":"uniFEF3","advance":9,"spans":[[-8,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,8],[-3,0,8],[-2,0,8],[-1,0,8],[2,2,2],[2,6,2],[3,2,2],[3,6,1]],"top":-8,"bottom":4},"ﻴ":{"name":"uniFEF4","advance":8,"spans":[[-8,6,1],[-7,6,2],[-6,6,2],[-5,6,2],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9],[2,2,2],[2,6,2],[3,2,2],[3,6,1]],"top":-8,"bottom":4},"ﻵ":{"name":"uniFEF5","advance":11,"spans":[[-19,3,2],[-18,3,2],[-17,1,2],[-16,1,2],[-15,3,2],[-15,9,1],[-14,3,2],[-14,9,1],[-13,1,4],[-13,7,3],[-12,1,4],[-12,7,3],[-11,3,2],[-11,9,1],[-10,3,7],[-9,5,4],[-8,3,7],[-7,3,7],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,3,7],[-3,3,7],[-2,3,7],[-1,3,7]],"top":-19,"bottom":0},"ﻶ":{"name":"uniFEF6","advance":10,"spans":[[-19,5,2],[-18,5,2],[-17,3,2],[-16,3,2],[-15,9,1],[-14,9,1],[-13,3,2],[-13,7,3],[-12,3,2],[-12,7,3],[-11,1,4],[-11,9,1],[-10,1,4],[-10,9,1],[-9,3,2],[-9,9,1],[-8,3,2],[-8,9,1],[-7,3,2],[-7,9,1],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,1,11],[-3,1,11],[-2,3,5],[-1,3,5]],"top":-19,"bottom":0},"ﻷ":{"name":"uniFEF7","advance":11,"spans":[[-21,3,2],[-20,3,2],[-19,1,2],[-18,1,2],[-17,1,4],[-16,1,4],[-15,9,1],[-14,9,1],[-13,1,4],[-13,7,3],[-12,1,4],[-12,7,3],[-11,3,2],[-11,9,1],[-10,3,7],[-9,5,3],[-8,3,7],[-7,3,7],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,3,7],[-3,3,7],[-2,3,7],[-1,3,7]],"top":-21,"bottom":0},"ﻸ":{"name":"uniFEF8","advance":10,"spans":[[-21,5,2],[-20,5,2],[-19,3,2],[-18,3,2],[-17,3,4],[-16,3,4],[-15,9,1],[-14,9,1],[-13,3,2],[-13,7,3],[-12,3,2],[-12,7,3],[-11,1,4],[-11,9,1],[-10,1,4],[-10,9,1],[-9,3,2],[-9,9,1],[-8,3,2],[-8,9,1],[-7,3,2],[-7,9,1],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,1,11],[-3,1,11],[-2,3,5],[-1,3,5]],"top":-21,"bottom":0},"ﻹ":{"name":"uniFEF9","advance":11,"spans":[[-15,3,2],[-15,9,1],[-14,3,2],[-14,9,1],[-13,1,4],[-13,7,3],[-12,1,4],[-12,7,3],[-11,3,2],[-11,9,1],[-9,5,3],[-8,3,7],[-7,3,7],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,3,7],[-3,3,7],[-2,3,7],[-1,3,7],[0,5,2],[1,5,2],[2,3,2],[3,3,2],[4,3,4],[5,3,4]],"top":-15,"bottom":6},"ﻺ":{"name":"uniFEFA","advance":10,"spans":[[-15,9,1],[-14,9,1],[-13,3,2],[-13,7,3],[-12,3,2],[-12,7,3],[-11,1,4],[-11,9,1],[-10,1,4],[-10,9,1],[-9,3,2],[-9,9,1],[-8,3,2],[-8,9,1],[-7,3,2],[-7,9,1],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,1,11],[-3,1,11],[-2,3,5],[-1,3,5],[0,3,2],[1,3,2],[2,1,2],[3,1,2],[4,1,4],[5,1,4]],"top":-15,"bottom":6},"ﻻ":{"name":"uniFEFB","advance":11,"spans":[[-15,3,2],[-15,9,1],[-14,3,2],[-14,9,1],[-13,1,4],[-13,7,3],[-12,1,4],[-12,7,3],[-11,3,2],[-11,9,1],[-9,5,3],[-8,3,7],[-7,3,7],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,3,7],[-3,3,7],[-2,3,7],[-1,3,7]],"top":-15,"bottom":0},"ﻼ":{"name":"uniFEFC","advance":10,"spans":[[-15,9,1],[-14,9,1],[-13,3,2],[-13,7,3],[-12,3,2],[-12,7,3],[-11,1,4],[-11,9,1],[-10,1,4],[-10,9,1],[-9,3,2],[-9,9,1],[-8,3,2],[-8,9,1],[-7,3,2],[-7,9,1],[-6,3,2],[-6,9,1],[-5,3,2],[-5,9,1],[-4,1,11],[-3,1,11],[-2,3,5],[-1,3,5]],"top":-15,"bottom":0},"ﺀ":{"name":"uni0621","advance":9,"spans":[[-10,5,3],[-9,5,3],[-8,3,5],[-7,3,5],[-6,3,2],[-5,3,2],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7]],"top":-10,"bottom":0},"ﺁ":{"name":"uni0622","advance":6,"spans":[[-19,3,2],[-18,3,2],[-17,1,2],[-16,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-19,"bottom":0},"ﺃ":{"name":"uni0623","advance":6,"spans":[[-21,3,2],[-20,3,2],[-19,1,2],[-18,1,2],[-17,1,4],[-16,1,4],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-21,"bottom":0},"ﺅ":{"name":"uni0624","advance":9,"spans":[[-17,5,2],[-16,5,2],[-15,3,2],[-14,3,2],[-13,3,4],[-12,3,4],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7],[0,7,1],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-17,"bottom":6},"ﺇ":{"name":"uni0625","advance":6,"spans":[[-15,1,2],[-14,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2],[0,3,2],[1,3,2],[2,1,2],[3,1,2],[4,1,4],[5,1,4]],"top":-15,"bottom":6},"ﺉ":{"name":"uni0626","advance":15,"spans":[[-11,3,2],[-10,3,2],[-9,1,2],[-9,9,3],[-8,1,4],[-8,7,7],[-7,1,4],[-7,7,7],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,5],[-3,3,2],[-3,7,5],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9]],"top":-11,"bottom":6},"ﺍ":{"name":"uni0627","advance":6,"spans":[[-15,1,2],[-14,1,2],[-13,1,4],[-12,1,4],[-11,1,2],[-10,1,2],[-9,1,2],[-8,1,2],[-7,1,2],[-6,1,2],[-5,1,2],[-4,1,2],[-3,1,2],[-2,1,2],[-1,1,2]],"top":-15,"bottom":0},"ﺏ":{"name":"uni0628","advance":15,"spans":[[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11],[2,9,1],[3,9,1]],"top":-8,"bottom":4},"ﺓ":{"name":"uni0629","advance":9,"spans":[[-13,1,2],[-13,5,2],[-12,1,2],[-12,5,1],[-9,3,4],[-8,2,5],[-7,1,7],[-6,1,2],[-6,6,2],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7]],"top":-13,"bottom":0},"ﺕ":{"name":"uni062A","advance":15,"spans":[[-11,7,1],[-11,10,2],[-10,7,1],[-10,10,2],[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11]],"top":-11,"bottom":0},"ﺙ":{"name":"uni062B","advance":15,"spans":[[-13,9,1],[-12,9,1],[-11,7,1],[-11,10,2],[-10,7,1],[-10,10,2],[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11]],"top":-13,"bottom":0},"ﺝ":{"name":"uni062C","advance":13,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,7],[2,10,2],[3,1,7],[3,10,2],[4,3,5],[5,3,5]],"top":-10,"bottom":6},"ﺡ":{"name":"uni062D","advance":13,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-10,"bottom":6},"ﺥ":{"name":"uni062E","advance":13,"spans":[[-13,5,2],[-12,5,2],[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"ﺩ":{"name":"uni062F","advance":8,"spans":[[-10,3,2],[-9,3,2],[-8,3,4],[-7,3,4],[-6,5,2],[-5,5,2],[-4,1,6],[-3,1,6],[-2,1,4],[-1,1,4]],"top":-10,"bottom":0},"ﺫ":{"name":"uni0630","advance":8,"spans":[[-13,3,2],[-12,3,2],[-9,3,2],[-8,3,4],[-7,3,4],[-6,5,2],[-5,5,2],[-4,1,6],[-3,1,6],[-2,1,4],[-1,1,4]],"top":-13,"bottom":0},"ﺭ":{"name":"uni0631","advance":6,"spans":[[-7,3,2],[-6,3,2],[-5,3,2],[-4,3,2],[-3,3,2],[-2,3,2],[-1,3,2],[0,3,2],[1,3,2],[2,-1,6],[3,-1,6],[4,1,4],[5,1,4]],"top":-7,"bottom":6},"ﺯ":{"name":"uni0632","advance":6,"spans":[[-11,3,2],[-10,3,2],[-7,3,2],[-6,3,2],[-5,3,2],[-4,3,2],[-3,3,2],[-2,3,2],[-1,3,2],[0,3,2],[1,3,2],[2,-1,6],[3,-1,6],[4,1,4],[5,1,4]],"top":-11,"bottom":6},"ﺱ":{"name":"uni0633","advance":17,"spans":[[-10,15,1],[-9,14,2],[-8,7,1],[-8,11,1],[-8,14,2],[-7,7,1],[-7,10,2],[-7,14,2],[-6,7,1],[-6,10,2],[-6,14,2],[-5,7,1],[-5,10,2],[-5,14,2],[-4,3,2],[-4,7,9],[-3,3,2],[-3,7,9],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-10,"bottom":6},"ﺵ":{"name":"uni0634","advance":17,"spans":[[-13,10,2],[-12,10,2],[-11,9,1],[-11,12,2],[-10,9,1],[-10,12,2],[-9,14,2],[-8,14,2],[-7,7,1],[-7,10,2],[-7,14,2],[-6,7,1],[-6,10,2],[-6,14,2],[-5,7,1],[-5,10,2],[-5,14,2],[-4,3,2],[-4,7,9],[-3,3,2],[-3,7,9],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-13,"bottom":6},"ﺹ":{"name":"uni0635","advance":21,"spans":[[-9,7,1],[-9,10,10],[-8,7,13],[-7,7,13],[-6,7,2],[-6,18,2],[-5,7,1],[-5,18,2],[-4,3,2],[-4,7,13],[-3,3,2],[-3,7,13],[-2,1,2],[-2,7,11],[-1,1,2],[-1,7,11],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-9,"bottom":6},"ﺽ":{"name":"uni0636","advance":21,"spans":[[-13,12,2],[-12,13,1],[-9,7,1],[-9,10,10],[-8,7,13],[-7,7,13],[-6,7,2],[-6,18,2],[-5,7,1],[-5,18,2],[-4,3,2],[-4,7,13],[-3,3,2],[-3,7,13],[-2,1,2],[-2,7,11],[-1,1,2],[-1,7,11],[0,1,2],[0,7,2],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-13,"bottom":6},"ﻁ":{"name":"uni0637","advance":17,"spans":[[-15,3,2],[-14,3,2],[-13,3,4],[-12,3,4],[-11,3,2],[-10,3,2],[-9,3,2],[-9,7,9],[-8,3,13],[-7,3,13],[-6,3,2],[-6,14,2],[-5,3,2],[-5,14,2],[-4,1,15],[-3,1,15],[-2,3,11],[-1,3,11]],"top":-15,"bottom":0},"ﻅ":{"name":"uni0638","advance":17,"spans":[[-15,3,2],[-14,3,2],[-13,3,4],[-13,9,1],[-12,3,4],[-12,9,1],[-11,3,2],[-10,3,2],[-9,3,2],[-9,7,9],[-8,3,13],[-7,3,13],[-6,3,2],[-6,14,2],[-5,3,2],[-5,14,2],[-4,1,15],[-3,1,15],[-2,3,11],[-1,3,11]],"top":-15,"bottom":0},"ﻉ":{"name":"uni0639","advance":13,"spans":[[-10,5,3],[-9,5,3],[-8,5,5],[-7,5,5],[-6,5,2],[-5,5,2],[-4,3,9],[-3,3,9],[-2,1,9],[-1,1,9],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-10,"bottom":6},"ﻍ":{"name":"uni063A","advance":13,"spans":[[-13,5,2],[-12,5,2],[-9,5,3],[-8,5,5],[-7,5,5],[-6,5,2],[-5,5,2],[-4,3,9],[-3,3,9],[-2,1,9],[-1,1,9],[0,1,2],[1,1,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"ﻑ":{"name":"uni0641","advance":19,"spans":[[-13,14,2],[-12,14,2],[-9,12,6],[-8,3,2],[-8,10,8],[-7,3,2],[-7,10,8],[-6,1,2],[-6,10,2],[-6,16,2],[-5,1,2],[-5,10,2],[-5,16,2],[-4,1,17],[-3,1,17],[-2,3,15],[-1,3,15]],"top":-13,"bottom":0},"ﻕ":{"name":"uni0642","advance":13,"spans":[[-13,7,1],[-13,10,2],[-12,7,1],[-12,10,2],[-9,7,5],[-8,5,7],[-7,5,7],[-6,5,2],[-6,10,2],[-5,5,2],[-5,10,2],[-4,1,2],[-4,5,7],[-3,1,2],[-3,5,7],[-2,1,2],[-2,5,7],[-1,1,2],[-1,5,7],[0,1,2],[0,10,2],[1,1,2],[1,10,2],[2,1,11],[3,1,11],[4,3,7],[5,3,7]],"top":-13,"bottom":6},"ﻙ":{"name":"uni0643","advance":15,"spans":[[-15,12,2],[-14,12,2],[-13,10,4],[-12,10,4],[-11,7,1],[-11,12,2],[-10,7,1],[-10,12,2],[-9,5,2],[-9,12,2],[-8,3,7],[-8,12,2],[-7,3,7],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,9],[-1,3,9]],"top":-15,"bottom":0},"ﻝ":{"name":"uni0644","advance":9,"spans":[[-15,7,1],[-14,7,1],[-13,5,3],[-12,5,3],[-11,7,1],[-10,7,1],[-9,7,1],[-8,7,1],[-7,7,1],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,1],[-3,3,2],[-3,7,1],[-2,1,2],[-2,7,1],[-1,1,2],[-1,7,1],[0,1,2],[0,7,1],[1,1,2],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-15,"bottom":6},"ﻡ":{"name":"uni0645","advance":11,"spans":[[-10,5,3],[-9,5,3],[-8,5,5],[-7,5,5],[-6,5,2],[-6,9,1],[-5,5,2],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,9],[-1,1,9],[0,1,2],[1,1,2],[2,1,2],[3,1,2],[4,1,2],[5,1,2]],"top":-10,"bottom":6},"ﻥ":{"name":"uni0646","advance":11,"spans":[[-8,5,2],[-7,5,2],[-6,9,1],[-5,9,1],[-4,3,2],[-4,9,1],[-3,3,2],[-3,9,1],[-2,1,2],[-2,9,1],[-1,1,2],[-1,9,1],[0,1,2],[0,9,1],[1,1,2],[1,9,1],[2,1,9],[3,1,9],[4,3,5],[5,3,5]],"top":-8,"bottom":6},"ﻩ":{"name":"uni0647","advance":9,"spans":[[-10,3,3],[-9,3,4],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7]],"top":-10,"bottom":0},"ﻭ":{"name":"uni0648","advance":9,"spans":[[-10,3,5],[-9,3,5],[-8,1,7],[-7,1,7],[-6,1,2],[-6,7,1],[-5,1,2],[-5,7,1],[-4,1,7],[-3,1,7],[-2,1,7],[-1,1,7],[0,7,1],[1,7,1],[2,1,7],[3,1,7],[4,3,4],[5,3,3]],"top":-10,"bottom":6},"ﻯ":{"name":"uni0649","advance":15,"spans":[[-10,9,3],[-9,9,3],[-8,7,7],[-7,7,7],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,5],[-3,3,2],[-3,7,5],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9]],"top":-10,"bottom":6},"ﻱ":{"name":"uni064A","advance":15,"spans":[[-10,9,3],[-9,9,3],[-8,7,7],[-7,7,7],[-6,7,1],[-5,7,1],[-4,3,2],[-4,7,5],[-3,3,2],[-3,7,5],[-2,1,2],[-2,7,7],[-1,1,2],[-1,7,7],[0,1,2],[0,12,2],[1,1,2],[1,12,2],[2,1,13],[3,1,13],[4,3,9],[5,3,9],[8,5,2],[8,9,1],[9,5,2],[9,9,1]],"top":-10,"bottom":10},"ﭖ":{"name":"uni067E","advance":15,"spans":[[-8,3,2],[-8,12,2],[-7,3,2],[-7,12,2],[-6,1,2],[-6,12,2],[-5,1,2],[-5,12,2],[-4,1,13],[-3,1,13],[-2,3,11],[-1,3,11],[2,7,1],[2,10,2],[3,7,1],[3,10,2],[4,9,1],[5,9,1]],"top":-8,"bottom":6},"ﭺ":{"name":"uni0686","advance":13,"spans":[[-10,3,5],[-9,3,5],[-8,1,11],[-7,1,11],[-6,9,1],[-5,9,1],[-4,3,7],[-3,3,7],[-2,1,7],[-1,1,7],[0,1,2],[1,1,2],[2,1,4],[2,7,1],[2,10,2],[3,1,4],[3,7,1],[3,10,2],[4,3,4],[4,9,1],[5,3,4],[5,9,1]],"top":-10,"bottom":6},"ﭪ":{"name":"uni06A4","advance":19,"spans":[[-15,14,2],[-14,14,2],[-13,12,2],[-13,16,2],[-12,12,2],[-12,16,2],[-9,12,6],[-8,3,2],[-8,10,8],[-7,3,2],[-7,10,8],[-6,1,2],[-6,10,2],[-6,16,2],[-5,1,2],[-5,10,2],[-5,16,2],[-4,1,17],[-3,1,17],[-2,3,15],[-1,3,15]],"top":-15,"bottom":0},"glyph:shaddaAlefabovear":{"name":"shaddaAlefabovear","advance":0,"spans":[[-17,-2,2],[-16,-2,2],[-15,-2,2],[-14,-2,2],[-13,-2,2],[-10,-4,2],[-10,0,2],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-17,"bottom":-6},"glyph:shaddaFathatanar":{"name":"shaddaFathatanar","advance":0,"spans":[[-17,-2,4],[-16,-2,4],[-13,-2,4],[-12,-2,4],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-17,"bottom":-6},"glyph:shaddaDammatanar":{"name":"shaddaDammatanar","advance":0,"spans":[[-19,0,2],[-18,0,2],[-17,-2,4],[-16,-2,4],[-15,0,2],[-14,0,2],[-13,-2,4],[-12,-2,4],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-19,"bottom":-6},"glyph:shaddaKasratanar":{"name":"shaddaKasratanar","advance":0,"spans":[[-17,-2,4],[-16,-2,4],[-13,-2,4],[-12,-2,4],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-17,"bottom":-6},"glyph:shaddaFathaar":{"name":"shaddaFathaar","advance":0,"spans":[[-13,-2,4],[-12,-2,4],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-13,"bottom":-6},"glyph:shaddaDammaar":{"name":"shaddaDammaar","advance":0,"spans":[[-15,0,2],[-14,0,2],[-13,-2,4],[-12,-2,4],[-9,-4,2],[-9,0,2],[-8,-4,6],[-7,-4,6]],"top":-15,"bottom":-6},"glyph:shaddaKasraar":{"name":"shaddaKasraar","advance":0,"spans":[[-13,-4,2],[-13,0,2],[-12,-4,2],[-12,0,2],[-11,-4,6],[-10,-4,6],[-8,-2,4],[-7,-2,4]],"top":-13,"bottom":-6},"$":{"name":"dollar","advance":6,"spans":[[-9,2,1],[-8,1,3],[-7,0,1],[-7,2,1],[-7,4,1],[-6,0,1],[-6,2,1],[-5,1,3],[-4,2,1],[-4,4,1],[-3,0,1],[-3,2,1],[-3,4,1],[-2,1,3],[-1,2,1]],"top":-9,"bottom":0},"&":{"name":"ampersand","advance":7,"spans":[[-9,2,1],[-8,1,1],[-8,3,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,2,1],[-4,1,1],[-4,3,1],[-4,5,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3],[-1,5,1]],"top":-9,"bottom":0},"@":{"name":"at","advance":8,"spans":[[-9,2,3],[-8,1,1],[-8,5,1],[-7,0,1],[-7,3,2],[-7,6,1],[-6,0,1],[-6,2,1],[-6,4,1],[-6,6,1],[-5,0,1],[-5,2,1],[-5,4,1],[-5,6,1],[-4,0,1],[-4,2,1],[-4,4,1],[-4,6,1],[-3,0,1],[-3,3,3],[-2,1,1],[-1,2,4]],"top":-9,"bottom":0},"A":{"name":"A","advance":6,"spans":[[-9,2,1],[-8,2,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,1,1],[-5,3,1],[-4,0,5],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"B":{"name":"B","advance":6,"spans":[[-9,0,4],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,4],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,4]],"top":-9,"bottom":0},"C":{"name":"C","advance":6,"spans":[[-9,1,4],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,1,4]],"top":-9,"bottom":0},"D":{"name":"D","advance":6,"spans":[[-9,0,4],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,4]],"top":-9,"bottom":0},"E":{"name":"E","advance":5,"spans":[[-9,0,4],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,4],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,4]],"top":-9,"bottom":0},"F":{"name":"F","advance":5,"spans":[[-9,0,4],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,4],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"G":{"name":"G","advance":6,"spans":[[-9,1,4],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-5,2,3],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-9,"bottom":0},"H":{"name":"H","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,5],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"I":{"name":"I","advance":2,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"J":{"name":"J","advance":5,"spans":[[-9,3,1],[-8,3,1],[-7,3,1],[-6,3,1],[-5,3,1],[-4,3,1],[-3,3,1],[-2,3,1],[-1,0,3]],"top":-9,"bottom":0},"K":{"name":"K","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,3,1],[-7,0,1],[-7,3,1],[-6,0,1],[-6,2,1],[-5,0,2],[-4,0,1],[-4,2,1],[-3,0,1],[-3,3,1],[-2,0,1],[-2,3,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"L":{"name":"L","advance":5,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,4]],"top":-9,"bottom":0},"M":{"name":"M","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,0,2],[-7,3,2],[-6,0,2],[-6,3,2],[-5,0,1],[-5,2,1],[-5,4,1],[-4,0,1],[-4,2,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"N":{"name":"N","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,2],[-8,4,1],[-7,0,2],[-7,4,1],[-6,0,1],[-6,2,1],[-6,4,1],[-5,0,1],[-5,2,1],[-5,4,1],[-4,0,1],[-4,3,2],[-3,0,1],[-3,3,2],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"O":{"name":"O","advance":6,"spans":[[-9,1,3],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-9,"bottom":0},"P":{"name":"P","advance":6,"spans":[[-9,0,4],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,4],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"Q":{"name":"Q","advance":6,"spans":[[-9,1,3],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,2,1],[-2,4,1],[-1,1,3],[0,3,2]],"top":-9,"bottom":1},"R":{"name":"R","advance":6,"spans":[[-9,0,4],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,4],[-4,0,1],[-4,3,1],[-3,0,1],[-3,3,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"S":{"name":"S","advance":5,"spans":[[-9,1,3],[-8,0,1],[-7,0,1],[-6,0,1],[-5,1,2],[-4,3,1],[-3,3,1],[-2,3,1],[-1,0,3]],"top":-9,"bottom":0},"T":{"name":"T","advance":6,"spans":[[-9,0,5],[-8,2,1],[-7,2,1],[-6,2,1],[-5,2,1],[-4,2,1],[-3,2,1],[-2,2,1],[-1,2,1]],"top":-9,"bottom":0},"U":{"name":"U","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-9,"bottom":0},"V":{"name":"V","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,1,1],[-6,3,1],[-5,1,1],[-5,3,1],[-4,1,1],[-4,3,1],[-3,1,1],[-3,3,1],[-2,2,1],[-1,2,1]],"top":-9,"bottom":0},"W":{"name":"W","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,2,1],[-7,4,1],[-6,0,1],[-6,2,1],[-6,4,1],[-5,0,1],[-5,2,1],[-5,4,1],[-4,0,1],[-4,2,1],[-4,4,1],[-3,1,1],[-3,3,1],[-2,1,1],[-2,3,1],[-1,1,1],[-1,3,1]],"top":-9,"bottom":0},"X":{"name":"X","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,2,1],[-4,1,1],[-4,3,1],[-3,1,1],[-3,3,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"Y":{"name":"Y","advance":6,"spans":[[-9,0,1],[-9,4,1],[-8,0,1],[-8,4,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,2,1],[-4,2,1],[-3,2,1],[-2,2,1],[-1,2,1]],"top":-9,"bottom":0},"Z":{"name":"Z","advance":5,"spans":[[-9,0,4],[-8,3,1],[-7,2,1],[-6,2,1],[-5,1,2],[-4,1,1],[-3,1,1],[-2,0,1],[-1,0,4]],"top":-9,"bottom":0},"a":{"name":"a","advance":6,"spans":[[-6,0,4],[-5,4,1],[-4,1,4],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-6,"bottom":0},"b":{"name":"b","advance":6,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,4]],"top":-9,"bottom":0},"c":{"name":"c","advance":6,"spans":[[-6,1,4],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,1,4]],"top":-6,"bottom":0},"d":{"name":"d","advance":6,"spans":[[-9,4,1],[-8,4,1],[-7,4,1],[-6,1,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-9,"bottom":0},"e":{"name":"e","advance":6,"spans":[[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,5],[-3,0,1],[-2,0,1],[-1,1,4]],"top":-6,"bottom":0},"f":{"name":"f","advance":5,"spans":[[-9,2,2],[-8,1,1],[-7,1,1],[-6,0,4],[-5,1,1],[-4,1,1],[-3,1,1],[-2,1,1],[-1,1,1]],"top":-9,"bottom":0},"g":{"name":"g","advance":6,"spans":[[-6,1,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,1,4],[-1,4,1],[0,0,4]],"top":-6,"bottom":1},"h":{"name":"h","advance":6,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-9,"bottom":0},"i":{"name":"i","advance":2,"spans":[[-8,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-8,"bottom":0},"j":{"name":"j","advance":4,"spans":[[-8,2,1],[-6,2,1],[-5,2,1],[-4,2,1],[-3,2,1],[-2,2,1],[-1,2,1],[0,0,2]],"top":-8,"bottom":1},"k":{"name":"k","advance":5,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-6,3,1],[-5,0,1],[-5,2,1],[-4,0,2],[-3,0,1],[-3,2,1],[-2,0,1],[-2,3,1],[-1,0,1],[-1,3,1]],"top":-9,"bottom":0},"l":{"name":"l","advance":3,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,1,1]],"top":-9,"bottom":0},"m":{"name":"m","advance":8,"spans":[[-6,0,6],[-5,0,1],[-5,3,1],[-5,6,1],[-4,0,1],[-4,3,1],[-4,6,1],[-3,0,1],[-3,3,1],[-3,6,1],[-2,0,1],[-2,3,1],[-2,6,1],[-1,0,1],[-1,3,1],[-1,6,1]],"top":-6,"bottom":0},"n":{"name":"n","advance":6,"spans":[[-6,0,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-6,"bottom":0},"o":{"name":"o","advance":6,"spans":[[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-6,"bottom":0},"p":{"name":"p","advance":6,"spans":[[-6,0,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,4],[-1,0,1],[0,0,1]],"top":-6,"bottom":1},"q":{"name":"q","advance":6,"spans":[[-6,1,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,1,4],[-1,4,1],[0,4,1]],"top":-6,"bottom":1},"r":{"name":"r","advance":5,"spans":[[-6,0,1],[-6,2,2],[-5,0,2],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-6,"bottom":0},"s":{"name":"s","advance":5,"spans":[[-6,1,3],[-5,0,1],[-4,1,2],[-3,3,1],[-2,3,1],[-1,0,3]],"top":-6,"bottom":0},"t":{"name":"t","advance":5,"spans":[[-8,1,1],[-7,1,1],[-6,0,4],[-5,1,1],[-4,1,1],[-3,1,1],[-2,1,1],[-1,2,2]],"top":-8,"bottom":0},"u":{"name":"u","advance":6,"spans":[[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-6,"bottom":0},"v":{"name":"v","advance":6,"spans":[[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,1,1],[-4,3,1],[-3,1,1],[-3,3,1],[-2,2,1],[-1,2,1]],"top":-6,"bottom":0},"w":{"name":"w","advance":6,"spans":[[-6,0,1],[-6,4,1],[-5,0,1],[-5,2,1],[-5,4,1],[-4,0,1],[-4,2,1],[-4,4,1],[-3,0,1],[-3,2,1],[-3,4,1],[-2,1,1],[-2,3,1],[-1,1,1],[-1,3,1]],"top":-6,"bottom":0},"x":{"name":"x","advance":6,"spans":[[-6,0,1],[-6,4,1],[-5,1,1],[-5,3,1],[-4,2,1],[-3,2,1],[-2,1,1],[-2,3,1],[-1,0,1],[-1,4,1]],"top":-6,"bottom":0},"y":{"name":"y","advance":6,"spans":[[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,1,1],[-4,3,1],[-3,1,1],[-3,3,1],[-2,2,1],[-1,2,1],[0,0,2]],"top":-6,"bottom":1},"z":{"name":"z","advance":6,"spans":[[-6,0,5],[-5,3,1],[-4,2,1],[-3,1,1],[-2,0,1],[-1,0,5]],"top":-6,"bottom":0},"~":{"name":"asciitilde","advance":6,"spans":[[-9,1,1],[-8,0,1],[-8,2,1],[-8,4,1],[-7,3,1]],"top":-9,"bottom":-6}," ":{"name":"nonbreakingspace","advance":5,"spans":[],"top":0,"bottom":0},"¢":{"name":"cent","advance":7,"spans":[[-9,3,1],[-8,1,4],[-7,0,1],[-7,3,1],[-7,5,1],[-6,0,1],[-6,2,1],[-5,0,1],[-5,2,1],[-4,0,1],[-4,2,1],[-3,0,2],[-3,5,1],[-2,1,4],[-1,1,1]],"top":-9,"bottom":0},"£":{"name":"sterling","advance":6,"spans":[[-9,2,2],[-8,1,1],[-8,4,1],[-7,1,1],[-6,2,1],[-5,0,5],[-4,2,1],[-3,1,1],[-2,0,1],[-1,0,5]],"top":-9,"bottom":0},"©":{"name":"copyright","advance":10,"spans":[[-9,2,5],[-8,1,1],[-8,7,1],[-7,0,1],[-7,3,3],[-7,8,1],[-6,0,1],[-6,2,1],[-6,6,1],[-6,8,1],[-5,0,1],[-5,2,1],[-5,8,1],[-4,0,1],[-4,2,1],[-4,6,1],[-4,8,1],[-3,0,1],[-3,3,3],[-3,8,1],[-2,1,1],[-2,7,1],[-1,2,5]],"top":-9,"bottom":0},"®":{"name":"registered","advance":10,"spans":[[-9,2,5],[-8,1,1],[-8,7,1],[-7,0,1],[-7,2,4],[-7,8,1],[-6,0,1],[-6,2,1],[-6,6,1],[-6,8,1],[-5,0,1],[-5,2,4],[-5,8,1],[-4,0,1],[-4,2,1],[-4,5,1],[-4,8,1],[-3,0,1],[-3,2,1],[-3,6,1],[-3,8,1],[-2,1,1],[-2,7,1],[-1,2,5]],"top":-9,"bottom":0},"°":{"name":"degree","advance":4,"spans":[[-9,1,1],[-8,0,1],[-8,2,1],[-7,1,1]],"top":-9,"bottom":-6},"±":{"name":"plusminus","advance":6,"spans":[[-8,2,1],[-7,2,1],[-6,0,5],[-5,2,1],[-4,2,1],[-2,0,5]],"top":-8,"bottom":-1},"´":{"name":"acute","advance":3,"spans":[[-9,1,1],[-8,0,1]],"top":-9,"bottom":-7},"À":{"name":"Agrave","advance":6,"spans":[[-12,2,1],[-10,3,1],[-8,2,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,1,1],[-5,3,1],[-4,0,5],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-12,"bottom":0},"Á":{"name":"Aacute","advance":6,"spans":[[-12,2,1],[-10,1,1],[-8,2,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,1,1],[-5,3,1],[-4,0,5],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-12,"bottom":0},"Â":{"name":"Acircumflex","advance":6,"spans":[[-12,2,1],[-10,1,1],[-10,3,1],[-8,2,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,1,1],[-5,3,1],[-4,0,5],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-12,"bottom":0},"Ä":{"name":"Adieresis","advance":6,"spans":[[-10,1,1],[-10,3,1],[-8,2,1],[-7,1,1],[-7,3,1],[-6,1,1],[-6,3,1],[-5,1,1],[-5,3,1],[-4,0,5],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-10,"bottom":0},"Ç":{"name":"Ccedilla","advance":5,"spans":[[-9,1,3],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,1,3],[-1,2,1],[0,1,1]],"top":-9,"bottom":1},"È":{"name":"Egrave","advance":5,"spans":[[-12,1,1],[-10,2,1],[-8,0,4],[-7,0,1],[-6,0,1],[-5,0,4],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,4]],"top":-12,"bottom":0},"É":{"name":"Eacute","advance":5,"spans":[[-12,2,1],[-10,1,1],[-8,0,4],[-7,0,1],[-6,0,1],[-5,0,4],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,4]],"top":-12,"bottom":0},"Ê":{"name":"Ecircumflex","advance":5,"spans":[[-12,2,1],[-10,1,1],[-10,3,1],[-8,0,4],[-7,0,1],[-6,0,1],[-5,0,4],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,4]],"top":-12,"bottom":0},"Ë":{"name":"Edieresis","advance":5,"spans":[[-10,1,1],[-10,3,1],[-8,0,4],[-7,0,1],[-6,0,1],[-5,0,4],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,4]],"top":-10,"bottom":0},"Ì":{"name":"Igrave","advance":2,"spans":[[-12,-1,1],[-10,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-12,"bottom":0},"Í":{"name":"Iacute","advance":2,"spans":[[-12,1,1],[-10,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-12,"bottom":0},"Î":{"name":"Icircumflex","advance":2,"spans":[[-12,0,1],[-10,-1,1],[-10,1,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-12,"bottom":0},"Ï":{"name":"Idieresis","advance":2,"spans":[[-10,-1,1],[-10,1,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-10,"bottom":0},"Ñ":{"name":"Ntilde","advance":6,"spans":[[-12,2,1],[-12,4,1],[-10,1,1],[-10,3,1],[-8,0,1],[-8,4,1],[-7,0,2],[-7,4,1],[-6,0,2],[-6,4,1],[-5,0,1],[-5,2,1],[-5,4,1],[-4,0,1],[-4,2,1],[-4,4,1],[-3,0,1],[-3,3,2],[-2,0,1],[-2,3,2],[-1,0,1],[-1,4,1]],"top":-12,"bottom":0},"Ò":{"name":"Ograve","advance":6,"spans":[[-12,1,1],[-10,2,1],[-8,1,3],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-12,"bottom":0},"Ó":{"name":"Oacute","advance":6,"spans":[[-12,3,1],[-10,2,1],[-8,1,3],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-12,"bottom":0},"Ô":{"name":"Ocircumflex","advance":6,"spans":[[-12,2,1],[-10,1,1],[-10,3,1],[-8,1,3],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-12,"bottom":0},"Ö":{"name":"Odieresis","advance":6,"spans":[[-11,1,1],[-11,3,1],[-8,1,3],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-11,"bottom":0},"Ù":{"name":"Ugrave","advance":6,"spans":[[-12,1,1],[-10,2,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-12,"bottom":0},"Ú":{"name":"Uacute","advance":6,"spans":[[-12,3,1],[-10,2,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-12,"bottom":0},"Û":{"name":"Ucircumflex","advance":6,"spans":[[-12,2,1],[-10,1,1],[-10,3,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-12,"bottom":0},"Ü":{"name":"Udieresis","advance":6,"spans":[[-10,1,1],[-10,3,1],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-10,"bottom":0},"ß":{"name":"germandbls","advance":6,"spans":[[-9,1,3],[-8,0,1],[-8,4,1],[-7,0,1],[-7,4,1],[-6,0,1],[-6,2,2],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,2,2],[-1,0,1]],"top":-9,"bottom":0},"à":{"name":"agrave","advance":6,"spans":[[-10,2,1],[-8,3,1],[-6,0,4],[-5,4,1],[-4,1,4],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-10,"bottom":0},"á":{"name":"aacute","advance":6,"spans":[[-10,3,1],[-8,2,1],[-6,0,4],[-5,4,1],[-4,1,4],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-10,"bottom":0},"â":{"name":"acircumflex","advance":6,"spans":[[-10,2,1],[-8,1,1],[-8,3,1],[-6,0,4],[-5,4,1],[-4,1,4],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-10,"bottom":0},"ä":{"name":"adieresis","advance":6,"spans":[[-8,1,1],[-8,3,1],[-6,0,4],[-5,4,1],[-4,1,4],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-8,"bottom":0},"ç":{"name":"ccedilla","advance":5,"spans":[[-6,1,3],[-5,0,1],[-4,0,1],[-3,0,1],[-2,1,3],[-1,2,1],[0,1,1]],"top":-6,"bottom":1},"è":{"name":"egrave","advance":6,"spans":[[-10,1,1],[-8,2,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,5],[-3,0,1],[-2,0,1],[-1,1,4]],"top":-10,"bottom":0},"é":{"name":"eacute","advance":6,"spans":[[-10,3,1],[-8,2,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,5],[-3,0,1],[-2,0,1],[-1,1,4]],"top":-10,"bottom":0},"ê":{"name":"ecircumflex","advance":6,"spans":[[-10,2,1],[-8,1,1],[-8,3,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,5],[-3,0,1],[-2,0,1],[-1,1,4]],"top":-10,"bottom":0},"ë":{"name":"edieresis","advance":6,"spans":[[-8,1,1],[-8,3,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,5],[-3,0,1],[-2,0,1],[-1,1,4]],"top":-8,"bottom":0},"ì":{"name":"igrave","advance":2,"spans":[[-10,-1,1],[-8,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-10,"bottom":0},"í":{"name":"iacute","advance":2,"spans":[[-10,1,1],[-8,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-10,"bottom":0},"î":{"name":"icircumflex","advance":2,"spans":[[-10,0,1],[-8,-1,1],[-8,1,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-10,"bottom":0},"ï":{"name":"idieresis","advance":2,"spans":[[-8,-1,1],[-8,1,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-8,"bottom":0},"ñ":{"name":"ntilde","advance":6,"spans":[[-10,2,1],[-10,4,1],[-8,1,1],[-8,3,1],[-6,0,4],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,0,1],[-1,4,1]],"top":-10,"bottom":0},"ò":{"name":"ograve","advance":6,"spans":[[-10,1,1],[-8,2,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-10,"bottom":0},"ó":{"name":"oacute","advance":6,"spans":[[-10,3,1],[-8,2,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-10,"bottom":0},"ô":{"name":"ocircumflex","advance":6,"spans":[[-10,2,1],[-8,1,1],[-8,3,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-10,"bottom":0},"ö":{"name":"odieresis","advance":6,"spans":[[-8,1,1],[-8,3,1],[-6,1,3],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,3]],"top":-8,"bottom":0},"ù":{"name":"ugrave","advance":6,"spans":[[-9,1,1],[-7,2,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-9,"bottom":0},"ú":{"name":"uacute","advance":6,"spans":[[-9,3,1],[-7,2,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-9,"bottom":0},"û":{"name":"ucircumflex","advance":6,"spans":[[-10,2,1],[-8,1,1],[-8,3,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-10,"bottom":0},"ü":{"name":"udieresis","advance":6,"spans":[[-8,1,1],[-8,3,1],[-6,0,1],[-6,4,1],[-5,0,1],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,1,4]],"top":-8,"bottom":0},"Œ":{"name":"OE","advance":8,"spans":[[-9,1,2],[-9,4,3],[-8,0,1],[-8,3,1],[-7,0,1],[-7,3,1],[-6,0,1],[-6,3,1],[-5,0,1],[-5,3,4],[-4,0,1],[-4,3,1],[-3,0,1],[-3,3,1],[-2,0,1],[-2,3,1],[-1,1,2],[-1,4,3]],"top":-9,"bottom":0},"œ":{"name":"oe","advance":8,"spans":[[-6,1,2],[-6,4,2],[-5,0,1],[-5,3,1],[-5,6,1],[-4,0,1],[-4,3,4],[-3,0,1],[-3,3,1],[-2,0,1],[-2,3,1],[-1,1,2],[-1,4,3]],"top":-6,"bottom":0},"※":{"name":"referencemark","advance":10,"spans":[[-9,0,1],[-9,4,1],[-9,8,1],[-8,1,1],[-8,4,1],[-8,7,1],[-7,2,1],[-7,6,1],[-6,3,1],[-6,5,1],[-5,0,2],[-5,4,1],[-5,7,2],[-4,3,1],[-4,5,1],[-3,2,1],[-3,6,1],[-2,1,1],[-2,4,1],[-2,7,1],[-1,0,1],[-1,4,1],[-1,8,1]],"top":-9,"bottom":0},"€":{"name":"Euro","advance":7,"spans":[[-9,3,2],[-8,2,1],[-8,5,1],[-7,1,1],[-6,0,4],[-5,1,1],[-4,0,4],[-3,1,1],[-2,2,1],[-2,5,1],[-1,3,2]],"top":-9,"bottom":0},"™":{"name":"trademark","advance":10,"spans":[[-9,0,3],[-9,4,1],[-9,8,1],[-8,1,1],[-8,4,2],[-8,7,2],[-7,1,1],[-7,4,1],[-7,6,1],[-7,8,1],[-6,1,1],[-6,4,1],[-6,8,1]],"top":-9,"bottom":-5},"←":{"name":"arrowleft","advance":10,"spans":[[-7,2,1],[-6,1,1],[-5,0,9],[-4,1,1],[-3,2,1]],"top":-7,"bottom":-2},"↑":{"name":"arrowup","advance":6,"spans":[[-9,2,1],[-8,1,3],[-7,0,1],[-7,2,1],[-7,4,1],[-6,2,1],[-5,2,1],[-4,2,1],[-3,2,1],[-2,2,1],[-1,2,1]],"top":-9,"bottom":0},"→":{"name":"arrowright","advance":10,"spans":[[-7,6,1],[-6,7,1],[-5,0,9],[-4,7,1],[-3,6,1]],"top":-7,"bottom":-2},"↓":{"name":"arrowdown","advance":6,"spans":[[-9,2,1],[-8,2,1],[-7,2,1],[-6,2,1],[-5,2,1],[-4,2,1],[-3,0,1],[-3,2,1],[-3,4,1],[-2,1,3],[-1,2,1]],"top":-9,"bottom":0},"∞":{"name":"infinity","advance":10,"spans":[[-7,1,2],[-7,6,2],[-6,0,1],[-6,3,1],[-6,5,1],[-6,8,1],[-5,0,1],[-5,4,1],[-5,8,1],[-4,0,1],[-4,3,1],[-4,5,1],[-4,8,1],[-3,1,2],[-3,6,2]],"top":-7,"bottom":-2},"∱":{"name":"uni2231","advance":10,"spans":[[-10,2,1],[-10,6,1],[-9,1,3],[-9,5,3],[-8,0,9],[-7,1,1],[-7,7,1],[-6,0,1],[-6,4,1],[-6,8,1],[-5,0,1],[-5,4,1],[-5,8,1],[-4,0,1],[-4,4,3],[-4,8,1],[-3,0,1],[-3,8,1],[-2,1,1],[-2,7,1],[-1,2,5]],"top":-10,"bottom":0},"∴":{"name":"therefore","advance":9,"spans":[[-9,3,2],[-8,3,2],[-3,0,2],[-3,6,2],[-2,0,2],[-2,6,2]],"top":-9,"bottom":-1},"①":{"name":"onecircle","advance":10,"spans":[[-9,0,9],[-8,0,4],[-8,5,4],[-7,0,4],[-7,5,4],[-6,0,4],[-6,5,4],[-5,0,4],[-5,5,4],[-4,0,4],[-4,5,4],[-3,0,9],[-2,0,4],[-2,5,4],[-1,0,9]],"top":-9,"bottom":0},"⑦":{"name":"sevencircle","advance":10,"spans":[[-9,0,9],[-8,0,3],[-8,6,3],[-7,0,2],[-7,3,3],[-7,7,2],[-6,0,6],[-6,7,2],[-5,0,5],[-5,6,3],[-4,0,4],[-4,5,4],[-3,0,9],[-2,0,4],[-2,5,4],[-1,0,9]],"top":-9,"bottom":0},"Ⓐ":{"name":"Acircle","advance":10,"spans":[[-9,2,5],[-8,1,7],[-7,0,4],[-7,5,4],[-6,0,3],[-6,4,1],[-6,6,3],[-5,0,3],[-5,4,1],[-5,6,3],[-4,0,2],[-4,7,2],[-3,0,2],[-3,3,3],[-3,7,2],[-2,1,7],[-1,2,5]],"top":-9,"bottom":0},"Ⓑ":{"name":"Bcircle","advance":10,"spans":[[-9,2,5],[-8,1,7],[-7,0,2],[-7,6,3],[-6,0,2],[-6,3,3],[-6,7,2],[-5,0,2],[-5,6,3],[-4,0,2],[-4,3,3],[-4,7,2],[-3,0,2],[-3,6,3],[-2,1,7],[-1,2,5]],"top":-9,"bottom":0},"Ⓛ":{"name":"Lcircle","advance":10,"spans":[[-9,3,6],[-8,2,7],[-7,1,2],[-7,4,5],[-6,0,3],[-6,4,5],[-5,0,3],[-5,4,5],[-4,0,3],[-4,4,5],[-3,0,3],[-3,7,2],[-2,0,9],[-1,0,9]],"top":-9,"bottom":0},"Ⓡ":{"name":"Rcircle","advance":10,"spans":[[-9,0,6],[-8,0,7],[-7,0,2],[-7,6,2],[-6,0,2],[-6,3,3],[-6,7,2],[-5,0,2],[-5,6,3],[-4,0,2],[-4,3,2],[-4,6,3],[-3,0,2],[-3,3,3],[-3,7,2],[-2,0,9],[-1,0,9]],"top":-9,"bottom":0},"Ⓧ":{"name":"Xcircle","advance":10,"spans":[[-9,2,5],[-8,1,7],[-7,0,2],[-7,3,3],[-7,7,2],[-6,0,3],[-6,4,1],[-6,6,3],[-5,0,4],[-5,5,4],[-4,0,3],[-4,4,1],[-4,6,3],[-3,0,2],[-3,3,3],[-3,7,2],[-2,1,7],[-1,2,5]],"top":-9,"bottom":0},"Ⓨ":{"name":"Ycircle","advance":10,"spans":[[-9,2,5],[-8,1,7],[-7,0,2],[-7,3,3],[-7,7,2],[-6,0,3],[-6,4,1],[-6,6,3],[-5,0,4],[-5,5,4],[-4,0,4],[-4,5,4],[-3,0,4],[-3,5,4],[-2,1,7],[-1,2,5]],"top":-9,"bottom":0},"■":{"name":"blacksquare","advance":10,"spans":[[-9,0,9],[-8,0,9],[-7,0,9],[-6,0,9],[-5,0,9],[-4,0,9],[-3,0,9],[-2,0,9],[-1,0,9]],"top":-9,"bottom":0},"□":{"name":"H22073","advance":10,"spans":[[-9,0,9],[-8,0,1],[-8,8,1],[-7,0,1],[-7,8,1],[-6,0,1],[-6,8,1],[-5,0,1],[-5,8,1],[-4,0,1],[-4,8,1],[-3,0,1],[-3,8,1],[-2,0,1],[-2,8,1],[-1,0,9]],"top":-9,"bottom":0},"▲":{"name":"blackuppointingtriangle","advance":10,"spans":[[-9,4,1],[-8,3,3],[-7,3,3],[-6,2,5],[-5,2,5],[-4,1,7],[-3,1,7],[-2,0,9],[-1,0,9]],"top":-9,"bottom":0},"△":{"name":"whiteuppointingtriangle","advance":10,"spans":[[-9,4,1],[-8,3,1],[-8,5,1],[-7,3,1],[-7,5,1],[-6,2,1],[-6,6,1],[-5,2,1],[-5,6,1],[-4,1,1],[-4,7,1],[-3,1,1],[-3,7,1],[-2,0,1],[-2,8,1],[-1,0,9]],"top":-9,"bottom":0},"▼":{"name":"blackdownpointingtriangle","advance":10,"spans":[[-9,0,9],[-8,0,9],[-7,1,7],[-6,1,7],[-5,2,5],[-4,2,5],[-3,3,3],[-2,3,3],[-1,4,1]],"top":-9,"bottom":0},"▽":{"name":"whitedownpointingtriangle","advance":10,"spans":[[-9,0,9],[-8,0,1],[-8,8,1],[-7,1,1],[-7,7,1],[-6,1,1],[-6,7,1],[-5,2,1],[-5,6,1],[-4,2,1],[-4,6,1],[-3,3,1],[-3,5,1],[-2,3,1],[-2,5,1],[-1,4,1]],"top":-9,"bottom":0},"◆":{"name":"blackdiamond","advance":10,"spans":[[-9,4,1],[-8,3,3],[-7,2,5],[-6,1,7],[-5,0,9],[-4,1,7],[-3,2,5],[-2,3,3],[-1,4,1]],"top":-9,"bottom":0},"◇":{"name":"whitediamond","advance":10,"spans":[[-9,4,1],[-8,3,1],[-8,5,1],[-7,2,1],[-7,6,1],[-6,1,1],[-6,7,1],[-5,0,1],[-5,8,1],[-4,1,1],[-4,7,1],[-3,2,1],[-3,6,1],[-2,3,1],[-2,5,1],[-1,4,1]],"top":-9,"bottom":0},"○":{"name":"circle","advance":10,"spans":[[-9,2,5],[-8,1,1],[-8,7,1],[-7,0,1],[-7,8,1],[-6,0,1],[-6,8,1],[-5,0,1],[-5,8,1],[-4,0,1],[-4,8,1],[-3,0,1],[-3,8,1],[-2,1,1],[-2,7,1],[-1,2,5]],"top":-9,"bottom":0},"●":{"name":"blackcircle","advance":10,"spans":[[-9,2,5],[-8,1,7],[-7,0,9],[-6,0,9],[-5,0,9],[-4,0,9],[-3,0,9],[-2,1,7],[-1,2,5]],"top":-9,"bottom":0},"☁":{"name":"uni2601","advance":10,"spans":[[-9,3,3],[-8,2,1],[-8,6,1],[-7,1,2],[-7,6,2],[-6,0,1],[-6,8,1],[-5,0,1],[-5,8,1],[-4,0,1],[-4,5,1],[-4,8,1],[-3,1,1],[-3,4,1],[-3,7,1],[-2,2,2],[-2,5,2]],"top":-9,"bottom":-1},"☂":{"name":"uni2602","advance":10,"spans":[[-9,4,1],[-8,2,5],[-7,1,7],[-6,0,9],[-5,4,1],[-4,4,1],[-3,4,1],[-2,4,1],[-2,6,1],[-1,5,1]],"top":-9,"bottom":0},"☃":{"name":"uni2603","advance":8,"spans":[[-9,2,3],[-8,1,1],[-8,5,1],[-7,1,1],[-7,5,1],[-6,1,2],[-6,4,2],[-5,0,1],[-5,6,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,1,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"★":{"name":"blackstar","advance":10,"spans":[[-9,4,1],[-8,4,1],[-7,3,3],[-6,0,9],[-5,2,5],[-4,3,3],[-3,2,2],[-3,5,2],[-2,2,1],[-2,6,1],[-1,1,1],[-1,7,1]],"top":-9,"bottom":0},"☆":{"name":"whitestar","advance":10,"spans":[[-9,4,1],[-8,4,1],[-7,3,1],[-7,5,1],[-6,0,4],[-6,5,4],[-5,1,1],[-5,7,1],[-4,2,1],[-4,4,1],[-4,6,1],[-3,2,2],[-3,5,2],[-2,2,1],[-2,6,1],[-1,1,1],[-1,7,1]],"top":-9,"bottom":0},"☎":{"name":"telephoneblack","advance":6,"spans":[[-9,4,1],[-8,0,5],[-7,0,1],[-7,4,1],[-6,0,1],[-6,4,1],[-5,0,5],[-4,0,1],[-4,2,1],[-4,4,1],[-3,0,2],[-3,3,2],[-2,0,1],[-2,2,1],[-2,4,1],[-1,0,5]],"top":-9,"bottom":0},"☓":{"name":"uni2613","advance":10,"spans":[[-9,0,1],[-9,8,1],[-8,1,1],[-8,7,1],[-7,2,1],[-7,6,1],[-6,3,1],[-6,5,1],[-5,4,1],[-4,3,1],[-4,5,1],[-3,2,1],[-3,6,1],[-2,1,1],[-2,7,1],[-1,0,1],[-1,8,1]],"top":-9,"bottom":0},"☹":{"name":"uni2639","advance":10,"spans":[[-9,0,9],[-8,0,1],[-8,8,1],[-7,0,1],[-7,8,1],[-6,0,1],[-6,3,1],[-6,5,1],[-6,8,1],[-5,0,1],[-5,2,1],[-5,6,1],[-5,8,1],[-4,0,1],[-4,8,1],[-3,0,1],[-3,4,1],[-3,8,1],[-2,0,1],[-2,8,1],[-1,0,9]],"top":-9,"bottom":0},"☺":{"name":"smileface","advance":10,"spans":[[-9,0,9],[-8,0,1],[-8,8,1],[-7,0,1],[-7,3,1],[-7,5,1],[-7,8,1],[-6,0,1],[-6,3,1],[-6,5,1],[-6,8,1],[-5,0,1],[-5,8,1],[-4,0,1],[-4,2,1],[-4,6,1],[-4,8,1],[-3,0,1],[-3,3,3],[-3,8,1],[-2,0,1],[-2,8,1],[-1,0,9]],"top":-9,"bottom":0},"☻":{"name":"blacksmilingface","advance":10,"spans":[[-9,0,9],[-8,0,1],[-8,8,1],[-7,0,1],[-7,2,1],[-7,6,1],[-7,8,1],[-6,0,1],[-6,3,1],[-6,5,1],[-6,8,1],[-5,0,1],[-5,8,1],[-4,0,1],[-4,3,3],[-4,8,1],[-3,0,1],[-3,2,1],[-3,6,1],[-3,8,1],[-2,0,1],[-2,8,1],[-1,0,9]],"top":-9,"bottom":0},"☼":{"name":"compass","advance":10,"spans":[[-9,4,1],[-8,1,1],[-8,7,1],[-7,3,3],[-6,2,1],[-6,6,1],[-5,0,1],[-5,2,1],[-5,6,1],[-5,8,1],[-4,2,1],[-4,6,1],[-3,3,3],[-2,1,1],[-2,7,1],[-1,4,1]],"top":-9,"bottom":0},"♠":{"name":"spade","advance":10,"spans":[[-9,4,1],[-8,3,3],[-7,2,5],[-6,1,7],[-5,0,9],[-4,0,9],[-3,1,2],[-3,4,1],[-3,6,2],[-2,3,3],[-1,2,5]],"top":-9,"bottom":0},"♣":{"name":"club","advance":10,"spans":[[-9,3,3],[-8,2,5],[-7,3,3],[-6,1,2],[-6,4,1],[-6,6,2],[-5,0,9],[-4,0,9],[-3,1,2],[-3,4,1],[-3,6,2],[-2,3,3],[-1,2,5]],"top":-9,"bottom":0},"♥":{"name":"heart","advance":10,"spans":[[-8,1,2],[-8,6,2],[-7,0,4],[-7,5,4],[-6,0,9],[-5,0,9],[-4,1,7],[-3,2,5],[-2,3,3],[-1,4,1]],"top":-8,"bottom":0},"♦":{"name":"diamond","advance":10,"spans":[[-9,4,1],[-8,3,3],[-7,2,5],[-6,1,7],[-5,0,9],[-4,1,7],[-3,2,5],[-2,3,3],[-1,4,1]],"top":-9,"bottom":0},"♩":{"name":"quarternote","advance":7,"spans":[[-9,3,1],[-8,3,2],[-7,3,1],[-7,5,1],[-6,3,1],[-6,5,1],[-5,3,1],[-5,5,1],[-4,3,1],[-3,1,3],[-2,0,3],[-1,0,2]],"top":-9,"bottom":0},"♭":{"name":"musicflatsign","advance":6,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-6,2,2],[-5,0,2],[-5,4,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,3,1],[-2,0,1],[-2,2,1],[-1,0,2]],"top":-9,"bottom":0},"♯":{"name":"musicsharpsign","advance":8,"spans":[[-9,3,1],[-9,5,1],[-8,3,1],[-8,5,1],[-7,1,6],[-6,2,1],[-6,4,1],[-5,2,1],[-5,4,1],[-4,2,1],[-4,4,1],[-3,0,6],[-2,1,1],[-2,3,1],[-1,1,1],[-1,3,1]],"top":-9,"bottom":0},"⚀":{"name":"uni2680","advance":10,"spans":[[-9,0,9],[-8,0,1],[-8,8,1],[-7,0,1],[-7,8,1],[-6,0,1],[-6,2,2],[-6,5,2],[-6,8,1],[-5,0,1],[-5,8,1],[-4,0,1],[-4,8,1],[-3,0,1],[-3,3,3],[-3,8,1],[-2,0,1],[-2,8,1],[-1,0,9]],"top":-9,"bottom":0},"✉":{"name":"uni2709","advance":10,"spans":[[-8,0,9],[-7,0,2],[-7,7,2],[-6,0,1],[-6,2,1],[-6,6,1],[-6,8,1],[-5,0,1],[-5,3,1],[-5,5,1],[-5,8,1],[-4,0,1],[-4,4,1],[-4,8,1],[-3,0,1],[-3,8,1],[-2,0,9]],"top":-8,"bottom":-1},"✚":{"name":"uni271A","advance":10,"spans":[[-9,3,3],[-8,3,3],[-7,3,3],[-6,0,9],[-5,0,4],[-5,5,4],[-4,0,9],[-3,3,3],[-2,3,3],[-1,3,3]],"top":-9,"bottom":0},"➡":{"name":"uni27A1","advance":10,"spans":[[-9,2,3],[-8,3,3],[-7,4,3],[-6,0,8],[-5,0,9],[-4,0,8],[-3,4,3],[-2,3,3],[-1,2,3]],"top":-9,"bottom":0},"⦾":{"name":"uni29BE","advance":10,"spans":[[-9,2,5],[-8,1,1],[-8,7,1],[-7,0,1],[-7,3,3],[-7,8,1],[-6,0,1],[-6,2,1],[-6,6,1],[-6,8,1],[-5,0,1],[-5,2,1],[-5,6,1],[-5,8,1],[-4,0,1],[-4,2,1],[-4,6,1],[-4,8,1],[-3,0,1],[-3,3,3],[-3,8,1],[-2,1,1],[-2,7,1],[-1,2,5]],"top":-9,"bottom":0},"⬅":{"name":"uni2B05","advance":10,"spans":[[-9,4,3],[-8,3,3],[-7,2,3],[-6,1,8],[-5,0,9],[-4,1,8],[-3,2,3],[-2,3,3],[-1,4,3]],"top":-9,"bottom":0},"⬆":{"name":"uni2B06","advance":10,"spans":[[-9,4,1],[-8,3,3],[-7,2,5],[-6,1,7],[-5,0,9],[-4,0,2],[-4,3,3],[-4,7,2],[-3,0,1],[-3,3,3],[-3,8,1],[-2,3,3],[-1,3,3]],"top":-9,"bottom":0},"⬇":{"name":"uni2B07","advance":10,"spans":[[-9,3,3],[-8,3,3],[-7,0,1],[-7,3,3],[-7,8,1],[-6,0,2],[-6,3,3],[-6,7,2],[-5,0,9],[-4,1,7],[-3,2,5],[-2,3,3],[-1,4,1]],"top":-9,"bottom":0}," ":{"name":"ideographicspace","advance":16,"spans":[],"top":0,"bottom":0},"、":{"name":"ideographiccomma","advance":3,"spans":[[-3,0,1],[-2,1,1],[-1,1,1]],"top":-3,"bottom":0},"。":{"name":"ideographicperiod","advance":5,"spans":[[-4,1,2],[-3,0,1],[-3,3,1],[-2,0,1],[-2,3,1],[-1,1,2]],"top":-4,"bottom":0},"「":{"name":"cornerbracketleft","advance":5,"spans":[[-9,0,4],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1]],"top":-9,"bottom":-2},"」":{"name":"cornerbracketright","advance":5,"spans":[[-7,3,1],[-6,3,1],[-5,3,1],[-4,3,1],[-3,3,1],[-2,3,1],[-1,0,4]],"top":-7,"bottom":0},"〒":{"name":"postalmark","advance":10,"spans":[[-9,0,9],[-6,0,9],[-5,4,1],[-4,4,1],[-3,4,1],[-2,4,1],[-1,4,1]],"top":-9,"bottom":0},"ぁ":{"name":"asmallhiragana","advance":9,"spans":[[-8,3,1],[-7,1,5],[-6,3,1],[-5,2,5],[-4,1,1],[-4,3,1],[-4,5,1],[-4,7,1],[-3,0,1],[-3,3,2],[-3,7,1],[-2,0,1],[-2,3,1],[-2,7,1],[-1,1,2],[-1,5,2]],"top":-8,"bottom":0},"あ":{"name":"ahiragana","advance":10,"spans":[[-9,3,1],[-8,1,7],[-7,3,1],[-6,2,5],[-5,1,1],[-5,3,1],[-5,5,1],[-5,7,1],[-4,0,1],[-4,3,1],[-4,5,1],[-4,8,1],[-3,0,1],[-3,3,2],[-3,8,1],[-2,0,1],[-2,3,1],[-2,8,1],[-1,1,2],[-1,6,2]],"top":-9,"bottom":0},"ぃ":{"name":"ismallhiragana","advance":8,"spans":[[-7,0,1],[-7,5,1],[-6,0,1],[-6,5,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,2,1],[-3,6,1],[-2,0,1],[-2,2,1],[-2,6,1],[-1,1,1]],"top":-7,"bottom":0},"い":{"name":"ihiragana","advance":10,"spans":[[-8,0,1],[-8,6,1],[-7,0,1],[-7,7,1],[-6,0,1],[-6,7,1],[-5,0,1],[-5,8,1],[-4,0,1],[-4,8,1],[-3,0,1],[-3,3,1],[-3,8,1],[-2,1,1],[-2,3,1],[-2,8,1],[-1,1,2]],"top":-8,"bottom":0},"ぅ":{"name":"usmallhiragana","advance":8,"spans":[[-8,1,4],[-6,1,5],[-5,0,1],[-5,6,1],[-4,6,1],[-3,6,1],[-2,4,2],[-1,1,3]],"top":-8,"bottom":0},"う":{"name":"uhiragana","advance":9,"spans":[[-9,2,4],[-7,2,5],[-6,0,2],[-6,7,1],[-5,7,1],[-4,7,1],[-3,7,1],[-2,5,2],[-1,2,3]],"top":-9,"bottom":0},"ぇ":{"name":"esmallhiragana","advance":8,"spans":[[-8,1,4],[-6,0,6],[-5,4,1],[-4,3,1],[-3,2,1],[-3,4,1],[-2,1,1],[-2,4,1],[-1,0,1],[-1,5,2]],"top":-8,"bottom":0},"え":{"name":"ehiragana","advance":10,"spans":[[-9,2,4],[-7,0,7],[-6,5,1],[-5,4,1],[-4,3,2],[-3,2,1],[-3,5,1],[-2,1,1],[-2,5,1],[-1,0,1],[-1,6,3]],"top":-9,"bottom":0},"ぉ":{"name":"osmallhiragana","advance":10,"spans":[[-8,3,1],[-7,1,5],[-7,7,1],[-6,3,1],[-6,8,1],[-5,2,5],[-4,1,1],[-4,3,1],[-4,7,1],[-3,0,1],[-3,3,1],[-3,7,1],[-2,0,1],[-2,3,1],[-2,7,1],[-1,1,2],[-1,5,2]],"top":-8,"bottom":0},"お":{"name":"ohiragana","advance":10,"spans":[[-9,3,1],[-9,6,1],[-8,1,5],[-8,7,1],[-7,3,1],[-7,8,1],[-6,2,5],[-5,1,1],[-5,3,1],[-5,7,1],[-4,0,1],[-4,3,1],[-4,8,1],[-3,0,1],[-3,3,1],[-3,8,1],[-2,0,1],[-2,3,1],[-2,8,1],[-1,1,2],[-1,6,2]],"top":-9,"bottom":0},"か":{"name":"kahiragana","advance":10,"spans":[[-9,2,1],[-8,2,1],[-8,6,1],[-7,0,5],[-7,7,1],[-6,2,1],[-6,5,1],[-6,8,1],[-5,2,1],[-5,5,1],[-5,8,1],[-4,2,1],[-4,5,1],[-3,1,1],[-3,5,1],[-2,1,1],[-2,5,1],[-1,0,1],[-1,3,2]],"top":-9,"bottom":0},"が":{"name":"gahiragana","advance":10,"spans":[[-9,2,1],[-9,7,2],[-8,2,1],[-8,6,1],[-7,0,5],[-7,7,1],[-6,2,1],[-6,5,1],[-6,8,1],[-5,2,1],[-5,5,1],[-5,8,1],[-4,2,1],[-4,5,1],[-3,1,1],[-3,5,1],[-2,1,1],[-2,5,1],[-1,0,1],[-1,3,2]],"top":-9,"bottom":0},"き":{"name":"kihiragana","advance":10,"spans":[[-9,4,1],[-8,1,7],[-7,5,1],[-6,0,9],[-5,6,1],[-4,2,6],[-3,1,1],[-3,7,1],[-2,1,1],[-1,2,5]],"top":-9,"bottom":0},"ぎ":{"name":"gihiragana","advance":10,"spans":[[-9,4,1],[-9,6,1],[-9,8,1],[-8,1,6],[-8,8,1],[-7,5,1],[-6,0,9],[-5,6,1],[-4,2,6],[-3,1,1],[-3,7,1],[-2,1,1],[-1,2,5]],"top":-9,"bottom":0},"く":{"name":"kuhiragana","advance":8,"spans":[[-9,6,1],[-8,5,1],[-7,3,2],[-6,1,2],[-5,0,1],[-4,1,2],[-3,3,2],[-2,5,1],[-1,6,1]],"top":-9,"bottom":0},"ぐ":{"name":"guhiragana","advance":10,"spans":[[-9,6,1],[-8,5,1],[-7,3,2],[-7,6,1],[-7,8,1],[-6,1,2],[-6,6,1],[-6,8,1],[-5,0,1],[-4,1,2],[-3,3,2],[-2,5,1],[-1,6,1]],"top":-9,"bottom":0},"け":{"name":"kehiragana","advance":10,"spans":[[-9,0,1],[-9,6,1],[-8,0,1],[-8,6,1],[-7,0,1],[-7,2,7],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,0,1],[-2,5,1],[-1,3,2]],"top":-9,"bottom":0},"げ":{"name":"gehiragana","advance":10,"spans":[[-9,0,1],[-9,6,1],[-9,8,1],[-8,0,1],[-8,6,1],[-8,8,1],[-7,0,1],[-7,2,6],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,0,1],[-2,5,1],[-1,3,2]],"top":-9,"bottom":0},"こ":{"name":"kohiragana","advance":9,"spans":[[-8,1,6],[-4,1,1],[-3,0,1],[-2,0,1],[-1,1,7]],"top":-8,"bottom":0},"ご":{"name":"gohiragana","advance":10,"spans":[[-9,6,1],[-9,8,1],[-8,1,4],[-8,6,1],[-8,8,1],[-4,1,1],[-3,0,1],[-2,0,1],[-1,1,7]],"top":-9,"bottom":0},"さ":{"name":"sahiragana","advance":9,"spans":[[-9,5,1],[-8,0,8],[-7,5,1],[-6,5,1],[-5,1,6],[-4,0,1],[-4,6,1],[-3,0,1],[-2,0,1],[-1,1,6]],"top":-9,"bottom":0},"ざ":{"name":"zahiragana","advance":10,"spans":[[-9,4,1],[-9,6,1],[-9,8,1],[-8,0,7],[-8,8,1],[-7,5,1],[-6,5,1],[-5,1,6],[-4,0,1],[-4,6,1],[-3,0,1],[-2,0,1],[-1,1,6]],"top":-9,"bottom":0},"し":{"name":"sihiragana","advance":8,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,0,1],[-2,5,1],[-1,1,4]],"top":-9,"bottom":0},"じ":{"name":"zihiragana","advance":8,"spans":[[-9,0,1],[-8,0,1],[-8,4,1],[-8,6,1],[-7,0,1],[-7,4,1],[-7,6,1],[-6,0,1],[-5,0,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,0,1],[-2,5,1],[-1,1,4]],"top":-9,"bottom":0},"す":{"name":"suhiragana","advance":9,"spans":[[-9,5,1],[-8,0,8],[-7,5,1],[-6,2,4],[-5,1,1],[-5,5,1],[-4,1,1],[-4,5,1],[-3,2,4],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ず":{"name":"zuhiragana","advance":10,"spans":[[-9,5,2],[-9,8,1],[-8,0,9],[-7,5,1],[-6,2,4],[-5,1,1],[-5,5,1],[-4,1,1],[-4,5,1],[-3,2,4],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"せ":{"name":"sehiragana","advance":10,"spans":[[-9,1,1],[-9,6,1],[-8,1,1],[-8,6,1],[-7,0,9],[-6,1,1],[-6,6,1],[-5,1,1],[-5,6,1],[-4,1,1],[-4,4,2],[-3,1,1],[-2,1,1],[-1,2,6]],"top":-9,"bottom":0},"ぜ":{"name":"zehiragana","advance":10,"spans":[[-9,1,1],[-9,6,1],[-9,8,1],[-8,1,1],[-8,6,1],[-8,8,1],[-7,0,9],[-6,1,1],[-6,6,1],[-5,1,1],[-5,6,1],[-4,1,1],[-4,4,2],[-3,1,1],[-2,1,1],[-1,2,6]],"top":-9,"bottom":0},"そ":{"name":"sohiragana","advance":10,"spans":[[-9,1,6],[-8,4,1],[-7,3,1],[-6,0,9],[-5,4,1],[-4,3,1],[-3,3,1],[-2,3,1],[-1,4,3]],"top":-9,"bottom":0},"ぞ":{"name":"zohiragana","advance":10,"spans":[[-9,1,6],[-9,8,1],[-8,4,1],[-8,6,1],[-8,8,1],[-7,3,1],[-6,0,9],[-5,4,1],[-4,3,1],[-3,3,1],[-2,3,1],[-1,4,3]],"top":-9,"bottom":0},"た":{"name":"tahiragana","advance":10,"spans":[[-9,3,1],[-8,1,7],[-7,3,1],[-6,3,1],[-6,5,4],[-5,2,1],[-4,2,1],[-3,2,1],[-3,5,1],[-2,1,1],[-2,4,1],[-1,1,1],[-1,5,4]],"top":-9,"bottom":0},"だ":{"name":"dahiragana","advance":11,"spans":[[-9,3,1],[-9,7,1],[-9,9,1],[-8,1,5],[-8,7,1],[-8,9,1],[-7,3,1],[-6,3,1],[-6,5,4],[-5,2,1],[-4,2,1],[-3,2,1],[-3,5,1],[-2,1,1],[-2,4,1],[-1,1,1],[-1,5,4]],"top":-9,"bottom":0},"ち":{"name":"tihiragana","advance":9,"spans":[[-9,3,1],[-8,0,8],[-7,2,1],[-6,2,1],[-5,1,6],[-4,1,1],[-4,7,1],[-3,7,1],[-2,7,1],[-1,1,6]],"top":-9,"bottom":0},"ぢ":{"name":"dihiragana","advance":10,"spans":[[-9,3,1],[-9,6,1],[-9,8,1],[-8,0,7],[-8,8,1],[-7,2,1],[-6,2,1],[-5,1,6],[-4,1,1],[-4,7,1],[-3,7,1],[-2,7,1],[-1,1,6]],"top":-9,"bottom":0},"っ":{"name":"tusmallhiragana","advance":9,"spans":[[-7,2,5],[-6,0,2],[-6,7,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,2,4]],"top":-7,"bottom":-1},"つ":{"name":"tuhiragana","advance":10,"spans":[[-8,2,5],[-7,0,2],[-7,7,1],[-6,8,1],[-5,8,1],[-4,8,1],[-3,8,1],[-2,6,2],[-1,2,4]],"top":-8,"bottom":0},"づ":{"name":"duhiragana","advance":10,"spans":[[-9,6,1],[-9,8,1],[-8,2,5],[-8,8,1],[-7,0,2],[-7,7,1],[-6,8,1],[-5,8,1],[-4,8,1],[-3,8,1],[-2,6,2],[-1,2,4]],"top":-9,"bottom":0},"て":{"name":"tehiragana","advance":10,"spans":[[-8,0,9],[-7,5,1],[-6,4,1],[-5,3,1],[-4,3,1],[-3,3,1],[-2,4,1],[-1,5,3]],"top":-8,"bottom":0},"で":{"name":"dehiragana","advance":11,"spans":[[-8,0,9],[-7,5,1],[-6,4,1],[-6,7,1],[-6,9,1],[-5,3,1],[-5,7,1],[-5,9,1],[-4,3,1],[-3,3,1],[-2,4,1],[-1,5,3]],"top":-8,"bottom":0},"と":{"name":"tohiragana","advance":9,"spans":[[-9,1,1],[-8,1,1],[-8,6,2],[-7,2,1],[-7,4,2],[-6,2,2],[-5,1,1],[-4,0,1],[-3,0,1],[-2,1,1],[-1,2,6]],"top":-9,"bottom":0},"ど":{"name":"dohiragana","advance":10,"spans":[[-9,1,1],[-9,6,1],[-9,8,1],[-8,1,1],[-8,6,1],[-8,8,1],[-7,2,1],[-7,4,2],[-6,2,2],[-5,1,1],[-4,0,1],[-3,0,1],[-2,1,1],[-1,2,6]],"top":-9,"bottom":0},"な":{"name":"nahiragana","advance":11,"spans":[[-9,3,1],[-8,1,4],[-8,6,3],[-7,3,1],[-7,9,1],[-6,3,1],[-6,7,1],[-5,2,1],[-5,7,1],[-4,2,1],[-4,5,3],[-3,2,1],[-3,4,1],[-3,7,2],[-2,1,1],[-2,4,1],[-2,7,1],[-2,9,1],[-1,1,1],[-1,5,2]],"top":-9,"bottom":0},"に":{"name":"nihiragana","advance":10,"spans":[[-9,0,1],[-8,0,1],[-8,3,6],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-4,4,1],[-3,0,1],[-3,3,1],[-2,0,1],[-2,3,1],[-1,0,1],[-1,4,5]],"top":-9,"bottom":0},"ぬ":{"name":"nuhiragana","advance":10,"spans":[[-9,2,1],[-9,5,1],[-8,2,5],[-7,1,2],[-7,5,1],[-7,7,1],[-6,0,1],[-6,2,1],[-6,4,1],[-6,8,1],[-5,0,1],[-5,3,2],[-5,8,1],[-4,0,1],[-4,3,1],[-4,8,1],[-3,0,1],[-3,3,1],[-3,5,2],[-3,8,1],[-2,0,1],[-2,2,1],[-2,4,1],[-2,7,1],[-1,1,1],[-1,5,2],[-1,8,1]],"top":-9,"bottom":0},"ね":{"name":"nehiragana","advance":10,"spans":[[-9,2,1],[-8,0,3],[-8,4,3],[-7,2,2],[-7,7,1],[-6,2,1],[-6,8,1],[-5,1,2],[-5,8,1],[-4,0,1],[-4,2,1],[-4,8,1],[-3,0,1],[-3,2,1],[-3,5,2],[-3,8,1],[-2,2,1],[-2,4,1],[-2,7,1],[-1,2,1],[-1,5,2],[-1,8,1]],"top":-9,"bottom":0},"の":{"name":"nohiragana","advance":10,"spans":[[-8,2,5],[-7,1,1],[-7,4,1],[-7,7,1],[-6,0,1],[-6,4,1],[-6,8,1],[-5,0,1],[-5,4,1],[-5,8,1],[-4,0,1],[-4,4,1],[-4,8,1],[-3,0,1],[-3,3,1],[-3,8,1],[-2,0,1],[-2,3,1],[-2,7,1],[-1,1,2],[-1,5,2]],"top":-8,"bottom":0},"は":{"name":"hahiragana","advance":10,"spans":[[-9,0,1],[-9,6,1],[-8,0,1],[-8,6,1],[-7,0,1],[-7,3,6],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,4,3],[-3,0,1],[-3,3,1],[-3,6,2],[-2,0,1],[-2,3,1],[-2,6,1],[-2,8,1],[-1,0,1],[-1,4,2]],"top":-9,"bottom":0},"ば":{"name":"bahiragana","advance":10,"spans":[[-9,0,1],[-9,6,1],[-9,8,1],[-8,0,1],[-8,6,1],[-8,8,1],[-7,0,1],[-7,3,6],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,4,3],[-3,0,1],[-3,3,1],[-3,6,2],[-2,0,1],[-2,3,1],[-2,6,1],[-2,8,1],[-1,0,1],[-1,4,2]],"top":-9,"bottom":0},"ぱ":{"name":"pahiragana","advance":11,"spans":[[-9,0,1],[-9,6,1],[-9,8,1],[-8,0,1],[-8,6,2],[-8,9,1],[-7,0,1],[-7,3,4],[-7,8,1],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,4,3],[-3,0,1],[-3,3,1],[-3,6,2],[-2,0,1],[-2,3,1],[-2,6,1],[-2,8,1],[-1,0,1],[-1,4,2]],"top":-9,"bottom":0},"ひ":{"name":"hihiragana","advance":10,"spans":[[-9,0,4],[-9,5,1],[-8,2,1],[-8,5,1],[-7,1,1],[-7,6,1],[-6,0,1],[-6,6,3],[-5,0,1],[-5,6,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,1,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"び":{"name":"bihiragana","advance":10,"spans":[[-9,0,4],[-9,6,1],[-9,8,1],[-8,2,1],[-8,6,1],[-8,8,1],[-7,1,1],[-7,5,1],[-6,0,1],[-6,6,2],[-5,0,1],[-5,6,1],[-5,8,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,1,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ぴ":{"name":"pihiragana","advance":10,"spans":[[-9,0,4],[-9,7,1],[-8,2,1],[-8,6,1],[-8,8,1],[-7,1,1],[-7,5,1],[-7,7,1],[-6,0,1],[-6,6,2],[-5,0,1],[-5,6,1],[-5,8,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,6,1],[-2,1,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ふ":{"name":"huhiragana","advance":10,"spans":[[-9,2,4],[-8,4,1],[-7,3,1],[-6,3,1],[-5,1,1],[-5,4,1],[-5,7,1],[-4,1,1],[-4,5,1],[-4,7,1],[-3,0,1],[-3,5,1],[-3,8,1],[-2,0,1],[-2,5,1],[-2,8,1],[-1,2,3]],"top":-9,"bottom":0},"ぶ":{"name":"buhiragana","advance":10,"spans":[[-9,2,4],[-8,4,1],[-8,6,1],[-8,8,1],[-7,3,1],[-7,6,1],[-7,8,1],[-6,3,1],[-5,1,1],[-5,4,1],[-5,7,1],[-4,1,1],[-4,5,1],[-4,7,1],[-3,0,1],[-3,5,1],[-3,8,1],[-2,0,1],[-2,5,1],[-2,8,1],[-1,2,3]],"top":-9,"bottom":0},"ぷ":{"name":"puhiragana","advance":10,"spans":[[-9,2,4],[-9,7,1],[-8,4,1],[-8,6,1],[-8,8,1],[-7,3,1],[-7,7,1],[-6,3,1],[-5,1,1],[-5,4,1],[-5,7,1],[-4,1,1],[-4,5,1],[-4,7,1],[-3,0,1],[-3,5,1],[-3,8,1],[-2,0,1],[-2,5,1],[-2,8,1],[-1,2,3]],"top":-9,"bottom":0},"へ":{"name":"hehiragana","advance":10,"spans":[[-8,3,1],[-7,2,1],[-7,4,1],[-6,1,1],[-6,5,1],[-5,1,1],[-5,5,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,7,1],[-2,8,1]],"top":-8,"bottom":-1},"べ":{"name":"behiragana","advance":10,"spans":[[-9,6,1],[-9,8,1],[-8,3,1],[-8,6,1],[-8,8,1],[-7,2,1],[-7,4,1],[-6,1,1],[-6,5,1],[-5,1,1],[-5,5,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,7,1],[-2,8,1]],"top":-9,"bottom":-1},"ぺ":{"name":"pehiragana","advance":10,"spans":[[-9,7,1],[-8,3,1],[-8,6,1],[-8,8,1],[-7,2,1],[-7,4,1],[-7,7,1],[-6,1,1],[-6,5,1],[-5,1,1],[-5,5,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,7,1],[-2,8,1]],"top":-9,"bottom":-1},"ほ":{"name":"hohiragana","advance":10,"spans":[[-9,0,1],[-9,3,6],[-8,0,1],[-8,6,1],[-7,0,1],[-7,3,6],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,4,3],[-3,0,1],[-3,3,1],[-3,6,2],[-2,0,1],[-2,3,1],[-2,6,1],[-2,8,1],[-1,0,1],[-1,4,2]],"top":-9,"bottom":0},"ぼ":{"name":"bohiragana","advance":11,"spans":[[-9,0,1],[-9,3,5],[-9,9,1],[-8,0,1],[-8,6,1],[-8,9,1],[-7,0,1],[-7,3,6],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,4,3],[-3,0,1],[-3,3,1],[-3,6,2],[-2,0,1],[-2,3,1],[-2,6,1],[-2,8,1],[-1,0,1],[-1,4,2]],"top":-9,"bottom":0},"ぽ":{"name":"pohiragana","advance":11,"spans":[[-9,0,1],[-9,3,4],[-9,8,1],[-8,0,1],[-8,6,2],[-8,9,1],[-7,0,1],[-7,3,4],[-7,8,1],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,4,3],[-3,0,1],[-3,3,1],[-3,6,2],[-2,0,1],[-2,3,1],[-2,6,1],[-2,8,1],[-1,0,1],[-1,4,2]],"top":-9,"bottom":0},"ま":{"name":"mahiragana","advance":10,"spans":[[-9,5,1],[-8,1,7],[-7,5,1],[-6,0,9],[-5,5,1],[-4,2,4],[-3,1,1],[-3,5,2],[-2,1,1],[-2,5,1],[-2,7,1],[-1,2,3]],"top":-9,"bottom":0},"み":{"name":"mihiragana","advance":10,"spans":[[-9,1,4],[-8,4,1],[-7,4,1],[-7,7,1],[-6,2,4],[-6,7,1],[-5,1,1],[-5,4,1],[-5,6,2],[-4,0,1],[-4,3,1],[-4,7,2],[-3,0,1],[-3,3,1],[-3,7,1],[-2,1,2],[-2,6,1],[-1,5,1]],"top":-9,"bottom":0},"む":{"name":"muhiragana","advance":11,"spans":[[-9,3,1],[-8,3,1],[-8,8,1],[-7,1,5],[-7,9,1],[-6,3,1],[-5,2,2],[-4,1,1],[-4,3,1],[-4,8,1],[-3,1,1],[-3,3,1],[-3,8,1],[-2,2,2],[-2,8,1],[-1,3,5]],"top":-9,"bottom":0},"め":{"name":"mehiragana","advance":10,"spans":[[-9,2,1],[-9,5,1],[-8,2,5],[-7,1,2],[-7,5,1],[-7,7,1],[-6,0,1],[-6,2,1],[-6,5,1],[-6,8,1],[-5,0,1],[-5,2,1],[-5,4,1],[-5,8,1],[-4,0,1],[-4,3,2],[-4,8,1],[-3,0,1],[-3,3,1],[-3,8,1],[-2,1,2],[-2,7,1],[-1,5,2]],"top":-9,"bottom":0},"も":{"name":"mohiragana","advance":9,"spans":[[-9,2,1],[-8,0,6],[-7,2,1],[-6,2,1],[-5,0,6],[-4,2,1],[-4,7,1],[-3,2,1],[-3,7,1],[-2,2,1],[-2,7,1],[-1,3,4]],"top":-9,"bottom":0},"ゃ":{"name":"yasmallhiragana","advance":9,"spans":[[-8,1,1],[-8,4,1],[-7,1,1],[-7,3,4],[-6,0,3],[-6,4,1],[-6,7,1],[-5,2,1],[-5,7,1],[-4,2,1],[-4,7,1],[-3,3,1],[-3,5,2],[-2,3,1],[-1,3,1]],"top":-8,"bottom":0},"や":{"name":"yahiragana","advance":11,"spans":[[-9,2,1],[-9,5,1],[-8,2,1],[-8,5,1],[-8,7,2],[-7,3,2],[-7,6,1],[-7,9,1],[-6,0,4],[-6,6,1],[-6,9,1],[-5,2,1],[-5,9,1],[-4,2,1],[-4,6,3],[-3,3,1],[-2,3,1],[-1,3,1]],"top":-9,"bottom":0},"ゅ":{"name":"yusmallhiragana","advance":9,"spans":[[-8,0,1],[-8,4,1],[-7,0,1],[-7,3,4],[-6,0,1],[-6,2,1],[-6,4,1],[-6,7,1],[-5,0,2],[-5,4,1],[-5,7,1],[-4,0,1],[-4,4,1],[-4,7,1],[-3,0,1],[-3,2,5],[-2,4,1],[-1,2,2]],"top":-8,"bottom":0},"ゆ":{"name":"yuhiragana","advance":10,"spans":[[-9,5,1],[-8,0,1],[-8,3,5],[-7,0,1],[-7,2,1],[-7,5,1],[-7,8,1],[-6,0,2],[-6,5,1],[-6,8,1],[-5,0,1],[-5,2,1],[-5,5,1],[-5,8,1],[-4,0,1],[-4,2,1],[-4,5,1],[-4,8,1],[-3,0,1],[-3,3,5],[-2,5,1],[-1,3,2]],"top":-9,"bottom":0},"ょ":{"name":"yosmallhiragana","advance":9,"spans":[[-8,4,1],[-7,4,1],[-6,4,4],[-5,4,1],[-4,1,4],[-3,0,1],[-3,4,2],[-2,0,1],[-2,4,1],[-2,6,1],[-1,1,3],[-1,7,1]],"top":-8,"bottom":0},"よ":{"name":"yohiragana","advance":9,"spans":[[-9,4,1],[-8,4,1],[-7,4,4],[-6,4,1],[-5,4,1],[-4,1,4],[-3,0,1],[-3,4,2],[-2,0,1],[-2,4,1],[-2,6,1],[-1,1,3],[-1,7,1]],"top":-9,"bottom":0},"ら":{"name":"rahiragana","advance":9,"spans":[[-9,3,2],[-8,1,1],[-8,5,2],[-7,1,1],[-6,0,1],[-6,2,4],[-5,0,2],[-5,6,1],[-4,0,1],[-4,7,1],[-3,7,1],[-2,6,1],[-1,2,4]],"top":-9,"bottom":0},"り":{"name":"rihiragana","advance":8,"spans":[[-9,0,1],[-9,3,2],[-8,0,1],[-8,2,1],[-8,5,1],[-7,0,2],[-7,6,1],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,6,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"る":{"name":"ruhiragana","advance":10,"spans":[[-9,1,7],[-8,5,1],[-7,3,2],[-6,2,5],[-5,1,1],[-5,7,1],[-4,0,1],[-4,8,1],[-3,3,2],[-3,8,1],[-2,2,1],[-2,5,1],[-2,7,1],[-1,3,4]],"top":-9,"bottom":0},"れ":{"name":"rehiragana","advance":10,"spans":[[-9,2,1],[-8,2,1],[-8,4,2],[-7,0,4],[-7,6,1],[-6,2,1],[-6,6,1],[-5,2,1],[-5,6,1],[-4,1,2],[-4,6,1],[-3,0,1],[-3,2,1],[-3,6,1],[-3,8,1],[-2,2,1],[-2,6,2],[-1,2,1]],"top":-9,"bottom":0},"ろ":{"name":"rohiragana","advance":10,"spans":[[-9,1,6],[-8,4,1],[-7,3,1],[-6,2,5],[-5,1,1],[-5,7,1],[-4,0,1],[-4,8,1],[-3,8,1],[-2,7,1],[-1,2,5]],"top":-9,"bottom":0},"ゎ":{"name":"wasmallhiragana","advance":9,"spans":[[-8,2,1],[-7,2,1],[-7,4,3],[-6,0,4],[-6,7,1],[-5,2,1],[-5,7,1],[-4,1,2],[-4,7,1],[-3,0,1],[-3,2,1],[-3,7,1],[-2,2,1],[-2,6,1],[-1,2,1],[-1,5,1]],"top":-8,"bottom":0},"わ":{"name":"wahiragana","advance":10,"spans":[[-9,2,1],[-8,0,3],[-8,4,3],[-7,2,2],[-7,7,1],[-6,2,1],[-6,8,1],[-5,1,2],[-5,8,1],[-4,0,1],[-4,2,1],[-4,8,1],[-3,0,1],[-3,2,1],[-3,8,1],[-2,2,1],[-2,7,1],[-1,2,1],[-1,5,2]],"top":-9,"bottom":0},"を":{"name":"wohiragana","advance":10,"spans":[[-9,4,1],[-8,1,7],[-7,3,1],[-6,2,4],[-6,7,2],[-5,1,1],[-5,5,2],[-4,0,1],[-4,3,2],[-4,6,1],[-3,2,1],[-3,6,1],[-2,2,1],[-1,3,6]],"top":-9,"bottom":0},"ん":{"name":"nhiragana","advance":10,"spans":[[-9,3,1],[-8,3,1],[-7,2,1],[-6,2,1],[-5,1,1],[-5,3,1],[-4,1,2],[-4,4,1],[-4,8,1],[-3,1,1],[-3,4,1],[-3,8,1],[-2,0,1],[-2,4,1],[-2,7,1],[-1,0,1],[-1,5,2]],"top":-9,"bottom":0},"゛":{"name":"voicedmarkkana","advance":5,"spans":[[-9,2,1],[-8,0,1],[-8,3,1],[-7,1,1],[-7,3,1],[-6,1,1]],"top":-9,"bottom":-5},"゜":{"name":"semivoicedmarkkana","advance":5,"spans":[[-9,1,2],[-8,0,1],[-8,3,1],[-7,0,1],[-7,3,1],[-6,1,2]],"top":-9,"bottom":-5},"ァ":{"name":"asmallkatakana","advance":9,"spans":[[-8,0,8],[-7,7,1],[-6,3,1],[-6,7,1],[-5,3,1],[-5,6,1],[-4,3,1],[-4,5,1],[-3,3,1],[-2,2,1],[-1,1,1]],"top":-8,"bottom":0},"ア":{"name":"akatakana","advance":9,"spans":[[-9,0,8],[-8,7,1],[-7,3,1],[-7,7,1],[-6,3,1],[-6,7,1],[-5,3,1],[-5,6,1],[-4,3,1],[-4,5,1],[-3,3,1],[-2,2,1],[-1,1,1]],"top":-9,"bottom":0},"ィ":{"name":"ismallkatakana","advance":8,"spans":[[-8,6,1],[-7,5,1],[-6,3,2],[-5,0,3],[-5,4,1],[-4,4,1],[-3,4,1],[-2,4,1],[-1,4,1]],"top":-8,"bottom":0},"イ":{"name":"ikatakana","advance":8,"spans":[[-9,6,1],[-8,5,1],[-7,4,1],[-6,2,3],[-5,0,2],[-5,4,1],[-4,4,1],[-3,4,1],[-2,4,1],[-1,4,1]],"top":-9,"bottom":0},"ゥ":{"name":"usmallkatakana","advance":8,"spans":[[-8,3,1],[-7,0,7],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,6,1],[-3,5,1],[-2,4,1],[-1,2,2]],"top":-8,"bottom":0},"ウ":{"name":"ukatakana","advance":9,"spans":[[-9,3,1],[-8,0,8],[-7,0,1],[-7,7,1],[-6,0,1],[-6,7,1],[-5,0,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,3,2]],"top":-9,"bottom":0},"ェ":{"name":"esmallkatakana","advance":8,"spans":[[-7,1,5],[-6,3,1],[-5,3,1],[-4,3,1],[-3,3,1],[-2,0,7]],"top":-7,"bottom":-1},"エ":{"name":"ekatakana","advance":10,"spans":[[-8,1,7],[-7,4,1],[-6,4,1],[-5,4,1],[-4,4,1],[-3,4,1],[-2,0,9]],"top":-8,"bottom":-1},"ォ":{"name":"osmallkatakana","advance":8,"spans":[[-8,4,1],[-7,4,1],[-6,0,7],[-5,3,2],[-4,2,1],[-4,4,1],[-3,1,1],[-3,4,1],[-2,0,1],[-2,4,1],[-1,3,2]],"top":-8,"bottom":0},"オ":{"name":"okatakana","advance":10,"spans":[[-9,6,1],[-8,6,1],[-7,0,9],[-6,4,1],[-6,6,1],[-5,3,1],[-5,6,1],[-4,2,1],[-4,6,1],[-3,1,1],[-3,6,1],[-2,0,1],[-2,6,1],[-1,4,2]],"top":-9,"bottom":0},"カ":{"name":"kakatakana","advance":10,"spans":[[-9,3,1],[-8,3,1],[-7,0,9],[-6,3,1],[-6,8,1],[-5,3,1],[-5,8,1],[-4,2,1],[-4,8,1],[-3,2,1],[-3,8,1],[-2,1,1],[-2,8,1],[-1,0,1],[-1,5,3]],"top":-9,"bottom":0},"ガ":{"name":"gakatakana","advance":11,"spans":[[-9,3,1],[-9,7,1],[-9,9,1],[-8,3,1],[-8,7,1],[-8,9,1],[-7,0,9],[-6,3,1],[-6,8,1],[-5,3,1],[-5,8,1],[-4,2,1],[-4,8,1],[-3,2,1],[-3,8,1],[-2,1,1],[-2,8,1],[-1,0,1],[-1,5,3]],"top":-9,"bottom":0},"キ":{"name":"kikatakana","advance":9,"spans":[[-9,3,1],[-8,3,1],[-7,0,8],[-6,4,1],[-5,4,1],[-4,0,8],[-3,5,1],[-2,5,1],[-1,5,1]],"top":-9,"bottom":0},"ギ":{"name":"gikatakana","advance":11,"spans":[[-9,3,1],[-9,7,1],[-9,9,1],[-8,3,1],[-8,7,1],[-8,9,1],[-7,0,8],[-6,4,1],[-5,4,1],[-4,0,8],[-3,5,1],[-2,5,1],[-1,5,1]],"top":-9,"bottom":0},"ク":{"name":"kukatakana","advance":9,"spans":[[-9,2,1],[-8,2,6],[-7,2,1],[-7,7,1],[-6,1,1],[-6,7,1],[-5,0,1],[-5,7,1],[-4,6,1],[-3,6,1],[-2,4,2],[-1,1,3]],"top":-9,"bottom":0},"グ":{"name":"gukatakana","advance":10,"spans":[[-9,2,1],[-9,6,1],[-9,8,1],[-8,2,5],[-8,8,1],[-7,2,1],[-7,7,1],[-6,1,1],[-6,7,1],[-5,0,1],[-5,7,1],[-4,6,1],[-3,6,1],[-2,4,2],[-1,1,3]],"top":-9,"bottom":0},"ケ":{"name":"kekatakana","advance":11,"spans":[[-9,2,1],[-8,2,1],[-7,2,8],[-6,1,1],[-6,7,1],[-5,0,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,3,2]],"top":-9,"bottom":0},"ゲ":{"name":"gekatakana","advance":11,"spans":[[-9,2,1],[-9,7,1],[-9,9,1],[-8,2,1],[-8,7,1],[-8,9,1],[-7,2,8],[-6,1,1],[-6,7,1],[-5,0,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,3,2]],"top":-9,"bottom":0},"コ":{"name":"kokatakana","advance":9,"spans":[[-8,0,8],[-7,7,1],[-6,7,1],[-5,7,1],[-4,7,1],[-3,7,1],[-2,7,1],[-1,0,8]],"top":-8,"bottom":0},"ゴ":{"name":"gokatakana","advance":10,"spans":[[-9,6,1],[-9,8,1],[-8,0,7],[-8,8,1],[-7,6,1],[-6,6,1],[-5,6,1],[-4,6,1],[-3,6,1],[-2,6,1],[-1,0,7]],"top":-9,"bottom":0},"サ":{"name":"sakatakana","advance":10,"spans":[[-9,2,1],[-9,6,1],[-8,2,1],[-8,6,1],[-7,0,9],[-6,2,1],[-6,6,1],[-5,2,1],[-5,6,1],[-4,2,1],[-4,6,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ザ":{"name":"zakatakana","advance":10,"spans":[[-9,2,1],[-9,6,1],[-9,8,1],[-8,2,1],[-8,6,1],[-8,8,1],[-7,0,9],[-6,2,1],[-6,6,1],[-5,2,1],[-5,6,1],[-4,2,1],[-4,6,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"シ":{"name":"sikatakana","advance":9,"spans":[[-9,1,2],[-8,3,1],[-6,0,2],[-6,7,1],[-5,2,1],[-5,7,1],[-4,6,1],[-3,6,1],[-2,4,2],[-1,0,4]],"top":-9,"bottom":0},"ジ":{"name":"zikatakana","advance":9,"spans":[[-9,1,2],[-9,5,1],[-9,7,1],[-8,3,1],[-8,5,1],[-8,7,1],[-6,0,2],[-6,7,1],[-5,2,1],[-5,7,1],[-4,6,1],[-3,6,1],[-2,4,2],[-1,0,4]],"top":-9,"bottom":0},"ス":{"name":"sukatakana","advance":10,"spans":[[-9,0,7],[-8,6,1],[-7,6,1],[-6,6,1],[-5,5,1],[-4,4,2],[-3,3,1],[-3,6,1],[-2,2,1],[-2,7,1],[-1,0,2],[-1,8,1]],"top":-9,"bottom":0},"ズ":{"name":"zukatakana","advance":10,"spans":[[-9,0,7],[-9,8,1],[-8,6,1],[-8,8,1],[-7,6,1],[-6,6,1],[-5,5,1],[-4,4,2],[-3,3,1],[-3,6,1],[-2,2,1],[-2,7,1],[-1,0,2],[-1,8,1]],"top":-9,"bottom":0},"セ":{"name":"sekatakana","advance":11,"spans":[[-9,3,1],[-8,3,1],[-7,3,1],[-7,5,5],[-6,1,4],[-6,9,1],[-5,3,1],[-5,8,1],[-4,3,1],[-4,7,1],[-3,3,1],[-2,3,1],[-1,4,5]],"top":-9,"bottom":0},"ゼ":{"name":"zekatakana","advance":11,"spans":[[-9,3,1],[-9,7,1],[-9,9,1],[-8,3,1],[-8,7,1],[-8,9,1],[-7,3,1],[-7,5,5],[-6,1,4],[-6,9,1],[-5,3,1],[-5,8,1],[-4,3,1],[-4,7,1],[-3,3,1],[-2,3,1],[-1,4,5]],"top":-9,"bottom":0},"ソ":{"name":"sokatakana","advance":10,"spans":[[-9,0,1],[-9,8,1],[-8,1,1],[-8,8,1],[-7,1,1],[-7,8,1],[-6,7,1],[-5,7,1],[-4,6,1],[-3,5,1],[-2,4,1],[-1,2,2]],"top":-9,"bottom":0},"ゾ":{"name":"zokatakana","advance":11,"spans":[[-10,7,1],[-10,9,1],[-9,0,1],[-9,7,1],[-9,9,1],[-8,1,1],[-8,8,1],[-7,1,1],[-7,8,1],[-6,7,1],[-5,6,1],[-4,6,1],[-3,5,1],[-2,4,1],[-1,2,2]],"top":-10,"bottom":0},"タ":{"name":"takatakana","advance":10,"spans":[[-9,2,1],[-8,2,7],[-7,1,1],[-7,8,1],[-6,0,1],[-6,2,1],[-6,8,1],[-5,3,1],[-5,7,1],[-4,4,1],[-4,7,1],[-3,5,2],[-2,4,2],[-1,1,3]],"top":-9,"bottom":0},"ダ":{"name":"dakatakana","advance":11,"spans":[[-9,2,1],[-9,7,1],[-9,9,1],[-8,2,6],[-8,9,1],[-7,1,1],[-7,8,1],[-6,0,1],[-6,2,1],[-6,8,1],[-5,3,1],[-5,7,1],[-4,4,1],[-4,7,1],[-3,5,2],[-2,4,2],[-1,1,3]],"top":-9,"bottom":0},"チ":{"name":"tikatakana","advance":10,"spans":[[-9,7,1],[-8,1,6],[-7,4,1],[-6,4,1],[-5,0,9],[-4,4,1],[-3,4,1],[-2,3,1],[-1,1,2]],"top":-9,"bottom":0},"ヂ":{"name":"dikatakana","advance":10,"spans":[[-9,6,1],[-9,8,1],[-8,1,6],[-8,8,1],[-7,4,1],[-6,4,1],[-5,0,9],[-4,4,1],[-3,4,1],[-2,3,1],[-1,1,2]],"top":-9,"bottom":0},"ッ":{"name":"tusmallkatakana","advance":9,"spans":[[-7,0,1],[-7,3,1],[-7,7,1],[-6,1,1],[-6,4,1],[-6,7,1],[-5,1,1],[-5,4,1],[-5,7,1],[-4,6,1],[-3,6,1],[-2,4,2],[-1,2,2]],"top":-7,"bottom":0},"ツ":{"name":"tukatakana","advance":10,"spans":[[-8,0,1],[-8,3,1],[-8,8,1],[-7,1,1],[-7,4,1],[-7,8,1],[-6,1,1],[-6,4,1],[-6,8,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,4,2],[-1,2,2]],"top":-8,"bottom":0},"ヅ":{"name":"dukatakana","advance":10,"spans":[[-9,6,1],[-9,8,1],[-8,0,1],[-8,3,1],[-8,6,1],[-8,8,1],[-7,1,1],[-7,4,1],[-6,1,1],[-6,4,1],[-6,8,1],[-5,8,1],[-4,7,1],[-3,6,1],[-2,4,2],[-1,2,2]],"top":-9,"bottom":0},"テ":{"name":"tekatakana","advance":10,"spans":[[-9,1,7],[-6,0,9],[-5,5,1],[-4,5,1],[-3,5,1],[-2,4,1],[-1,2,2]],"top":-9,"bottom":0},"デ":{"name":"dekatakana","advance":10,"spans":[[-9,1,6],[-9,8,1],[-8,6,1],[-8,8,1],[-6,0,9],[-5,5,1],[-4,5,1],[-3,5,1],[-2,4,1],[-1,2,2]],"top":-9,"bottom":0},"ト":{"name":"tokatakana","advance":7,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,3],[-5,0,1],[-5,3,2],[-4,0,1],[-4,5,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"ド":{"name":"dokatakana","advance":8,"spans":[[-9,0,1],[-9,4,1],[-9,6,1],[-8,0,1],[-8,4,1],[-8,6,1],[-7,0,1],[-6,0,3],[-5,0,1],[-5,3,2],[-4,0,1],[-4,5,1],[-3,0,1],[-2,0,1],[-1,0,1]],"top":-9,"bottom":0},"ナ":{"name":"nakatakana","advance":10,"spans":[[-9,4,1],[-8,4,1],[-7,0,9],[-6,4,1],[-5,4,1],[-4,4,1],[-3,4,1],[-2,3,1],[-1,1,2]],"top":-9,"bottom":0},"ニ":{"name":"nikatakana","advance":9,"spans":[[-8,1,6],[-2,0,8]],"top":-8,"bottom":-1},"ヌ":{"name":"nukatakana","advance":10,"spans":[[-9,2,7],[-8,8,1],[-7,8,1],[-6,3,2],[-6,7,1],[-5,5,1],[-5,7,1],[-4,6,1],[-3,5,1],[-3,7,1],[-2,3,2],[-2,8,1],[-1,1,2]],"top":-9,"bottom":0},"ネ":{"name":"nekatakana","advance":10,"spans":[[-9,4,1],[-8,1,7],[-7,7,1],[-6,6,1],[-5,4,2],[-4,2,3],[-4,6,1],[-3,0,2],[-3,4,1],[-3,7,2],[-2,4,1],[-1,4,1]],"top":-9,"bottom":0},"ノ":{"name":"nokatakana","advance":8,"spans":[[-9,6,1],[-8,6,1],[-7,6,1],[-6,6,1],[-5,5,1],[-4,5,1],[-3,4,1],[-2,2,2],[-1,0,2]],"top":-9,"bottom":0},"ハ":{"name":"hakatakana","advance":10,"spans":[[-9,2,1],[-9,6,1],[-8,2,1],[-8,6,1],[-7,2,1],[-7,6,1],[-6,2,1],[-6,6,1],[-5,1,1],[-5,7,1],[-4,1,1],[-4,7,1],[-3,1,1],[-3,7,1],[-2,0,1],[-2,8,1],[-1,0,1],[-1,8,1]],"top":-9,"bottom":0},"バ":{"name":"bakatakana","advance":10,"spans":[[-9,2,1],[-9,6,1],[-9,8,1],[-8,2,1],[-8,6,1],[-8,8,1],[-7,2,1],[-7,6,1],[-6,2,1],[-6,6,1],[-5,1,1],[-5,7,1],[-4,1,1],[-4,7,1],[-3,1,1],[-3,7,1],[-2,0,1],[-2,8,1],[-1,0,1],[-1,8,1]],"top":-9,"bottom":0},"パ":{"name":"pakatakana","advance":10,"spans":[[-9,2,1],[-9,6,2],[-8,2,1],[-8,6,1],[-8,8,1],[-7,2,1],[-7,6,2],[-6,2,1],[-6,6,1],[-5,1,1],[-5,7,1],[-4,1,1],[-4,7,1],[-3,1,1],[-3,7,1],[-2,0,1],[-2,8,1],[-1,0,1],[-1,8,1]],"top":-9,"bottom":0},"ヒ":{"name":"hikatakana","advance":9,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-7,5,3],[-6,0,5],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,1,7]],"top":-9,"bottom":0},"ビ":{"name":"bikatakana","advance":9,"spans":[[-9,0,1],[-9,5,1],[-9,7,1],[-8,0,1],[-8,5,1],[-8,7,1],[-7,0,1],[-7,5,2],[-6,0,5],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,1,7]],"top":-9,"bottom":0},"ピ":{"name":"pikatakana","advance":9,"spans":[[-9,0,1],[-9,6,1],[-8,0,1],[-8,5,1],[-8,7,1],[-7,0,1],[-7,4,3],[-6,0,4],[-5,0,1],[-4,0,1],[-3,0,1],[-2,0,1],[-1,1,7]],"top":-9,"bottom":0},"フ":{"name":"hukatakana","advance":9,"spans":[[-8,0,8],[-7,7,1],[-6,7,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-8,"bottom":0},"ブ":{"name":"bukatakana","advance":10,"spans":[[-9,6,1],[-9,8,1],[-8,0,7],[-8,8,1],[-7,7,1],[-6,7,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"プ":{"name":"pukatakana","advance":10,"spans":[[-9,7,1],[-8,0,7],[-8,8,1],[-7,7,1],[-6,7,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ヘ":{"name":"hekatakana","advance":11,"spans":[[-8,4,1],[-7,3,1],[-7,5,1],[-6,2,1],[-6,6,1],[-5,2,1],[-5,6,1],[-4,1,1],[-4,7,1],[-3,1,1],[-3,8,1],[-2,9,1]],"top":-8,"bottom":-1},"ベ":{"name":"bekatakana","advance":11,"spans":[[-9,7,1],[-9,9,1],[-8,4,1],[-8,7,1],[-8,9,1],[-7,3,1],[-7,5,1],[-6,2,1],[-6,6,1],[-5,2,1],[-5,6,1],[-4,1,1],[-4,7,1],[-3,1,1],[-3,8,1],[-2,9,1]],"top":-9,"bottom":-1},"ペ":{"name":"pekatakana","advance":11,"spans":[[-9,8,1],[-8,4,1],[-8,7,1],[-8,9,1],[-7,3,1],[-7,5,1],[-7,8,1],[-6,2,1],[-6,6,1],[-5,2,1],[-5,6,1],[-4,1,1],[-4,7,1],[-3,1,1],[-3,8,1],[-2,9,1]],"top":-9,"bottom":-1},"ホ":{"name":"hokatakana","advance":10,"spans":[[-9,4,1],[-8,4,1],[-7,0,9],[-6,4,1],[-5,1,1],[-5,4,1],[-5,7,1],[-4,1,1],[-4,4,1],[-4,7,1],[-3,0,1],[-3,4,1],[-3,8,1],[-2,0,1],[-2,4,1],[-2,8,1],[-1,4,1]],"top":-9,"bottom":0},"ボ":{"name":"bokatakana","advance":10,"spans":[[-9,4,1],[-9,6,1],[-9,8,1],[-8,4,1],[-8,6,1],[-8,8,1],[-7,0,9],[-6,4,1],[-5,1,1],[-5,4,1],[-5,7,1],[-4,1,1],[-4,4,1],[-4,7,1],[-3,0,1],[-3,4,1],[-3,8,1],[-2,0,1],[-2,4,1],[-2,8,1],[-1,4,1]],"top":-9,"bottom":0},"ポ":{"name":"pokatakana","advance":10,"spans":[[-9,4,1],[-9,7,1],[-8,4,1],[-8,6,1],[-8,8,1],[-7,0,9],[-6,4,1],[-5,1,1],[-5,4,1],[-5,7,1],[-4,1,1],[-4,4,1],[-4,7,1],[-3,0,1],[-3,4,1],[-3,8,1],[-2,0,1],[-2,4,1],[-2,8,1],[-1,4,1]],"top":-9,"bottom":0},"マ":{"name":"makatakana","advance":10,"spans":[[-8,0,9],[-7,8,1],[-6,7,1],[-5,6,1],[-4,1,2],[-4,5,1],[-3,3,2],[-2,4,1],[-1,5,1]],"top":-8,"bottom":0},"ミ":{"name":"mikatakana","advance":9,"spans":[[-9,1,3],[-8,4,3],[-7,7,1],[-6,1,3],[-5,4,3],[-4,7,1],[-3,0,4],[-2,4,3],[-1,7,1]],"top":-9,"bottom":0},"ム":{"name":"mukatakana","advance":10,"spans":[[-9,3,1],[-8,3,1],[-7,3,1],[-6,2,1],[-5,2,1],[-5,6,1],[-4,2,1],[-4,7,1],[-3,1,1],[-3,6,2],[-2,0,6],[-2,8,1],[-1,8,1]],"top":-9,"bottom":0},"メ":{"name":"mekatakana","advance":9,"spans":[[-9,6,1],[-8,6,1],[-7,2,1],[-7,6,1],[-6,3,1],[-6,5,1],[-5,4,2],[-4,4,2],[-3,3,1],[-3,6,1],[-2,2,1],[-2,7,1],[-1,0,2]],"top":-9,"bottom":0},"モ":{"name":"mokatakana","advance":10,"spans":[[-9,1,7],[-8,3,1],[-7,3,1],[-6,0,9],[-5,3,1],[-4,3,1],[-3,3,1],[-2,3,1],[-1,4,4]],"top":-9,"bottom":0},"ャ":{"name":"yasmallkatakana","advance":9,"spans":[[-8,1,1],[-7,2,1],[-7,4,4],[-6,0,4],[-6,7,1],[-5,2,1],[-5,7,1],[-4,3,1],[-4,6,1],[-3,3,1],[-2,4,1],[-1,4,1]],"top":-8,"bottom":0},"ヤ":{"name":"yakatakana","advance":10,"spans":[[-9,1,1],[-8,2,1],[-8,5,4],[-7,2,3],[-7,8,1],[-6,0,3],[-6,8,1],[-5,3,1],[-5,7,1],[-4,3,1],[-4,6,1],[-3,3,1],[-2,4,1],[-1,4,1]],"top":-9,"bottom":0},"ュ":{"name":"yusmallkatakana","advance":9,"spans":[[-7,1,6],[-6,6,1],[-5,6,1],[-4,6,1],[-3,5,1],[-2,5,1],[-1,0,8]],"top":-7,"bottom":0},"ユ":{"name":"yukatakana","advance":10,"spans":[[-8,1,7],[-7,7,1],[-6,7,1],[-5,7,1],[-4,6,1],[-3,6,1],[-2,6,1],[-1,0,9]],"top":-8,"bottom":0},"ョ":{"name":"yosmallkatakana","advance":7,"spans":[[-7,0,6],[-6,5,1],[-5,5,1],[-4,0,6],[-3,5,1],[-2,5,1],[-1,0,6]],"top":-7,"bottom":0},"ヨ":{"name":"yokatakana","advance":8,"spans":[[-9,0,7],[-8,6,1],[-7,6,1],[-6,6,1],[-5,0,7],[-4,6,1],[-3,6,1],[-2,6,1],[-1,0,7]],"top":-9,"bottom":0},"ラ":{"name":"rakatakana","advance":9,"spans":[[-9,1,6],[-6,0,8],[-5,7,1],[-4,6,1],[-3,6,1],[-2,4,2],[-1,2,2]],"top":-9,"bottom":0},"リ":{"name":"rikatakana","advance":8,"spans":[[-9,0,1],[-9,6,1],[-8,0,1],[-8,6,1],[-7,0,1],[-7,6,1],[-6,0,1],[-6,6,1],[-5,0,1],[-5,6,1],[-4,6,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ル":{"name":"rukatakana","advance":10,"spans":[[-9,2,1],[-9,5,1],[-8,2,1],[-8,5,1],[-7,2,1],[-7,5,1],[-6,2,1],[-6,5,1],[-5,2,1],[-5,5,1],[-5,8,1],[-4,2,1],[-4,5,1],[-4,8,1],[-3,1,1],[-3,5,1],[-3,8,1],[-2,1,1],[-2,5,1],[-2,7,1],[-1,0,1],[-1,5,2]],"top":-9,"bottom":0},"レ":{"name":"rekatakana","advance":8,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-5,6,1],[-4,0,1],[-4,6,1],[-3,0,1],[-3,5,1],[-2,0,1],[-2,3,2],[-1,0,3]],"top":-9,"bottom":0},"ロ":{"name":"rokatakana","advance":9,"spans":[[-9,0,8],[-8,0,1],[-8,7,1],[-7,0,1],[-7,7,1],[-6,0,1],[-6,7,1],[-5,0,1],[-5,7,1],[-4,0,1],[-4,7,1],[-3,0,1],[-3,7,1],[-2,0,8]],"top":-9,"bottom":-1},"ヮ":{"name":"wasmallkatakana","advance":7,"spans":[[-7,0,6],[-6,0,1],[-6,5,1],[-5,0,1],[-5,5,1],[-4,5,1],[-3,5,1],[-2,4,1],[-1,1,3]],"top":-7,"bottom":0},"ワ":{"name":"wakatakana","advance":9,"spans":[[-9,0,8],[-8,0,1],[-8,7,1],[-7,0,1],[-7,7,1],[-6,0,1],[-6,7,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ヲ":{"name":"wokatakana","advance":9,"spans":[[-9,0,8],[-8,7,1],[-7,7,1],[-6,0,8],[-5,7,1],[-4,7,1],[-3,6,1],[-2,5,1],[-1,2,3]],"top":-9,"bottom":0},"ン":{"name":"nkatakana","advance":10,"spans":[[-8,0,2],[-7,2,1],[-7,8,1],[-6,8,1],[-5,7,1],[-4,7,1],[-3,6,1],[-2,4,2],[-1,1,3]],"top":-8,"bottom":0},"ヵ":{"name":"kasmallkatakana","advance":8,"spans":[[-8,2,1],[-7,2,1],[-6,0,7],[-5,2,1],[-5,6,1],[-4,2,1],[-4,6,1],[-3,2,1],[-3,6,1],[-2,1,1],[-2,6,1],[-1,0,1],[-1,4,2]],"top":-8,"bottom":0},"ヶ":{"name":"kesmallkatakana","advance":9,"spans":[[-8,2,1],[-7,2,1],[-6,2,6],[-5,1,1],[-5,5,1],[-4,0,1],[-4,5,1],[-3,5,1],[-2,4,1],[-1,2,2]],"top":-8,"bottom":0},"!":{"name":"exclammonospace","advance":2,"spans":[[-9,0,1],[-8,0,1],[-7,0,1],[-6,0,1],[-5,0,1],[-4,0,1],[-3,0,1],[-1,0,1]],"top":-9,"bottom":0},"/":{"name":"slashmonospace","advance":10,"spans":[[-9,8,1],[-8,7,1],[-7,6,1],[-6,5,1],[-5,4,1],[-4,3,1],[-3,2,1],[-2,1,1],[-1,0,1]],"top":-9,"bottom":0},"?":{"name":"questionmonospace","advance":7,"spans":[[-9,1,4],[-8,0,1],[-8,5,1],[-7,0,1],[-7,5,1],[-6,5,1],[-5,4,1],[-4,3,1],[-3,3,1],[-1,3,1]],"top":-9,"bottom":0},"\":{"name":"backslashmonospace","advance":10,"spans":[[-9,0,1],[-8,1,1],[-7,2,1],[-6,3,1],[-5,4,1],[-4,5,1],[-3,6,1],[-2,7,1],[-1,8,1]],"top":-9,"bottom":0},"~":{"name":"asciitildemonospace","advance":10,"spans":[[-6,1,4],[-6,8,1],[-5,0,1],[-5,5,3]],"top":-6,"bottom":-4}},"letters":{"ء":["ﺀ","","",""],"آ":["ﺁ","","","ﺂ"],"أ":["ﺃ","","","ﺄ"],"ؤ":["ﺅ","","","ﺆ"],"إ":["ﺇ","","","ﺈ"],"ئ":["ﺉ","ﺋ","ﺌ","ﺊ"],"ا":["ﺍ","","","ﺎ"],"ب":["ﺏ","ﺑ","ﺒ","ﺐ"],"ة":["ﺓ","","","ﺔ"],"ت":["ﺕ","ﺗ","ﺘ","ﺖ"],"ث":["ﺙ","ﺛ","ﺜ","ﺚ"],"ج":["ﺝ","ﺟ","ﺠ","ﺞ"],"ح":["ﺡ","ﺣ","ﺤ","ﺢ"],"خ":["ﺥ","ﺧ","ﺨ","ﺦ"],"د":["ﺩ","","","ﺪ"],"ذ":["ﺫ","","","ﺬ"],"ر":["ﺭ","","","ﺮ"],"ز":["ﺯ","","","ﺰ"],"س":["ﺱ","ﺳ","ﺴ","ﺲ"],"ش":["ﺵ","ﺷ","ﺸ","ﺶ"],"ص":["ﺹ","ﺻ","ﺼ","ﺺ"],"ض":["ﺽ","ﺿ","ﻀ","ﺾ"],"ط":["ﻁ","ﻃ","ﻄ","ﻂ"],"ظ":["ﻅ","ﻇ","ﻈ","ﻆ"],"ع":["ﻉ","ﻋ","ﻌ","ﻊ"],"غ":["ﻍ","ﻏ","ﻐ","ﻎ"],"ـ":["ـ","ـ","ـ","ـ"],"ف":["ﻑ","ﻓ","ﻔ","ﻒ"],"ق":["ﻕ","ﻗ","ﻘ","ﻖ"],"ك":["ﻙ","ﻛ","ﻜ","ﻚ"],"ل":["ﻝ","ﻟ","ﻠ","ﻞ"],"م":["ﻡ","ﻣ","ﻤ","ﻢ"],"ن":["ﻥ","ﻧ","ﻨ","ﻦ"],"ه":["ﻩ","ﻫ","ﻬ","ﻪ"],"و":["ﻭ","","","ﻮ"],"ى":["ﻯ","","","ﻰ"],"ي":["ﻱ","ﻳ","ﻴ","ﻲ"],"پ":["ﭖ","ﭘ","ﭙ","ﭗ"],"چ":["ﭺ","ﭼ","ﭽ","ﭻ"],"ڤ":["ﭪ","ﭬ","ﭭ","ﭫ"],"‍":["‍","‍","‍","‍"]},"ligatures":[["لا",["ﻻ","","","ﻼ"]],["لأ",["ﻷ","","","ﻸ"]],["لإ",["ﻹ","","","ﻺ"]],["لآ",["ﻵ","","","ﻶ"]]],"configuration":{"delete_harakat":false,"shift_harakat_position":false,"use_unshaped_instead_of_isolated":true,"support_ligatures":true,"ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM":false,"ARABIC LIGATURE JALLAJALALOUHOU":false,"ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM":false,"ARABIC LIGATURE ALLAH":false,"ARABIC LIGATURE AKBAR":false,"ARABIC LIGATURE ALAYHE":false,"ARABIC LIGATURE MOHAMMAD":false,"ARABIC LIGATURE RASOUL":false,"ARABIC LIGATURE SALAM":false,"ARABIC LIGATURE SALLA":false,"ARABIC LIGATURE WASALLAM":false,"RIAL SIGN":false,"ARABIC LIGATURE AIN WITH ALEF MAKSURA":false,"ARABIC LIGATURE AIN WITH JEEM":false,"ARABIC LIGATURE AIN WITH JEEM WITH MEEM":false,"ARABIC LIGATURE AIN WITH MEEM":false,"ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE AIN WITH MEEM WITH MEEM":false,"ARABIC LIGATURE AIN WITH MEEM WITH YEH":false,"ARABIC LIGATURE AIN WITH YEH":false,"ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF":false,"ARABIC LIGATURE ALEF WITH FATHATAN":false,"ARABIC LIGATURE BEH WITH ALEF MAKSURA":false,"ARABIC LIGATURE BEH WITH HAH":false,"ARABIC LIGATURE BEH WITH HAH WITH YEH":false,"ARABIC LIGATURE BEH WITH HEH":false,"ARABIC LIGATURE BEH WITH JEEM":false,"ARABIC LIGATURE BEH WITH KHAH":false,"ARABIC LIGATURE BEH WITH KHAH WITH YEH":false,"ARABIC LIGATURE BEH WITH MEEM":false,"ARABIC LIGATURE BEH WITH NOON":false,"ARABIC LIGATURE BEH WITH REH":false,"ARABIC LIGATURE BEH WITH YEH":false,"ARABIC LIGATURE BEH WITH ZAIN":false,"ARABIC LIGATURE DAD WITH ALEF MAKSURA":false,"ARABIC LIGATURE DAD WITH HAH":false,"ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE DAD WITH HAH WITH YEH":false,"ARABIC LIGATURE DAD WITH JEEM":false,"ARABIC LIGATURE DAD WITH KHAH":false,"ARABIC LIGATURE DAD WITH KHAH WITH MEEM":false,"ARABIC LIGATURE DAD WITH MEEM":false,"ARABIC LIGATURE DAD WITH REH":false,"ARABIC LIGATURE DAD WITH YEH":false,"ARABIC LIGATURE FEH WITH ALEF MAKSURA":false,"ARABIC LIGATURE FEH WITH HAH":false,"ARABIC LIGATURE FEH WITH JEEM":false,"ARABIC LIGATURE FEH WITH KHAH":false,"ARABIC LIGATURE FEH WITH KHAH WITH MEEM":false,"ARABIC LIGATURE FEH WITH MEEM":false,"ARABIC LIGATURE FEH WITH MEEM WITH YEH":false,"ARABIC LIGATURE FEH WITH YEH":false,"ARABIC LIGATURE GHAIN WITH ALEF MAKSURA":false,"ARABIC LIGATURE GHAIN WITH JEEM":false,"ARABIC LIGATURE GHAIN WITH MEEM":false,"ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM":false,"ARABIC LIGATURE GHAIN WITH MEEM WITH YEH":false,"ARABIC LIGATURE GHAIN WITH YEH":false,"ARABIC LIGATURE HAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE HAH WITH JEEM":false,"ARABIC LIGATURE HAH WITH JEEM WITH YEH":false,"ARABIC LIGATURE HAH WITH MEEM":false,"ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE HAH WITH MEEM WITH YEH":false,"ARABIC LIGATURE HAH WITH YEH":false,"ARABIC LIGATURE HEH WITH ALEF MAKSURA":false,"ARABIC LIGATURE HEH WITH JEEM":false,"ARABIC LIGATURE HEH WITH MEEM":false,"ARABIC LIGATURE HEH WITH MEEM WITH JEEM":false,"ARABIC LIGATURE HEH WITH MEEM WITH MEEM":false,"ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF":false,"ARABIC LIGATURE HEH WITH YEH":false,"ARABIC LIGATURE JEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE JEEM WITH HAH":false,"ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE JEEM WITH HAH WITH YEH":false,"ARABIC LIGATURE JEEM WITH MEEM":false,"ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE JEEM WITH MEEM WITH HAH":false,"ARABIC LIGATURE JEEM WITH MEEM WITH YEH":false,"ARABIC LIGATURE JEEM WITH YEH":false,"ARABIC LIGATURE KAF WITH ALEF":false,"ARABIC LIGATURE KAF WITH ALEF MAKSURA":false,"ARABIC LIGATURE KAF WITH HAH":false,"ARABIC LIGATURE KAF WITH JEEM":false,"ARABIC LIGATURE KAF WITH KHAH":false,"ARABIC LIGATURE KAF WITH LAM":false,"ARABIC LIGATURE KAF WITH MEEM":false,"ARABIC LIGATURE KAF WITH MEEM WITH MEEM":false,"ARABIC LIGATURE KAF WITH MEEM WITH YEH":false,"ARABIC LIGATURE KAF WITH YEH":false,"ARABIC LIGATURE KHAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE KHAH WITH HAH":false,"ARABIC LIGATURE KHAH WITH JEEM":false,"ARABIC LIGATURE KHAH WITH MEEM":false,"ARABIC LIGATURE KHAH WITH YEH":false,"ARABIC LIGATURE LAM WITH ALEF":true,"ARABIC LIGATURE LAM WITH ALEF MAKSURA":false,"ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE":true,"ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW":true,"ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE":true,"ARABIC LIGATURE LAM WITH HAH":false,"ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE LAM WITH HAH WITH MEEM":false,"ARABIC LIGATURE LAM WITH HAH WITH YEH":false,"ARABIC LIGATURE LAM WITH HEH":false,"ARABIC LIGATURE LAM WITH JEEM":false,"ARABIC LIGATURE LAM WITH JEEM WITH JEEM":false,"ARABIC LIGATURE LAM WITH JEEM WITH MEEM":false,"ARABIC LIGATURE LAM WITH JEEM WITH YEH":false,"ARABIC LIGATURE LAM WITH KHAH":false,"ARABIC LIGATURE LAM WITH KHAH WITH MEEM":false,"ARABIC LIGATURE LAM WITH MEEM":false,"ARABIC LIGATURE LAM WITH MEEM WITH HAH":false,"ARABIC LIGATURE LAM WITH MEEM WITH YEH":false,"ARABIC LIGATURE LAM WITH YEH":false,"ARABIC LIGATURE MEEM WITH ALEF":false,"ARABIC LIGATURE MEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE MEEM WITH HAH":false,"ARABIC LIGATURE MEEM WITH HAH WITH JEEM":false,"ARABIC LIGATURE MEEM WITH HAH WITH MEEM":false,"ARABIC LIGATURE MEEM WITH HAH WITH YEH":false,"ARABIC LIGATURE MEEM WITH JEEM":false,"ARABIC LIGATURE MEEM WITH JEEM WITH HAH":false,"ARABIC LIGATURE MEEM WITH JEEM WITH KHAH":false,"ARABIC LIGATURE MEEM WITH JEEM WITH MEEM":false,"ARABIC LIGATURE MEEM WITH JEEM WITH YEH":false,"ARABIC LIGATURE MEEM WITH KHAH":false,"ARABIC LIGATURE MEEM WITH KHAH WITH JEEM":false,"ARABIC LIGATURE MEEM WITH KHAH WITH MEEM":false,"ARABIC LIGATURE MEEM WITH KHAH WITH YEH":false,"ARABIC LIGATURE MEEM WITH MEEM":false,"ARABIC LIGATURE MEEM WITH MEEM WITH YEH":false,"ARABIC LIGATURE MEEM WITH YEH":false,"ARABIC LIGATURE NOON WITH ALEF MAKSURA":false,"ARABIC LIGATURE NOON WITH HAH":false,"ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE NOON WITH HAH WITH MEEM":false,"ARABIC LIGATURE NOON WITH HAH WITH YEH":false,"ARABIC LIGATURE NOON WITH HEH":false,"ARABIC LIGATURE NOON WITH JEEM":false,"ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE NOON WITH JEEM WITH HAH":false,"ARABIC LIGATURE NOON WITH JEEM WITH MEEM":false,"ARABIC LIGATURE NOON WITH JEEM WITH YEH":false,"ARABIC LIGATURE NOON WITH KHAH":false,"ARABIC LIGATURE NOON WITH MEEM":false,"ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE NOON WITH MEEM WITH YEH":false,"ARABIC LIGATURE NOON WITH NOON":false,"ARABIC LIGATURE NOON WITH REH":false,"ARABIC LIGATURE NOON WITH YEH":false,"ARABIC LIGATURE NOON WITH ZAIN":false,"ARABIC LIGATURE QAF WITH ALEF MAKSURA":false,"ARABIC LIGATURE QAF WITH HAH":false,"ARABIC LIGATURE QAF WITH MEEM":false,"ARABIC LIGATURE QAF WITH MEEM WITH HAH":false,"ARABIC LIGATURE QAF WITH MEEM WITH MEEM":false,"ARABIC LIGATURE QAF WITH MEEM WITH YEH":false,"ARABIC LIGATURE QAF WITH YEH":false,"ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN":false,"ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF":false,"ARABIC LIGATURE SAD WITH ALEF MAKSURA":false,"ARABIC LIGATURE SAD WITH HAH":false,"ARABIC LIGATURE SAD WITH HAH WITH HAH":false,"ARABIC LIGATURE SAD WITH HAH WITH YEH":false,"ARABIC LIGATURE SAD WITH KHAH":false,"ARABIC LIGATURE SAD WITH MEEM":false,"ARABIC LIGATURE SAD WITH MEEM WITH MEEM":false,"ARABIC LIGATURE SAD WITH REH":false,"ARABIC LIGATURE SAD WITH YEH":false,"ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN":false,"ARABIC LIGATURE SEEN WITH ALEF MAKSURA":false,"ARABIC LIGATURE SEEN WITH HAH":false,"ARABIC LIGATURE SEEN WITH HAH WITH JEEM":false,"ARABIC LIGATURE SEEN WITH HEH":false,"ARABIC LIGATURE SEEN WITH JEEM":false,"ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE SEEN WITH JEEM WITH HAH":false,"ARABIC LIGATURE SEEN WITH KHAH":false,"ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE SEEN WITH KHAH WITH YEH":false,"ARABIC LIGATURE SEEN WITH MEEM":false,"ARABIC LIGATURE SEEN WITH MEEM WITH HAH":false,"ARABIC LIGATURE SEEN WITH MEEM WITH JEEM":false,"ARABIC LIGATURE SEEN WITH MEEM WITH MEEM":false,"ARABIC LIGATURE SEEN WITH REH":false,"ARABIC LIGATURE SEEN WITH YEH":false,"ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM":false,"ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM":false,"ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM":false,"ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM":false,"ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM":false,"ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF":false,"ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM":false,"ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM":false,"ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM":false,"ARABIC LIGATURE SHADDA WITH FATHA":false,"ARABIC LIGATURE SHADDA WITH DAMMA":false,"ARABIC LIGATURE SHADDA WITH KASRA":false,"ARABIC LIGATURE SHEEN WITH ALEF MAKSURA":false,"ARABIC LIGATURE SHEEN WITH HAH":false,"ARABIC LIGATURE SHEEN WITH HAH WITH MEEM":false,"ARABIC LIGATURE SHEEN WITH HAH WITH YEH":false,"ARABIC LIGATURE SHEEN WITH HEH":false,"ARABIC LIGATURE SHEEN WITH JEEM":false,"ARABIC LIGATURE SHEEN WITH JEEM WITH YEH":false,"ARABIC LIGATURE SHEEN WITH KHAH":false,"ARABIC LIGATURE SHEEN WITH MEEM":false,"ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH":false,"ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM":false,"ARABIC LIGATURE SHEEN WITH REH":false,"ARABIC LIGATURE SHEEN WITH YEH":false,"ARABIC LIGATURE TAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE TAH WITH HAH":false,"ARABIC LIGATURE TAH WITH MEEM":false,"ARABIC LIGATURE TAH WITH MEEM WITH HAH":false,"ARABIC LIGATURE TAH WITH MEEM WITH MEEM":false,"ARABIC LIGATURE TAH WITH MEEM WITH YEH":false,"ARABIC LIGATURE TAH WITH YEH":false,"ARABIC LIGATURE TEH WITH ALEF MAKSURA":false,"ARABIC LIGATURE TEH WITH HAH":false,"ARABIC LIGATURE TEH WITH HAH WITH JEEM":false,"ARABIC LIGATURE TEH WITH HAH WITH MEEM":false,"ARABIC LIGATURE TEH WITH HEH":false,"ARABIC LIGATURE TEH WITH JEEM":false,"ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE TEH WITH JEEM WITH MEEM":false,"ARABIC LIGATURE TEH WITH JEEM WITH YEH":false,"ARABIC LIGATURE TEH WITH KHAH":false,"ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA":false,"ARABIC LIGATURE TEH WITH KHAH WITH MEEM":false,"ARABIC LIGATURE TEH WITH KHAH WITH YEH":false,"ARABIC LIGATURE TEH WITH MEEM":false,"ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA":false,"ARABIC LIGATURE TEH WITH MEEM WITH HAH":false,"ARABIC LIGATURE TEH WITH MEEM WITH JEEM":false,"ARABIC LIGATURE TEH WITH MEEM WITH KHAH":false,"ARABIC LIGATURE TEH WITH MEEM WITH YEH":false,"ARABIC LIGATURE TEH WITH NOON":false,"ARABIC LIGATURE TEH WITH REH":false,"ARABIC LIGATURE TEH WITH YEH":false,"ARABIC LIGATURE TEH WITH ZAIN":false,"ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF":false,"ARABIC LIGATURE THEH WITH ALEF MAKSURA":false,"ARABIC LIGATURE THEH WITH HEH":false,"ARABIC LIGATURE THEH WITH JEEM":false,"ARABIC LIGATURE THEH WITH MEEM":false,"ARABIC LIGATURE THEH WITH NOON":false,"ARABIC LIGATURE THEH WITH REH":false,"ARABIC LIGATURE THEH WITH YEH":false,"ARABIC LIGATURE THEH WITH ZAIN":false,"ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA":false,"ARABIC LIGATURE YEH WITH ALEF MAKSURA":false,"ARABIC LIGATURE YEH WITH HAH":false,"ARABIC LIGATURE YEH WITH HAH WITH YEH":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU":false,"ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN":false,"ARABIC LIGATURE YEH WITH HEH":false,"ARABIC LIGATURE YEH WITH JEEM":false,"ARABIC LIGATURE YEH WITH JEEM WITH YEH":false,"ARABIC LIGATURE YEH WITH KHAH":false,"ARABIC LIGATURE YEH WITH MEEM":false,"ARABIC LIGATURE YEH WITH MEEM WITH MEEM":false,"ARABIC LIGATURE YEH WITH MEEM WITH YEH":false,"ARABIC LIGATURE YEH WITH NOON":false,"ARABIC LIGATURE YEH WITH REH":false,"ARABIC LIGATURE YEH WITH YEH":false,"ARABIC LIGATURE YEH WITH ZAIN":false,"ARABIC LIGATURE ZAH WITH MEEM":false},"supported":" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£©«­®°±´·»¿ÀÁÂÄÇÈÉÊËÌÍÎÏÑÒÓÔÖ×ÙÚÛÜßàáâäçèéêëìíîïñòóôö÷ùúûüŒœ،ؕ؛؟ءآأؤإئابةتثجحخدذرزسشصضطظعغـفقكلمنهوىيًٌٍَُِّْٖٓٔٗ٘٠١٢٣٤٥٦٧٨٩٪٫٬٭ٰپچڤ۔۰۱۲۳۴۵۶۷۸۹–—‘’‚“”„•…‰‹›※€™←↑→↓−∞∱∴≠①⑦ⒶⒷⓁⓇⓍⓎ■□▲△▼▽◆◇○●☁☂☃★☆☎☓☹☺☻☼♠♣♥♦♩♭♯⚀✉✚➡⦾⬅⬆⬇ 、。「」〒ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわをん゛゜ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヲンヵヶﭖﭗﭘﭙﭪﭫﭬﭭﭺﭻﭼﭽﰉﰊﱙﱚﱮﱯﲕﲖ﴾﴿ﷲﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ!/?\~","marks":"ًٌٍَُِّْٰٖؕٓٔٗ٘","mark_ligatures":[["ّٰ","glyph:shaddaAlefabovear"],["ًّ","glyph:shaddaFathatanar"],["ٌّ","glyph:shaddaDammatanar"],["ٍّ","glyph:shaddaKasratanar"],["َّ","glyph:shaddaFathaar"],["ُّ","glyph:shaddaDammaar"],["ِّ","glyph:shaddaKasraar"],["ّٰ","glyph:shaddaAlefabovear"],["ًّ","glyph:shaddaFathatanar"],["ٌّ","glyph:shaddaDammatanar"],["ٍّ","glyph:shaddaKasratanar"],["َّ","glyph:shaddaFathaar"],["ُّ","glyph:shaddaDammaar"],["ِّ","glyph:shaddaKasraar"]],"positioning":[{"kind":"base","marks":{"uni0615":[1,0,284],"uni0670":[1,0,284],"uni0656":[1,0,284],"uni0654":[1,0,284],"uni064B":[1,0,284],"uni064C":[1,0,284],"uni064D":[0,0,284],"uni064E":[1,0,284],"uni064F":[1,0,284],"uni0650":[0,0,284],"uni0651":[1,0,284],"uni0652":[1,0,284],"uni0653":[1,0,284],"shaddaAlefabovear":[1,0,284],"shaddaDammaar":[1,0,284],"shaddaDammatanar":[1,0,284],"shaddaFathaar":[1,0,284],"shaddaFathatanar":[1,0,255],"shaddaKasraar":[1,0,284],"shaddaKasratanar":[1,0,284],"uni0658":[1,0,850],"uni0657":[1,0,284]},"bases":{"uni0621":[[354,0],[354,710]],"uni0627":[[213,0],[213,1134]],"uniFE8E":[[213,0],[213,1134]],"uni0623":[[213,0],[213,1559]],"uni0623.001":[[213,0],[213,1559]],"uniFE84":[[213,0],[213,1559]],"uni0625":[[213,-425],[213,1134]],"uniFE88":[[211,-425],[211,1134]],"uni0622":[[213,0],[213,1417]],"uniFE82":[[213,0],[213,1417]],"uni0628":[[780,-284],[354,567]],"uniFE90":[[780,-284],[354,567]],"uniFE92":[[142,-284],[142,567]],"uniFE91":[[142,-284],[142,567]],"uni067E":[[780,-426],[354,567]],"uniFB57":[[780,-426],[354,567]],"uniFB59":[[283,-426],[425,566]],"uniFB58":[[283,-426],[425,566]],"uni062A":[[780,0],[780,850]],"uniFE96":[[780,0],[780,852]],"uniFE98":[[283,0],[425,851]],"uniFE98.ss01":[[142,0],[141,1136]],"uniFE97":[[283,0],[425,851]],"uniFE97.ss01":[[142,0],[141,1136]],"uni062B":[[780,0],[780,992]],"uniFE9A":[[780,0],[780,992]],"uniFE9C":[[283,0],[425,993]],"uniFE9C.ss01":[[142,0],[141,1420]],"uniFE9B":[[283,0],[425,993]],"uniFE9B.ss01":[[142,0],[141,1420]],"uni062C":[[638,-426],[496,709]],"uniFE9E":[[638,-426],[496,709]],"uniFEA0":[[425,-285],[425,709]],"uniFE9F":[[425,-285],[425,709]],"uni0686":[[780,-426],[496,709]],"uniFB7B":[[780,-426],[496,709]],"uniFB7D":[[425,-426],[425,709]],"uniFB7C":[[425,-426],[425,709]],"uni062D":[[496,-426],[496,709]],"uniFEA2":[[496,-426],[496,709]],"uniFEA4":[[425,0],[425,709]],"uniFEA3":[[425,0],[425,709]],"uni062E":[[496,-426],[496,992]],"uniFEA6":[[496,-426],[496,992]],"uniFEA8":[[425,0],[425,993]],"uniFEA7":[[425,0],[425,993]],"uni062F":[[213,0],[354,710]],"uniFEAA":[[213,0],[354,710]],"uni0630":[[213,0],[354,992]],"uniFEAC":[[213,0],[354,992]],"uni0631":[[212,-425],[212,569]],"uniFEAE":[[212,-425],[212,569]],"uni0632":[[212,-425],[212,852]],"uniFEB0":[[212,-425],[212,852]],"uni0633":[[354,-426],[779,567]],"uniFEB2":[[354,-426],[779,567]],"uniFEB4":[[425,0],[425,992]],"uniFEB3":[[425,0],[425,567]],"uni0634":[[354,-426],[780,992]],"uniFEB6":[[354,-426],[779,992]],"uniFEB8":[[425,0],[425,992]],"uniFEB7":[[425,0],[425,992]],"uni0635":[[354,-425],[921,710]],"uniFEBA":[[354,-425],[921,710]],"uniFEBC":[[567,0],[567,710]],"uniFEBB":[[567,0],[567,710]],"uni0636":[[354,-425],[921,991]],"uniFEBE":[[354,-425],[921,991]],"uniFEC0":[[567,0],[567,992]],"uniFEBF":[[567,0],[567,992]],"uni0637":[[638,0],[354,1134]],"uniFEC2":[[638,0],[354,1134]],"uniFEC4":[[567,0],[283,1134]],"uniFEC3":[[567,0],[283,1134]],"uni0638":[[638,0],[638,992]],"uniFEC6":[[638,0],[638,992]],"uniFEC8":[[567,0],[567,992]],"uniFEC7":[[567,0],[567,992]],"uni0639":[[496,-426],[496,710]],"uniFECA":[[496,-426],[496,710]],"uniFECC":[[425,0],[425,710]],"uniFECB":[[283,0],[283,710]],"uni063A":[[496,-426],[496,992]],"uniFECE":[[496,-426],[496,992]],"uniFED0":[[425,0],[425,992]],"uniFECF":[[283,0],[283,992]],"uni0641":[[1062,0],[1062,992]],"uniFED2":[[1062,0],[1062,992]],"uniFED4":[[425,0],[425,992]],"uniFED3":[[425,0],[425,992]],"uni06A4":[[1063,0],[1063,1134]],"uniFB6B":[[1063,0],[1063,1134]],"uniFB6D":[[425,0],[425,1134]],"uniFB6C":[[425,0],[425,1134]],"uni0642":[[496,-426],[638,992]],"uniFED6":[[496,-426],[638,992]],"uniFED8":[[425,0],[426,992]],"uniFED7":[[425,0],[425,993]],"uni0643":[[638,0],[496,851]],"uniFEDA":[[638,0],[496,851]],"uniFEDC":[[567,0],[567,1134]],"uniFEDB":[[567,0],[567,1134]],"uni0644":[[354,-426],[496,1134]],"uniFEDE":[[354,-426],[496,1135]],"uniFEE0":[[283,0],[283,1134]],"uniFEE0.alt":[[283,0],[283,1134]],"uniFEDF":[[283,0],[283,1134]],"uniFEDF.alt":[[275,0],[283,1134]],"uni0645":[[213,-426],[496,710]],"uniFEE2":[[213,-426],[496,710]],"uniFEE4":[[425,0],[425,710]],"uniFEE3":[[425,0],[425,710]],"uni0646":[[496,-426],[354,568]],"uniFEE6":[[496,-426],[354,568]],"uniFEE8":[[142,0],[142,852]],"uniFEE7":[[142,0],[142,852]],"uni0647":[[354,0],[354,710]],"uniFEEA":[[354,0],[213,710]],"uniFEEC":[[567,0],[567,710]],"uniFEEB":[[567,0],[567,710]],"uni0629":[[354,0],[354,992]],"uniFE94":[[354,0],[354,993]],"uni0648":[[354,-426],[354,710]],"uniFEEE":[[354,-426],[354,710]],"uni0624":[[354,-426],[354,1275]],"uniFE86":[[354,-426],[354,1275]],"uni0649":[[638,-426],[780,710]],"uniFEF0":[[638,-426],[496,426]],"uni064A":[[638,-709],[780,710]],"uniFEF2":[[638,-709],[496,426]],"uniFEF4":[[283,-284],[425,566]],"uniFEF4.ss01":[[141,-568],[142,567]],"uniFEF3":[[283,-284],[425,566]],"uniFEF3.ss01":[[141,-568],[142,567]],"uni0626":[[638,-426],[213,852]],"uniFE8A":[[638,-426],[213,852]],"uniFE8C":[[283,0],[280,1121]],"uniFE8B":[[283,0],[280,1121]],"uni0640":[[142,0],[142,284]]}},{"kind":"ligature","marks":{"uni0615":[0,0,284],"uni0670":[0,0,284],"uni0656":[0,0,284],"uni0654":[0,0,284],"uni064B":[0,0,284],"uni064C":[0,0,284],"uni064D":[1,0,284],"uni064E":[0,0,284],"uni064F":[0,0,284],"uni0650":[1,0,284],"uni0651":[0,0,284],"uni0652":[0,0,284],"uni0653":[0,0,284],"shaddaAlefabovear":[0,0,284],"shaddaDammaar":[0,0,284],"shaddaDammatanar":[0,0,284],"shaddaFathaar":[0,0,284],"shaddaFathatanar":[0,0,255],"shaddaKasraar":[0,0,284],"shaddaKasratanar":[0,0,284],"uni0658":[0,0,850],"uni0657":[0,0,284]},"bases":{"uniFEFB":[[[354,1134],[496,0]],[[354,1134],[496,0]]],"uniFEFC":[[[354,992],[354,0]],[[354,992],[354,0]]],"uniFEF7":[[[354,1559],[496,0]],[[354,1559],[496,0]]],"uniFEF8":[[[496,1559],[354,0]],[[496,1559],[354,0]]],"uniFEF9":[[[354,1134],[496,-426]],[[354,1134],[496,-426]]],"uniFEFA":[[[354,992],[354,-426]],[[354,992],[354,-426]]],"uniFEF5":[[[355,1418],[497,0]],[[355,1418],[497,0]]],"uniFEF6":[[[354,1418],[354,0]],[[354,1418],[354,0]]],"uniFC09":[[[1205,567],[1205,-284]],[[496,426],[638,-426]]],"uniFC6E":[[[1205,567],[1205,-284]],[[496,426],[638,-426]]],"uniFC0A":[[[1205,567],[1205,-284]],[[496,426],[638,-709]]],"uniFC6F":[[[1205,567],[1205,-284]],[[496,426],[638,-709]]],"uni06280626":[[[1205,567],[1205,-284]],[[354,852],[638,-426]]],"uni06280626.fina":[[[1205,567],[1205,-284]],[[354,852],[638,-426]]],"uni067E0649":[[[1488,566],[1346,-426]],[[496,426],[638,-426]]],"uni067E0649.fina":[[[1488,566],[1346,-426]],[[496,426],[638,-426]]],"uni067E064A":[[[1488,566],[1488,-426]],[[496,426],[638,-709]]],"uni067E064A.fina":[[[1488,566],[1488,-426]],[[496,426],[638,-709]]],"uni067E0626":[[[1488,566],[1488,-426]],[[354,852],[638,-426]]],"uni067E0626.fina":[[[1488,566],[1488,-426]],[[354,852],[638,-426]]],"uniFC59":[[[1488,566],[1488,-284]],[[496,426],[638,-426]]],"uniFC95":[[[1488,566],[1488,-284]],[[496,426],[638,-426]]],"uni064A0626":[[[1488,566],[1488,-284]],[[348,830],[638,-426]]],"uni064A0626.fina":[[[1488,566],[1488,-284]],[[348,830],[638,-426]]]}},{"kind":"mark","marks":{"uni064D":[0,0,284],"uni0650":[0,0,284]},"bases":{"uni064D":[[0,284]],"uni0650":[[0,284]]}},{"kind":"mark","marks":{"uni0615":[0,0,284],"uni0670":[0,0,284],"uni0656":[0,0,284],"uni0654":[0,0,284],"uni064B":[0,0,284],"uni064C":[0,0,284],"uni064E":[0,0,284],"uni064F":[0,0,284],"uni0651":[0,0,284],"uni0652":[0,0,284],"uni0653":[0,0,284],"shaddaAlefabovear":[0,0,284],"shaddaDammaar":[0,0,284],"shaddaDammatanar":[0,0,284],"shaddaFathaar":[0,0,284],"shaddaFathatanar":[0,0,255],"shaddaKasraar":[0,0,284],"shaddaKasratanar":[0,0,284],"uni0658":[0,0,850],"uni0657":[0,0,284]},"bases":{"uni0670":[[0,284]],"uni0656":[[0,284]],"uni0654":[[0,284]],"uni064B":[[0,284]],"uni064C":[[0,284]],"uni064E":[[0,284]],"uni064F":[[0,284]],"uni0651":[[0,284]],"uni0652":[[0,284]],"uni0653":[[0,284]],"shaddaAlefabovear":[[0,284]],"shaddaDammaar":[[0,284]],"shaddaDammatanar":[[0,284]],"shaddaFathaar":[[0,284]],"shaddaFathatanar":[[0,256]],"shaddaKasraar":[[0,284]],"shaddaKasratanar":[[0,284]],"uni0658":[[0,850]],"uni0657":[[0,284]]}}],"pixel_scale":0.013333333333333334} \ No newline at end of file diff --git a/assets/flipnote-editor/base.png b/assets/flipnote-editor/base.png new file mode 100644 index 0000000000000000000000000000000000000000..47217fc757c524fed6340fa45763d47ecc5b539b GIT binary patch literal 38262 zcmeHPZA@EL7(OipFD!(D;NnQ24kq&prUo$%Y1u$%Fu)k-bYmrP^T*7@xJ+;sTDE9Z zT&gpZB_?AeOE8&`i7}f4*Zxo^PJ@4tY~!QZhmycX!7X$f6c6CJJ-5D1OpIAD4+-Ih zL+?F(&-*^l`#k5A_sUC)Vq;dsFveo7#RV0NMSA$RMTz3Ssg1b(6#rq^wa!|p@GrZv z?On!9%vz9NS-*As{1^MT3?vk8*id6U+TLsatS#e;X-9D&Kpj#hJC>uf|>pl|jQrqde~qsijbTy*JE9S!z39~*2fYaBm8{K5Q7yw#Bg zU)|6Lb@8DuU$~(sO{bfQZ%w_M9_@}%yc4JHomd(2=*7u273uZ-4w8a~784SK6d*ie8=){92r+e(>o@UKk7Oyp=L@QpGf94Q9I$UL8j)9-+my>Z* zaubZ6)XAHV(xW}coGj4!>0~AqbH!MnZgQG0{_#?n(dT(Qt8*?@)63y2<`et&_cLKe zJM}iR!yd3topibL4$lnMM*nTm7gw#MseSgmLl-#EpGyBpO|^yDMvhE}S=SOi%T>+h z1bWp^Ioavc`4buRvbOL=FS*B&PLJ-^>Ff>rhRf(x&Em<}@^GM-9v$O%z7_RNE4`>& zc`jzXF!K>T`YXTljo3ZrFl(A<^w_%mY4qsl%M|Ze^7Y0rOZouM#BGyB^ysGLM$bUb z=xmr3t>Ek3d(lOY9?$gMI1%UT8*$G@r;y>)>D?CZg=w45H9TjXi}Vlu*h1fG4bRHB zN6v1fv%#Bba*lM^t#sJe^RWNYm3xp5>@_}$$1W^Bf*edzq*M?OP(VRY0$ivy0=Q5^ zVF6qKE`kvNTo8>n-~w<#=@WDpfD6C{^sOr1(Mtt834kNO5#R`L1ULpUQAGrRqZ$eW zd?{63L1?O>fG;H}PQb-~2N$K&$w}fYfcg{!Tn)wl5T9+1+ne1Db63lJjNoSe&PSvD zS)RAnX2(l47z~IqlVx(Qji6sz%=f~3zgqD=y=7~7EhmU@YG*WmVb$q!zkY5#eHO&Y zu0^Q_EHoD;z6G8!uwykn8KY;2o`;goH{~zC7#rpQi2h_U(Yo`QHyw4d!&{EEY3885 z4-2b{kisOLkob`A@_+nFb|M{5#rvvy5ADSPy}thSj(6xoE;`*{tn>^iuok|#U)I@Q zPEW?oJY|%8WyVOLc@ZF zMVl9p3KRiDD#*O6;wy9>MZgg0YJx}?5b0W=T`VX#EEEC5VWAuY*khDqfQAJPi$pWf zc@)h+=TS5RW16BF7}FHZz?i0J2FA2T7w>A`b5NWG5HJjrn$pNHsimGp-SC8WbxC@8os z6b->`(VXBD&o8WuDx+Pr|k2R~97n-aN"),u=t[0],l=t[1];u=String.fromCodePoint(a+=parseInt(u,36)),l=String.fromCodePoint(a+=parseInt(l,36)),n.set(u,l),e&&i.set(l,u)}})),{map:n,reverseMap:i}}function m(){if(!c){var r=k(s,!0),e=r.map,f=r.reverseMap;c=e,d=f,b=k(h,!1).map}}function j(r){return m(),c.get(r)||null}function g(r){return m(),d.get(r)||null}function p(r){return m(),b.get(r)||null}var q=f.L,w=f.R,y=f.EN,x=f.ES,_=f.ET,M=f.AN,z=f.CS,I=f.B,L=f.S,S=f.ON,R=f.BN,O=f.NSM,E=f.AL,N=f.LRO,T=f.RLO,A=f.LRE,D=f.RLE,P=f.PDF,W=f.LRI,B=f.RLI,F=f.FSI,U=f.PDI;var C;function G(r){return function(){if(!C){var r=k("14>1,j>2,t>2,u>2,1a>g,2v3>1,1>1,1ge>1,1wd>1,b>1,1j>1,f>1,ai>3,-2>3,+1,8>1k0,-1jq>1y7,-1y6>1hf,-1he>1h6,-1h5>1ha,-1h8>1qi,-1pu>1,6>3u,-3s>7,6>1,1>1,f>1,1>1,+2,3>1,1>1,+13,4>1,1>1,6>1eo,-1ee>1,3>1mg,-1me>1mk,-1mj>1mi,-1mg>1mi,-1md>1,1>1,+2,1>10k,-103>1,1>1,4>1,5>1,1>1,+10,3>1,1>8,-7>8,+1,-6>7,+1,a>1,1>1,u>1,u6>1,1>1,+5,26>1,1>1,2>1,2>2,8>1,7>1,4>1,1>1,+5,b8>1,1>1,+3,1>3,-2>1,2>1,1>1,+2,c>1,3>1,1>1,+2,h>1,3>1,a>1,1>1,2>1,3>1,1>1,d>1,f>1,3>1,1a>1,1>1,6>1,7>1,13>1,k>1,1>1,+19,4>1,1>1,+2,2>1,1>1,+18,m>1,a>1,1>1,lk>1,1>1,4>1,2>1,f>1,3>1,1>1,+3,db>1,1>1,+3,3>1,1>1,+2,14qm>1,1>1,+1,6>1,4j>1,j>2,t>2,u>2,2>1,+1",!0),e=r.map;r.reverseMap.forEach((function(r,f){e.set(f,r)})),C=e}}(),C.get(r)||null}function H(r,e,f,a){var n=r.length;f=Math.max(0,null==f?0:+f),a=Math.min(n-1,null==a?n-1:+a);var i=[];return e.paragraphs.forEach((function(n){var v=Math.max(f,n.start),o=Math.min(a,n.end);if(v=v&&l(r[c])&t;c--)u[c]=n.level;for(var d=n.level,b=1/0,s=0;sd&&(d=h),h=b;k--)for(var m=0;m=k){for(var j=m;m+1=k;)m++;m>j&&i.push([j+v,m+v])}}})),i}function J(r,e,f,a){for(var n=H(r,e,f,a),i=[],v=0;v0)Q--;else if(X>0){for(V=0;!J[J.length-1].o;)J.pop();var fr=J[J.length-1].t;null!=fr&&(b.set(fr,Y),b.set(Y,fr)),J.pop(),X--}K=J[J.length-1],d[Y]=K.i,K.v&&c(Y,K.v)}else Z&P?(0===Q&&(V>0?V--:!K.o&&J.length>1&&(J.pop(),K=J[J.length-1])),d[Y]=K.i):Z&I&&(d[Y]=h.level);else d[Y]=K.i,K.v&&Z!==R&&c(Y,K.v)}for(var ar=[],nr=null,ir=h.start;ir<=h.end;ir++){var vr=f[ir];if(!(vr&o)){var or=d[ir],tr=vr&n,ur=vr===U;nr&&or===nr.i?(nr.u=ir,nr.l=tr):ar.push(nr={h:ir,u:ir,i:or,k:ur,l:tr})}}for(var lr=[],cr=0;cr=0;wr--)if(!(f[wr]&o)){qr=d[wr];break}var yr=kr[kr.length-1],xr=d[yr],_r=h.level;if(!(f[yr]&n))for(var Mr=yr+1;Mr<=h.end;Mr++)if(!(f[Mr]&o)){_r=d[Mr];break}lr.push({m:kr,j:Math.max(qr,pr)%2?w:q,g:Math.max(_r,xr)%2?w:q})}}for(var zr=0;zr=0;Ar--)if(!(f[Lr[Ar]]&o)){Tr=f[Lr[Ar]];break}c(Nr,Tr&(n|U)?S:Tr)}}if(u.get(y))for(var Dr=0;Dr=-1;Wr--){var Br=-1===Wr?Sr:f[Lr[Wr]];if(Br&i){Br===E&&c(Pr,M);break}}}if(u.get(E))for(var Fr=0;Fr=0&&(Hr=f[Lr[Kr]])&o;Kr--);for(var Qr=Cr+1;Qr=0&&f[Lr[Yr]]&(_|o);Yr--)c(Lr[Yr],y);for(Vr++;Vr=0&&f[Lr[re]]&o;re--)c(Lr[re],S);for(var ee=Zr+1;ee=0;be--){var se=ue[be].char;if(se===de||se===g(p(ce))||j(p(se))===ce){te.push([ue[be].seqIndex,le]),ue.length=be;break}}}te.sort((function(r,e){return r[0]-e[0]}));for(var he=0;he=0;xe--){var _e=Lr[xe];if(f[_e]&oe){var Me=f[_e]&ve?w:q;pe=Me!==Or?Me:Or;break}}}if(pe){if(f[Lr[me]]=f[Lr[je]]=pe,pe!==Or)for(var ze=me+1;ze=0;Ee--){if(!(f[Lr[Ee]]&o)){Oe=f[Lr[Ee]]&ve?w:q;break}Se=Ee}for(var Ne=Rr,Te=Le+1;Te=0&&l(r[Be])&t;Be--)d[Be]=h.level}}return{levels:d,paragraphs:s};function Fe(e,a){for(var i=e;i7Z>^G3;y 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

gHyujX z!-u8eL8Q4Tn1<+ri(Uc!(Sl zk?E6q{5{dOw=^v{OuNeA&oZXwv3Mp0#7gh=bTgd`+dp(#M45* zX5sH}X6ygXEI0;TjMQQEwztHru@e5<@iO&Y5^7lU{H^PfNi&u5Ts?Ku;Q>PSWipZL z@(NAB;U+zNDR#WL@iOo)e?9!mjNtfmYB{{lcOW}(y8m-BS0dxZxm=pz2E@F8`rmOr g=TXCnvjb~<;YNmdZJ$Ix_E}QB^G4?i&pJl_3k<)cSO5S3 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/clear.png b/assets/flipnote-editor/clear.png new file mode 100644 index 0000000000000000000000000000000000000000..94fc21462f59a3820973475ccc11e55feeb926d7 GIT binary patch literal 16729 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 E0FU&w%K!iX literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/clone-placeholder.png b/assets/flipnote-editor/clone-placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..b6c9e9d085987d1ad707c5d3121b1de966e37453 GIT binary patch literal 2446 zcmeAS@N?(olHy`uVBq!ia0y~yV0^&9z|_FO1Qa>F%7&GJfiu+8#WAGf*4w-L{a)oT z97z0oD0W3+Mo(w6vBmBTiPyFkPG5{vcAT_5@b1x?y-eHrjudd-J%2!a-?Dhk+@Hl= zcIR2z#e?q6xL^6%>1E+06?X=c^Y+Ua92PJNWH2v#P8v8jp8 zf`N%w;Q|APNrMA3kY?hA(Cy1YGfST7h%;!dEZJ7J`qk>=g*jb}SsYCZE>%R!Fz~8g zVBo#F>hGCV*`*ElSAILULDa;-nG@L%4ikuBU>a-#cALO9Vk(9>1Kn8&#R?Y?z5@D= zMGELU7!C9)m=C65UPq!a6+vPW!wbkqm;|%|$wcxMV_J`DGT0+1*5h#o0mY*p z1EmFGL&3sCH`rm_$&^oDzJxsP+neU<$v?>W1-dD zXZz}YF7|r;{de=h1NWuFpjqcn$hQS2%gf5js;h6GNzQ)Ls5^JgoS4|ysgjj-b?^Fl z8`Jild7k$8>t^B8hk2j)+3 z?kOlZWH4u`OW+khLwi`py7KJ!q=esg_V)5}a;JLUe689WcfLpQ!UpxvKWpsfPnWDL zEKJ-Laa4>QlBMh$Y%@+s@wLBvz3_@fxGIlxc*sF6Y$3!Vb;2V)2dF@}$$=S`L1k?A zryZ}4i;thaegFRb-f>$rlWpYISFQNgy5JIfSyffkzXO;2@2F4TVQeA(83+Bx~497huEqT8`K3=S6-_IXik1GnT zIhZc9ZwRxRoh_Zs%3A=BMMO47$rTN(;US5$Z@n#>nYj68PGz-;)ZRUNMEWDGD;C~= z&(GGps#*N_oXWhb+ox4;Nnqs#qSs%3|NV0JDATsfFH5ZEhWfuO+x;|te?`Hwi(bv% zT6Ovg7fiy_rYA_gxO2Dp(cacGi&f2)PVM=m7PeRX1}n_rut+dzTz|80g4^PY|Nn2i z{q|X%=gvzHk^)7Z?wj@gp5lcM>qDOxJU82Z>A}{i@0Q*Z@JYlmU1*a()*y4= zXcTUC^OS3{xn#^I>nnOv-nlPx*5&5>TBpm$05o1ZLp!5E9%co7$}=-otce+3>7bPI zu#^T1Sp^$vXJA@S)e4cMP{2|jljK&C6oX@m zq)3>sIP~|-LYdiipcLE4wm?!VzmUZ-;TnUK_>1qay@S~{$Zxa2sN=!q{Ee>1DxPPU k8bl!lCNSO(=zn5geKn#u@@Kv;u${%=>FVdQ&MBb@0EZ{iMF0Q* literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/closepages.png b/assets/flipnote-editor/closepages.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9f24bd0b094d8eee9be456420cf23dabb10b38 GIT binary patch literal 7712 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 aO_?%n&hlp#zJkwS{zJ)g_C1*S^nU{dajDS& literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/cut.png b/assets/flipnote-editor/cut.png new file mode 100644 index 0000000000000000000000000000000000000000..8def3c206e6e2a0f6aeea20417f8ab3b7a4ea71e GIT binary patch literal 5977 zcmeHLYfw{X8va6H0$K<=sBsIJxa`!~DFv&uMVgRAM~6jP*d^^s3nfuj?5Gtbm0&|I z-~}r?&|N!4swBJPAdAY)R3Snx8U!6M#|z3ulQam#n#2%dPDoBj_OR2x#iQjRdEebMtkl`v5lvcih z<{zmmSS4YUP1W~z>0lqy(s|6(CYX|G9JN9pSK}}xC#~p?X(vhocX$L>nE>~Zy~bGv zZKYpzTf^|njBZi6t6Qd(A`@%;{sElj)SAGzqg4E3c+cFNjR=Xn4YoHR(PT9{PQsY` zH!%_t+3%kH#O1b|+lUa`uQS9-{LjdugJnJj7hUf0ZHj$@}-gECm_@@`0LT;IFKxH>; z9K&?SsL`Fy@XL*E2Z>~}?NIMK6cUgLg@o`LwNc|J73k~bu|G8MI+Y-LG9oZX;wc+K zpz?_Xjbj(XXE(Z40>^2ML+T%DLThyQ#cCV`viHYnuSp^(Eu&9bZHiQy{}xfW)_id4 zeYcuWB$%TzyvD65IJhq+FiEM*{k?Q}WG|x69`#MsxCzL}&)R+mtk#)N?2VF$OMBd+WjI zPv?vG3%QDCW;}2~)*F-O+iZa!`o%2xbtLL_y1)92FfUzO%0G~5Hbcd&3gxiN18Q#t zro)-}QTMSyP47q6Up_o;%BY9Jz8uuyPY+-`r+G-?47Nsi6QxTzf51?gAjW3g6(uO_ zB-Lpgj?-qUGQv^?g|#osUi4Ir%43T=(2y-{p!*3Imv|#^SwVF0%zWh*bOM(Ngpe@_sV}il`;wEYOs14!UU|$2@g062I%Q47p0oHGI%iz7wT>Gs8N)U;U;6jQJ zI7RtKA2C_Ejz)K1Fi4#quImVxnhD&U{-`@y4Tk(;LD;DmPa8?cOe}2TB!|PAsA!1?-M zWWoQX2s`;AnM?K#!d!;d0dh|*yp5KM#Tl@$rdmLG9)m&&mc}f&pY%sXb8IGwjM)1w zcJnqKSCI7s^k_bIu9XQ_;H+a%@ks#%R%0WV&S_42Lz*r*f~Ne>cnT07jabmw&kwrk zA(q*e_QB|G_TMhpNRr;ObpjkRwc%l!P#85g4-bGoz`IJ;NOqL!y(P zhX{>|b8yAEK^@`yahF?|i`bel22-gx_eKs9$$#{u{wg%aU(OXp@e#L%KS~%sfZeIG z-}Ng~WU#h3e~&+|X3kKJe97$k~<`bVm zb#29=_~*wkLd+xC-Z-Ww#+_}Rlc2BVGiXs5kXgV#+F?DE&vKy*s)h8#?CTgVi^_c5 z+yWP$D-c-)|B|<r(;OBq!TeK(Jv9o{9v zFm-61)2vKu1P(DR+Ni$X^McqCULSe&-$-BC#r&T&ddLHyi?#U8H;Cmph$G(Q3F=q1 V-&gF&M?aZ^j4fH4TQ_kk{|h?AsJH+C literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/delete-frame.png b/assets/flipnote-editor/delete-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..b73f5ee09dcb35e7f438536bb8a0d580ae3c4344 GIT binary patch literal 2795 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/drawing-engine.js b/assets/flipnote-editor/drawing-engine.js new file mode 100644 index 0000000..32b36d7 --- /dev/null +++ b/assets/flipnote-editor/drawing-engine.js @@ -0,0 +1,661 @@ +(function (global) { + 'use strict'; + + const WIDTH = 256; + const HEIGHT = 192; + const RNG_INITIAL_STATE = 0x1F01; + + const PEN_NAMES = [ + '1 px', + 'Direction-aware 2 px', + '3×3 square', + 'Squiggle', + 'Directional =', + 'Sparse airbrush', + 'Jitter line', + 'Dense spray', + '5×5 square', + '7×7 square', + '9×9 square', + 'Dynamic-width pen', + 'Hollow diamond', + 'Large airbrush', + 'Wide jitter line', + 'Heavy spray', + ]; + + const PAINT_NAMES = [ + 'Sparse 3×3 dots', + 'Sparse 2×2 dots', + 'Vertical stripes', + 'Horizontal stripes', + 'Checkerboard', + 'Dense 2×2', + 'Dense 3×3', + 'Custom motif', + ]; + + const FILL_NAMES = [ + 'Sparse 3×3 fill', + 'Sparse 2×2 fill', + 'Vertical stripe fill', + 'Horizontal stripe fill', + 'Checkerboard fill', + 'Dense 2×2 fill', + 'Dense 3×3 fill', + 'Solid fill', + ]; + + // The supplied ROM notes identify all eight eraser action slots and state + // that advanced erasing keeps the base geometry while filtering candidate + // pixels, but do not include the exact geometry/filter routines. These + // names/geometries preserve the restored editor's existing DSi palette + // until those remaining routines are ported from the ROM. + const ERASER_NAMES = [ + 'Pixel', + '3×3 square', + 'Medium round', + 'Large round', + '9×9 square', + '15×15 square', + 'Vertical bar', + 'Horizontal bar', + ]; + + const AIRBRUSH_SMALL_MASK = [ + '..#..', + '.###.', + '#####', + '.###.', + '..#..', + ]; + + const AIRBRUSH_LARGE_MASK = [ + '......###......', + '.....#####.....', + '...#########...', + '..###########..', + '.#############.', + '###############', + '###############', + '###############', + '###############', + '###############', + '.#############.', + '..###########..', + '...#########...', + '.....#####.....', + '......###......', + ]; + + const RING_OUTER_MASK = [ + '..###..', + '.#####.', + '#######', + '#######', + '#######', + '.#####.', + '..###..', + ]; + + const RING_INNER_MASK = [ + '..#..', + '.###.', + '#####', + '.###.', + '..#..', + ]; + + let rngState = RNG_INITIAL_STATE; + + function resetRandom(state) { + const next = Number(state); + rngState = Number.isFinite(next) ? (next & 0xffff) : RNG_INITIAL_STATE; + if (!rngState) rngState = RNG_INITIAL_STATE; + return rngState; + } + + function getRandomState() { + return rngState & 0xffff; + } + + function random16() { + const old = rngState & 0xffff; + const bit0 = old & 1; + const bit3 = (old >> 3) & 1; + rngState = old >> 1; + if (bit0 === bit3) rngState |= 0x8000; + rngState &= 0xffff; + return rngState; + } + + function randomRange(range) { + const safeRange = Math.max(1, Math.floor(range)); + return random16() % safeRange; + } + + function clamp(value, min, max) { + return Math.max(min, Math.min(max, value)); + } + + function inBounds(x, y) { + return x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT; + } + + function indexOf(x, y) { + return y * WIDTH + x; + } + + function writePixel(mask, x, y, value) { + if (!inBounds(x, y)) return; + mask[indexOf(x, y)] = value ? 1 : 0; + } + + function setPixel(mask, x, y) { + writePixel(mask, x, y, 1); + } + + function clearPixel(mask, x, y) { + writePixel(mask, x, y, 0); + } + + function lineSteps(x0, y0, x1, y1, includeStart) { + let x = Math.round(x0); + let y = Math.round(y0); + const tx = Math.round(x1); + const ty = Math.round(y1); + const points = []; + const dx = Math.abs(tx - x); + const sx = x < tx ? 1 : -1; + const dyAbs = Math.abs(ty - y); + const sy = y < ty ? 1 : -1; + let err = dx - dyAbs; + let first = true; + let previousX = x; + let previousY = y; + + while (true) { + if (includeStart || !first) { + points.push({ + x, + y, + sx: first ? 0 : x - previousX, + sy: first ? 0 : y - previousY, + }); + } + if (x === tx && y === ty) break; + first = false; + previousX = x; + previousY = y; + const e2 = 2 * err; + if (e2 > -dyAbs) { + err -= dyAbs; + x += sx; + } + if (e2 < dx) { + err += dx; + y += sy; + } + } + return points; + } + + function stampSquare(mask, x, y, radius, value) { + for (let dy = -radius; dy <= radius; dy++) { + for (let dx = -radius; dx <= radius; dx++) { + writePixel(mask, x + dx, y + dy, value); + } + } + } + + function stampMask(mask, rows, x, y, value) { + const height = rows.length; + const width = rows[0].length; + const ox = Math.floor(width / 2); + const oy = Math.floor(height / 2); + for (let row = 0; row < height; row++) { + for (let col = 0; col < width; col++) { + if (rows[row][col] === '#') writePixel(mask, x + col - ox, y + row - oy, value); + } + } + } + + function stampDirectionAware(mask, point, value) { + const x = point.x; + const y = point.y; + const sx = point.sx || 0; + const sy = point.sy || 0; + writePixel(mask, x, y, value); + writePixel(mask, x + sy, y, value); + writePixel(mask, x, y + sx, value); + } + + function stampDirectionalEqual(mask, point) { + const x = point.x; + const y = point.y; + const sx = point.sx || 0; + const sy = point.sy || 0; + const horizontalMotion = sx !== 0 && sy === 0; + const verticalMotion = sy !== 0 && sx === 0; + + if (horizontalMotion) { + setPixel(mask, x, y - 1); + setPixel(mask, x, y + 1); + } else if (verticalMotion) { + setPixel(mask, x - 1, y); + setPixel(mask, x + 1, y); + } else if ((sx === sy) || (sx === 0 && sy === 0)) { + setPixel(mask, x - 1, y); + setPixel(mask, x + 1, y); + } else { + setPixel(mask, x, y - 1); + setPixel(mask, x, y + 1); + } + + clearPixel(mask, x, y); + if (Math.abs(sx) >= Math.abs(sy)) setPixel(mask, x, y + (sx || 1)); + else setPixel(mask, x + (sy || 1), y); + } + + function stampSquiggle(mask, point, state) { + const qx = point.x & ~1; + const qy = point.y & ~1; + const anchor = state.squiggleAnchor; + if (anchor && (anchor.x !== qx || anchor.y !== qy)) { + const mx = ((anchor.x + qx) >> 1) & ~1; + const my = ((anchor.y + qy) >> 1) & ~1; + stampSquare(mask, mx, my, 0, 1); + setPixel(mask, mx + 1, my); + setPixel(mask, mx, my + 1); + setPixel(mask, mx + 1, my + 1); + } + setPixel(mask, qx, qy); + setPixel(mask, qx + 1, qy); + setPixel(mask, qx, qy + 1); + setPixel(mask, qx + 1, qy + 1); + state.squiggleAnchor = {x: qx, y: qy}; + } + + function airbrushCandidatePass(segmentMagnitude, large) { + // The ROM consumes the LFSR while walking candidate pixels and folds + // segment delta into its skip logic. The supplied notes do not include + // the exact branch constants, so preserve both deterministic LFSR call + // order and motion sensitivity here pending the direct routine port. + const base = large ? 72 : 98; + const motionPenalty = Math.min(38, Math.max(0, segmentMagnitude - 1) * (large ? 2 : 4)); + return (random16() & 0xff) < Math.max(24, base - motionPenalty); + } + + function stampStochasticMask(mask, rows, point, segmentMagnitude) { + const height = rows.length; + const width = rows[0].length; + const ox = Math.floor(width / 2); + const oy = Math.floor(height / 2); + const large = width > 5; + for (let row = 0; row < height; row++) { + for (let col = 0; col < width; col++) { + if (rows[row][col] !== '#') continue; + if (!airbrushCandidatePass(segmentMagnitude, large)) continue; + setPixel(mask, point.x + col - ox, point.y + row - oy); + } + } + } + + function randomOffset(mode) { + return mode ? randomRange(10) - 5 : randomRange(5) - 2; + } + + function drawOnePixelLine(mask, x0, y0, x1, y1) { + for (const point of lineSteps(x0, y0, x1, y1, true)) setPixel(mask, point.x, point.y); + } + + function drawJitterSegment(mask, x0, y0, x1, y1, mode) { + const ax = clamp(Math.round(x0) + randomOffset(mode), 0, WIDTH - 1); + const ay = clamp(Math.round(y0) + randomOffset(mode), 0, HEIGHT - 1); + const bx = clamp(Math.round(x1) + randomOffset(mode), 0, WIDTH - 1); + const by = clamp(Math.round(y1) + randomOffset(mode), 0, HEIGHT - 1); + drawOnePixelLine(mask, ax, ay, bx, by); + } + + function sprayClass(dx, dy, auxiliaryValue) { + const dynamic = Number(auxiliaryValue) || 0; + if (dynamic) return clamp(Math.ceil(Math.abs(dynamic) / 120), 1, 6); + const d2 = dx * dx + dy * dy; + if (d2 < 4) return 6; + if (d2 < 8) return 5; + if (d2 < 12) return 4; + if (d2 < 24) return 3; + if (d2 < 48) return 2; + return 1; + } + + function drawComplexSpray(mask, x0, y0, x1, y1, mode, auxiliaryValue) { + const dx = x1 - x0; + const dy = y1 - y0; + const cls = sprayClass(dx, dy, auxiliaryValue); + const count = cls + (mode ? 3 : 2); + const spread = (7 - cls) + (mode ? 5 : 2); + for (let i = 0; i < count; i++) { + const scale = mode && (i & 1) ? 2 : 1; + const ax = clamp(Math.round(x0) + (randomRange(spread * 2 + 1) - spread) * scale, 0, WIDTH - 1); + const ay = clamp(Math.round(y0) + (randomRange(spread * 2 + 1) - spread) * scale, 0, HEIGHT - 1); + const bx = clamp(Math.round(x1) + (randomRange(spread * 2 + 1) - spread) * scale, 0, WIDTH - 1); + const by = clamp(Math.round(y1) + (randomRange(spread * 2 + 1) - spread) * scale, 0, HEIGHT - 1); + drawOnePixelLine(mask, ax, ay, bx, by); + } + } + + function paintPattern(id, x, y, motif) { + switch (id & 7) { + case 0: + return x % 3 === 1 && y % 3 === 1; + case 1: + return (x & 1) === 0 && (y & 1) === 0; + case 2: + return (x & 1) === 1; + case 3: + return (y & 1) === 1; + case 4: + return ((x + y) & 1) === 1; + case 5: + return (x & 1) !== 0 || (y & 1) !== 0; + case 6: + return !(x % 3 === 1 && y % 3 === 1); + case 7: + if (motif && motif.data && motif.width && motif.height) { + const mx = ((x % motif.width) + motif.width) % motif.width; + const my = ((y % motif.height) + motif.height) % motif.height; + return !!motif.data[my * motif.width + mx]; + } + // The supplied excerpt identifies slot 7 as a separate action + // but does not include its editor/capture routine. Keep the + // restored editor's neutral criss-cross motif until that ROM + // routine is supplied/ported. + return ((x - y) % 5 === 0) || ((x + y) % 5 === 0); + default: + return false; + } + } + + function stampPaint(mask, logicalId, x, y, options) { + const baseId = logicalId & 7; + const expert = logicalId >= 8; + const paintState = Number(options && options.paintState); + const radius = paintState === 2 ? 3 : (expert ? 7 : 1); + for (let dy = -radius; dy <= radius; dy++) { + for (let dx = -radius; dx <= radius; dx++) { + const px = x + dx; + const py = y + dy; + if (!inBounds(px, py)) continue; + if (paintPattern(baseId, px, py, options && options.motif)) setPixel(mask, px, py); + } + } + } + + + // Bucket-style patterned fill. The connected transparent region is found + // from the pre-fill layer state so setting patterned pixels cannot split + // the traversal while it runs. Slots 0-6 reuse Paint 1-7's absolute-canvas + // predicates. Slot 7 is deliberately solid, per the editor UI spec. + function floodFillPattern(mask, slot, startX, startY, options) { + const sx = Math.round(startX); + const sy = Math.round(startY); + if (!inBounds(sx, sy)) return 0; + const startIndex = sy * WIDTH + sx; + // Normally the current layer is both the destination and the fill + // boundary. Shift-fill can supply the other layer as boundaryMask so + // its ink contains the flood while the current layer still receives + // the selected fill pattern/colour. + const suppliedBoundary = options && options.boundaryMask; + const boundaryMask = suppliedBoundary && suppliedBoundary.length === mask.length + ? suppliedBoundary + : mask; + if (boundaryMask[startIndex]) return 0; + + const original = new Uint8Array(boundaryMask); + const visited = new Uint8Array(WIDTH * HEIGHT); + const queueX = new Int16Array(WIDTH * HEIGHT); + const queueY = new Int16Array(WIDTH * HEIGHT); + let head = 0; + let tail = 0; + let changed = 0; + queueX[tail] = sx; + queueY[tail] = sy; + tail++; + visited[startIndex] = 1; + + const patternId = clamp(Math.round(Number(slot) || 0), 0, 7); + const solid = patternId === 7; + const motif = options && options.motif; + + while (head < tail) { + const x = queueX[head]; + const y = queueY[head]; + head++; + const index = y * WIDTH + x; + + if (solid || paintPattern(patternId, x, y, motif)) { + if (!mask[index]) { + mask[index] = 1; + changed++; + } + } + + if (x > 0) { + const n = index - 1; + if (!visited[n] && !original[n]) { + visited[n] = 1; + queueX[tail] = x - 1; + queueY[tail] = y; + tail++; + } + } + if (x + 1 < WIDTH) { + const n = index + 1; + if (!visited[n] && !original[n]) { + visited[n] = 1; + queueX[tail] = x + 1; + queueY[tail] = y; + tail++; + } + } + if (y > 0) { + const n = index - WIDTH; + if (!visited[n] && !original[n]) { + visited[n] = 1; + queueX[tail] = x; + queueY[tail] = y - 1; + tail++; + } + } + if (y + 1 < HEIGHT) { + const n = index + WIDTH; + if (!visited[n] && !original[n]) { + visited[n] = 1; + queueX[tail] = x; + queueY[tail] = y + 1; + tail++; + } + } + } + return changed; + } + + function eraserContains(slot, dx, dy) { + switch (slot & 7) { + case 0: return dx === 0 && dy === 0; + case 1: return Math.abs(dx) <= 1 && Math.abs(dy) <= 1; + case 2: return dx * dx + dy * dy <= 12; + case 3: return dx * dx + dy * dy <= 42; + case 4: return Math.abs(dx) <= 4 && Math.abs(dy) <= 4; + case 5: return Math.abs(dx) <= 7 && Math.abs(dy) <= 7; + case 6: return Math.abs(dx) <= 1 && Math.abs(dy) <= 6; + case 7: return Math.abs(dy) <= 1 && Math.abs(dx) <= 6; + default: return false; + } + } + + function eraserRadius(slot) { + return [0, 1, 3, 6, 4, 7, 6, 6][slot & 7]; + } + + function stampEraser(mask, logicalId, x, y, options) { + const slot = logicalId & 7; + const expert = logicalId >= 8; + const radius = eraserRadius(slot); + // Advanced Erase keeps the base Eraser geometry, but its candidate + // pixels use the most recently used Paint texture. Paint 5 + // (checkerboard / pattern id 4) is the fallback until Paint has + // actually been used or selected in this editor session. + const requestedPattern = Number(options && options.eraserPatternId); + const eraserPatternId = Number.isFinite(requestedPattern) + ? clamp(Math.round(requestedPattern), 0, 7) + : 4; + for (let dy = -radius; dy <= radius; dy++) { + for (let dx = -radius; dx <= radius; dx++) { + if (!eraserContains(slot, dx, dy)) continue; + const px = x + dx; + const py = y + dy; + if (expert && !paintPattern(eraserPatternId, px, py, options && options.motif)) continue; + clearPixel(mask, px, py); + } + } + } + + function createStrokeState() { + return { + squiggleAnchor: null, + ringPrevious: null, + }; + } + + function stampPenPoint(mask, logicalId, point, state, segmentMagnitude, options) { + switch (logicalId) { + case 0: + setPixel(mask, point.x, point.y); + break; + case 1: + stampDirectionAware(mask, point, 1); + break; + case 2: + stampSquare(mask, point.x, point.y, 1, 1); + break; + case 3: + stampSquiggle(mask, point, state); + break; + case 4: + stampDirectionalEqual(mask, point); + break; + case 5: + stampStochasticMask(mask, AIRBRUSH_SMALL_MASK, point, segmentMagnitude); + break; + case 8: + stampSquare(mask, point.x, point.y, 2, 1); + break; + case 9: + stampSquare(mask, point.x, point.y, 3, 1); + break; + case 10: + stampSquare(mask, point.x, point.y, 4, 1); + break; + case 11: { + const aux = Number(options && options.auxiliaryValue) || 0; + const d2 = segmentMagnitude * segmentMagnitude; + let cls; + if (aux) cls = clamp(Math.ceil(Math.abs(aux) / 120), 1, 3); + else if (d2 < 4) cls = 3; + else if (d2 < 32) cls = 2; + else cls = 1; + if (cls === 1) setPixel(mask, point.x, point.y); + else if (cls === 2) stampDirectionAware(mask, point, 1); + else stampSquare(mask, point.x, point.y, 1, 1); + break; + } + case 12: { + const previous = state.ringPrevious || {x: point.x, y: point.y}; + stampMask(mask, RING_OUTER_MASK, point.x, point.y, 1); + stampMask(mask, RING_INNER_MASK, previous.x, previous.y, 0); + state.ringPrevious = {x: point.x, y: point.y}; + break; + } + case 13: + stampStochasticMask(mask, AIRBRUSH_LARGE_MASK, point, segmentMagnitude); + break; + default: + setPixel(mask, point.x, point.y); + break; + } + } + + function applyStrokeStart(mask, kind, logicalId, x, y, state, options) { + const point = {x: Math.round(x), y: Math.round(y), sx: 0, sy: 0}; + const strokeState = state || createStrokeState(); + if (kind === 'paint') stampPaint(mask, logicalId, point.x, point.y, options); + else if (kind === 'eraser') stampEraser(mask, logicalId, point.x, point.y, options); + else if (logicalId === 6 || logicalId === 14) { + setPixel(mask, point.x, point.y); + } else if (logicalId === 7 || logicalId === 15) { + setPixel(mask, point.x, point.y); + } else { + stampPenPoint(mask, logicalId, point, strokeState, 0, options); + } + return strokeState; + } + + function applyStrokeSegment(mask, kind, logicalId, x0, y0, x1, y1, state, options) { + const strokeState = state || createStrokeState(); + const dx = Math.round(x1) - Math.round(x0); + const dy = Math.round(y1) - Math.round(y0); + const segmentMagnitude = Math.max(Math.abs(dx), Math.abs(dy)); + + if (kind === 'pen' && (logicalId === 6 || logicalId === 14)) { + drawJitterSegment(mask, x0, y0, x1, y1, logicalId === 14 ? 1 : 0); + return strokeState; + } + if (kind === 'pen' && (logicalId === 7 || logicalId === 15)) { + drawComplexSpray(mask, x0, y0, x1, y1, logicalId === 15 ? 1 : 0, options && options.auxiliaryValue); + return strokeState; + } + + const points = lineSteps(x0, y0, x1, y1, false); + for (const point of points) { + if (kind === 'paint') stampPaint(mask, logicalId, point.x, point.y, options); + else if (kind === 'eraser') stampEraser(mask, logicalId, point.x, point.y, options); + else stampPenPoint(mask, logicalId, point, strokeState, segmentMagnitude, options); + } + return strokeState; + } + + function logicalId(slot, expert) { + return clamp(Math.round(Number(slot) || 0), 0, 7) + (expert ? 8 : 0); + } + + function toolName(kind, logical) { + const id = clamp(Math.round(Number(logical) || 0), 0, 15); + if (kind === 'paint') return PAINT_NAMES[id & 7]; + if (kind === 'fill') return FILL_NAMES[id & 7]; + if (kind === 'eraser') return ERASER_NAMES[id & 7]; + return PEN_NAMES[id]; + } + + global.FlipnoteDrawingEngine = Object.freeze({ + WIDTH, + HEIGHT, + RNG_INITIAL_STATE, + PEN_NAMES: PEN_NAMES.slice(), + PAINT_NAMES: PAINT_NAMES.slice(), + FILL_NAMES: FILL_NAMES.slice(), + ERASER_NAMES: ERASER_NAMES.slice(), + resetRandom, + getRandomState, + random16, + lineSteps, + paintPattern, + floodFillPattern, + logicalId, + toolName, + createStrokeState, + applyStrokeStart, + applyStrokeSegment, + }); +})(typeof window !== 'undefined' ? window : globalThis); diff --git a/assets/flipnote-editor/earsHurt.png b/assets/flipnote-editor/earsHurt.png new file mode 100644 index 0000000000000000000000000000000000000000..726e1244ce7a25f140c453264c1d5571294d5457 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQY^(zo*^7SP{WbZ0py2!x;Tbp zIKREx$jcxg;_R4n;@|EG5s&Bc)?L0_`F*GIpRga_>US}iD9mGE`pEQyMbH7lieO|B z=umHP*uxOUz{v85Q$aw#LDYevv4V-?2zLUj+_bAq5C3d)p8&Li!PC{xWt~$(69AGf BFYy2X literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/editor.js b/assets/flipnote-editor/editor.js new file mode 100644 index 0000000..87f7672 --- /dev/null +++ b/assets/flipnote-editor/editor.js @@ -0,0 +1,7265 @@ +const filenameElement = document.getElementById('filename'); + +// Track whether this editor session has made any user-visible change. The +// browser's native beforeunload confirmation is only armed after the first +// opening baseline, not as an edit. +let editorHasUnsavedChanges = false; +let editorDirtyTrackingReady = false; +let editorDirtyTrackingSuspended = 0; + +function markEditorDirty() { + if (!editorDirtyTrackingReady || editorDirtyTrackingSuspended > 0) return; + editorHasUnsavedChanges = true; +} + +function resetEditorDirtyBaseline() { + editorHasUnsavedChanges = false; +} + +const defaultTitle = 'Untitled flipnote - Flipnote Editor'; +document.title = defaultTitle; +const EDITOR_SHORTCUT_STORAGE_KEY = 'flipnote-editor.flipnote-editor.shortcuts.v1'; +const EDITOR_SHORTCUTS = [ + ['playPause', 'Play / pause', 'Space', 'global'], + ['alternateActions', 'Advanced / alternate actions (hold)', 'Shift', 'modifier'], + ['closeDialog', 'Close an editor window', 'Escape', 'dialog'], + ['confirmTitle', 'Finish editing the title', 'Enter', 'title'], + ['previousFrame', 'Previous page', 'ArrowLeft', 'stopped'], + ['nextFrame', 'Next page', 'ArrowRight', 'stopped'], + ['nudgeLeft', 'Nudge left', 'Shift+ArrowLeft', 'editing'], + ['nudgeRight', 'Nudge right', 'Shift+ArrowRight', 'editing'], + ['nudgeUp', 'Nudge up', 'Shift+ArrowUp', 'editing'], + ['nudgeDown', 'Nudge down', 'Shift+ArrowDown', 'editing'], + ['undo', 'Undo', 'U', 'editing'], + ['redo', 'Redo', 'R', 'editing'], + ['clear', 'Clear active layer', 'C', 'editing'], + ['clearAlt', 'Clear active layer (second shortcut)', 'Delete', 'editing'], + ['cycleTool', 'Cycle drawing tools', 'T', 'editing'], + ['text', 'Text tool', 'Shift+T', 'editing'], + ['cycleInk', 'Cycle layer colour', 'X', 'editing'], + ['draw', 'Draw tool', 'P', 'editing'], + ['paint', 'Paint tool', 'B', 'editing'], + ['erase', 'Erase tool', 'E', 'editing'], + ['fill', 'Fill tool', 'F', 'editing'], + ...Array.from({length: 8}, (_, index) => [`slot${index + 1}`, `Tool slot ${index + 1}`, String(index + 1), 'editing']), + ...Array.from({length: 3}, (_, index) => [`sound${index + 1}`, `Play Sound ${index + 1} while playing`, String(index + 1), 'playing']), + ['speedDown', 'Decrease speed (focused speed control)', 'ArrowDown', 'speed'], + ['speedDownAlt', 'Decrease speed (second shortcut)', 'ArrowLeft', 'speed'], + ['speedUp', 'Increase speed (focused speed control)', 'ArrowUp', 'speed'], + ['speedUpAlt', 'Increase speed (second shortcut)', 'ArrowRight', 'speed'], + ['speedFirst', 'Speed 1 (focused speed control)', 'Home', 'speed'], + ['speedLast', 'Speed 8 (focused speed control)', 'End', 'speed'], +]; +const editorShortcutDefaults = Object.fromEntries(EDITOR_SHORTCUTS.map(([id, , key]) => [id, key])); +let editorShortcutBindings = {...editorShortcutDefaults}; +function shortcutFromEvent(event) { + if (['Shift', 'Control', 'Alt', 'Meta'].includes(event.key)) return ''; + const key = event.code === 'Space' ? 'Space' : event.key.length === 1 ? event.key.toUpperCase() : event.key; + return [event.ctrlKey && 'Ctrl', event.metaKey && 'Meta', event.altKey && 'Alt', event.shiftKey && 'Shift', key].filter(Boolean).join('+'); +} +function shortcutAction(event, group) { + const pressed = shortcutFromEvent(event); + if (!pressed) return ''; + const match = EDITOR_SHORTCUTS.find(([id, , , context]) => context === group && editorShortcutBindings[id] === pressed); + return match ? match[0] : ''; +} +let editorAlternateHeld = false; +function editorAlternateKeyEvent(event) { + if (!event || typeof event.key !== 'string') return false; + if (!event.isTrusted && event.key === 'Shift') return true; // Mobile Shift button. + const binding = editorShortcutBindings.alternateActions; + return binding === event.key || binding === shortcutFromEvent(event); +} +function editorAlternateReleaseEvent(event) { + if (!event || typeof event.key !== 'string') return false; + if (!event.isTrusted && event.key === 'Shift') return true; + const key = event.key === 'Control' ? 'Ctrl' : event.key.length === 1 ? event.key.toUpperCase() : event.key; + return editorShortcutBindings.alternateActions.split('+').includes(key); +} +function editorAlternateActive(event) { + return editorAlternateHeld || (event && editorAlternateKeyEvent(event)); +} +document.addEventListener('keydown', (event) => { + if (editorAlternateKeyEvent(event)) editorAlternateHeld = true; +}, true); +document.addEventListener('keyup', (event) => { + if (editorAlternateReleaseEvent(event)) editorAlternateHeld = false; +}, true); +window.addEventListener('blur', () => { editorAlternateHeld = false; }); +function saveEditorShortcuts() { + try { localStorage.setItem(EDITOR_SHORTCUT_STORAGE_KEY, JSON.stringify(editorShortcutBindings)); } catch (error) {} +} +try { + const saved = JSON.parse(localStorage.getItem(EDITOR_SHORTCUT_STORAGE_KEY) || '{}'); + if (saved && typeof saved === 'object' && !Array.isArray(saved)) { + for (const [id, key] of Object.entries(saved)) { + if (id in editorShortcutDefaults && typeof key === 'string' && key.length <= 40) editorShortcutBindings[id] = key; + } + } +} catch (error) {} +function editorShortcutsBlocked() { + return !!document.querySelector('.flipnote-editor-info-modal:not([hidden]), .flipnote-editor-pattern-modal:not([hidden]), .flipnote-editor-image-import-modal:not([hidden]), .flipnote-editor-audio-trim-modal:not([hidden])'); +} +// Initialize the restored legacy editor after the page has loaded. +window.addEventListener('load', () => { + canvas.setTool('pen1'); +}); + +let noteInfo = {title: "Untitled flipnote"} + +// Flipnote Studio pages have two independent monochrome drawing layers. The +// ink colour belongs to the layer (and therefore to the page), not to each +// individual stroke. +const FLIPNOTE_INKS = { + black: [0, 0, 0], + red: [255, 53, 77], + blue: [88, 92, 255], +}; +const FLIPNOTE_INK_ORDER = ['black', 'red', 'blue']; +const FLIPNOTE_SELECTION_YELLOW = '#02D720'; +// Flipnote Studio's Resize tool is a fixed one-step transform: Shrink is +// one-half size and the shoulder-button variant Enlarge is double size. +const FLIPNOTE_RESIZE_SHRINK_FACTOR = 0.5; +const FLIPNOTE_RESIZE_GROW_FACTOR = 2; + +// Mobile/touch support is deliberately capability-gated so desktop behavior +// remains identical. Touch devices get long-press access to Shift-only actions. +const FLIPNOTE_TOUCH_UI = (() => { + try { + return (navigator.maxTouchPoints || 0) > 0 && window.matchMedia('(pointer: coarse)').matches; + } catch (error) { + return false; + } +})(); + +function installFlipnoteTouchLongPress(element, callback, delay = 520) { + if (!FLIPNOTE_TOUCH_UI || !element || typeof callback !== 'function') return; + let timer = null; + let fired = false; + let pointerId = null; + let startX = 0; + let startY = 0; + let lastTouchStartAt = 0; + const clearTimer = () => { + if (timer !== null) { + clearTimeout(timer); + timer = null; + } + }; + element.addEventListener('pointerdown', (event) => { + if (event.pointerType !== 'touch' || event.button !== 0) return; + clearTimer(); + fired = false; + pointerId = event.pointerId; + startX = event.clientX; + startY = event.clientY; + lastTouchStartAt = performance.now(); + timer = window.setTimeout(() => { + timer = null; + fired = true; + element.classList.add('is-touch-long-press'); + try { if (navigator.vibrate) navigator.vibrate(16); } catch (error) {} + callback(event); + window.setTimeout(() => element.classList.remove('is-touch-long-press'), 180); + }, delay); + }); + ['pointerup', 'pointercancel', 'lostpointercapture'].forEach((type) => { + element.addEventListener(type, (event) => { + if (pointerId !== null && event.pointerId !== undefined && event.pointerId !== pointerId) return; + clearTimer(); + pointerId = null; + }); + }); + element.addEventListener('pointermove', (event) => { + if (event.pointerType !== 'touch' || pointerId !== event.pointerId) return; + if (Math.hypot(event.clientX - startX, event.clientY - startY) > 10) clearTimer(); + }); + element.addEventListener('click', (event) => { + if (!fired) return; + fired = false; + event.preventDefault(); + event.stopImmediatePropagation(); + }, true); + element.addEventListener('contextmenu', (event) => { + if (performance.now() - lastTouchStartAt < 1200) event.preventDefault(); + }); +} +const FLIPNOTE_PAPERS = { + white: [255, 255, 255], + black: [0, 0, 0], +}; + +const FLIPNOTE_CUSTOM_PATTERN_SIZE = 8; +const FLIPNOTE_CUSTOM_PATTERN_STORAGE_KEY = 'flipnote-editor.flipnote.customPaintPattern.v1'; + +function defaultCustomPaintPattern() { + const size = FLIPNOTE_CUSTOM_PATTERN_SIZE; + const data = new Uint8Array(size * size); + // Match the editor's existing provisional custom-motif fallback as closely + // as possible, but make it a real editable/repeating 8x8 binary tile. + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + if (((x - y) % 5 === 0) || ((x + y) % 5 === 0)) data[y * size + x] = 1; + } + } + return {width: size, height: size, data}; +} + +function loadCustomPaintPattern() { + const fallback = defaultCustomPaintPattern(); + try { + const saved = JSON.parse(window.localStorage.getItem(FLIPNOTE_CUSTOM_PATTERN_STORAGE_KEY) || 'null'); + if (!saved || saved.width !== FLIPNOTE_CUSTOM_PATTERN_SIZE || saved.height !== FLIPNOTE_CUSTOM_PATTERN_SIZE || !Array.isArray(saved.data)) return fallback; + if (saved.data.length !== FLIPNOTE_CUSTOM_PATTERN_SIZE * FLIPNOTE_CUSTOM_PATTERN_SIZE) return fallback; + return { + width: FLIPNOTE_CUSTOM_PATTERN_SIZE, + height: FLIPNOTE_CUSTOM_PATTERN_SIZE, + data: Uint8Array.from(saved.data, (value) => value ? 1 : 0), + }; + } catch (error) { + return fallback; + } +} + +function saveCustomPaintPattern(motif) { + try { + window.localStorage.setItem(FLIPNOTE_CUSTOM_PATTERN_STORAGE_KEY, JSON.stringify({ + width: motif.width, + height: motif.height, + data: Array.from(motif.data, (value) => value ? 1 : 0), + })); + } catch (error) { + // Local persistence is a convenience only; the editor must keep working + // even when storage is disabled/private. + } +} + +const FLIPNOTE_HISTORY_SPRITES = { + undo: './assets/flipnote-editor/undo.png', + undoDisabled: './assets/flipnote-editor/undo_grey.png', + redo: './assets/flipnote-editor/redo.png', + redoDisabled: './assets/flipnote-editor/redo_grey.png', +}; + +const FLIPNOTE_SPEEDS = [ + { level: 1, fps: 0.4985, src: './assets/flipnote-editor/speed1.png' }, + { level: 2, fps: 1, src: './assets/flipnote-editor/speed2.png' }, + { level: 3, fps: 2, src: './assets/flipnote-editor/speed3.png' }, + { level: 4, fps: 4, src: './assets/flipnote-editor/speed4.png' }, + { level: 5, fps: 5.9972, src: './assets/flipnote-editor/speed5.png' }, + { level: 6, fps: 11.986, src: './assets/flipnote-editor/speed6.png' }, + { level: 7, fps: 19.9439, src: './assets/flipnote-editor/speed7.png' }, + { level: 8, fps: 29.9275, src: './assets/flipnote-editor/speed8.png' }, +]; +const FLIPNOTE_SPEED_HITBOXES = [ + { level: 1, x: 1, y: 1, width: 29, height: 18 }, + { level: 2, x: 30, y: 2, width: 8, height: 17 }, + { level: 3, x: 38, y: 2, width: 8, height: 17 }, + { level: 4, x: 46, y: 2, width: 8, height: 17 }, + { level: 5, x: 54, y: 2, width: 8, height: 17 }, + { level: 6, x: 62, y: 2, width: 8, height: 17 }, + { level: 7, x: 70, y: 1, width: 8, height: 17 }, + { level: 8, x: 78, y: 2, width: 15, height: 17 }, +]; +const FLIPNOTE_LOOP_SPRITES = { + enabled: './assets/flipnote-editor/loop_enabled.png', + disabled: './assets/flipnote-editor/loop_disabled.png', +}; + +function speedSetting(level) { + const safeLevel = Math.max(1, Math.min(8, Math.round(Number(level) || 1))); + return FLIPNOTE_SPEEDS[safeLevel - 1]; +} + +function updatePlaybackSettingsUI(timelineInstance) { + if (!timelineInstance) return; + const speed = speedSetting(timelineInstance.speedLevel); + const speedControl = document.getElementById('flipnote-speed-control'); + const speedImage = document.getElementById('flipnote-speed-image'); + const loopButton = document.getElementById('flipnote-loop-toggle'); + const loopImage = document.getElementById('flipnote-loop-image'); + if (speedControl) { + speedControl.setAttribute('aria-valuenow', String(speed.level)); + speedControl.setAttribute('aria-valuetext', `Speed ${speed.level}, ${speed.fps} FPS`); + speedControl.title = `Speed ${speed.level} · ${speed.fps} FPS`; + } + if (speedImage) { + if (speedImage.getAttribute('src') !== speed.src) speedImage.src = speed.src; + speedImage.alt = `Speed ${speed.level}`; + } + if (loopButton) { + loopButton.setAttribute('aria-pressed', timelineInstance.loopEnabled ? 'true' : 'false'); + loopButton.title = timelineInstance.loopEnabled ? 'Loop enabled' : 'Loop disabled'; + } + const loopSrc = timelineInstance.loopEnabled ? FLIPNOTE_LOOP_SPRITES.enabled : FLIPNOTE_LOOP_SPRITES.disabled; + if (loopImage) { + if (loopImage.getAttribute('src') !== loopSrc) loopImage.src = loopSrc; + loopImage.alt = timelineInstance.loopEnabled ? 'Loop enabled' : 'Loop disabled'; + } +} + +function syncEditorPlaybackState(timelineInstance) { + if (!timelineInstance) return; + const workspace = document.querySelector('[data-legacy-flipnote-editor]'); + const controls = document.querySelector('.flipnote-editor-controls'); + const speedRow = document.querySelector('.flipnote-editor-stage-speed-row'); + const filmstripTimeline = document.getElementById('timeline-container'); + const pageToolsTray = document.getElementById('flipnote-page-tools-tray'); + const canvasElement = document.getElementById('flipnote-canvas'); + const navIds = ['first-frame', 'prev-frame', 'next-frame', 'last-frame']; + const playing = !!timelineInstance.playing; + const fps = Math.max(0.01, Number(timelineInstance.framerate) || 12); + + // One full white+black sprocket pair travels right in twelve animation + // frames. That makes the decorative film speed track the Flipnote's + // actual playback FPS while keeping the movement visually readable. + const filmDuration = Math.max(0.18, Math.min(30, 12 / fps)); + if (workspace) { + workspace.classList.toggle('is-playing', playing); + workspace.style.setProperty('--flipnote-filmstrip-duration', `${filmDuration.toFixed(4)}s`); + } + + // Inert is the hard interaction lock; the matching CSS provides the + // greyscale visual state. Play/Stop remains outside these inert groups. + // Keep only Page 4 itself non-inert so SE1/SE2/SE3 can be performed live. + if (controls) { + controls.inert = false; + Array.from(controls.children).forEach((child) => { + child.inert = playing && !child.classList.contains('flipnote-editor-audio-page'); + }); + } + if (speedRow) speedRow.inert = playing; + if (filmstripTimeline) filmstripTimeline.inert = playing; + if (pageToolsTray) pageToolsTray.inert = playing; + navIds.forEach((id) => { + const element = document.getElementById(id); + if (element) { + element.inert = playing; + element.setAttribute('aria-disabled', playing ? 'true' : 'false'); + } + }); + if (canvasElement) canvasElement.inert = playing; + const importImageButton = document.getElementById('flipnote-import-image-button'); + const importImageInput = document.getElementById('flipnote-image-file-input'); + if (importImageButton) { + importImageButton.classList.toggle('is-disabled', playing); + importImageButton.setAttribute('aria-disabled', playing ? 'true' : 'false'); + importImageButton.tabIndex = playing ? -1 : 0; + } + if (importImageInput) importImageInput.disabled = playing; + + // Keyboard shortcuts use this flag too, so playback blocks editing even + // when the user had an editor control focused before pressing Play. + if (typeof canvas !== 'undefined' && canvas) { + canvas.allowEditing = !playing; + if (playing && canvas.drawing) canvas.finishStroke(); + canvas.renderCurrentFrame(); + } +} + +function updateHistoryButtons(frame) { + const undo = document.getElementById('undoButton'); + const redo = document.getElementById('redoButton'); + const canUndo = !!frame && frame.undoPointer > 0; + const canRedo = !!frame && frame.undoPointer < frame.undoBuffer.length - 1; + if (undo) { + undo.src = canUndo ? FLIPNOTE_HISTORY_SPRITES.undo : FLIPNOTE_HISTORY_SPRITES.undoDisabled; + undo.classList.toggle('is-disabled', !canUndo); + undo.setAttribute('aria-disabled', canUndo ? 'false' : 'true'); + } + if (redo) { + redo.src = canRedo ? FLIPNOTE_HISTORY_SPRITES.redo : FLIPNOTE_HISTORY_SPRITES.redoDisabled; + redo.classList.toggle('is-disabled', !canRedo); + redo.setAttribute('aria-disabled', canRedo ? 'false' : 'true'); + } +} + +function normalizePaperColor(value) { + return value === 'black' ? 'black' : 'white'; +} + +// Flipnote's monochrome ink is interpreted against the page paper: the same +// stored layer colour is black on white paper and white on black paper. +function resolveFrameInk(frame, inkName) { + if (inkName === 'black') return frame && frame.paperColor === 'black' ? [255, 255, 255] : [0, 0, 0]; + return FLIPNOTE_INKS[inkName] || FLIPNOTE_INKS.black; +} + +function resolveFramePaper(frame) { + return FLIPNOTE_PAPERS[normalizePaperColor(frame && frame.paperColor)]; +} +const LIGHTBOX_PAGES_STORAGE_KEY = 'flipnote-editor.flipnote.pagesToLightbox'; +let lightboxEnabled = false; +let cloneEnabled = false; +let activeToolTab = 1; + +function pagesToLightbox() { + let value = 1; + try { + value = Number(window.localStorage.getItem(LIGHTBOX_PAGES_STORAGE_KEY) || 1); + } catch (error) { + value = 1; + } + return Math.max(1, Math.min(4, Number.isFinite(value) ? Math.round(value) : 1)); +} + +function cloneMask(mask) { + return new Uint8Array(mask || (256 * 192)); +} + +function normalizeFrameSoundFlags(value) { + const source = value && typeof value === 'object' ? value : {}; + return { + sound1: !!source.sound1, + sound2: !!source.sound2, + sound3: !!source.sound3, + }; +} + +function frameSnapshot(frame) { + return { + layers: [cloneMask(frame.layers[0]), cloneMask(frame.layers[1])], + colors: [frame.colors[0], frame.colors[1]], + paperColor: normalizePaperColor(frame.paperColor), + }; +} + +// Flipnote Space mirrors Flipnote Studio's editor strategy: measure the actual +// compressed frame representation at edit commit points instead of serialising +// a complete PPM for every pointer sample. Editor uses that exact running +// frame-byte count together with the fixed PPM structures/current audio to +// enforce the 1,024 KB whole-file limit used by the server encoder. +const FLIPNOTE_EDITOR_MAX_PAGES = 999; +const FLIPNOTE_SPACE_MAX_BYTES = 1024 * 1024; +const FLIPNOTE_SPACE_HEADER_BYTES = 0x6A0; +const FLIPNOTE_SPACE_SIGNATURE_TRAILER_BYTES = 0x80 + 0x10; +const FLIPNOTE_SPACE_SOUND_HEADER_BYTES = 32; +const FLIPNOTE_SPACE_ADPCM_STATE_BYTES = 4; +const FLIPNOTE_SPACE_AUDIO_SAMPLE_RATE = 8192; +const FLIPNOTE_SPACE_MAX_BGM_BYTES = 245408; +let flipnoteSpaceFrameCache = new WeakMap(); +let flipnoteSpaceEncodedFrameBytes = 0; +let flipnoteSpaceAudioTracks = null; +let flipnoteSpaceNoticeTimer = null; + +function flipnoteSpacePackedLayerByteLength(mask) { + let payloadBytes = 0; + const source = mask || new Uint8Array(256 * 192); + for (let y = 0; y < 192; y += 1) { + const rowBase = y * 256; + let nonzero = 0; + let nonff = 0; + let any = false; + for (let chunk = 0; chunk < 32; chunk += 1) { + let value = 0; + const chunkBase = rowBase + (chunk * 8); + for (let bit = 0; bit < 8; bit += 1) { + if (source[chunkBase + bit]) value |= (1 << bit); + } + if (value !== 0) { nonzero += 1; any = true; } + if (value !== 0xFF) nonff += 1; + } + if (!any) continue; + const normalBytes = 4 + nonzero; + const invertedBytes = 4 + nonff; + if (normalBytes <= invertedBytes && normalBytes < 32) payloadBytes += normalBytes; + else if (invertedBytes < 32) payloadBytes += invertedBytes; + else payloadBytes += 32; + } + // 48 bytes hold the two-bit line encoding tags for all 192 rows. + return 48 + payloadBytes; +} + +function flipnoteSpaceFrameByteLength(frame) { + if (!frame) return 0; + // One PPM frame header byte followed by both line-tag blocks/payloads. + return 1 + + flipnoteSpacePackedLayerByteLength(frame.layers && frame.layers[0]) + + flipnoteSpacePackedLayerByteLength(frame.layers && frame.layers[1]); +} + +function rebuildFlipnoteSpaceFrameCache(frames) { + flipnoteSpaceFrameCache = new WeakMap(); + flipnoteSpaceEncodedFrameBytes = 0; + (Array.isArray(frames) ? frames : []).forEach((frame) => { + if (!frame) return; + const size = flipnoteSpaceFrameByteLength(frame); + flipnoteSpaceFrameCache.set(frame, size); + flipnoteSpaceEncodedFrameBytes += size; + }); +} + +function flipnoteSpaceRegisterFrame(frame) { + if (!frame) return 0; + const size = flipnoteSpaceFrameByteLength(frame); + const previous = flipnoteSpaceFrameCache.get(frame) || 0; + flipnoteSpaceFrameCache.set(frame, size); + flipnoteSpaceEncodedFrameBytes += size - previous; + return size; +} + +function flipnoteSpaceForgetFrame(frame) { + if (!frame) return; + const previous = flipnoteSpaceFrameCache.get(frame) || 0; + flipnoteSpaceEncodedFrameBytes = Math.max(0, flipnoteSpaceEncodedFrameBytes - previous); + flipnoteSpaceFrameCache.delete(frame); +} + +function flipnoteSpaceStoredAudioDuration(key, track, speedLevel) { + if (!track) return 0; + const duration = Math.max(0, Number(track.duration) || 0); + if (key !== 'music') return duration; + const speed6FileImport = Number(speedLevel) === 6 && String(track.sourceKind || '').toLowerCase() === 'file_import'; + if (!speed6FileImport) return duration; + const doubled = duration * 2; + const maxSeconds = (FLIPNOTE_SPACE_MAX_BGM_BYTES / 4096) + 0.02; + return doubled <= maxSeconds ? doubled : duration; +} + +function flipnoteSpaceAudioByteLength(key, track, speedLevel) { + if (!track) return 0; + const duration = flipnoteSpaceStoredAudioDuration(key, track, speedLevel); + if (!(duration > 0)) return 0; + // Server output is 8,192 Hz mono IMA ADPCM: two samples per payload byte, + // preceded by the four-byte predictor/index state header. + return FLIPNOTE_SPACE_ADPCM_STATE_BYTES + + Math.ceil((duration * FLIPNOTE_SPACE_AUDIO_SAMPLE_RATE) / 2); +} + +function flipnoteSpacePpmByteLength(timelineInstance, options = {}) { + const instance = timelineInstance; + const frameCount = Math.max(1, Number(options.frameCount ?? (instance && instance.frames ? instance.frames.length : 1)) || 1); + const animationFrameBytes = Math.max(0, Number(options.animationFrameBytes ?? flipnoteSpaceEncodedFrameBytes) || 0); + const speedLevel = Math.max(1, Math.min(8, Number(options.speedLevel ?? (instance && instance.speedLevel) ?? 6) || 6)); + const tracks = options.audioTracks || flipnoteSpaceAudioTracks || {}; + let animationBytes = 8 + (frameCount * 4) + animationFrameBytes; + animationBytes += (4 - (animationBytes % 4)) % 4; + const soundPadding = (4 - ((FLIPNOTE_SPACE_HEADER_BYTES + animationBytes + frameCount) % 4)) % 4; + const audioBytes = ['music', 'sound1', 'sound2', 'sound3'].reduce( + (total, key) => total + flipnoteSpaceAudioByteLength(key, tracks[key], speedLevel), + 0, + ); + return FLIPNOTE_SPACE_HEADER_BYTES + + animationBytes + + frameCount + + soundPadding + + FLIPNOTE_SPACE_SOUND_HEADER_BYTES + + audioBytes + + FLIPNOTE_SPACE_SIGNATURE_TRAILER_BYTES; +} + +function renderFlipnoteSpaceMeter(timelineInstance) { + const meter = document.getElementById('flipnote-space-meter'); + const fill = document.getElementById('flipnote-space-meter-fill'); + const value = document.getElementById('flipnote-space-meter-value'); + if (!meter || !fill || !timelineInstance) return; + const used = flipnoteSpacePpmByteLength(timelineInstance); + const ratio = Math.max(0, Math.min(1, used / FLIPNOTE_SPACE_MAX_BYTES)); + const percent = ratio * 100; + fill.style.height = `${percent.toFixed(3)}%`; + meter.setAttribute('aria-valuenow', String(Math.min(FLIPNOTE_SPACE_MAX_BYTES, Math.round(used)))); + meter.classList.toggle('is-near-limit', ratio >= 0.9); + meter.classList.toggle('is-full', used >= FLIPNOTE_SPACE_MAX_BYTES); + const usedKb = Math.min(1024, Math.ceil(used / 1024)); + if (value) value.textContent = used >= FLIPNOTE_SPACE_MAX_BYTES ? 'FULL' : `${usedKb} KB`; + meter.title = `Flipnote Space: ${usedKb} KB of 1,024 KB used`; +} + +function notifyFlipnoteSpaceLimit(timelineInstance) { + renderFlipnoteSpaceMeter(timelineInstance); + const meter = document.getElementById('flipnote-space-meter'); + const status = document.getElementById('flipnote-space-status'); + if (status) status.textContent = 'Flipnote Space is full. Remove pages, audio, or drawing detail before adding more.'; + if (!meter) return; + meter.classList.add('is-limit-hit'); + if (flipnoteSpaceNoticeTimer) clearTimeout(flipnoteSpaceNoticeTimer); + flipnoteSpaceNoticeTimer = setTimeout(() => meter.classList.remove('is-limit-hit'), 900); +} + +function notifyFlipnotePageLimit(timelineInstance) { + renderFlipnoteSpaceMeter(timelineInstance); + const meter = document.getElementById('flipnote-space-meter'); + const status = document.getElementById('flipnote-space-status'); + if (status) status.textContent = `Flipnote Editor supports up to ${FLIPNOTE_EDITOR_MAX_PAGES} pages. Remove a page before adding another.`; + if (!meter) return; + meter.classList.add('is-limit-hit'); + if (flipnoteSpaceNoticeTimer) clearTimeout(flipnoteSpaceNoticeTimer); + flipnoteSpaceNoticeTimer = setTimeout(() => meter.classList.remove('is-limit-hit'), 900); +} + +function flipnoteSpaceCanFit(timelineInstance, options = {}) { + return flipnoteSpacePpmByteLength(timelineInstance, options) <= FLIPNOTE_SPACE_MAX_BYTES; +} + +function flipnoteSpaceAllowsProjection(timelineInstance, options = {}) { + const projected = flipnoteSpacePpmByteLength(timelineInstance, options); + if (projected <= FLIPNOTE_SPACE_MAX_BYTES) return true; + // An imported PPM can recompress slightly larger in the editor because the + // browser/server editor path emits independent native frames. Never trap + // such a project above the limit: allow edits that strictly reduce size so + // the user can work it back under 1,024 KB. + return projected < flipnoteSpacePpmByteLength(timelineInstance); +} + +function flipnoteSpaceCanAddFrame(timelineInstance, frame) { + const size = flipnoteSpaceFrameByteLength(frame); + return flipnoteSpaceCanFit(timelineInstance, { + frameCount: timelineInstance.frames.length + 1, + animationFrameBytes: flipnoteSpaceEncodedFrameBytes + size, + }); +} + +// Frame stores the two real drawing layers, their per-page colours, and an +// undo buffer containing complete layer snapshots. +let editorLayerVisibility = [true, true]; + +class Frame { + constructor(copyFrom) { + if (copyFrom instanceof Frame) { + this.layers = [cloneMask(copyFrom.layers[0]), cloneMask(copyFrom.layers[1])]; + this.colors = [copyFrom.colors[0], copyFrom.colors[1]]; + } else if (copyFrom && Array.isArray(copyFrom.layers)) { + this.layers = [cloneMask(copyFrom.layers[0]), cloneMask(copyFrom.layers[1])]; + this.colors = [copyFrom.colors?.[0] || 'black', copyFrom.colors?.[1] || 'red']; + } else { + this.layers = [new Uint8Array(256 * 192), new Uint8Array(256 * 192)]; + this.colors = ['black', 'red']; + } + // Paper is page-local PPM-style state. Blank pages default to white; + // callers can seed black paper without copying any drawing data. + this.paperColor = normalizePaperColor(copyFrom && copyFrom.paperColor); + // SE1/SE2/SE3 are page-local Flipnote triggers. They are deliberately + // separate from drawing undo history: tapping a sound during playback + // stamps that sound onto the current page without changing artwork. + this.soundFlags = normalizeFrameSoundFlags(copyFrom && copyFrom.soundFlags); + this.thumbnailData = []; + this.undoBuffer = []; + this.undoPointer = -1; + this.undoMax = 255; + } + getData() { + return this.compositeImageData(); + } + setData(data) { + // Kept for compatibility with the restored editor API. New editor + // operations update layer masks directly instead of flattening here. + if (data instanceof Frame) { + this.layers = [cloneMask(data.layers[0]), cloneMask(data.layers[1])]; + this.colors = [data.colors[0], data.colors[1]]; + this.paperColor = normalizePaperColor(data.paperColor); + this.soundFlags = normalizeFrameSoundFlags(data.soundFlags); + } + } + compositeImageData() { + const out = new Uint8ClampedArray(256 * 192 * 4); + const layer1Color = resolveFrameInk(this, this.colors[0]); + const layer2Color = resolveFrameInk(this, this.colors[1]); + const paper = resolveFramePaper(this); + for (let i = 0; i < 256 * 192; i++) { + let r = paper[0], g = paper[1], b = paper[2]; + // Layer 1 sits above Layer 2, matching Flipnote's compositing. + if (editorLayerVisibility[1] && this.layers[1][i]) [r, g, b] = layer2Color; + if (editorLayerVisibility[0] && this.layers[0][i]) [r, g, b] = layer1Color; + const p = i * 4; + out[p] = r; + out[p + 1] = g; + out[p + 2] = b; + out[p + 3] = 255; + } + return new ImageData(out, 256, 192); + } + pushState() { + markEditorDirty(); + if (this.undoPointer < this.undoBuffer.length - 1) { + this.undoBuffer.splice(this.undoPointer + 1); + } + this.undoBuffer.push(frameSnapshot(this)); + if (this.undoBuffer.length > this.undoMax) this.undoBuffer.shift(); + this.undoPointer = this.undoBuffer.length - 1; + updateHistoryButtons(this); + } + restoreSnapshot(snapshot) { + if (!snapshot) return; + this.layers = [cloneMask(snapshot.layers[0]), cloneMask(snapshot.layers[1])]; + this.colors = [snapshot.colors[0], snapshot.colors[1]]; + this.paperColor = normalizePaperColor(snapshot.paperColor); + canvas.renderCurrentFrame(); + timeline.createThumbnails(); + updateLayerPanel(); + updatePaperToolState(); + updateHistoryButtons(this); + if (typeof timeline !== 'undefined' && timeline) { + flipnoteSpaceRegisterFrame(this); + renderFlipnoteSpaceMeter(timeline); + } + } + undo() { + if (this.undoPointer > 0) { + const target = this.undoBuffer[this.undoPointer - 1]; + const currentSize = flipnoteSpaceFrameCache.get(this) || flipnoteSpaceFrameByteLength(this); + const targetSize = flipnoteSpaceFrameByteLength(target); + if (!flipnoteSpaceAllowsProjection(timeline, { + animationFrameBytes: flipnoteSpaceEncodedFrameBytes - currentSize + targetSize, + })) { + notifyFlipnoteSpaceLimit(timeline); + updateHistoryButtons(this); + return; + } + markEditorDirty(); + this.undoPointer--; + this.restoreSnapshot(target); + } else { + updateHistoryButtons(this); + } + } + redo() { + if (this.undoPointer < this.undoBuffer.length - 1) { + const target = this.undoBuffer[this.undoPointer + 1]; + const currentSize = flipnoteSpaceFrameCache.get(this) || flipnoteSpaceFrameByteLength(this); + const targetSize = flipnoteSpaceFrameByteLength(target); + if (!flipnoteSpaceAllowsProjection(timeline, { + animationFrameBytes: flipnoteSpaceEncodedFrameBytes - currentSize + targetSize, + })) { + notifyFlipnoteSpaceLimit(timeline); + updateHistoryButtons(this); + return; + } + markEditorDirty(); + this.undoPointer++; + this.restoreSnapshot(target); + } else { + updateHistoryButtons(this); + } + } + setUndoPointer(pointer) { + this.undoPointer = Math.max(0, Math.min(this.undoBuffer.length - 1, pointer)); + updateHistoryButtons(this); + } +} + +// Timeline owns pages and composites the two layers for thumbnails/playback. +class Timeline { + constructor(timelineElement) { + this.timelineElement = timelineElement; + this.frameData = []; + this.frames = []; + this.speedLevel = 6; + this.framerate = speedSetting(this.speedLevel).fps; + this.loopEnabled = true; + this.currentFrame = 0; + this.lastFrame = 0; + this.playing = false; + this.playInterval = null; + // Page Tools clipboard: copies the two drawing layers and their layer + // colours. Paper/audio stay page-local when pasted. + this.pageClipboard = null; + // Native frame dragging keeps the actual Frame object intact while its + // place in the timeline changes. The source slot gets a dotted lift + // marker until the frame is dropped. + this.frameDrag = null; + this.frameDragClickSuppressedUntil = 0; + this.addFrame(); + + this.timelineElement.addEventListener('dragover', (event) => { + if (!this.frameDrag || this.playing) return; + event.preventDefault(); + if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'; + const slot = this.frameDropSlotFromClientX(event.clientX); + this.frameDrag.dropSlot = slot; + this.updateFrameDropMarker(slot); + }); + this.timelineElement.addEventListener('drop', (event) => { + if (!this.frameDrag || this.playing) return; + event.preventDefault(); + const slot = this.frameDropSlotFromClientX(event.clientX); + this.finishFrameDrag(slot); + }); + this.timelineElement.addEventListener('dragleave', (event) => { + if (!this.frameDrag) return; + const next = event.relatedTarget; + if (next instanceof Node && this.timelineElement.contains(next)) return; + this.removeFrameDropMarker(); + }); + + document.addEventListener('keydown', (event) => { + if (editorShortcutsBlocked()) return; + const target = event.target instanceof Element ? event.target : null; + const typingText = !!(target && target.matches('input, textarea, select, [contenteditable="true"]')); + if (typingText) return; + if (editorAlternateKeyEvent(event)) this.shiftPressed = true; + if (this.playing || (target && target.closest('#flipnote-speed-control'))) return; + const action = shortcutAction(event, 'stopped'); + if (action !== 'nextFrame' && action !== 'previousFrame') return; + event.preventDefault(); + this.setActiveFrame(this.currentFrame + (action === 'nextFrame' ? 1 : -1)); + this.updateThumbnailSelection(this.currentFrame); + }); + document.addEventListener('keyup', (event) => { + if (editorAlternateReleaseEvent(event)) this.shiftPressed = false; + }); + } + + addFrame(copyData, index) { + if (this.frames.length >= FLIPNOTE_EDITOR_MAX_PAGES) { + notifyFlipnotePageLimit(this); + return -1; + } + if (index === undefined) index = this.frames.length; + // A normal blank page inherits only the previous page's paper colour. + // Clone mode passes a full Frame and therefore copies drawing + colours too. + if (copyData === undefined && index > 0 && this.frames[index - 1]) { + copyData = {paperColor: this.frames[index - 1].paperColor}; + } + const newFrame = copyData instanceof Frame ? new Frame(copyData) : new Frame(copyData); + if (this.frames.length && !flipnoteSpaceCanAddFrame(this, newFrame)) { + notifyFlipnoteSpaceLimit(this); + return -1; + } + this.frames.splice(index, 0, newFrame); + flipnoteSpaceRegisterFrame(newFrame); + newFrame.pushState(); + this.createThumbnails(); + renderFlipnoteSpaceMeter(this); + return index; + } + + updateFrame(index) { + const frame = this.frames[index]; + if (!frame) return false; + const previousSize = flipnoteSpaceFrameCache.get(frame) || flipnoteSpaceFrameByteLength(frame); + const nextSize = flipnoteSpaceFrameByteLength(frame); + const projectedAnimationBytes = flipnoteSpaceEncodedFrameBytes - previousSize + nextSize; + if (!flipnoteSpaceAllowsProjection(this, {animationFrameBytes: projectedAnimationBytes})) { + const acceptedSnapshot = frame.undoBuffer[frame.undoPointer]; + if (acceptedSnapshot) frame.restoreSnapshot(acceptedSnapshot); + notifyFlipnoteSpaceLimit(this); + return false; + } + flipnoteSpaceFrameCache.set(frame, nextSize); + flipnoteSpaceEncodedFrameBytes = projectedAnimationBytes; + frame.pushState(); + this.createThumbnails(); + renderFlipnoteSpaceMeter(this); + return true; + } + removeFrame(index) { + if (index >= 0 && index < this.frames.length) { + markEditorDirty(); + flipnoteSpaceForgetFrame(this.frames[index]); + this.frames.splice(index, 1); + } + this.currentFrame = Math.max(0, Math.min(this.currentFrame, this.frames.length - 1)); + this.createThumbnails(); + renderFlipnoteSpaceMeter(this); + } + getFrame(index) { + return index >= 0 && index < this.frames.length ? this.frames[index] : null; + } + getAllFrames() { + return this.frames; + } + copyPage(index = this.currentFrame) { + const frame = this.getFrame(index); + if (!frame) return false; + this.pageClipboard = { + layers: [cloneMask(frame.layers[0]), cloneMask(frame.layers[1])], + colors: [frame.colors[0], frame.colors[1]], + }; + return true; + } + pastePage(index = this.currentFrame) { + const frame = this.getFrame(index); + const clip = this.pageClipboard; + if (!frame || !clip || this.playing) return false; + frame.layers = [cloneMask(clip.layers[0]), cloneMask(clip.layers[1])]; + frame.colors = [clip.colors[0], clip.colors[1]]; + if (typeof canvas !== 'undefined' && canvas) { + if (typeof canvas.clearSelectionForFrameChange === 'function') canvas.clearSelectionForFrameChange(); + canvas.renderCurrentFrame(); + } + this.updateFrame(index); + if (index === this.currentFrame) { + updateLayerPanel(); + updateHistoryButtons(frame); + } + return true; + } + ensureFrameVisible(index = this.currentFrame) { + const frameElement = this.timelineElement.querySelector(`.timeline-thumbnail[data-frame-index="${index}"]`); + if (!frameElement) return; + const padding = 8; + const viewLeft = this.timelineElement.scrollLeft; + const viewRight = viewLeft + this.timelineElement.clientWidth; + const itemLeft = frameElement.offsetLeft; + const itemRight = itemLeft + frameElement.offsetWidth; + if (itemLeft < viewLeft + padding) { + this.timelineElement.scrollLeft = Math.max(0, itemLeft - padding); + } else if (itemRight > viewRight - padding) { + this.timelineElement.scrollLeft = Math.min( + Math.max(0, this.timelineElement.scrollWidth - this.timelineElement.clientWidth), + itemRight - this.timelineElement.clientWidth + padding + ); + } + } + frameDropSlotFromClientX(clientX) { + const thumbnails = Array.from(this.timelineElement.querySelectorAll('.timeline-thumbnail')); + if (!thumbnails.length) return 0; + for (let index = 0; index < thumbnails.length; index++) { + const rect = thumbnails[index].getBoundingClientRect(); + if (clientX < rect.left + rect.width / 2) return index; + } + return thumbnails.length; + } + removeFrameDropMarker() { + const marker = this.timelineElement.querySelector('.timeline-frame-drop-marker'); + if (marker) marker.remove(); + } + updateFrameDropMarker(slot) { + const thumbnails = Array.from(this.timelineElement.querySelectorAll('.timeline-thumbnail')); + if (!thumbnails.length) return; + let marker = this.timelineElement.querySelector('.timeline-frame-drop-marker'); + if (!marker) { + marker = document.createElement('div'); + marker.className = 'timeline-frame-drop-marker'; + this.timelineElement.appendChild(marker); + } + const safeSlot = Math.max(0, Math.min(thumbnails.length, Number(slot) || 0)); + let left; + if (safeSlot < thumbnails.length) { + left = thumbnails[safeSlot].offsetLeft - 5; + } else { + const last = thumbnails[thumbnails.length - 1]; + left = last.offsetLeft + last.offsetWidth + 5; + } + marker.style.left = `${Math.max(0, left)}px`; + } + beginFrameDrag(index, thumb, event) { + if (this.playing || index < 0 || index >= this.frames.length) { + event.preventDefault(); + return; + } + const placeholder = document.createElement('div'); + placeholder.className = 'timeline-frame-lift-placeholder'; + placeholder.style.left = `${thumb.offsetLeft}px`; + placeholder.style.top = `${thumb.offsetTop}px`; + placeholder.style.width = `${thumb.offsetWidth}px`; + placeholder.style.height = `${thumb.offsetHeight}px`; + this.timelineElement.appendChild(placeholder); + this.frameDrag = { + sourceIndex: index, + sourceFrame: this.frames[index], + sourceElement: thumb, + placeholder, + dropSlot: index, + }; + if (event.dataTransfer) { + event.dataTransfer.effectAllowed = 'move'; + event.dataTransfer.setData('text/plain', `flipnote-editor-frame:${index}`); + } + // Let the browser capture the normal thumbnail as its drag ghost first; + // then leave only the dotted source slot behind in the filmstrip. + requestAnimationFrame(() => { + if (this.frameDrag && this.frameDrag.sourceElement === thumb) { + thumb.classList.add('is-frame-drag-source'); + } + }); + this.updateFrameDropMarker(index); + } + cleanupFrameDrag() { + const drag = this.frameDrag; + if (drag && drag.sourceElement) drag.sourceElement.classList.remove('is-frame-drag-source'); + if (drag && drag.placeholder && drag.placeholder.isConnected) drag.placeholder.remove(); + this.removeFrameDropMarker(); + this.frameDrag = null; + } + finishFrameDrag(slot) { + const drag = this.frameDrag; + if (!drag) return; + const sourceIndex = drag.sourceIndex; + const frameCount = this.frames.length; + const safeSlot = Math.max(0, Math.min(frameCount, Number(slot) || 0)); + let destination = safeSlot > sourceIndex ? safeSlot - 1 : safeSlot; + destination = Math.max(0, Math.min(frameCount - 1, destination)); + const activeFrame = this.frames[this.currentFrame] || null; + const movedFrame = drag.sourceFrame; + this.cleanupFrameDrag(); + this.frameDragClickSuppressedUntil = performance.now() + 180; + + if (movedFrame && destination !== sourceIndex) { + const actualSource = this.frames.indexOf(movedFrame); + if (actualSource !== -1) { + this.frames.splice(actualSource, 1); + this.frames.splice(destination, 0, movedFrame); + markEditorDirty(); + } + } + if (activeFrame) { + const activeIndex = this.frames.indexOf(activeFrame); + if (activeIndex !== -1) this.currentFrame = activeIndex; + } + this.createThumbnails(); + const counter = document.getElementById('frame-counter'); + if (counter) counter.textContent = `${this.currentFrame + 1} / ${this.frames.length}`; + this.updateThumbnailSelection(this.currentFrame); + if (window.flipnoteEditorAudioReady && typeof updateAudioFillBars === 'function') updateAudioFillBars(); + } + setActiveFrame(index) { + if (canvas.textTool) canvas.textTool.finish(); + openColorPickerLayer = null; + const previousFrame = this.currentFrame; + if (index >= 0 && index < this.frames.length) { + this.currentFrame = index; + } else if (index >= this.frames.length) { + const sourceFrame = cloneEnabled ? this.frames[this.currentFrame] : undefined; + const addedIndex = this.addFrame(sourceFrame); + if (addedIndex >= 0) this.currentFrame = this.frames.length - 1; + } + if (typeof canvas !== 'undefined' && canvas && previousFrame !== this.currentFrame && typeof canvas.clearSelectionForFrameChange === 'function') { + canvas.clearSelectionForFrameChange(); + } + canvas.renderCurrentFrame(); + updateLayerPanel(); + updatePaperToolState(); + updateHistoryButtons(this.getFrame(this.currentFrame)); + document.getElementById('frame-counter').textContent = `${this.currentFrame + 1} / ${this.frames.length}`; + this.updateThumbnailSelection(this.currentFrame); + if (window.flipnoteEditorAudioReady && typeof updateAudioFillBars === 'function') updateAudioFillBars(); + if (this.playing && typeof playStampedSoundEffectsForFrame === 'function') { + playStampedSoundEffectsForFrame(this.currentFrame); + } + } + stopPlayback() { + this.playing = false; + clearInterval(this.playInterval); + if (typeof stopFlipnoteTimelineAudio === 'function') stopFlipnoteTimelineAudio(); + this.playInterval = null; + const playPauseImage = document.getElementById('play-pause'); + if (playPauseImage) { + playPauseImage.title = 'Play'; + playPauseImage.alt = 'Play'; + playPauseImage.src = './assets/flipnote-editor/play.png'; + } + syncEditorPlaybackState(this); + canvas.renderCurrentFrame(); + } + restartPlaybackInterval() { + clearInterval(this.playInterval); + if (!this.playing) return; + this.playInterval = setInterval(() => { + if (!this.frames.length) return; + if (this.currentFrame >= this.frames.length - 1) { + if (this.loopEnabled) { + if (typeof stopTimelineSoundEffects === 'function') stopTimelineSoundEffects(); + this.setActiveFrame(0); + if (typeof restartFlipnoteTimelineMusic === 'function') restartFlipnoteTimelineMusic(0); + } else { + this.stopPlayback(); + } + return; + } + this.setActiveFrame(this.currentFrame + 1); + }, 1000 / this.framerate); + } + setSpeedLevel(level) { + const speed = speedSetting(level); + if (this.speedLevel === speed.level && this.framerate === speed.fps) return; + if (!flipnoteSpaceAllowsProjection(this, {speedLevel: speed.level})) { + notifyFlipnoteSpaceLimit(this); + return; + } + markEditorDirty(); + this.speedLevel = speed.level; + this.framerate = speed.fps; + updatePlaybackSettingsUI(this); + syncEditorPlaybackState(this); + if (typeof updateAudioFillBars === 'function') updateAudioFillBars(); + if (typeof syncActiveAudioPreviewPlaybackRate === 'function') syncActiveAudioPreviewPlaybackRate(); + renderFlipnoteSpaceMeter(this); + if (this.playing) { + this.restartPlaybackInterval(); + if (typeof restartFlipnoteTimelineMusic === 'function') restartFlipnoteTimelineMusic(this.currentFrame); + } + } + setLoopEnabled(enabled) { + const nextEnabled = !!enabled; + if (this.loopEnabled === nextEnabled) return; + markEditorDirty(); + this.loopEnabled = nextEnabled; + updatePlaybackSettingsUI(this); + } + togglePlayPause() { + if (canvas.textTool) canvas.textTool.finish(); + const playPauseImage = document.getElementById('play-pause'); + if (this.playing) { + this.stopPlayback(); + } else { + if (typeof stopFlipnoteEditorAudio === 'function') stopFlipnoteEditorAudio(); + this.playing = true; + if (playPauseImage) { + playPauseImage.title = 'Stop'; + playPauseImage.alt = 'Stop'; + playPauseImage.src = './assets/flipnote-editor/stop.png'; + } + // Light-box is an editing aid only. Remove ghosts immediately when + // playback starts, before the first playback tick. Playback also + // hard-locks every editor/playback control except this Stop button. + syncEditorPlaybackState(this); + canvas.renderCurrentFrame(); + this.ensureFrameVisible(this.currentFrame); + if (typeof startFlipnoteTimelineAudio === 'function') startFlipnoteTimelineAudio(this.currentFrame); + this.restartPlaybackInterval(); + } + } + createThumbnails() { + this.cleanupFrameDrag(); + this.timelineElement.textContent = ''; + this.frames.forEach((frame, idx) => { + const thumbCanvas = document.createElement('canvas'); + thumbCanvas.classList.add('timeline-thumbnail'); + thumbCanvas.setAttribute('data-frame-index', idx); + if (idx === this.currentFrame) thumbCanvas.classList.add('selected'); + thumbCanvas.draggable = !this.playing; + thumbCanvas.addEventListener('click', () => { + if (performance.now() < this.frameDragClickSuppressedUntil) return; + this.setActiveFrame(idx); + }); + thumbCanvas.addEventListener('dragstart', (event) => this.beginFrameDrag(idx, thumbCanvas, event)); + thumbCanvas.addEventListener('dragend', () => { + if (!this.frameDrag) return; + this.cleanupFrameDrag(); + this.frameDragClickSuppressedUntil = performance.now() + 180; + this.ensureFrameVisible(this.currentFrame); + }); + const thumbctx = thumbCanvas.getContext('2d'); + thumbCanvas.width = 256; + thumbCanvas.height = 192; + thumbctx.putImageData(frame.compositeImageData(), 0, 0); + this.timelineElement.appendChild(thumbCanvas); + }); + // Re-rendering thumbnails happens after page edits, undo/redo, inserts, + // imports and other meaningful changes. Return to the current page then, + // but never fight a manual timeline scroll while nothing is changing. + this.ensureFrameVisible(this.currentFrame); + } + updateThumbnailSelection(idx) { + const lastElement = this.timelineElement.querySelector('.selected'); + if (lastElement) lastElement.classList.remove('selected'); + const newElement = this.timelineElement.querySelector(`.timeline-thumbnail[data-frame-index="${idx}"]`); + if (newElement) newElement.classList.add('selected'); + // Any page change re-enables timeline following. A manual scroll is left + // alone until the current page changes or another edit recreates thumbs. + this.ensureFrameVisible(idx); + } +} + +// CanvasEditor edits one selected layer at a time, while the visible canvas is +// always a composite of both layers. +class CanvasEditor { + constructor(canvasElement) { + if (!window.FlipnoteDrawingEngine) throw new Error('Flipnote drawing engine did not load'); + this.engine = window.FlipnoteDrawingEngine; + this.canvasElement = canvasElement; + this.ctx = canvasElement.getContext('2d', {alpha:false}); + this.ctx.imageSmoothingEnabled = false; + this.width = this.engine.WIDTH; + this.height = this.engine.HEIGHT; + this.drawing = false; + this.allowEditing = true; + this.currentToolFamily = 'pen'; + this.selectedToolSlots = {pen: 0, paint: 0, eraser: 0, fill: 0}; + // Advanced Erase follows the most recently used Paint texture. Until + // Paint has actually been chosen/used, Flipnote-style Paint 5 is the + // fallback pattern. + this.lastPaintPatternSlot = 4; + // Shift is still a live Advanced modifier, but a tool explicitly chosen + // while Shift is held stays Advanced after Shift is released. Each + // family remembers that choice independently until another tool in the + // same family is selected. + this.selectedToolAdvanced = {pen: false, paint: false, eraser: false, fill: false}; + this.customPaintMotif = loadCustomPaintPattern(); + this.expertTools = false; + this.activeLayer = 0; + this.cursorPosition = {x: 0, y: 0}; + this.pointerInsideCanvas = false; + this.cursorPreviewMask = new Uint8Array(this.width * this.height); + this.canvasElement.classList.add('flipnote-brush-preview-cursor'); + this.lastPosition = {x: null, y: null}; + this.strokeState = null; + this.selectionMode = false; + this.selectionRect = null; + this.selectionMask = new Uint8Array(this.width * this.height); + this.selectionPixelCount = 0; + this.selectionDrag = null; + this.selectionMove = null; + this.selectionClipboard = null; + // Long-pressing Select on touch arms a one-shot rectangle selection, + // mirroring the desktop Shift modifier without changing mouse behavior. + this.mobileRectangleSelection = false; + this.rectangleSelectionMode = false; + this.selectionTransformGeometry = null; + this.ctx.fillStyle = 'white'; + this.ctx.fillRect(0, 0, this.width, this.height); + this.suppressLightbox = false; + this.inkBuffer = document.createElement('canvas'); + this.inkBuffer.width = this.width; + this.inkBuffer.height = this.height; + this.inkBufferCtx = this.inkBuffer.getContext('2d'); + this.inkBufferCtx.imageSmoothingEnabled = false; + + // Right-click temporarily switches the editor to the retained Eraser + // and restores the user's previous tool as soon as the button is released. + // Until an Eraser has actually been chosen/used in this session, the + // right-click fallback is Erase 2 (slot index 1). + this.rightEraseActive = false; + this.rightErasePreviousTool = null; + this.hasRetainedEraserChoice = false; + + this.canvasElement.addEventListener('pointerdown', (e) => { + const rightErase = e.button === 2; + if (e.button !== 0 && !rightErase) return; + if (!this.allowEditing) return; + e.preventDefault(); + if (!rightErase && this.textTool && this.textTool.enabled) { + void this.textTool.begin(this.getCanvasCoords(e)); + return; + } + if (this.textTool) this.textTool.finish(); + // Keep keyboard shortcuts live after the user clicks/draws on the canvas. + // pointerdown is prevented for drawing, so focus must be applied explicitly. + if (typeof this.canvasElement.focus === 'function') this.canvasElement.focus({preventScroll: true}); + this.cursorPosition = this.getCanvasCoords(e); + + // Right-click always erases. While the right button is held, make the + // Eraser the editor's *actual* current/selected tool so the toolbar and + // cursor show exactly what is being used. Mouse-up restores the prior tool. + // If no Eraser has been chosen/used yet, start with Flipnote Erase 2. + if (rightErase) { + const previousRetainedChoice = (typeof retainedToolChoice !== 'undefined' && retainedToolChoice) + ? {...retainedToolChoice} + : null; + this.rightErasePreviousTool = { + family: this.currentToolFamily, + retainedChoice: previousRetainedChoice, + }; + if (!this.hasRetainedEraserChoice) { + this.selectedToolSlots.eraser = 1; + this.selectedToolAdvanced.eraser = false; + } + this.hasRetainedEraserChoice = true; + this.rightEraseActive = true; + this.currentToolFamily = 'eraser'; + this.canvasElement.classList.remove('pen1-cursor', 'pen2-cursor', 'eraser-cursor', 'checker-cursor', 'paint-cursor', 'fill-cursor'); + this.canvasElement.classList.add('eraser-cursor'); + if (typeof retainedToolChoice !== 'undefined') { + retainedToolChoice = { + family: 'eraser', + slot: this.selectedToolSlots.eraser, + advanced: !!this.selectedToolAdvanced.eraser, + }; + } + if (typeof updateDrawingToolUI === 'function') updateDrawingToolUI(); + this.drawing = true; + this.updateCursorMode(); + if (this.canvasElement.setPointerCapture) this.canvasElement.setPointerCapture(e.pointerId); + this.lastPosition = {x: this.cursorPosition.x, y: this.cursorPosition.y}; + this.strokeState = this.engine.createStrokeState(); + const family = 'eraser'; + this.engine.applyStrokeStart( + this.currentMask(), + family, + this.logicalToolId(family), + this.cursorPosition.x, + this.cursorPosition.y, + this.strokeState, + this.toolEngineOptions(family) + ); + this.renderCurrentFrame(); + return; + } + + if (this.selectionMode) { + const x = this.cursorPosition.x; + const y = this.cursorPosition.y; + if (this.rectangleSelectionMode && this.hasSelection()) { + const handle = this.selectionTransformHandleAt(x, y); + if (handle) { + this.beginSelectionHandleDrag(handle, x, y); + if (this.canvasElement.setPointerCapture) this.canvasElement.setPointerCapture(e.pointerId); + this.renderCurrentFrame(); + return; + } + } + // Flipnote-style Select is a freehand lasso by default. Shift + // temporarily switches it to a rectangle. On touch, dragging + // from inside an existing selection moves it directly, because + // phones do not have a Shift key. A long-press on Select arms a + // one-shot rectangle selection for the same reason. + const touchMovesSelection = e.pointerType === 'touch' + && !this.mobileRectangleSelection + && !this.rectangleSelectionMode + && this.hasSelection() + && this.selectionContains(x, y); + if ((editorAlternateActive(e) || touchMovesSelection) && this.hasSelection() && this.selectionContains(x, y)) { + this.beginSelectionMove(x, y); + } else { + this.beginSelectionDrag(x, y, !!(editorAlternateActive(e) || this.mobileRectangleSelection || this.rectangleSelectionMode)); + } + if (this.canvasElement.setPointerCapture) this.canvasElement.setPointerCapture(e.pointerId); + this.renderCurrentFrame(); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + return; + } + + // Fill is a one-click bucket operation, not a continuous stroke. + // Slots 1-7 reuse the matching Paint pattern; Fill 8 is solid. + if (this.currentToolFamily === 'fill') { + const fillOptions = this.toolEngineOptions(); + // Shift-fill paints the active layer/colour, but treats the + // opposite layer as the flood boundary. This makes it possible + // to ink a region on one layer using linework on the other as + // the enclosing border. + if (editorAlternateActive(e)) fillOptions.boundaryMask = this.otherMask(); + const changed = this.engine.floodFillPattern( + this.currentMask(), + this.selectedToolSlots.fill, + this.cursorPosition.x, + this.cursorPosition.y, + fillOptions + ); + if (changed) { + this.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + } + return; + } + + // "Previously used Paint" means the Paint slot that most + // recently entered actual drawing use. This also covers keyboard + // switching to Paint without opening the palette first. + if (this.currentToolFamily === 'paint') { + this.lastPaintPatternSlot = this.selectedToolSlots.paint; + } + if (this.currentToolFamily === 'eraser') { + this.hasRetainedEraserChoice = true; + } + this.drawing = true; + if (this.canvasElement.setPointerCapture) this.canvasElement.setPointerCapture(e.pointerId); + this.lastPosition = {x: this.cursorPosition.x, y: this.cursorPosition.y}; + this.strokeState = this.engine.createStrokeState(); + this.engine.applyStrokeStart( + this.currentMask(), + this.currentToolFamily, + this.logicalToolId(), + this.cursorPosition.x, + this.cursorPosition.y, + this.strokeState, + this.toolEngineOptions() + ); + this.renderCurrentFrame(); + }); + this.canvasElement.addEventListener('pointerenter', (e) => { + this.pointerInsideCanvas = true; + this.cursorPosition = this.getCanvasCoords(e); + if (!this.drawing) this.renderCurrentFrame(); + }); + this.canvasElement.addEventListener('pointerleave', () => { + if (this.drawing) return; + this.pointerInsideCanvas = false; + this.renderCurrentFrame(); + }); + this.canvasElement.addEventListener('pointermove', (e) => { + this.pointerInsideCanvas = true; + this.cursorPosition = this.getCanvasCoords(e); + if (this.selectionDrag) { + e.preventDefault(); + const drag = this.selectionDrag; + if (drag.type === 'move') { + this.updateSelectionMove(this.cursorPosition.x, this.cursorPosition.y); + } else if (drag.type === 'resize' || drag.type === 'rotate') { + this.updateSelectionHandleDrag(this.cursorPosition.x, this.cursorPosition.y); + } else if (drag.type === 'rect') { + drag.currentX = this.cursorPosition.x; + drag.currentY = this.cursorPosition.y; + this.selectionRect = this.normalizeSelectionRect(drag.anchorX, drag.anchorY, drag.currentX, drag.currentY); + } else if (drag.type === 'lasso') { + drag.currentX = this.cursorPosition.x; + drag.currentY = this.cursorPosition.y; + const last = drag.points[drag.points.length - 1]; + if (!last || last.x !== this.cursorPosition.x || last.y !== this.cursorPosition.y) { + drag.points.push({x: this.cursorPosition.x, y: this.cursorPosition.y}); + } + } + this.renderCurrentFrame(); + return; + } + if (!this.drawing) { + this.renderCurrentFrame(); + return; + } + e.preventDefault(); + if (this.cursorPosition.x === this.lastPosition.x && this.cursorPosition.y === this.lastPosition.y) return; + const family = this.rightEraseActive ? 'eraser' : this.currentToolFamily; + this.engine.applyStrokeSegment( + this.currentMask(), + family, + this.logicalToolId(family), + this.lastPosition.x, + this.lastPosition.y, + this.cursorPosition.x, + this.cursorPosition.y, + this.strokeState, + this.toolEngineOptions(family) + ); + this.lastPosition = {x: this.cursorPosition.x, y: this.cursorPosition.y}; + this.renderCurrentFrame(); + }); + const finishPointerStroke = (e) => { + if (this.selectionDrag) { + if (e && this.canvasElement.hasPointerCapture && this.canvasElement.hasPointerCapture(e.pointerId)) { + this.canvasElement.releasePointerCapture(e.pointerId); + } + this.finishSelection(); + return; + } + if (!this.drawing) return; + if (e && this.canvasElement.hasPointerCapture && this.canvasElement.hasPointerCapture(e.pointerId)) { + this.canvasElement.releasePointerCapture(e.pointerId); + } + this.finishStroke(); + }; + this.canvasElement.addEventListener('pointerup', finishPointerStroke); + this.canvasElement.addEventListener('pointercancel', finishPointerStroke); + this.canvasElement.addEventListener('lostpointercapture', () => { + if (this.selectionDrag) this.finishSelection(); + else if (this.drawing) this.finishStroke(); + }); + + document.addEventListener('keydown', (e) => { + if (editorShortcutsBlocked()) return; + const target = e.target instanceof Element ? e.target : null; + // Editor shortcuts are global within the editor: focusing the canvas, + // a toolbar button, or the page background must not change which keys + // work. Only real text-entry controls keep their normal keystrokes. + const typingText = !!(target && target.matches('input, textarea, select, [contenteditable="true"]')); + if (typingText) return; + + // Space is always Play/Pause (unless the user is typing). Handle it + // before allowEditing because playback itself sets allowEditing=false, + // and Space still needs to be able to stop the animation. + if (shortcutAction(e, 'global') === 'playPause') { + e.preventDefault(); + if (!e.repeat) timeline.togglePlayPause(); + return; + } + + // The focused speed slider owns its remappable keys. + if (target && target.closest('#flipnote-speed-control')) return; + + if (editorAlternateKeyEvent(e) && this.allowEditing) { + if (!this.selectionMode) this.setExpertTools(true); + return; + } + + if (!this.allowEditing) return; + + const nudge = shortcutAction(e, 'editing'); + if (nudge.startsWith('nudge')) { + e.preventDefault(); + const dx = nudge === 'nudgeLeft' ? -1 : (nudge === 'nudgeRight' ? 1 : 0); + const dy = nudge === 'nudgeUp' ? -1 : (nudge === 'nudgeDown' ? 1 : 0); + this.nudgeSelectionOrLayer(dx, dy); + return; + } + if (!nudge) return; + e.preventDefault(); + switch (nudge) { + case 'undo': + timeline.getFrame(timeline.currentFrame).undo(); + break; + case 'redo': + timeline.getFrame(timeline.currentFrame).redo(); + break; + case 'clear': + case 'clearAlt': + if (e.repeat) break; + this.clearCurrentFrame(!!e.shiftKey); + break; + case 'cycleTool': + if (e.repeat) break; + this.cycleLastToolFamily(); + break; + case 'text': + if (!e.repeat && this.textTool) this.textTool.toggle(); + break; + case 'cycleInk': + if (e.repeat) break; + this.cycleActiveLayerColor(); + break; + case 'draw': + this.setToolFamily('pen'); + break; + case 'paint': + this.setToolFamily('paint'); + break; + case 'erase': + this.setToolFamily('eraser'); + break; + case 'fill': + this.setToolFamily('fill'); + break; + default: + if (/^slot[1-8]$/.test(nudge)) this.setToolSlot(Number(nudge.slice(4)) - 1); + break; + } + }); + document.addEventListener('keyup', (e) => { + if (editorAlternateReleaseEvent(e)) this.setExpertTools(false); + }); + window.addEventListener('blur', () => this.setExpertTools(false)); + + // Keep the marching selection outline animated even when the pointer is + // completely still. renderSelectionOverlay() derives its phase from + // time, so a small redraw tick is all that is required. + this.selectionAnimationTimer = window.setInterval(() => { + if (this.selectionMode || this.selectionDrag || this.hasSelection() || (this.textTool && this.textTool.session)) { + this.renderCurrentFrame(); + } + }, 100); + } + normalizeSelectionRect(x0, y0, x1, y1) { + const minX = Math.max(0, Math.min(x0, x1)); + const minY = Math.max(0, Math.min(y0, y1)); + const maxX = Math.min(this.width - 1, Math.max(x0, x1)); + const maxY = Math.min(this.height - 1, Math.max(y0, y1)); + return { + x: minX, + y: minY, + width: Math.max(1, maxX - minX + 1), + height: Math.max(1, maxY - minY + 1), + }; + } + selectionBoundsFromMask(mask = this.selectionMask) { + let minX = this.width; + let minY = this.height; + let maxX = -1; + let maxY = -1; + let count = 0; + for (let i = 0; i < mask.length; i++) { + if (!mask[i]) continue; + count += 1; + const x = i % this.width; + const y = Math.floor(i / this.width); + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (y < minY) minY = y; + if (y > maxY) maxY = y; + } + if (!count) return {rect: null, count: 0}; + return { + rect: {x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1}, + count, + }; + } + applySelectionMask(mask) { + this.selectionMask.fill(0); + if (mask) this.selectionMask.set(mask); + const bounds = this.selectionBoundsFromMask(this.selectionMask); + this.selectionRect = bounds.rect; + this.selectionPixelCount = bounds.count; + this.selectionTransformGeometry = null; + } + hasSelection() { + return this.selectionPixelCount > 0 && !!this.selectionRect; + } + selectionContains(x, y) { + const sx = Math.round(x); + const sy = Math.round(y); + if (sx < 0 || sy < 0 || sx >= this.width || sy >= this.height) return false; + return !!this.selectionMask[sy * this.width + sx]; + } + setSelectionMode(enabled) { + const next = !!enabled; + if (next && this.textTool) this.textTool.disable(); + this.selectionMode = next; + if (!next) { + this.selectionDrag = null; + this.selectionMove = null; + this.mobileRectangleSelection = false; + this.rectangleSelectionMode = false; + this.selectionTransformGeometry = null; + } + this.updateCursorMode(); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + this.renderCurrentFrame(); + } + clearSelection() { + this.selectionMode = false; + this.selectionDrag = null; + this.selectionMove = null; + this.mobileRectangleSelection = false; + this.rectangleSelectionMode = false; + this.selectionTransformGeometry = null; + this.selectionMask.fill(0); + this.selectionPixelCount = 0; + this.selectionRect = null; + this.updateCursorMode(); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + this.renderCurrentFrame(); + } + clearSelectionForFrameChange() { + this.selectionMode = false; + this.selectionDrag = null; + this.selectionMove = null; + this.mobileRectangleSelection = false; + this.rectangleSelectionMode = false; + this.selectionTransformGeometry = null; + this.selectionMask.fill(0); + this.selectionPixelCount = 0; + this.selectionRect = null; + this.updateCursorMode(); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + } + beginSelectionDrag(x, y, rectangular = false) { + this.selectionMove = null; + this.selectionTransformGeometry = null; + this.selectionMask.fill(0); + this.selectionPixelCount = 0; + this.selectionRect = {x, y, width: 1, height: 1}; + this.selectionDrag = rectangular + ? {type: 'rect', anchorX: x, anchorY: y, currentX: x, currentY: y} + : {type: 'lasso', points: [{x, y}], currentX: x, currentY: y}; + } + beginSelectionMove(x, y) { + if (!this.hasSelection()) return false; + const baseMask = cloneMask(this.currentMask()); + const sourceSelection = cloneMask(this.selectionMask); + let sourceInkCount = 0; + for (let i = 0; i < sourceSelection.length; i++) { + if (sourceSelection[i] && baseMask[i]) sourceInkCount += 1; + } + this.selectionDrag = { + type: 'move', + anchorX: x, + anchorY: y, + currentX: x, + currentY: y, + baseMask, + sourceSelection, + sourceInkCount, + geometry: this.selectionTransformGeometry ? {...this.selectionTransformGeometry} : null, + dx: 0, + dy: 0, + }; + this.selectionMove = this.selectionDrag; + return true; + } + updateSelectionMove(x, y) { + const move = this.selectionDrag; + if (!move || move.type !== 'move') return; + const dx = Math.round(x - move.anchorX); + const dy = Math.round(y - move.anchorY); + move.currentX = x; + move.currentY = y; + if (dx === move.dx && dy === move.dy) return; + move.dx = dx; + move.dy = dy; + const target = this.currentMask(); + target.set(move.baseMask); + for (let i = 0; i < move.sourceSelection.length; i++) { + if (move.sourceSelection[i] && move.baseMask[i]) target[i] = 0; + } + const translated = new Uint8Array(this.width * this.height); + for (let i = 0; i < move.sourceSelection.length; i++) { + if (!move.sourceSelection[i]) continue; + const x0 = i % this.width; + const y0 = Math.floor(i / this.width); + const nx = x0 + dx; + const ny = y0 + dy; + if (nx < 0 || ny < 0 || nx >= this.width || ny >= this.height) continue; + const ni = ny * this.width + nx; + translated[ni] = 1; + if (move.baseMask[i]) target[ni] = 1; + } + this.applySelectionMask(translated); + if (move.geometry) this.selectionTransformGeometry = {...move.geometry, cx: move.geometry.cx + dx, cy: move.geometry.cy + dy}; + } + rasterLineIntoMask(mask, x0, y0, x1, y1) { + let ax = Math.round(x0); + let ay = Math.round(y0); + const bx = Math.round(x1); + const by = Math.round(y1); + const dx = Math.abs(bx - ax); + const sx = ax < bx ? 1 : -1; + const dy = -Math.abs(by - ay); + const sy = ay < by ? 1 : -1; + let err = dx + dy; + while (true) { + if (ax >= 0 && ay >= 0 && ax < this.width && ay < this.height) mask[ay * this.width + ax] = 1; + if (ax === bx && ay === by) break; + const e2 = 2 * err; + if (e2 >= dy) { err += dy; ax += sx; } + if (e2 <= dx) { err += dx; ay += sy; } + } + } + lassoMaskFromPoints(points) { + const mask = new Uint8Array(this.width * this.height); + if (!Array.isArray(points) || !points.length) return mask; + if (points.length < 3) { + for (let i = 1; i < points.length; i++) { + this.rasterLineIntoMask(mask, points[i - 1].x, points[i - 1].y, points[i].x, points[i].y); + } + if (points.length === 1) mask[points[0].y * this.width + points[0].x] = 1; + return mask; + } + let minX = this.width - 1; + let maxX = 0; + let minY = this.height - 1; + let maxY = 0; + points.forEach((point) => { + minX = Math.min(minX, point.x); + maxX = Math.max(maxX, point.x); + minY = Math.min(minY, point.y); + maxY = Math.max(maxY, point.y); + }); + minX = Math.max(0, Math.floor(minX)); + maxX = Math.min(this.width - 1, Math.ceil(maxX)); + minY = Math.max(0, Math.floor(minY)); + maxY = Math.min(this.height - 1, Math.ceil(maxY)); + const insidePolygon = (px, py) => { + let inside = false; + for (let i = 0, j = points.length - 1; i < points.length; j = i++) { + const xi = points[i].x; + const yi = points[i].y; + const xj = points[j].x; + const yj = points[j].y; + const crosses = ((yi > py) !== (yj > py)) + && (px < ((xj - xi) * (py - yi)) / ((yj - yi) || 1e-9) + xi); + if (crosses) inside = !inside; + } + return inside; + }; + for (let y = minY; y <= maxY; y++) { + for (let x = minX; x <= maxX; x++) { + if (insidePolygon(x + 0.5, y + 0.5)) mask[y * this.width + x] = 1; + } + } + for (let i = 1; i < points.length; i++) { + this.rasterLineIntoMask(mask, points[i - 1].x, points[i - 1].y, points[i].x, points[i].y); + } + this.rasterLineIntoMask(mask, points[points.length - 1].x, points[points.length - 1].y, points[0].x, points[0].y); + return mask; + } + rectangleMask(rect) { + const mask = new Uint8Array(this.width * this.height); + if (!rect) return mask; + for (let y = rect.y; y < rect.y + rect.height; y++) { + const row = y * this.width; + for (let x = rect.x; x < rect.x + rect.width; x++) mask[row + x] = 1; + } + return mask; + } + selectBoundingRectangle() { + if (!this.hasSelection()) return false; + this.selectionDrag = null; + this.selectionMove = null; + this.applySelectionMask(this.rectangleMask(this.selectionRect)); + this.selectionTransformGeometry = this.selectionGeometryFromRect(this.selectionRect); + this.selectionMode = true; + this.rectangleSelectionMode = true; + this.mobileRectangleSelection = false; + this.updateCursorMode(); + this.renderCurrentFrame(); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + return true; + } + selectionTransformHandles() { + if (!this.rectangleSelectionMode || !this.hasSelection()) return []; + const geometry = this.selectionTransformGeometry || this.selectionGeometryFromRect(this.selectionRect); + const halfWidth = (geometry.width - 1) / 2; + const halfHeight = (geometry.height - 1) / 2; + const cos = Math.cos(geometry.angle), sin = Math.sin(geometry.angle); + const point = (localX, localY) => ({ + x: geometry.cx + localX * cos - localY * sin, + y: geometry.cy + localX * sin + localY * cos, + }); + const handles = []; + for (const [row, localY] of [['n', -halfHeight], ['', 0], ['s', halfHeight]]) { + for (const [column, localX] of [['w', -halfWidth], ['', 0], ['e', halfWidth]]) { + if (row || column) handles.push({name: row + column, ...point(localX, localY)}); + } + } + handles.push({name: 'rotate', ...point(0, -halfHeight - 11)}); + return handles; + } + selectionGeometryFromRect(rect) { + return { + cx: rect.x + (rect.width - 1) / 2, + cy: rect.y + (rect.height - 1) / 2, + width: rect.width, height: rect.height, angle: 0, + }; + } + selectionTransformHandleAt(x, y) { + const handles = this.selectionTransformHandles(); + let nearest = null; + let distance = Infinity; + for (const handle of handles.slice().reverse()) { + const d = Math.hypot(x - handle.x, y - handle.y); + if (d <= 5 && d < distance) { nearest = handle.name; distance = d; } + } + return nearest; + } + beginSelectionHandleDrag(handle, x, y) { + const geometry = {...(this.selectionTransformGeometry || this.selectionGeometryFromRect(this.selectionRect))}; + this.selectionDrag = { + type: handle === 'rotate' ? 'rotate' : 'resize', handle, geometry, + sourceLayer: cloneMask(this.currentMask()), + sourceSelection: cloneMask(this.selectionMask), + startAngle: Math.atan2(y - geometry.cy, x - geometry.cx), + changed: false, + }; + } + updateSelectionHandleDrag(x, y) { + const drag = this.selectionDrag; + if (!drag || (drag.type !== 'resize' && drag.type !== 'rotate')) return; + const source = drag.geometry; + const geometry = {...source}; + if (drag.type === 'rotate') { + geometry.angle = source.angle + Math.atan2(y - source.cy, x - source.cx) - drag.startAngle; + } else { + const cos = Math.cos(source.angle), sin = Math.sin(source.angle); + const localX = (x - source.cx) * cos + (y - source.cy) * sin; + const localY = -(x - source.cx) * sin + (y - source.cy) * cos; + let left = -(source.width - 1) / 2, right = (source.width - 1) / 2; + let top = -(source.height - 1) / 2, bottom = (source.height - 1) / 2; + if (drag.handle.includes('w')) left = Math.min(localX, right); + if (drag.handle.includes('e')) right = Math.max(localX, left); + if (drag.handle.includes('n')) top = Math.min(localY, bottom); + if (drag.handle.includes('s')) bottom = Math.max(localY, top); + geometry.width = Math.max(1, right - left + 1); + geometry.height = Math.max(1, bottom - top + 1); + const centerX = (left + right) / 2, centerY = (top + bottom) / 2; + geometry.cx = source.cx + centerX * cos - centerY * sin; + geometry.cy = source.cy + centerX * sin + centerY * cos; + } + const target = this.currentMask(); + target.set(drag.sourceLayer); + for (let i = 0; i < drag.sourceSelection.length; i++) { + if (drag.sourceSelection[i]) target[i] = 0; + } + const transformedSelection = new Uint8Array(this.width * this.height); + const targetCos = Math.cos(geometry.angle), targetSin = Math.sin(geometry.angle); + const sourceCos = Math.cos(source.angle), sourceSin = Math.sin(source.angle); + const halfWidth = geometry.width / 2, halfHeight = geometry.height / 2; + const corners = [ + [-halfWidth, -halfHeight], [halfWidth, -halfHeight], + [-halfWidth, halfHeight], [halfWidth, halfHeight], + ].map(([px, py]) => [geometry.cx + px * targetCos - py * targetSin, geometry.cy + px * targetSin + py * targetCos]); + const left = Math.max(0, Math.floor(Math.min(...corners.map((point) => point[0])))); + const right = Math.min(this.width - 1, Math.ceil(Math.max(...corners.map((point) => point[0])))); + const top = Math.max(0, Math.floor(Math.min(...corners.map((point) => point[1])))); + const bottom = Math.min(this.height - 1, Math.ceil(Math.max(...corners.map((point) => point[1])))); + for (let dy = top; dy <= bottom; dy++) { + for (let dx = left; dx <= right; dx++) { + const localX = (dx - geometry.cx) * targetCos + (dy - geometry.cy) * targetSin; + const localY = -(dx - geometry.cx) * targetSin + (dy - geometry.cy) * targetCos; + if (Math.abs(localX) > halfWidth || Math.abs(localY) > halfHeight) continue; + const scaledX = localX * source.width / geometry.width; + const scaledY = localY * source.height / geometry.height; + const sx = Math.round(source.cx + scaledX * sourceCos - scaledY * sourceSin); + const sy = Math.round(source.cy + scaledX * sourceSin + scaledY * sourceCos); + if (sx < 0 || sy < 0 || sx >= this.width || sy >= this.height) continue; + const sourceIndex = sy * this.width + sx; + if (!drag.sourceSelection[sourceIndex]) continue; + const destIndex = dy * this.width + dx; + transformedSelection[destIndex] = 1; + if (drag.sourceLayer[sourceIndex]) target[destIndex] = 1; + } + } + this.applySelectionMask(transformedSelection); + this.selectionTransformGeometry = geometry; + drag.changed = true; + this.renderCurrentFrame(); + } + invertSelection(swapLayerColors = false) { + if (!this.hasSelection() || !this.allowEditing || timeline.playing) return false; + if (swapLayerColors) { + const frame = this.currentFrame(); + if (frame.colors[0] === frame.colors[1]) return false; + let changed = false; + for (let i = 0; i < this.selectionMask.length; i++) { + if (!this.selectionMask[i] || frame.layers[0][i] === frame.layers[1][i]) continue; + [frame.layers[0][i], frame.layers[1][i]] = [frame.layers[1][i], frame.layers[0][i]]; + changed = true; + } + if (!changed) return false; + this.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + return true; + } + const mask = this.currentMask(); + for (let i = 0; i < this.selectionMask.length; i++) { + if (this.selectionMask[i]) mask[i] = mask[i] ? 0 : 1; + } + this.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + return true; + } + finishSelection() { + const drag = this.selectionDrag; + if (!drag) return; + if (drag.type === 'resize' || drag.type === 'rotate') { + this.selectionDrag = null; + if (drag.changed) timeline.updateFrame(timeline.currentFrame); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + this.renderCurrentFrame(); + return; + } + if (drag.type === 'move') { + const moved = drag.dx !== 0 || drag.dy !== 0; + const changedInk = moved && drag.sourceInkCount > 0; + this.selectionDrag = null; + this.selectionMove = null; + this.updateCursorMode(); + if (changedInk) timeline.updateFrame(timeline.currentFrame); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + this.renderCurrentFrame(); + return; + } + let mask; + if (drag.type === 'rect') { + const rect = this.normalizeSelectionRect(drag.anchorX, drag.anchorY, drag.currentX, drag.currentY); + mask = this.rectangleMask(rect); + } else { + mask = this.lassoMaskFromPoints(drag.points || []); + } + this.selectionDrag = null; + this.mobileRectangleSelection = false; + this.applySelectionMask(mask); + if (this.rectangleSelectionMode && this.hasSelection()) { + this.selectionTransformGeometry = this.selectionGeometryFromRect(this.selectionRect); + } + this.updateCursorMode(); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + this.renderCurrentFrame(); + } + copySelection(cut = false) { + if (!this.hasSelection()) return false; + const rect = this.selectionRect; + const source = this.currentMask(); + const data = new Uint8Array(rect.width * rect.height); + const shape = new Uint8Array(rect.width * rect.height); + for (let y = 0; y < rect.height; y++) { + for (let x = 0; x < rect.width; x++) { + const srcIndex = (rect.y + y) * this.width + (rect.x + x); + const localIndex = y * rect.width + x; + if (!this.selectionMask[srcIndex]) continue; + shape[localIndex] = 1; + data[localIndex] = source[srcIndex] ? 1 : 0; + } + } + this.selectionClipboard = { + width: rect.width, + height: rect.height, + data, + shape, + sourceX: rect.x, + sourceY: rect.y, + }; + if (cut) { + let changed = false; + for (let y = 0; y < rect.height; y++) { + for (let x = 0; x < rect.width; x++) { + const localIndex = y * rect.width + x; + if (!shape[localIndex] || !data[localIndex]) continue; + const srcIndex = (rect.y + y) * this.width + (rect.x + x); + if (source[srcIndex]) { + source[srcIndex] = 0; + changed = true; + } + } + } + if (changed) { + this.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + } + } + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + return true; + } + pasteSelection() { + const clip = this.selectionClipboard; + if (!clip) return false; + const targetAnchor = this.hasSelection() ? this.selectionRect : {x: clip.sourceX, y: clip.sourceY}; + const targetX = Math.max(0, Math.min(this.width - 1, targetAnchor.x)); + const targetY = Math.max(0, Math.min(this.height - 1, targetAnchor.y)); + const target = this.currentMask(); + const pastedSelection = new Uint8Array(this.width * this.height); + let changed = false; + for (let y = 0; y < clip.height; y++) { + for (let x = 0; x < clip.width; x++) { + const localIndex = y * clip.width + x; + if (!clip.shape[localIndex]) continue; + const destX = targetX + x; + const destY = targetY + y; + if (destX < 0 || destY < 0 || destX >= this.width || destY >= this.height) continue; + const destIndex = destY * this.width + destX; + pastedSelection[destIndex] = 1; + if (clip.data[localIndex] && !target[destIndex]) { + target[destIndex] = 1; + changed = true; + } + } + } + this.applySelectionMask(pastedSelection); + this.selectionMode = true; + if (changed) timeline.updateFrame(timeline.currentFrame); + this.renderCurrentFrame(); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + return true; + } + selectionTransformRegion() { + if (this.hasSelection()) { + return { + selected: true, + rect: {...this.selectionRect}, + shape: cloneMask(this.selectionMask), + }; + } + return { + selected: false, + rect: {x: 0, y: 0, width: this.width, height: this.height}, + shape: null, + }; + } + applySelectionOrLayerTransform(kind) { + const region = this.selectionTransformRegion(); + const rect = region.rect; + const sourceLayer = cloneMask(this.currentMask()); + const targetLayer = this.currentMask(); + const sourceWidth = rect.width; + const sourceHeight = rect.height; + let destWidth = sourceWidth; + let destHeight = sourceHeight; + let mapSource; + + if (kind === 'shrink' || kind === 'grow') { + const factor = kind === 'grow' ? FLIPNOTE_RESIZE_GROW_FACTOR : FLIPNOTE_RESIZE_SHRINK_FACTOR; + destWidth = Math.max(1, Math.round(sourceWidth * factor)); + destHeight = Math.max(1, Math.round(sourceHeight * factor)); + mapSource = (dx, dy) => ({ + x: Math.min(sourceWidth - 1, Math.floor(dx / factor)), + y: Math.min(sourceHeight - 1, Math.floor(dy / factor)), + }); + } else if (kind === 'flip-x') { + mapSource = (dx, dy) => ({x: sourceWidth - 1 - dx, y: dy}); + } else if (kind === 'flip-y') { + mapSource = (dx, dy) => ({x: dx, y: sourceHeight - 1 - dy}); + } else if (kind === 'rotate-cw') { + destWidth = sourceHeight; + destHeight = sourceWidth; + mapSource = (dx, dy) => ({x: dy, y: sourceHeight - 1 - dx}); + } else if (kind === 'rotate-ccw') { + destWidth = sourceHeight; + destHeight = sourceWidth; + mapSource = (dx, dy) => ({x: sourceWidth - 1 - dy, y: dx}); + } else { + return false; + } + + const sourceCenterX = rect.x + (sourceWidth - 1) / 2; + const sourceCenterY = rect.y + (sourceHeight - 1) / 2; + const destX = Math.round(sourceCenterX - (destWidth - 1) / 2); + const destY = Math.round(sourceCenterY - (destHeight - 1) / 2); + const transformedSelection = region.selected ? new Uint8Array(this.width * this.height) : null; + + if (region.selected) { + // Match selection moving/pasting semantics: remove only ink that was + // inside the selected shape. Empty pixels in the selection do not + // erase unrelated ink already sitting under the transformed result. + for (let i = 0; i < region.shape.length; i++) { + if (region.shape[i] && targetLayer[i]) targetLayer[i] = 0; + } + } else { + targetLayer.fill(0); + } + + for (let dy = 0; dy < destHeight; dy++) { + const canvasY = destY + dy; + if (canvasY < 0 || canvasY >= this.height) continue; + for (let dx = 0; dx < destWidth; dx++) { + const canvasX = destX + dx; + if (canvasX < 0 || canvasX >= this.width) continue; + const source = mapSource(dx, dy); + if (!source || source.x < 0 || source.y < 0 || source.x >= sourceWidth || source.y >= sourceHeight) continue; + const sourceIndex = (rect.y + source.y) * this.width + (rect.x + source.x); + if (region.selected && !region.shape[sourceIndex]) continue; + const destIndex = canvasY * this.width + canvasX; + if (transformedSelection) transformedSelection[destIndex] = 1; + if (sourceLayer[sourceIndex]) targetLayer[destIndex] = 1; + } + } + + if (transformedSelection) { + this.selectionDrag = null; + this.selectionMove = null; + this.applySelectionMask(transformedSelection); + this.selectionMode = true; + this.updateCursorMode(); + } + + let changed = false; + for (let i = 0; i < sourceLayer.length; i++) { + if (sourceLayer[i] !== targetLayer[i]) { + changed = true; + break; + } + } + this.renderCurrentFrame(); + if (changed) timeline.updateFrame(timeline.currentFrame); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + return true; + } + resizeSelectionOrLayer(enlarge = false) { + return this.applySelectionOrLayerTransform(enlarge ? 'grow' : 'shrink'); + } + flipSelectionOrLayer(vertical = false) { + return this.applySelectionOrLayerTransform(vertical ? 'flip-y' : 'flip-x'); + } + rotateSelectionOrLayer(anticlockwise = false) { + return this.applySelectionOrLayerTransform(anticlockwise ? 'rotate-ccw' : 'rotate-cw'); + } + nudgeSelectionOrLayer(dx, dy) { + dx = Math.sign(Number(dx) || 0); + dy = Math.sign(Number(dy) || 0); + if ((!dx && !dy) || (dx && dy)) return false; + + const sourceLayer = cloneMask(this.currentMask()); + const targetLayer = this.currentMask(); + const movingSelection = this.hasSelection(); + const sourceSelection = movingSelection ? cloneMask(this.selectionMask) : null; + const movedSelection = movingSelection ? new Uint8Array(this.width * this.height) : null; + const sourceGeometry = this.selectionTransformGeometry ? {...this.selectionTransformGeometry} : null; + + if (movingSelection) { + // Remove only selected ink from its old position. Like the pointer + // move tool, empty pixels inside the selection do not punch holes in + // unrelated artwork at the destination. + for (let i = 0; i < sourceSelection.length; i++) { + if (sourceSelection[i] && targetLayer[i]) targetLayer[i] = 0; + } + for (let i = 0; i < sourceSelection.length; i++) { + if (!sourceSelection[i]) continue; + const x = i % this.width; + const y = Math.floor(i / this.width); + const nx = x + dx; + const ny = y + dy; + if (nx < 0 || ny < 0 || nx >= this.width || ny >= this.height) continue; + const ni = ny * this.width + nx; + movedSelection[ni] = 1; + if (sourceLayer[i]) targetLayer[ni] = 1; + } + this.selectionDrag = null; + this.selectionMove = null; + this.applySelectionMask(movedSelection); + if (sourceGeometry) this.selectionTransformGeometry = {...sourceGeometry, cx: sourceGeometry.cx + dx, cy: sourceGeometry.cy + dy}; + this.selectionMode = true; + this.updateCursorMode(); + } else { + targetLayer.fill(0); + for (let i = 0; i < sourceLayer.length; i++) { + if (!sourceLayer[i]) continue; + const x = i % this.width; + const y = Math.floor(i / this.width); + const nx = x + dx; + const ny = y + dy; + if (nx < 0 || ny < 0 || nx >= this.width || ny >= this.height) continue; + targetLayer[ny * this.width + nx] = 1; + } + } + + let changed = false; + for (let i = 0; i < sourceLayer.length; i++) { + if (sourceLayer[i] !== targetLayer[i]) { + changed = true; + break; + } + } + this.renderCurrentFrame(); + if (changed) timeline.updateFrame(timeline.currentFrame); + if (typeof updateSelectionToolState === 'function') updateSelectionToolState(); + return true; + } + currentFrame() { + return timeline.getFrame(timeline.currentFrame); + } + currentMask() { + return this.currentFrame().layers[this.activeLayer]; + } + otherMask() { + return this.currentFrame().layers[this.activeLayer === 0 ? 1 : 0]; + } + finishStroke() { + if (!this.drawing) return; + const wasRightErase = this.rightEraseActive; + const previousTool = this.rightErasePreviousTool; + this.drawing = false; + this.rightEraseActive = false; + this.rightErasePreviousTool = null; + this.strokeState = null; + + if (wasRightErase && previousTool) { + this.currentToolFamily = previousTool.family; + this.canvasElement.classList.remove('pen1-cursor', 'pen2-cursor', 'eraser-cursor', 'checker-cursor', 'paint-cursor', 'fill-cursor'); + this.canvasElement.classList.add( + this.currentToolFamily === 'eraser' ? 'eraser-cursor' + : (this.currentToolFamily === 'paint' ? 'paint-cursor' + : (this.currentToolFamily === 'fill' ? 'fill-cursor' : 'pen1-cursor')) + ); + if (typeof retainedToolChoice !== 'undefined' && previousTool.retainedChoice) { + retainedToolChoice = {...previousTool.retainedChoice}; + } + if (typeof updateDrawingToolUI === 'function') updateDrawingToolUI(); + } + + this.updateCursorMode(); + this.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + } + setActiveLayer(index) { + if (this.textTool) this.textTool.finish(); + this.activeLayer = index === 1 ? 1 : 0; + updateLayerPanel(); + if (this.pointerInsideCanvas && !this.drawing) this.renderCurrentFrame(); + } + setLayerColor(layerIndex, color) { + if (!FLIPNOTE_INKS[color]) return; + const frame = this.currentFrame(); + frame.colors[layerIndex] = color; + this.activeLayer = layerIndex; + this.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + updateLayerPanel(); + } + cycleActiveLayerColor() { + const frame = this.currentFrame(); + const oldColor = frame.colors[this.activeLayer]; + const next = FLIPNOTE_INK_ORDER[(FLIPNOTE_INK_ORDER.indexOf(oldColor) + 1) % FLIPNOTE_INK_ORDER.length]; + this.setLayerColor(this.activeLayer, next); + } + cycleLastToolFamily() { + // Each family already remembers its own slot and Advanced choice. T simply + // walks through those retained tools in the editor's familiar order. + const order = ['pen', 'paint', 'fill', 'eraser']; + const currentIndex = order.indexOf(this.currentToolFamily); + const nextFamily = order[(currentIndex + 1 + order.length) % order.length]; + this.setToolFamily(nextFamily); + } + activeToolAdvanced(family = this.currentToolFamily) { + if (family === 'fill') return false; + return !!(this.expertTools || this.selectedToolAdvanced[family]); + } + toolEngineOptions(family = this.currentToolFamily) { + return { + paintState: this.activeToolAdvanced('paint') && family === 'paint' ? 1 : 0, + motif: this.customPaintMotif, + eraserPatternId: this.lastPaintPatternSlot, + }; + } + logicalToolId(family = this.currentToolFamily) { + // Fill has no separate Shift/Advanced geometry: its eight slots are the + // seven Paint patterns plus the solid Fill 8 bucket. + if (family === 'fill') return this.selectedToolSlots.fill; + return this.engine.logicalId( + this.selectedToolSlots[family], + this.activeToolAdvanced(family) + ); + } + setToolFamily(family) { + if (this.textTool) this.textTool.disable(); + if (this.selectionMode || this.hasSelection()) this.clearSelection(); + this.currentToolFamily = ['pen', 'paint', 'eraser', 'fill'].includes(family) ? family : 'pen'; + this.canvasElement.classList.remove('pen1-cursor', 'pen2-cursor', 'eraser-cursor', 'checker-cursor', 'paint-cursor', 'fill-cursor'); + this.canvasElement.classList.add( + this.currentToolFamily === 'eraser' ? 'eraser-cursor' + : (this.currentToolFamily === 'paint' ? 'paint-cursor' + : (this.currentToolFamily === 'fill' ? 'fill-cursor' : 'pen1-cursor')) + ); + if (typeof retainCurrentToolChoice === 'function') retainCurrentToolChoice(); + if (typeof updateDrawingToolUI === 'function') updateDrawingToolUI(); + this.updateCursorMode(); + if (this.pointerInsideCanvas && !this.drawing) this.renderCurrentFrame(); + } + setToolSlot(slot, advanced = this.expertTools) { + if (this.textTool) this.textTool.disable(); + const safeSlot = Math.max(0, Math.min(7, Math.round(Number(slot) || 0))); + this.selectedToolSlots[this.currentToolFamily] = safeSlot; + if (this.currentToolFamily === 'paint') this.lastPaintPatternSlot = safeSlot; + if (this.currentToolFamily === 'eraser') this.hasRetainedEraserChoice = true; + this.selectedToolAdvanced[this.currentToolFamily] = this.currentToolFamily === 'fill' ? false : !!advanced; + if (typeof retainCurrentToolChoice === 'function') retainCurrentToolChoice(); + if (typeof updateDrawingToolUI === 'function') updateDrawingToolUI(); + this.updateCursorMode(); + if (this.pointerInsideCanvas && !this.drawing) this.renderCurrentFrame(); + } + setExpertTools(enabled) { + const next = !!enabled; + if (this.expertTools === next) return; + this.expertTools = next; + if (typeof updateDrawingToolUI === 'function') updateDrawingToolUI(); + this.updateCursorMode(); + if (this.pointerInsideCanvas && !this.drawing) this.renderCurrentFrame(); + } + // Compatibility wrapper for old buttons/third-party scripts that used the + // restored legacy tool names. + setTool(tool) { + if (this.textTool) this.textTool.disable(); + if (tool === 'pen2') { + this.currentToolFamily = 'pen'; + this.selectedToolSlots.pen = 1; + this.selectedToolAdvanced.pen = false; + } else if (tool === 'checker') { + this.currentToolFamily = 'paint'; + this.selectedToolSlots.paint = 4; + this.selectedToolAdvanced.paint = false; + } else if (tool === 'eraser') { + this.currentToolFamily = 'eraser'; + this.selectedToolAdvanced.eraser = false; + this.hasRetainedEraserChoice = true; + } else { + this.currentToolFamily = 'pen'; + this.selectedToolSlots.pen = 0; + this.selectedToolAdvanced.pen = false; + } + this.setToolFamily(this.currentToolFamily); + } + setMaskPixel(x, y, value) { + if (x < 0 || y < 0 || x >= this.width || y >= this.height) return; + this.currentMask()[y * this.width + x] = value ? 1 : 0; + } + drawPixel(x, y) { + this.strokeState = this.engine.createStrokeState(); + this.engine.applyStrokeStart(this.currentMask(), this.currentToolFamily, this.logicalToolId(), x, y, this.strokeState, this.toolEngineOptions()); + } + drawLine(x0, y0, x1, y1) { + if (!this.strokeState) this.strokeState = this.engine.createStrokeState(); + this.engine.applyStrokeSegment(this.currentMask(), this.currentToolFamily, this.logicalToolId(), x0, y0, x1, y1, this.strokeState, this.toolEngineOptions()); + } + renderFrameInk(frame, ghost = false, opacity = 1) { + if (!frame) return; + const pixels = new Uint8ClampedArray(this.width * this.height * 4); + const layer1Color = resolveFrameInk(frame, frame.colors[0]); + const layer2Color = resolveFrameInk(frame, frame.colors[1]); + const ghostColor = [96, 96, 96]; + for (let i = 0; i < this.width * this.height; i++) { + let color = null; + if (editorLayerVisibility[0] && frame.layers[0][i]) color = ghost ? ghostColor : layer1Color; + else if (editorLayerVisibility[1] && frame.layers[1][i]) color = ghost ? ghostColor : layer2Color; + if (!color) continue; + const p = i * 4; + pixels[p] = color[0]; + pixels[p + 1] = color[1]; + pixels[p + 2] = color[2]; + pixels[p + 3] = 255; + } + this.inkBufferCtx.clearRect(0, 0, this.width, this.height); + this.inkBufferCtx.putImageData(new ImageData(pixels, this.width, this.height), 0, 0); + this.ctx.save(); + this.ctx.globalAlpha = opacity; + this.ctx.drawImage(this.inkBuffer, 0, 0); + this.ctx.restore(); + } + buildFillPreviewMask(startX, startY) { + const preview = this.cursorPreviewMask; + preview.fill(0); + const layer = this.currentMask(); + const sx = Math.round(startX); + const sy = Math.round(startY); + if (sx < 0 || sy < 0 || sx >= this.width || sy >= this.height) return preview; + const startIndex = sy * this.width + sx; + if (layer[startIndex]) return preview; + + const queue = new Int32Array(this.width * this.height); + let head = 0; + let tail = 0; + queue[tail++] = startIndex; + preview[startIndex] = 1; + while (head < tail) { + const index = queue[head++]; + const x = index % this.width; + const y = Math.floor(index / this.width); + if (x > 0) { + const next = index - 1; + if (!preview[next] && !layer[next]) { preview[next] = 1; queue[tail++] = next; } + } + if (x + 1 < this.width) { + const next = index + 1; + if (!preview[next] && !layer[next]) { preview[next] = 1; queue[tail++] = next; } + } + if (y > 0) { + const next = index - this.width; + if (!preview[next] && !layer[next]) { preview[next] = 1; queue[tail++] = next; } + } + if (y + 1 < this.height) { + const next = index + this.width; + if (!preview[next] && !layer[next]) { preview[next] = 1; queue[tail++] = next; } + } + } + return preview; + } + buildCursorPreviewMask() { + const family = this.rightEraseActive ? 'eraser' : this.currentToolFamily; + if (family === 'fill') { + return this.buildFillPreviewMask(this.cursorPosition.x, this.cursorPosition.y); + } + const preview = this.cursorPreviewMask; + const erasing = family === 'eraser'; + preview.fill(erasing ? 1 : 0); + const logicalId = this.logicalToolId(family); + // Advanced Erase keeps the same outer geometry as the matching regular + // Eraser, while candidate pixels mirror the most recently used Paint + // texture. Preview the actual filtered mask; renderCursorPreview() + // still gives Advanced Erase its spaced outline treatment. + const previewLogicalId = logicalId; + const randomState = this.engine.getRandomState ? this.engine.getRandomState() : null; + try { + this.engine.applyStrokeStart( + preview, + family, + previewLogicalId, + this.cursorPosition.x, + this.cursorPosition.y, + this.engine.createStrokeState(), + this.toolEngineOptions(family) + ); + } finally { + if (randomState !== null && this.engine.resetRandom) this.engine.resetRandom(randomState); + } + if (erasing) { + for (let i = 0; i < preview.length; i++) preview[i] = preview[i] ? 0 : 1; + } + return preview; + } + cursorUsesCrosshair() { + // Brush/nib tools use the editor's pixel-outline preview instead of the + // browser crosshair. Right-click temporarily forces the Eraser preview, + // even if Select or Fill is otherwise active. + if (this.rightEraseActive) return false; + return this.selectionMode || this.currentToolFamily === 'fill'; + } + updateCursorMode() { + this.canvasElement.classList.toggle('flipnote-brush-preview-crosshair', this.cursorUsesCrosshair()); + this.canvasElement.classList.toggle('flipnote-text-cursor', !!(this.textTool && this.textTool.enabled && !this.rightEraseActive)); + } + renderCursorPreview() { + if (this.textTool && this.textTool.enabled && !this.rightEraseActive) return; + // Keep the +1px brush outline visible while an Eraser stroke is actively + // held down. Other drawing tools retain the original pen-down behavior and + // hide their preview until the stroke ends. + const family = this.rightEraseActive ? 'eraser' : this.currentToolFamily; + if (!this.pointerInsideCanvas || (this.selectionMode && !this.rightEraseActive) || (this.drawing && family !== 'eraser') || !this.allowEditing || timeline.playing) return; + const frame = this.currentFrame(); + if (!frame) return; + const preview = this.buildCursorPreviewMask(); + const ink = resolveFrameInk(frame, frame.colors[this.activeLayer]); + + // The guide should sit one pixel outside the pixels that the selected + // tool would actually affect. Dilate the exact preview mask by one + // canvas pixel in every direction, then draw only that enlarged mask's + // outer boundary. This gives every nib/fill region a true +1px outline + // without changing the underlying drawing operation at all. + const enlarged = new Uint8Array(preview.length); + for (let i = 0; i < preview.length; i++) { + if (!preview[i]) continue; + const x = i % this.width; + const y = Math.floor(i / this.width); + const minY = Math.max(0, y - 1); + const maxY = Math.min(this.height - 1, y + 1); + const minX = Math.max(0, x - 1); + const maxX = Math.min(this.width - 1, x + 1); + for (let yy = minY; yy <= maxY; yy++) { + const row = yy * this.width; + for (let xx = minX; xx <= maxX; xx++) enlarged[row + xx] = 1; + } + } + + this.ctx.save(); + const normalOutline = `rgb(${ink[0]}, ${ink[1]}, ${ink[2]})`; + const layer1Color = resolveFrameInk(frame, frame.colors[0]); + const layer2Color = resolveFrameInk(frame, frame.colors[1]); + const outlineStyleAt = (index) => { + // If the guide crosses existing ink, draw the exact RGB inverse of + // the visible drawn pixel at full opacity. That keeps the cursor + // readable even when its normal guide colour matches the artwork. + let drawn = null; + if (frame.layers[0][index]) drawn = layer1Color; + else if (frame.layers[1][index]) drawn = layer2Color; + if (!drawn) return {style: normalOutline, alpha: 0.5}; + return { + style: `rgb(${255 - drawn[0]}, ${255 - drawn[1]}, ${255 - drawn[2]})`, + alpha: 1, + }; + }; + const advancedEraserOutline = family === 'eraser' && this.logicalToolId(family) >= 8; + let firstBoundary = -1; + let painted = 0; + for (let i = 0; i < enlarged.length; i++) { + if (!enlarged[i]) continue; + const x = i % this.width; + const y = Math.floor(i / this.width); + const boundary = x === 0 || y === 0 || x === this.width - 1 || y === this.height - 1 + || !enlarged[i - 1] || !enlarged[i + 1] + || (y > 0 && !enlarged[i - this.width]) + || (y + 1 < this.height && !enlarged[i + this.width]); + if (!boundary) continue; + if (firstBoundary < 0) firstBoundary = i; + // Regular tools use a continuous enlarged outline. Advanced Erase + // uses the same outer Eraser geometry, but with two-pixel gaps so + // the sparse/filtered erase mode remains visually distinct even + // though the +1px guide dilation would otherwise close its holes. + if (advancedEraserOutline && ((x + y) & 3) >= 2) continue; + const guide = outlineStyleAt(i); + this.ctx.globalAlpha = guide.alpha; + this.ctx.fillStyle = guide.style; + this.ctx.fillRect(x, y, 1, 1); + painted++; + } + if (!painted && firstBoundary >= 0) { + const guide = outlineStyleAt(firstBoundary); + this.ctx.globalAlpha = guide.alpha; + this.ctx.fillStyle = guide.style; + this.ctx.fillRect(firstBoundary % this.width, Math.floor(firstBoundary / this.width), 1, 1); + } + this.ctx.restore(); + } + renderSelectionOverlay() { + const drag = this.selectionDrag; + const phase = Math.floor(performance.now() / 100) % 2; + this.ctx.save(); + this.ctx.fillStyle = FLIPNOTE_SELECTION_YELLOW; + + const drawRect = (rect) => { + if (!rect) return; + const left = rect.x; + const top = rect.y; + const right = rect.x + rect.width - 1; + const bottom = rect.y + rect.height - 1; + for (let x = left; x <= right; x++) { + if (((x + phase) & 1) !== 0) continue; + this.ctx.fillRect(x, top, 1, 1); + if (bottom !== top) this.ctx.fillRect(x, bottom, 1, 1); + } + for (let y = top; y <= bottom; y++) { + if (((y + phase) & 1) !== 0) continue; + this.ctx.fillRect(left, y, 1, 1); + if (right !== left) this.ctx.fillRect(right, y, 1, 1); + } + }; + + if (drag && drag.type === 'lasso' && drag.points && drag.points.length) { + const points = drag.points; + this.ctx.lineWidth = 1; + this.ctx.lineJoin = 'round'; + this.ctx.lineCap = 'butt'; + this.ctx.beginPath(); + this.ctx.moveTo(points[0].x + 0.5, points[0].y + 0.5); + for (let i = 1; i < points.length; i++) this.ctx.lineTo(points[i].x + 0.5, points[i].y + 0.5); + this.ctx.strokeStyle = FLIPNOTE_SELECTION_YELLOW; + this.ctx.setLineDash([1, 1]); + this.ctx.lineDashOffset = -phase; + this.ctx.stroke(); + this.ctx.restore(); + return; + } + + if (drag && drag.type === 'rect') { + drawRect(this.selectionRect); + this.ctx.restore(); + return; + } + + if (!this.hasSelection()) { + this.ctx.restore(); + return; + } + + // Retained selections use the same one-pixel dotted yellow outline. + // Only the "on" ant pixel is painted; the gap is left transparent so + // there is never a black/white companion pixel underneath it. + const mask = this.selectionMask; + for (let i = 0; i < mask.length; i++) { + if (!mask[i]) continue; + const x = i % this.width; + const y = Math.floor(i / this.width); + const boundary = x === 0 || y === 0 || x === this.width - 1 || y === this.height - 1 + || !mask[i - 1] || !mask[i + 1] + || (y > 0 && !mask[i - this.width]) + || (y + 1 < this.height && !mask[i + this.width]); + if (!boundary || ((x + y + phase) & 1) !== 0) continue; + this.ctx.fillRect(x, y, 1, 1); + } + if (this.rectangleSelectionMode) { + const handles = this.selectionTransformHandles(); + const rotate = handles.find((handle) => handle.name === 'rotate'); + const north = handles.find((handle) => handle.name === 'n'); + if (rotate && north) { + this.ctx.beginPath(); + this.ctx.moveTo(north.x + .5, north.y + .5); + this.ctx.lineTo(rotate.x + .5, rotate.y + .5); + this.ctx.strokeStyle = '#7c291d'; + this.ctx.lineWidth = 1; + this.ctx.setLineDash([]); + this.ctx.stroke(); + } + for (const handle of handles) { + if (handle.name === 'rotate') { + this.ctx.beginPath(); + this.ctx.arc(handle.x + .5, handle.y + .5, 3.5, 0, Math.PI * 2); + this.ctx.fillStyle = '#fffbe5'; + this.ctx.fill(); + this.ctx.strokeStyle = '#7c291d'; + this.ctx.lineWidth = 1; + this.ctx.stroke(); + } else { + this.ctx.fillStyle = '#fffbe5'; + this.ctx.fillRect(handle.x - 2, handle.y - 2, 5, 5); + this.ctx.strokeStyle = '#7c291d'; + this.ctx.lineWidth = 1; + this.ctx.strokeRect(handle.x - 2.5, handle.y - 2.5, 5, 5); + } + } + } + this.ctx.restore(); + } + renderCurrentFrame() { + const frame = typeof timeline !== 'undefined' ? timeline.getFrame(timeline.currentFrame) : null; + if (!frame) return; + this.ctx.save(); + this.ctx.globalAlpha = 1; + const paper = resolveFramePaper(frame); + this.ctx.fillStyle = `rgb(${paper[0]}, ${paper[1]}, ${paper[2]})`; + this.ctx.fillRect(0, 0, this.width, this.height); + this.ctx.restore(); + + // Authentic DSi-style Light Box: previous pages only, oldest first, + // faint neutral traces, and never part of the actual page data. + if (lightboxEnabled && !this.suppressLightbox && !timeline.playing) { + const depth = pagesToLightbox(); + for (let offset = depth; offset >= 1; offset--) { + const previousIndex = timeline.currentFrame - offset; + if (previousIndex < 0) continue; + const ageFactor = 1 - ((offset - 1) / Math.max(depth, 1)); + const opacity = 0.08 + (0.12 * ageFactor); + this.renderFrameInk(timeline.getFrame(previousIndex), true, opacity); + } + } + this.renderFrameInk(frame, false, 1); + this.renderSelectionOverlay(); + this.renderCursorPreview(); + if (this.textTool) this.textTool.renderCaret(); + } + setCanvasData(data) { + if (data instanceof ImageData) this.ctx.putImageData(data, 0, 0); + else this.renderCurrentFrame(); + } + clearCurrentFrame(bothLayers = false) { + if (!this.allowEditing || timeline.playing) return; + if (this.hasSelection()) { + const layers = bothLayers ? this.currentFrame().layers : [this.currentMask()]; + for (const layer of layers) { + for (let i = 0; i < this.selectionMask.length; i++) if (this.selectionMask[i]) layer[i] = 0; + } + } else { + this.currentMask().fill(0); + } + this.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + } + toggleEditing() {} + getCanvasCoords(e) { + const rect = this.canvasElement.getBoundingClientRect(); + const scaleX = this.canvasElement.width / rect.width; + const scaleY = this.canvasElement.height / rect.height; + return { + x: Math.max(0, Math.min(this.width - 1, Math.floor((e.clientX - rect.left) * scaleX))), + y: Math.max(0, Math.min(this.height - 1, Math.floor((e.clientY - rect.top) * scaleY))), + }; + } +} + +class ExportManager { + constructor() { + } + + async exportAsWebm(onProgress) { + const progress = typeof onProgress === 'function' ? onProgress : () => {}; + if (!timeline || !timeline.frames || !timeline.frames.length) throw new Error('Add at least one page before exporting a WEBM.'); + if (typeof MediaRecorder === 'undefined') throw new Error('This browser does not support WEBM recording.'); + if (!canvas.canvasElement.captureStream) throw new Error('This browser cannot capture the Flipnote canvas.'); + + const originalFrame = timeline.currentFrame; + const totalFrames = timeline.frames.length; + const fps = Math.max(0.01, Number(timeline.framerate) || 12); + const frameDurationMs = 1000 / fps; + const exportDurationMs = frameDurationMs * totalFrames; + let timer = null; + let canvasStream = null; + let exportStream = null; + let audioDestination = null; + let audioContext = null; + const exportAudioSources = new Set(); + let cleanedUp = false; + + const cleanup = () => { + if (cleanedUp) return; + cleanedUp = true; + if (timer) clearTimeout(timer); + timer = null; + exportAudioSources.forEach((source) => { + source.onended = null; + try { source.stop(); } catch (error) {} + try { source.disconnect(); } catch (error) {} + }); + exportAudioSources.clear(); + if (exportStream) exportStream.getTracks().forEach((track) => { + try { track.stop(); } catch (error) {} + }); + if (canvasStream && canvasStream !== exportStream) canvasStream.getTracks().forEach((track) => { + try { track.stop(); } catch (error) {} + }); + canvas.suppressLightbox = false; + timeline.setActiveFrame(Math.min(originalFrame, timeline.frames.length - 1)); + }; + + canvas.suppressLightbox = true; + canvas.renderCurrentFrame(); + progress(2, 'Preparing WEBM renderer...'); + + try { + // Decode any editor audio before recording starts. WEBM export uses a + // private Web Audio destination, so the soundtrack is muxed into the + // video without playing through the user's speakers during export. + const exportBuffers = {}; + const audioKeys = ['music', 'sound1', 'sound2', 'sound3']; + const hasProjectAudio = typeof audioTracks !== 'undefined' && audioKeys.some((key) => !!audioTracks[key]); + if (hasProjectAudio) { + progress(4, 'Preparing audio...'); + audioContext = getFlipnoteAudioContext(); + try { await audioContext.resume(); } catch (error) {} + for (const key of audioKeys) { + if (!audioTracks[key]) continue; + try { + const buffer = await ensureAudioTrackBuffer(key); + if (buffer) exportBuffers[key] = buffer; + } catch (error) { + // A single broken optional track should not prevent the + // animation itself from being exported. + } + } + } + + canvasStream = canvas.canvasElement.captureStream(fps); + const tracks = [...canvasStream.getVideoTracks()]; + if (audioContext && Object.keys(exportBuffers).length) { + audioDestination = audioContext.createMediaStreamDestination(); + tracks.push(...audioDestination.stream.getAudioTracks()); + } + exportStream = new MediaStream(tracks); + + const mimeCandidates = [ + 'video/webm;codecs=vp9,opus', + 'video/webm;codecs=vp8,opus', + 'video/webm', + ]; + const mimeType = typeof MediaRecorder.isTypeSupported === 'function' + ? (mimeCandidates.find((candidate) => MediaRecorder.isTypeSupported(candidate)) || '') + : 'video/webm'; + if (!mimeType) throw new Error('This browser cannot encode WEBM video.'); + const mediaRecorder = new MediaRecorder(exportStream, { mimeType }); + const chunks = []; + + const connectExportSource = (source) => { + if (!audioDestination) return false; + source.connect(audioDestination); + exportAudioSources.add(source); + source.onended = () => { + exportAudioSources.delete(source); + try { source.disconnect(); } catch (error) {} + }; + return true; + }; + + const scheduleExportAudio = (startTime) => { + if (!audioContext || !audioDestination) return; + const animationSeconds = totalFrames / fps; + const musicTrack = audioTracks.music; + const musicBuffer = exportBuffers.music; + if (musicTrack && musicBuffer) { + const source = audioContext.createBufferSource(); + source.buffer = musicBuffer; + const playbackRate = audioTrackPlaybackRate(musicTrack, 'music'); + source.playbackRate.value = playbackRate; + const trackDuration = Math.min(Number(musicTrack.duration) || musicBuffer.duration, musicBuffer.duration); + const rawDuration = Math.max(0, Math.min(trackDuration, animationSeconds * playbackRate)); + if (rawDuration > 0.001 && connectExportSource(source)) { + source.start(startTime, 0, rawDuration); + } + } + + ['sound1', 'sound2', 'sound3'].forEach((key) => { + const track = audioTracks[key]; + const buffer = exportBuffers[key]; + if (!track || !buffer) return; + const clipDuration = Math.max(0, Math.min(Number(track.duration) || buffer.duration, buffer.duration)); + if (clipDuration <= 0.001) return; + timeline.frames.forEach((frame, frameIndex) => { + if (!frameHasSound(frame, key)) return; + const triggerSeconds = frameIndex / fps; + if (triggerSeconds >= animationSeconds) return; + const source = audioContext.createBufferSource(); + source.buffer = buffer; + source.playbackRate.value = 1; + if (connectExportSource(source)) { + source.start(startTime + triggerSeconds, 0, Math.min(clipDuration, animationSeconds - triggerSeconds)); + } + }); + }); + }; + + return await new Promise((resolve, reject) => { + let exportFrame = 0; + let stopped = false; + const stopRecorder = () => { + if (stopped) return; + stopped = true; + if (timer) clearTimeout(timer); + timer = null; + if (mediaRecorder.state !== 'inactive') mediaRecorder.stop(); + }; + + mediaRecorder.ondataavailable = (event) => { + if (event.data && event.data.size > 0) chunks.push(event.data); + }; + mediaRecorder.onerror = (event) => { + cleanup(); + reject(new Error((event && event.error && event.error.message) || 'The browser could not render this WEBM.')); + }; + mediaRecorder.onstop = () => { + try { + const blob = new Blob(chunks, { type: mimeType }); + if (!blob.size) throw new Error('The WEBM renderer returned an empty video.'); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + const filenameText = document.getElementById('filename').textContent.trim() || 'untitled'; + link.href = url; + link.download = filenameText + '.webm'; + document.body.appendChild(link); + link.click(); + link.remove(); + setTimeout(() => URL.revokeObjectURL(url), 1000); + cleanup(); + progress(100, 'WEBM saved.'); + resolve(blob); + } catch (error) { + cleanup(); + reject(error); + } + }; + + timeline.setActiveFrame(0); + canvas.renderCurrentFrame(); + progress(8, `Rendering page 1 / ${totalFrames}...`); + mediaRecorder.start(250); + + // Record at the Flipnote's actual speed. The old exporter used a + // hard-coded 100 ms delay, which silently forced every WEBM to + // 10 FPS and made Speed 6/7/8 Flipnotes play noticeably slowly. + const wallStart = performance.now(); + const audioStart = audioContext ? audioContext.currentTime : 0; + scheduleExportAudio(audioStart); + + const tick = () => { + const elapsed = performance.now() - wallStart; + const targetFrame = Math.min(totalFrames - 1, Math.floor(elapsed / frameDurationMs)); + if (targetFrame > exportFrame) { + exportFrame = targetFrame; + timeline.setActiveFrame(exportFrame); + canvas.renderCurrentFrame(); + const pct = 8 + Math.round(((exportFrame + 1) / totalFrames) * 86); + progress(Math.min(94, pct), `Rendering page ${exportFrame + 1} / ${totalFrames}...`); + } + if (elapsed >= exportDurationMs) { + stopRecorder(); + return; + } + const nextBoundary = (exportFrame + 1) * frameDurationMs; + const wait = Math.max(1, Math.min(16, nextBoundary - elapsed)); + timer = setTimeout(tick, wait); + }; + timer = setTimeout(tick, Math.max(1, Math.min(16, frameDurationMs))); + }); + } catch (error) { + cleanup(); + throw error; + } + } +} +// The Flipnote Editor intentionally owns right-click/long-click interactions. +// Suppress native browser context menus anywhere on the editor page, including +// dialogs and controls rendered outside the main workspace node. +document.addEventListener('contextmenu', (event) => { + if (document.querySelector('[data-legacy-flipnote-editor]')) event.preventDefault(); +}, true); + +const canvas = new CanvasEditor(document.getElementById('flipnote-canvas')); +canvas.updateCursorMode(); +const timeline = new Timeline(document.getElementById('timeline-container')); +canvas.textTool = new window.FlipnoteTextTool(canvas, timeline, { + canStoreFrame: (frame) => flipnoteSpaceAllowsProjection(timeline, { + animationFrameBytes: flipnoteSpaceEncodedFrameBytes + - (flipnoteSpaceFrameCache.get(frame) || flipnoteSpaceFrameByteLength(frame)) + + flipnoteSpaceFrameByteLength(frame), + }), + notifyFull: () => notifyFlipnoteSpaceLimit(timeline), + markDirty: markEditorDirty, +}); +const exportManager = new ExportManager(); +updatePlaybackSettingsUI(timeline); +syncEditorPlaybackState(timeline); + +// Never keep editor playback running after the user leaves the page/window or +// touches Alt. Returning to the editor never resumes automatically. Alt is an +// interruption safeguard only, not an editor shortcut, so it is intentionally +// omitted from the shortcut/help UI. +function pauseFlipnoteEditorPlayback() { + if (timeline.playing) timeline.stopPlayback(); +} + +document.addEventListener('visibilitychange', () => { + if (document.visibilityState !== 'hidden') return; + pauseFlipnoteEditorPlayback(); + // Standalone Music/Sound previews are playback too; stop those without + // affecting an in-progress microphone recording. + if (typeof stopAudioPlayback === 'function') stopAudioPlayback(); + if (typeof stopTrimPreview === 'function') stopTrimPreview(); +}); +window.addEventListener('blur', pauseFlipnoteEditorPlayback); +window.addEventListener('keydown', (event) => { + if (event.key === 'Alt' || event.code === 'AltLeft' || event.code === 'AltRight') { + pauseFlipnoteEditorPlayback(); + } +}, true); + +const speedControl = document.getElementById('flipnote-speed-control'); +const loopToggle = document.getElementById('flipnote-loop-toggle'); +let speedPointerActive = false; + +function speedLevelFromPointer(event) { + if (!speedControl) return null; + const rect = speedControl.getBoundingClientRect(); + if (!rect.width || !rect.height) return null; + const x = ((event.clientX - rect.left) / rect.width) * 94; + const y = ((event.clientY - rect.top) / rect.height) * 20; + const hit = FLIPNOTE_SPEED_HITBOXES.find((box) => + x >= box.x && x < box.x + box.width && y >= box.y && y < box.y + box.height + ); + return hit ? hit.level : null; +} + +function applySpeedPointer(event) { + if (timeline.playing) return; + const level = speedLevelFromPointer(event); + if (level !== null) timeline.setSpeedLevel(level); +} + +if (speedControl) { + speedControl.addEventListener('pointerdown', (event) => { + speedPointerActive = true; + const workspace = document.querySelector('[data-legacy-flipnote-editor]'); + if (workspace) workspace.classList.add('is-changing-speed'); + speedControl.setPointerCapture(event.pointerId); + applySpeedPointer(event); + event.preventDefault(); + }); + speedControl.addEventListener('pointermove', (event) => { + if (!speedPointerActive) return; + applySpeedPointer(event); + event.preventDefault(); + }); + const finishSpeedPointer = (event) => { + if (!speedPointerActive) return; + speedPointerActive = false; + const workspace = document.querySelector('[data-legacy-flipnote-editor]'); + if (workspace) workspace.classList.remove('is-changing-speed'); + if (speedControl.hasPointerCapture(event.pointerId)) speedControl.releasePointerCapture(event.pointerId); + }; + speedControl.addEventListener('pointerup', finishSpeedPointer); + speedControl.addEventListener('pointercancel', finishSpeedPointer); + speedControl.addEventListener('keydown', (event) => { + if (timeline.playing) return; + const action = shortcutAction(event, 'speed'); + if (!action) return; + let next = timeline.speedLevel; + if (action === 'speedDown' || action === 'speedDownAlt') next -= 1; + else if (action === 'speedUp' || action === 'speedUpAlt') next += 1; + else if (action === 'speedFirst') next = 1; + else if (action === 'speedLast') next = 8; + event.preventDefault(); + event.stopPropagation(); + timeline.setSpeedLevel(next); + }); +} +if (loopToggle) { + loopToggle.addEventListener('click', () => { + if (timeline.playing) return; + timeline.setLoopEnabled(!timeline.loopEnabled); + }); +} +// Drawing tools. The four family buttons open an image-based eight-slot picker. +// Holding Shift temporarily exposes the Advanced logical IDs (slot + 8). +const toolFamilyButtons = Array.from(document.querySelectorAll('[data-tool-family-picker]')); +const toolSlotsPanel = document.getElementById('flipnote-tool-slots'); +const toolSlotButtons = Array.from(document.querySelectorAll('[data-tool-slot]')); +const editPatternBtn = document.getElementById('flipnote-edit-pattern'); +const patternModal = document.getElementById('flipnote-pattern-modal'); +const patternGrid = document.getElementById('flipnote-pattern-grid'); +const patternCloseBtn = document.getElementById('flipnote-pattern-close'); +const patternCancelBtn = document.getElementById('flipnote-pattern-cancel'); +const patternSaveBtn = document.getElementById('flipnote-pattern-save'); +const patternClearBtn = document.getElementById('flipnote-pattern-clear'); +const patternFillBtn = document.getElementById('flipnote-pattern-fill'); +const patternInvertBtn = document.getElementById('flipnote-pattern-invert'); +const patternResetBtn = document.getElementById('flipnote-pattern-reset'); +const undoBtn = document.getElementById('undoButton'); +const redoBtn = document.getElementById('redoButton'); +const eraseBigBtn = document.getElementById('eraseBigButton'); + + + + + + + + + + +const ppmImportButton = document.getElementById('flipnote-open-ppm-button'); +const ppmImportInput = document.getElementById('flipnote-import-ppm-input'); + +const ppmImportStatus = document.getElementById('flipnote-import-ppm-status'); + + + + + + + + + + + + + +const webmExportModal = document.getElementById('flipnote-webm-export-modal'); +const webmExportClose = document.getElementById('flipnote-webm-export-close'); +const webmExportCancel = document.getElementById('flipnote-webm-export-cancel'); +const webmExportStart = document.getElementById('flipnote-webm-export-start'); +const webmExportProgress = document.getElementById('flipnote-webm-export-progress'); +const webmExportProgressFill = document.getElementById('flipnote-webm-export-progress-fill'); +const webmExportStatus = document.getElementById('flipnote-webm-export-status'); +const webmExportThumbnailTimeline = document.getElementById('flipnote-webm-export-thumbnail-timeline'); +const openPpmModal = document.getElementById('flipnote-open-ppm-modal'); +const openPpmClose = document.getElementById('flipnote-open-ppm-close'); +const openPpmCancel = document.getElementById('flipnote-open-ppm-cancel'); +const openPpmChoice = document.getElementById('flipnote-open-ppm-choice'); + +const openUploadLabel = document.getElementById('flipnote-open-upload'); + + + + + + + + + + + + + + + + + +let webmExporting = false; +let webmExportLastFocus = null; +let openPpmLastFocus = null; + + + +let exportThumbnailFrameRef = null; +let exportThumbnailFrameIndex = null; + + + +let openToolPickerFamily = null; +let lastToolPickerFamily = null; +let retainedToolChoice = { + family: canvas.currentToolFamily, + slot: canvas.selectedToolSlots[canvas.currentToolFamily], + advanced: canvas.currentToolFamily === 'fill' ? false : !!canvas.selectedToolAdvanced[canvas.currentToolFamily], +}; +let toolPickerAnimationTimer = null; +let toolPickerTransitionToken = 0; +let toolPickerTransitioning = false; +let editPatternAnimationTimer = null; + +const TOOL_PICKER_ANIMATION_MS = 275; + +const TOOL_FAMILY_ASSET_PREFIX = { + pen: 'pen', + paint: 'paint', + eraser: 'erase', + fill: 'fill', +}; + +const TOOL_FAMILY_LABEL = { + pen: 'Draw', + paint: 'Paint', + eraser: 'Erase', + fill: 'Fill', +}; + +// The Fill picker is intentionally ordered with the solid bucket first: +// Fill 8, then Fill 1 through Fill 7. Other families keep their normal 1-8 +// visual order. The returned value is the engine/tool slot (0-7), not the +// visual grid position. +function displaySlotToToolSlot(family, displaySlot) { + const visual = Math.max(0, Math.min(7, Number(displaySlot) || 0)); + if (family === 'fill') return visual === 0 ? 7 : visual - 1; + return visual; +} + +function toolChoiceAsset(family, slot, advanced) { + const prefix = TOOL_FAMILY_ASSET_PREFIX[family] || 'pen'; + // Fill does not have an Advanced/Shift palette: all eight buckets are + // fixed, with Fill 8 reserved for solid fill. + const advancedSuffix = family === 'fill' ? '' : (advanced ? '_Adv' : ''); + return `./assets/flipnote-editor/${prefix}${slot + 1}${advancedSuffix}.png`; +} + +function retainCurrentToolChoice() { + if (typeof canvas === 'undefined' || !canvas.selectedToolSlots) return; + const family = canvas.currentToolFamily; + if (!TOOL_FAMILY_LABEL[family]) return; + retainedToolChoice = { + family, + slot: canvas.selectedToolSlots[family] || 0, + advanced: family === 'fill' ? false : !!canvas.selectedToolAdvanced[family], + }; +} + +// Selected tool icons use the DSi-style selected palette without requiring a +// second copy of every PNG. Only exact source pixels are remapped: +// #FFFFFF -> #FBF318 +// #AE301F -> #000000 +// Everything else, including transparency and anti-aliased edge colours, is +// left untouched. Generated variants are cached for the lifetime of the page. +const selectedToolIconAssetCache = new Map(); + +function selectedToolChoiceAsset(sourceUrl) { + if (selectedToolIconAssetCache.has(sourceUrl)) { + return selectedToolIconAssetCache.get(sourceUrl); + } + + const result = new Promise((resolve) => { + const source = new Image(); + source.onload = () => { + try { + const recolorCanvas = document.createElement('canvas'); + recolorCanvas.width = source.naturalWidth || source.width; + recolorCanvas.height = source.naturalHeight || source.height; + const recolorContext = recolorCanvas.getContext('2d', {willReadFrequently: true}); + if (!recolorContext || !recolorCanvas.width || !recolorCanvas.height) { + resolve(sourceUrl); + return; + } + recolorContext.drawImage(source, 0, 0); + const imageData = recolorContext.getImageData(0, 0, recolorCanvas.width, recolorCanvas.height); + const pixels = imageData.data; + for (let i = 0; i < pixels.length; i += 4) { + if (pixels[i] === 255 && pixels[i + 1] === 255 && pixels[i + 2] === 255) { + pixels[i] = 251; + pixels[i + 1] = 243; + pixels[i + 2] = 24; + } else if (pixels[i] === 174 && pixels[i + 1] === 48 && pixels[i + 2] === 31) { + pixels[i] = 0; + pixels[i + 1] = 0; + pixels[i + 2] = 0; + } + } + recolorContext.putImageData(imageData, 0, 0); + resolve(recolorCanvas.toDataURL('image/png')); + } catch (error) { + // A normal icon is always preferable to a broken picker if an + // image ever becomes unreadable/cross-origin in deployment. + resolve(sourceUrl); + } + }; + source.onerror = () => resolve(sourceUrl); + source.src = sourceUrl; + }); + + selectedToolIconAssetCache.set(sourceUrl, result); + return result; +} + +function clearToolPickerAnimationTimer() { + if (toolPickerAnimationTimer !== null) { + window.clearTimeout(toolPickerAnimationTimer); + toolPickerAnimationTimer = null; + } +} + +function resetToolPickerAnimationClasses() { + if (!toolSlotsPanel) return; + toolSlotsPanel.classList.remove( + 'is-opening', + 'is-opening-from-selection', + 'is-closing', + 'is-closing-to-selection', + 'is-motion-locked' + ); +} + +function animateToolPicker(className, durationMs, onComplete) { + if (!toolSlotsPanel) { + if (typeof onComplete === 'function') onComplete(); + return; + } + clearToolPickerAnimationTimer(); + resetToolPickerAnimationClasses(); + // A click bounce or a hover scale on the retained icon must never compete + // with the snake's positional transform. Freeze all icon-level transforms + // for the entire picker motion, then restore hover only after it settles. + toolSlotButtons.forEach((button) => button.classList.remove('is-tool-clicking')); + toolSlotsPanel.classList.add('is-motion-locked'); + void toolSlotsPanel.offsetWidth; + toolSlotsPanel.classList.add(className); + const token = ++toolPickerTransitionToken; + toolPickerTransitioning = true; + toolPickerAnimationTimer = window.setTimeout(() => { + if (token !== toolPickerTransitionToken) return; + toolSlotsPanel.classList.remove(className); + toolSlotsPanel.classList.remove('is-motion-locked'); + toolPickerAnimationTimer = null; + toolPickerTransitioning = false; + if (typeof onComplete === 'function') onComplete(); + }, durationMs); +} + +function pickerHasRetainedChoice() { + return !!(retainedToolChoice && TOOL_FAMILY_LABEL[retainedToolChoice.family]); +} + +function showRetainedToolChoice() { + if (!toolSlotsPanel || !pickerHasRetainedChoice()) return false; + openToolPickerFamily = null; + lastToolPickerFamily = retainedToolChoice.family; + toolSlotsPanel.hidden = false; + toolSlotsPanel.classList.add('is-selection-only'); + updateDrawingToolUI(); + return true; +} + +function openToolPicker(family, fromRetainedIcon = false) { + if (typeof canvas !== 'undefined' && canvas && (canvas.selectionMode || canvas.hasSelection())) canvas.clearSelection(); + if (!toolSlotsPanel) { + openToolPickerFamily = family; + lastToolPickerFamily = family; + updateDrawingToolUI(); + return; + } + + const wasSelectionOnly = toolSlotsPanel.classList.contains('is-selection-only'); + const openingFromSameRetainedChoice = wasSelectionOnly && pickerHasRetainedChoice() + && retainedToolChoice.family === family; + + // When swapping full menus, retract the old snake completely before the + // replacement family grows out from the left edge. + if (openToolPickerFamily && openToolPickerFamily !== family) { + const nextFamily = family; + animateToolPicker('is-closing', TOOL_PICKER_ANIMATION_MS, () => { + openToolPickerFamily = nextFamily; + lastToolPickerFamily = nextFamily; + toolSlotsPanel.classList.remove('is-selection-only'); + toolSlotsPanel.hidden = false; + updateDrawingToolUI(); + animateToolPicker('is-opening', TOOL_PICKER_ANIMATION_MS); + }); + return; + } + + openToolPickerFamily = family; + lastToolPickerFamily = family; + toolSlotsPanel.hidden = false; + toolSlotsPanel.classList.remove('is-selection-only'); + updateDrawingToolUI(); + animateToolPicker( + openingFromSameRetainedChoice || fromRetainedIcon ? 'is-opening-from-selection' : 'is-opening', + TOOL_PICKER_ANIMATION_MS + ); +} + +function closeToolPicker(options = {}) { + const retainChoice = options.retainChoice || null; + const selectedNow = !!retainChoice; + if (retainChoice) retainedToolChoice = retainChoice; + + if (!toolSlotsPanel || (!openToolPickerFamily && toolSlotsPanel.hidden)) { + openToolPickerFamily = null; + if (!showRetainedToolChoice()) updateDrawingToolUI(); + return; + } + + if (openToolPickerFamily) lastToolPickerFamily = openToolPickerFamily; + const shouldRetain = pickerHasRetainedChoice(); + // Mark the newly chosen slot before the closing animation begins so that + // exact square can travel to the left edge while its neighbours retract. + if (selectedNow) updateDrawingToolUI(); + const closingClass = selectedNow ? 'is-closing-to-selection' : 'is-closing'; + + animateToolPicker(closingClass, TOOL_PICKER_ANIMATION_MS, () => { + openToolPickerFamily = null; + if (shouldRetain) { + toolSlotsPanel.classList.add('is-selection-only'); + toolSlotsPanel.hidden = false; + } else { + toolSlotsPanel.classList.remove('is-selection-only'); + toolSlotsPanel.hidden = true; + } + updateDrawingToolUI(); + }); + // If the retained choice is Paint 8, updateDrawingToolUI() in the + // completion callback reveals Edit Pattern only after the snake has fully + // retracted. Opening still hides it immediately so its slide-up can run in + // parallel with the grid opening motion. +} + +function customPaintToolSelected() { + return canvas.currentToolFamily === 'paint' && canvas.selectedToolSlots.paint === 7; +} + +function setEditPatternButtonVisible(visible, animate = true) { + if (!editPatternBtn) return; + if (editPatternAnimationTimer !== null) { + window.clearTimeout(editPatternAnimationTimer); + editPatternAnimationTimer = null; + } + const shouldShow = !!visible; + editPatternBtn.setAttribute('aria-hidden', shouldShow ? 'false' : 'true'); + + if (shouldShow) { + if (editPatternBtn.hidden) { + editPatternBtn.hidden = false; + editPatternBtn.classList.remove('is-visible'); + if (animate) void editPatternBtn.offsetWidth; + } + if (animate) editPatternBtn.classList.add('is-visible'); + else editPatternBtn.classList.add('is-visible'); + return; + } + + if (editPatternBtn.hidden) return; + editPatternBtn.classList.remove('is-visible'); + if (!animate) { + editPatternBtn.hidden = true; + return; + } + editPatternAnimationTimer = window.setTimeout(() => { + editPatternBtn.hidden = true; + editPatternAnimationTimer = null; + }, 145); +} + +function updateEditPatternButton() { + if (!editPatternBtn) return; + const selectionOnly = !!(toolSlotsPanel && toolSlotsPanel.classList.contains('is-selection-only')); + const visible = customPaintToolSelected() && selectionOnly && !openToolPickerFamily; + setEditPatternButtonVisible(visible, true); +} + +function updateDrawingToolUI() { + if (!window.FlipnoteDrawingEngine || typeof canvas === 'undefined') return; + const shiftAdvanced = canvas.expertTools; + const selectionOnly = !!(toolSlotsPanel && toolSlotsPanel.classList.contains('is-selection-only')); + + toolFamilyButtons.forEach((button) => { + const family = button.dataset.toolFamilyPicker; + const open = family === openToolPickerFamily; + const image = button.querySelector('img'); + button.classList.toggle('is-open', open); + button.setAttribute('aria-expanded', open ? 'true' : 'false'); + if (image) image.src = open ? image.dataset.selectedSrc : image.dataset.normalSrc; + }); + + if (toolSlotsPanel) { + const pickerVisible = Boolean(openToolPickerFamily) + || selectionOnly + || toolSlotsPanel.classList.contains('is-closing') + || toolSlotsPanel.classList.contains('is-closing-to-selection'); + if (!pickerVisible + && !toolSlotsPanel.classList.contains('is-opening') + && !toolSlotsPanel.classList.contains('is-opening-from-selection')) { + toolSlotsPanel.hidden = true; + } + const labelFamily = openToolPickerFamily + || (pickerHasRetainedChoice() ? retainedToolChoice.family : lastToolPickerFamily); + const labelAdvanced = labelFamily === 'fill' + ? false + : (selectionOnly && pickerHasRetainedChoice() ? !!retainedToolChoice.advanced : shiftAdvanced); + toolSlotsPanel.setAttribute('aria-label', labelFamily + ? `${labelAdvanced ? 'Advanced ' : ''}${TOOL_FAMILY_LABEL[labelFamily]} tools` + : 'Drawing tool choices'); + } + + toolSlotButtons.forEach((button) => { + const displaySlot = Number(button.dataset.toolSlot); + const family = openToolPickerFamily + || (pickerHasRetainedChoice() ? retainedToolChoice.family : null) + || lastToolPickerFamily + || canvas.currentToolFamily; + const slot = displaySlotToToolSlot(family, displaySlot); + const displayedAdvanced = family === 'fill' + ? false + : (selectionOnly && pickerHasRetainedChoice() && retainedToolChoice.family === family + // A Shift-locked choice stays Advanced. A normal retained tool + // previews its live Advanced variant only while Shift is held, + // then immediately returns to the normal icon on key release. + ? !!(retainedToolChoice.advanced || shiftAdvanced) + : shiftAdvanced); + const isRetained = pickerHasRetainedChoice() + && retainedToolChoice.family === family + && retainedToolChoice.slot === slot; + const logicalId = canvas.engine.logicalId(slot, displayedAdvanced); + const label = `${displayedAdvanced ? 'Advanced ' : ''}${TOOL_FAMILY_LABEL[family]} ${slot + 1}`; + const image = button.querySelector('img'); + + button.classList.toggle('is-current-tool', isRetained); + button.setAttribute('aria-label', selectionOnly && isRetained + ? `${label}, selected. Open ${TOOL_FAMILY_LABEL[family]} tools` + : label); + button.title = selectionOnly && isRetained + ? `${label} · Selected · Click to reopen ${TOOL_FAMILY_LABEL[family]} tools` + : `${label} · ${canvas.engine.toolName(family, logicalId)}`; + button.setAttribute('aria-pressed', isRetained ? 'true' : 'false'); + if (image) { + const baseSource = toolChoiceAsset(family, slot, displayedAdvanced); + image.dataset.toolBaseSrc = baseSource; + image.src = baseSource; + image.alt = label; + if (isRetained) { + selectedToolChoiceAsset(baseSource).then((selectedSource) => { + // The selection may have changed while the PNG was being + // recoloured; never let a stale async result repaint it. + if (image.dataset.toolBaseSrc !== baseSource) return; + if (!button.classList.contains('is-current-tool')) return; + image.src = selectedSource; + }); + } + } + }); + updateEditPatternButton(); +} + +toolFamilyButtons.forEach((button) => { + button.addEventListener('click', (event) => { + if (toolPickerTransitioning) return; + const family = button.dataset.toolFamilyPicker; + if (family === 'eraser' && canvas.hasSelection()) { + canvas.clearCurrentFrame(!!event.shiftKey); + return; + } + if (openToolPickerFamily === family) closeToolPicker(); + else openToolPicker(family); + canvas.setToolFamily(family); + }); +}); + +function animateToolSlotClick(button) { + if (!button) return; + button.classList.remove('is-tool-clicking'); + void button.offsetWidth; + button.classList.add('is-tool-clicking'); + window.setTimeout(() => button.classList.remove('is-tool-clicking'), 180); +} + +toolSlotButtons.forEach((button) => { + button.addEventListener('click', () => { + if (toolPickerTransitioning) return; + animateToolSlotClick(button); + + // Once a tool is chosen, its icon remains at the left edge. Clicking + // that retained icon expands the same family again for quick changes. + if (!openToolPickerFamily && toolSlotsPanel + && toolSlotsPanel.classList.contains('is-selection-only') + && button.classList.contains('is-current-tool') + && pickerHasRetainedChoice()) { + openToolPicker(retainedToolChoice.family, true); + return; + } + + if (!openToolPickerFamily) return; + const family = openToolPickerFamily; + const displaySlot = Number(button.dataset.toolSlot); + const slot = displaySlotToToolSlot(family, displaySlot); + const advanced = family === 'fill' ? false : !!canvas.expertTools; + canvas.setToolFamily(family); + canvas.setToolSlot(slot, advanced); + closeToolPicker({retainChoice: {family, slot, advanced}}); + }); +}); + +// On touch, long-pressing a Pen/Paint/Erase tile chooses its Advanced +// (desktop Shift) variant. Fill has no separate Advanced palette. +toolSlotButtons.forEach((button) => { + installFlipnoteTouchLongPress(button, () => { + if (toolPickerTransitioning || !openToolPickerFamily || openToolPickerFamily === 'fill') return; + const family = openToolPickerFamily; + const displaySlot = Number(button.dataset.toolSlot); + const slot = displaySlotToToolSlot(family, displaySlot); + animateToolSlotClick(button); + canvas.setToolFamily(family); + canvas.setToolSlot(slot, true); + closeToolPicker({retainChoice: {family, slot, advanced:true}}); + }); +}); + +// Pressing anywhere outside the open choice grid retracts it immediately on +// pointer-down, rather than waiting for the eventual click/pointer-up. Use the +// capture phase so the close starts before the pressed control/canvas handles +// that same pointer-down. Family buttons are excluded because they already own +// the close/switch animation (including switching between families). +document.addEventListener('pointerdown', (event) => { + if (!openToolPickerFamily || toolPickerTransitioning || !toolSlotsPanel) return; + const target = event.target instanceof Element ? event.target : null; + if (target && target.closest('#flipnote-tool-slots')) return; + if (target && target.closest('[data-tool-family-picker]')) return; + closeToolPicker(); +}, true); + +let patternDraft = null; +let patternPointerPainting = false; +let patternPointerValue = 1; +let patternPointerChanged = false; +let patternLastFocusedElement = null; + +function clonePatternData(motif) { + return { + width: FLIPNOTE_CUSTOM_PATTERN_SIZE, + height: FLIPNOTE_CUSTOM_PATTERN_SIZE, + data: Uint8Array.from(motif && motif.data ? motif.data : defaultCustomPaintPattern().data, (value) => value ? 1 : 0), + }; +} + +function renderPatternGrid() { + if (!patternGrid || !patternDraft) return; + const cells = Array.from(patternGrid.querySelectorAll('[data-pattern-cell]')); + cells.forEach((cell, index) => { + const on = !!patternDraft.data[index]; + cell.classList.toggle('is-on', on); + cell.setAttribute('aria-pressed', on ? 'true' : 'false'); + const x = index % FLIPNOTE_CUSTOM_PATTERN_SIZE; + const y = Math.floor(index / FLIPNOTE_CUSTOM_PATTERN_SIZE); + cell.setAttribute('aria-label', `Pattern pixel ${x + 1}, ${y + 1}: ${on ? 'painted' : 'transparent'}`); + }); +} + +function setPatternCell(index, value) { + if (!patternDraft || index < 0 || index >= patternDraft.data.length) return; + patternDraft.data[index] = value ? 1 : 0; + const cell = patternGrid ? patternGrid.querySelector(`[data-pattern-cell="${index}"]`) : null; + if (cell) { + const on = !!patternDraft.data[index]; + cell.classList.toggle('is-on', on); + cell.setAttribute('aria-pressed', on ? 'true' : 'false'); + const x = index % FLIPNOTE_CUSTOM_PATTERN_SIZE; + const y = Math.floor(index / FLIPNOTE_CUSTOM_PATTERN_SIZE); + cell.setAttribute('aria-label', `Pattern pixel ${x + 1}, ${y + 1}: ${on ? 'painted' : 'transparent'}`); + } +} + +function ensurePatternGridCells() { + if (!patternGrid || patternGrid.childElementCount) return; + const fragment = document.createDocumentFragment(); + for (let index = 0; index < FLIPNOTE_CUSTOM_PATTERN_SIZE * FLIPNOTE_CUSTOM_PATTERN_SIZE; index++) { + const cell = document.createElement('button'); + cell.type = 'button'; + cell.dataset.patternCell = String(index); + cell.setAttribute('role', 'gridcell'); + cell.setAttribute('aria-pressed', 'false'); + fragment.appendChild(cell); + } + patternGrid.appendChild(fragment); +} + +function closePatternModal() { + if (!patternModal || patternModal.hidden) return; + patternModal.hidden = true; + patternDraft = null; + patternPointerPainting = false; + document.body.classList.remove('flipnote-pattern-modal-open'); + if (patternLastFocusedElement && typeof patternLastFocusedElement.focus === 'function') { + patternLastFocusedElement.focus(); + } + patternLastFocusedElement = null; +} + +function openPatternModal() { + if (!patternModal || !patternGrid || !customPaintToolSelected()) return; + ensurePatternGridCells(); + patternDraft = clonePatternData(canvas.customPaintMotif); + renderPatternGrid(); + patternLastFocusedElement = document.activeElement; + patternModal.hidden = false; + document.body.classList.add('flipnote-pattern-modal-open'); + if (patternCloseBtn) patternCloseBtn.focus(); +} + +if (editPatternBtn) editPatternBtn.addEventListener('click', openPatternModal); +if (patternCloseBtn) patternCloseBtn.addEventListener('click', closePatternModal); +if (patternCancelBtn) patternCancelBtn.addEventListener('click', closePatternModal); +if (patternSaveBtn) patternSaveBtn.addEventListener('click', () => { + if (!patternDraft) return; + canvas.customPaintMotif = clonePatternData(patternDraft); + saveCustomPaintPattern(canvas.customPaintMotif); + closePatternModal(); +}); +if (patternClearBtn) patternClearBtn.addEventListener('click', () => { + if (!patternDraft) return; + patternDraft.data.fill(0); + renderPatternGrid(); +}); +if (patternFillBtn) patternFillBtn.addEventListener('click', () => { + if (!patternDraft) return; + patternDraft.data.fill(1); + renderPatternGrid(); +}); +if (patternInvertBtn) patternInvertBtn.addEventListener('click', () => { + if (!patternDraft) return; + for (let i = 0; i < patternDraft.data.length; i++) patternDraft.data[i] = patternDraft.data[i] ? 0 : 1; + renderPatternGrid(); +}); +if (patternResetBtn) patternResetBtn.addEventListener('click', () => { + patternDraft = defaultCustomPaintPattern(); + renderPatternGrid(); +}); +if (patternModal) patternModal.addEventListener('pointerdown', (event) => { + if (event.target === patternModal) closePatternModal(); +}); +if (patternGrid) { + ensurePatternGridCells(); + patternGrid.addEventListener('pointerdown', (event) => { + const cell = event.target instanceof Element ? event.target.closest('[data-pattern-cell]') : null; + if (!cell || !patternGrid.contains(cell) || !patternDraft) return; + event.preventDefault(); + const index = Number(cell.dataset.patternCell); + patternPointerPainting = true; + patternPointerChanged = true; + patternPointerValue = patternDraft.data[index] ? 0 : 1; + setPatternCell(index, patternPointerValue); + if (patternGrid.setPointerCapture) patternGrid.setPointerCapture(event.pointerId); + }); + patternGrid.addEventListener('pointermove', (event) => { + if (!patternPointerPainting || !patternDraft) return; + const hit = document.elementFromPoint(event.clientX, event.clientY); + const cell = hit instanceof Element ? hit.closest('[data-pattern-cell]') : null; + if (!cell || !patternGrid.contains(cell)) return; + setPatternCell(Number(cell.dataset.patternCell), patternPointerValue); + }); + const finishPatternPointer = (event) => { + if (!patternPointerPainting) return; + patternPointerPainting = false; + if (patternGrid.hasPointerCapture && patternGrid.hasPointerCapture(event.pointerId)) patternGrid.releasePointerCapture(event.pointerId); + }; + patternGrid.addEventListener('pointerup', finishPatternPointer); + patternGrid.addEventListener('pointercancel', finishPatternPointer); + patternGrid.addEventListener('click', (event) => { + const cell = event.target instanceof Element ? event.target.closest('[data-pattern-cell]') : null; + if (!cell || !patternGrid.contains(cell) || !patternDraft) return; + if (patternPointerChanged) { + patternPointerChanged = false; + return; + } + const index = Number(cell.dataset.patternCell); + setPatternCell(index, patternDraft.data[index] ? 0 : 1); + }); +} +document.addEventListener('keydown', (event) => { + if (shortcutAction(event, 'dialog') !== 'closeDialog' || !patternModal || patternModal.hidden) return; + event.preventDefault(); + closePatternModal(); +}); + +if (eraseBigBtn) eraseBigBtn.onclick = (event) => canvas.clearCurrentFrame(!!event.shiftKey); +if (undoBtn) undoBtn.onclick = () => { + const frame = timeline.getFrame(timeline.currentFrame); + if (frame && frame.undoPointer > 0) frame.undo(); + else updateHistoryButtons(frame); +}; +if (redoBtn) redoBtn.onclick = () => { + const frame = timeline.getFrame(timeline.currentFrame); + if (frame && frame.undoPointer < frame.undoBuffer.length - 1) frame.redo(); + else updateHistoryButtons(frame); +}; + + + + + + + + + + +function base64ToBytes(value) { + const binary = atob(String(value || '')); + const bytes = new Uint8Array(binary.length); + for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index); + return bytes; +} + +function unpackPpmLayer(value) { + const packed = base64ToBytes(value); + const mask = new Uint8Array(256 * 192); + const count = Math.min(mask.length, packed.length * 8); + for (let index = 0; index < count; index += 1) { + mask[index] = (packed[index >> 3] >> (index & 7)) & 1; + } + return mask; +} + +function importedAudioBlob(item) { + const bytes = base64ToBytes(item && item.data); + return new Blob([bytes], {type: (item && item.mime) || 'audio/wav'}); +} + +function clearEditorAudioTracksForPpmImport() { + stopFlipnoteEditorAudio(); + ['music','sound1','sound2','sound3'].forEach((key) => { + const track = audioTracks[key]; + if (track && track.url) URL.revokeObjectURL(track.url); + audioTracks[key] = null; + }); + if (typeof timeline !== 'undefined' && timeline) renderFlipnoteSpaceMeter(timeline); +} + + + +async function applyImportedPpmProject(payload) { + editorDirtyTrackingSuspended += 1; + try { + const project = payload && payload.project; + if (!project || !Array.isArray(project.frames) || !project.frames.length) throw new Error('That PPM did not contain any editable pages.'); + if (project.frames.length > FLIPNOTE_EDITOR_MAX_PAGES) throw new Error(`Flipnote Editor supports a maximum of ${FLIPNOTE_EDITOR_MAX_PAGES} pages.`); + if (timeline.playing) timeline.stopPlayback(); + clearEditorAudioTracksForPpmImport(); + + editorLayerVisibility = Array.isArray(project.layer_visibility) && project.layer_visibility.length === 2 + ? project.layer_visibility.map((visible) => visible !== false) : [true, true]; + const importedFrames = project.frames.map((row) => { + const frame = new Frame({ + layers: [unpackPpmLayer(row.layer1), unpackPpmLayer(row.layer2)], + colors: Array.isArray(row.colours) ? row.colours.slice(0, 2) : ['black', 'red'], + paperColor: row.paper === 'black' ? 'black' : 'white', + soundFlags: normalizeFrameSoundFlags(row.sounds), + }); + frame.pushState(); + return frame; + }); + + timeline.frames = importedFrames; + rebuildFlipnoteSpaceFrameCache(timeline.frames); + timeline.currentFrame = Math.max(0, Math.min(importedFrames.length - 1, Number(project.thumbnail_frame) || 0)); + timeline.lastFrame = timeline.currentFrame; + exportThumbnailFrameIndex = timeline.currentFrame; + exportThumbnailFrameRef = timeline.frames[timeline.currentFrame] || null; + const speed = speedSetting(project.speed_level); + timeline.speedLevel = speed.level; + timeline.framerate = speed.fps; + timeline.loopEnabled = project.loop !== false; + updatePlaybackSettingsUI(timeline); + + + + + + if (filenameElement) { + filenameElement.textContent = String(project.title || 'Imported Flipnote').slice(0, 32) || 'Imported Flipnote'; + document.title = `${filenameElement.textContent} - Flipnote Editor`; + } + + const audio = project.audio && typeof project.audio === 'object' ? project.audio : {}; + ['music','sound1','sound2','sound3'].forEach((key) => { + const item = audio[key]; + if (!item || !item.data) return; + const blob = importedAudioBlob(item); + setAudioTrack( + key, + blob, + Number(item.duration) || 0, + item.source_name || `Imported ${audioTrackLabel(key)}`, + null, + Number(item.reference_fps) || timeline.framerate, + item.source_kind || 'ppm_import' + ); + }); + + canvas.activeLayer = 0; + timeline.createThumbnails(); + timeline.setActiveFrame(timeline.currentFrame); + updateAudioFillBars(); + renderAudioButtons(); + + const source = payload.source || {}; + const author = source.original_author_name || source.original_author_id || 'the original creator'; + if (ppmImportStatus) { + ppmImportStatus.textContent = `Imported ${importedFrames.length} page${importedFrames.length === 1 ? '' : 's'} from ${author}.`; + } + } finally { + editorDirtyTrackingSuspended = Math.max(0, editorDirtyTrackingSuspended - 1); + } +} + +async function importPpmFile(file) { + if (!file) return false; + if (timeline.playing) { setOpenPpmStatus('Stop playback before opening a PPM.'); return false; } + if (activeAudioRecording) { setOpenPpmStatus('Stop recording before opening a PPM.'); return false; } + if (ppmImportButton) ppmImportButton.disabled = true; + setOpenPpmStatus('Reading PPM...'); + try { + const payload = await FlipnotePpmImport.read(file); + await applyImportedPpmProject(payload); + markEditorDirty(); + return true; + } catch (error) { + setOpenPpmStatus(error && error.message ? error.message : 'That PPM could not be opened.'); + return false; + } finally { + if (ppmImportButton) ppmImportButton.disabled = false; + if (ppmImportInput) ppmImportInput.value = ''; + } +} + + + + + + + + + + + + + + + + + + + +function resolveExportThumbnailFrameIndex() { + if (!timeline || !Array.isArray(timeline.frames) || !timeline.frames.length) return 0; + if (exportThumbnailFrameRef) { + const refIndex = timeline.frames.indexOf(exportThumbnailFrameRef); + if (refIndex >= 0) { + exportThumbnailFrameIndex = refIndex; + return refIndex; + } + } + const fallback = Number.isFinite(Number(exportThumbnailFrameIndex)) + ? Number(exportThumbnailFrameIndex) + : Number(timeline.currentFrame) || 0; + const safe = Math.max(0, Math.min(timeline.frames.length - 1, Math.round(fallback))); + exportThumbnailFrameIndex = safe; + exportThumbnailFrameRef = timeline.frames[safe] || null; + return safe; +} + +function chooseExportThumbnailFrame(index) { + + if (!timeline || !Array.isArray(timeline.frames) || !timeline.frames.length) return; + const safe = Math.max(0, Math.min(timeline.frames.length - 1, Math.round(Number(index) || 0))); + exportThumbnailFrameIndex = safe; + exportThumbnailFrameRef = timeline.frames[safe] || null; + renderExportThumbnailTimelines(); + +} + +function renderExportThumbnailTimeline(container) { + if (!container || !timeline || !Array.isArray(timeline.frames)) return; + container.textContent = ''; + const selected = resolveExportThumbnailFrameIndex(); + timeline.frames.forEach((frame, index) => { + const button = document.createElement('button'); + button.type = 'button'; + button.className = 'flipnote-editor-export-thumbnail'; + button.dataset.frameIndex = String(index); + button.setAttribute('aria-label', `Use page ${index + 1} as the Flipnote thumbnail`); + button.setAttribute('aria-pressed', index === selected ? 'true' : 'false'); + if (index === selected) button.classList.add('selected'); + + const preview = document.createElement('canvas'); + preview.width = 256; + preview.height = 192; + preview.setAttribute('aria-hidden', 'true'); + const previewCtx = preview.getContext('2d', {alpha:false}); + previewCtx.imageSmoothingEnabled = false; + previewCtx.putImageData(frame.compositeImageData(), 0, 0); + button.appendChild(preview); + button.addEventListener('click', () => chooseExportThumbnailFrame(index)); + container.appendChild(button); + }); + requestAnimationFrame(() => { + const selectedButton = container.querySelector('.flipnote-editor-export-thumbnail.selected'); + if (selectedButton && typeof selectedButton.scrollIntoView === 'function') { + selectedButton.scrollIntoView({block:'nearest', inline:'nearest'}); + } + }); +} + +function renderExportThumbnailTimelines() { renderExportThumbnailTimeline(webmExportThumbnailTimeline); } + + + + + + + + + + + + + + + + + +function setWebmExportProgress(percent, message) { + const safe = Math.max(0, Math.min(100, Math.round(Number(percent) || 0))); + if (webmExportProgress) webmExportProgress.setAttribute('aria-valuenow', String(safe)); + if (webmExportProgressFill) webmExportProgressFill.style.width = `${safe}%`; + if (webmExportStatus && message) webmExportStatus.textContent = message; +} + +function openWebmExportModal() { + if (!webmExportModal || webmExporting) return; + if (activeAudioRecording) { + alert('Stop the current audio recording before exporting a WEBM.'); + return; + } + webmExportLastFocus = document.activeElement; + webmExportModal.hidden = false; + document.body.classList.add('flipnote-editor-info-open'); + if (webmExportStart) { webmExportStart.hidden = false; webmExportStart.disabled = false; webmExportStart.textContent = 'Save .WEBM'; } + if (webmExportCancel) { webmExportCancel.disabled = false; webmExportCancel.textContent = 'Cancel'; } + if (webmExportClose) webmExportClose.disabled = false; + if (!exportThumbnailFrameRef || !timeline.frames.includes(exportThumbnailFrameRef)) { + exportThumbnailFrameIndex = Math.max(0, Math.min(timeline.frames.length - 1, Number(timeline.currentFrame) || 0)); + exportThumbnailFrameRef = timeline.frames[exportThumbnailFrameIndex] || null; + } + renderExportThumbnailTimelines(); + setWebmExportProgress(0, `Ready to render ${timeline.frames.length} page${timeline.frames.length === 1 ? '' : 's'}.`); + webmExportStart?.focus(); +} + +function closeWebmExportModal() { + if (!webmExportModal || webmExporting) return; + webmExportModal.hidden = true; + document.body.classList.remove('flipnote-editor-info-open'); + const target = webmExportLastFocus; + webmExportLastFocus = null; + if (target && typeof target.focus === 'function') target.focus(); +} + +async function exportWebmFromEditor() { + if (webmExporting) return; + webmExporting = true; + if (webmExportStart) { webmExportStart.disabled = true; webmExportStart.textContent = 'Rendering...'; } + if (webmExportCancel) webmExportCancel.disabled = true; + if (webmExportClose) webmExportClose.disabled = true; + try { + await exportManager.exportAsWebm(setWebmExportProgress); + if (webmExportStart) webmExportStart.hidden = true; + if (webmExportCancel) webmExportCancel.textContent = 'Close'; + } catch (error) { + setWebmExportProgress(0, error && error.message ? error.message : 'The browser could not export that WEBM.'); + if (webmExportStart) webmExportStart.textContent = 'Try again'; + } finally { + webmExporting = false; + if (webmExportStart) webmExportStart.disabled = false; + if (webmExportCancel) webmExportCancel.disabled = false; + if (webmExportClose) webmExportClose.disabled = false; + } +} + +function setOpenPpmStatus(message) { + if (ppmImportStatus) ppmImportStatus.textContent = String(message || ''); +} + +function showOpenPpmChoice() { + if (openPpmChoice) openPpmChoice.hidden = false; + + setOpenPpmStatus(''); +} + +function openOpenPpmModal() { + if (!openPpmModal || timeline.playing) return; + openPpmLastFocus = document.activeElement; + openPpmModal.hidden = false; + document.body.classList.add('flipnote-editor-info-open'); + showOpenPpmChoice(); + if (ppmImportInput) ppmImportInput.value = ''; + + +} + +function closeOpenPpmModal() { + if (!openPpmModal) return; + + openPpmModal.hidden = true; + document.body.classList.remove('flipnote-editor-info-open'); + const target = openPpmLastFocus; + openPpmLastFocus = null; + if (target && typeof target.focus === 'function') target.focus(); +} + + + + + + + +if (ppmImportButton) ppmImportButton.addEventListener('click', (event) => { + event.preventDefault(); + openOpenPpmModal(); +}); +if (ppmImportInput) ppmImportInput.addEventListener('change', async () => { + const file = ppmImportInput.files && ppmImportInput.files[0]; + if (!file) return; + const imported = await importPpmFile(file); + if (imported) closeOpenPpmModal(); +}); + + + +if (openUploadLabel) openUploadLabel.addEventListener('keydown', (event) => { + if (event.key !== 'Enter' && event.key !== ' ') return; + event.preventDefault(); + try { + if (ppmImportInput && typeof ppmImportInput.showPicker === 'function') ppmImportInput.showPicker(); + else ppmImportInput?.click(); + } catch (error) { + ppmImportInput?.click(); + } +}); + + + + + +if (openPpmClose) openPpmClose.addEventListener('click', closeOpenPpmModal); +if (openPpmCancel) openPpmCancel.addEventListener('click', closeOpenPpmModal); +if (openPpmModal) openPpmModal.addEventListener('pointerdown', (event) => { if (event.target === openPpmModal) closeOpenPpmModal(); }); + + + + + + + + + + + + + + + +const exportBtn = document.getElementById('export-btn'); +if (exportBtn) exportBtn.addEventListener('click', (event) => { event.preventDefault(); openWebmExportModal(); }); +if (webmExportStart) webmExportStart.addEventListener('click', () => { void exportWebmFromEditor(); }); +if (webmExportCancel) webmExportCancel.addEventListener('click', closeWebmExportModal); +if (webmExportClose) webmExportClose.addEventListener('click', closeWebmExportModal); +if (webmExportModal) webmExportModal.addEventListener('pointerdown', (event) => { if (event.target === webmExportModal) closeWebmExportModal(); }); + +document.addEventListener('keydown', (event) => { + if (shortcutAction(event, 'dialog') !== 'closeDialog') return; + + + if (webmExportModal && !webmExportModal.hidden) { event.preventDefault(); closeWebmExportModal(); return; } + if (openPpmModal && !openPpmModal.hidden) { event.preventDefault(); closeOpenPpmModal(); } +}); +// The editor starts on Draw 1. Present that default exactly like a tool the +// user has already chosen: its selected-colour icon stays visible at the top +// of the closed picker and can be clicked to reopen the Draw menu. +showRetainedToolChoice(); + +let openColorPickerLayer = null; + +function closeLayerColorPicker() { + if (openColorPickerLayer === null) return; + openColorPickerLayer = null; + updateLayerPanel(); +} + +function openLayerColorPicker(layer) { + openColorPickerLayer = layer === 1 ? 1 : 0; + canvas.setActiveLayer(openColorPickerLayer); + updateLayerPanel(); +} + +function updateLayerPanel() { + const frame = timeline.getFrame(timeline.currentFrame); + if (!frame) return; + document.querySelectorAll('[data-layer-row]').forEach((row) => { + const layer = Number(row.dataset.layerRow); + const active = layer === canvas.activeLayer; + const pickerOpen = layer === openColorPickerLayer; + row.classList.toggle('active', active); + row.classList.toggle('layer-hidden', !editorLayerVisibility[layer]); + row.classList.toggle('color-picker-open', pickerOpen); + + const colors = Array.from(row.querySelectorAll('[data-layer-color]')); + const selectedColor = frame.colors[layer]; + const otherColors = FLIPNOTE_INK_ORDER.filter((color) => color !== selectedColor); + // Keep the selected swatch anchored at the far right. The two alternate + // colours occupy slots to its left and animate out from underneath it. + const visibleOrder = [otherColors[0], otherColors[1], selectedColor]; + colors.forEach((button) => { + const selected = selectedColor === button.dataset.layerColor; + const pickerSlot = visibleOrder.indexOf(button.dataset.layerColor) + 1; + button.classList.toggle('selected', selected); + button.setAttribute('aria-pressed', selected ? 'true' : 'false'); + button.style.order = String(pickerSlot); + button.style.setProperty('--flipnote-picker-slot', String(pickerSlot)); + button.tabIndex = pickerOpen || selected ? 0 : -1; + if (button.dataset.layerColor === 'black') { + const monochromeInk = resolveFrameInk(frame, 'black'); + button.style.backgroundColor = `rgb(${monochromeInk[0]}, ${monochromeInk[1]}, ${monochromeInk[2]})`; + button.title = frame.paperColor === 'black' ? 'White ink' : 'Black ink'; + } else { + button.style.backgroundColor = ''; + button.title = button.dataset.layerColor === 'red' ? 'Red' : 'Blue'; + } + }); + + const colorGroup = row.querySelector('.flipnote-editor-layer-colors'); + if (colorGroup) colorGroup.setAttribute('aria-expanded', pickerOpen ? 'true' : 'false'); + }); + document.querySelectorAll('[data-layer-select]').forEach((button) => { + const layer = Number(button.dataset.layerSelect); + const active = layer === canvas.activeLayer; + button.setAttribute('aria-pressed', active ? 'true' : 'false'); + const status = button.querySelector('[data-layer-status]'); + if (status) status.textContent = !editorLayerVisibility[layer] ? 'Hidden' : (active ? 'Active' : 'Select'); + }); +} + +// Layer visibility is a PPM-wide flag; swapping exchanges this page's artwork and colours. +const layerMenu = document.createElement('div'); +layerMenu.className = 'flipnote-editor-layer-menu'; +layerMenu.hidden = true; +layerMenu.setAttribute('role', 'menu'); +layerMenu.innerHTML = ''; +document.body.appendChild(layerMenu); +let layerMenuTarget = 0; +function closeLayerMenu() { layerMenu.hidden = true; } +function refreshLayerVisibility() { + markEditorDirty(); + + canvas.renderCurrentFrame(); + timeline.createThumbnails(); + updateLayerPanel(); +} +layerMenu.querySelector('[data-layer-visibility]').addEventListener('click', () => { + editorLayerVisibility[layerMenuTarget] = !editorLayerVisibility[layerMenuTarget]; + closeLayerMenu(); + refreshLayerVisibility(); +}); +layerMenu.querySelector('[data-layer-swap]').addEventListener('click', () => { + if (timeline.playing || !canvas.allowEditing) return closeLayerMenu(); + const frame = timeline.getFrame(timeline.currentFrame); + if (frame) { + [frame.layers[0], frame.layers[1]] = [frame.layers[1], frame.layers[0]]; + [frame.colors[0], frame.colors[1]] = [frame.colors[1], frame.colors[0]]; + timeline.updateFrame(timeline.currentFrame); + canvas.renderCurrentFrame(); + updateLayerPanel(); + } + closeLayerMenu(); +}); +document.addEventListener('pointerdown', (event) => { if (!layerMenu.contains(event.target)) closeLayerMenu(); }); +document.addEventListener('keydown', (event) => { if (event.key === 'Escape') closeLayerMenu(); }); +document.querySelectorAll('[data-layer-row]').forEach((row) => { + row.addEventListener('contextmenu', (event) => { + event.preventDefault(); + layerMenuTarget = Number(row.dataset.layerRow) === 1 ? 1 : 0; + layerMenu.querySelector('[data-layer-visibility]').textContent = editorLayerVisibility[layerMenuTarget] ? 'Hide Layer' : 'Show Layer'; + layerMenu.hidden = false; + layerMenu.style.left = `${Math.max(0, Math.min(event.clientX, window.innerWidth - layerMenu.offsetWidth))}px`; + layerMenu.style.top = `${Math.max(0, Math.min(event.clientY, window.innerHeight - layerMenu.offsetHeight))}px`; + }); +}); + +// The artwork makes the whole row read as one layer button. Keep that entire +// visible row selectable (except the colour controls) instead of relying on a +// narrower invisible hit target. This also fixes clicks near the right side of +// the layer artwork being ignored. +document.querySelectorAll('[data-layer-row]').forEach((row) => { + row.addEventListener('click', (event) => { + if (event.target instanceof Element && event.target.closest('.flipnote-editor-layer-colors')) return; + canvas.setActiveLayer(Number(row.dataset.layerRow)); + }); +}); + +// Delegate the picker click from the colour group itself. This keeps the +// visible swatch clickable even when the image-backed layer row overlaps it. +document.querySelectorAll('.flipnote-editor-layer-colors').forEach((group) => { + const layerRow = group.closest('[data-layer-row]'); + const setColorInteraction = (active) => { + if (layerRow) layerRow.classList.toggle('color-picker-interaction', !!active); + }; + // Hovering/pressing the colour controls should animate only the swatches, + // not the large layer artwork underneath them. + group.addEventListener('pointerenter', () => setColorInteraction(true)); + group.addEventListener('pointerleave', () => setColorInteraction(false)); + group.addEventListener('focusin', () => setColorInteraction(true)); + group.addEventListener('focusout', (event) => { + if (!group.contains(event.relatedTarget)) setColorInteraction(false); + }); + group.addEventListener('pointerdown', (event) => { + setColorInteraction(true); + event.stopPropagation(); + }, true); + group.addEventListener('click', (event) => { + const button = event.target.closest('[data-layer-color]'); + if (!button) { + const row = group.closest('[data-layer-row]'); + if (row) canvas.setActiveLayer(Number(row.dataset.layerRow)); + return; + } + event.preventDefault(); + event.stopPropagation(); + const layer = Number(button.dataset.layer); + if (openColorPickerLayer !== layer) { + openLayerColorPicker(layer); + return; + } + canvas.setLayerColor(layer, button.dataset.layerColor); + openColorPickerLayer = null; + updateLayerPanel(); + }); +}); +document.addEventListener('click', (event) => { + if (openColorPickerLayer === null) return; + if (event.target instanceof Element && event.target.closest('.flipnote-editor-layer-colors')) return; + closeLayerColorPicker(); +}); +document.addEventListener('keydown', (event) => { + if (shortcutAction(event, 'dialog') === 'closeDialog') closeLayerColorPicker(); +}); +updateLayerPanel(); + +function setToolTab(tabNumber) { + const nextTab = Math.max(1, Math.min(5, Number(tabNumber) || 1)); + // The selection belongs to the current frame, not to the visible toolbar tab. + // Keep it visible until Select or Transform is switched off (or the frame changes). + activeToolTab = nextTab; + document.querySelectorAll('[data-tool-tab]').forEach((button) => { + const active = Number(button.dataset.toolTab) === nextTab; + button.classList.toggle('active', active); + button.setAttribute('aria-selected', active ? 'true' : 'false'); + button.tabIndex = active ? 0 : -1; + const image = button.querySelector('img'); + const normalSrc = button.dataset.tabNormalSrc; + const selectedSrc = button.dataset.tabSelectedSrc; + if (image && normalSrc && selectedSrc) { + image.src = active ? selectedSrc : normalSrc; + } + }); + document.querySelectorAll('[data-tool-tab-pane]').forEach((pane) => { + pane.hidden = Number(pane.dataset.toolTabPane) !== nextTab; + }); + document.querySelectorAll('[data-tool-tab-shared]').forEach((panel) => { + const tabs = String(panel.dataset.toolTabShared || '').split(/\s+/).map(Number); + panel.hidden = !tabs.includes(nextTab); + }); + closeLayerColorPicker(); +} + +document.querySelectorAll('[data-tool-tab]').forEach((button) => { + button.addEventListener('click', () => setToolTab(button.dataset.toolTab)); +}); +setToolTab(1); + +const paperTool = document.getElementById('paper-tool'); +const lightboxTool = document.getElementById('lightbox-tool'); +const cloneTool = document.getElementById('clone-tool'); +const selectTool = document.getElementById('select-tool'); +const copyTool = document.getElementById('copy-tool'); +const pasteTool = document.getElementById('paste-tool'); +const transformTool = document.getElementById('transform-tool'); +const invertTool = document.getElementById('invert-tool'); +const resizeTool = document.getElementById('resize-tool'); +const flipTool = document.getElementById('flip-tool'); +const rotateTool = document.getElementById('rotate-tool'); +const selectionStatus = document.getElementById('flipnote-selection-status'); +const selectionTools = document.querySelector('.flipnote-editor-selection-tools'); +let selectionShiftHeld = false; + +// Selection UI fixes live here in editor.js now; no separate +// selection-ui-fixes.js runtime patch is required. +let selectionBase = null; +if (selectionTools) { + selectionTools.style.position = 'relative'; + + const baseCandidates = Array.from(selectionTools.querySelectorAll( + '[data-selection-base], .flipnote-editor-selection-base, .selbase, .selection-base, img[src*="selbase"]' + )); + selectionBase = baseCandidates.find((node) => node.parentElement === selectionTools) || null; + if (!selectionBase) { + selectionBase = document.createElement('img'); + selectionBase.className = 'flipnote-editor-selection-base'; + selectionBase.dataset.selectionBase = ''; + selectionBase.alt = ''; + selectionBase.draggable = false; + selectionBase.setAttribute('aria-hidden', 'true'); + selectionTools.prepend(selectionBase); + } + selectionBase.src = './assets/flipnote-editor/selbase.png'; + selectionBase.hidden = false; + Object.assign(selectionBase.style, { + position: 'absolute', + zIndex: '0', + inset: '0', + display: 'block', + width: '100%', + height: '100%', + objectFit: 'fill', + pointerEvents: 'none', + userSelect: 'none', + transform: 'none', + filter: 'none', + transition: 'none', + }); + + // selbase is one backing plate for the whole group, never one per button. + baseCandidates.forEach((node) => { + if (node === selectionBase) return; + node.hidden = true; + node.setAttribute('aria-hidden', 'true'); + node.style.display = 'none'; + }); + + [selectTool, copyTool, pasteTool].forEach((button) => { + if (!button) return; + button.style.position = 'relative'; + button.style.zIndex = '1'; + // Filter changes must be instantaneous; only the button growth animates. + button.style.transition = 'transform .1s ease'; + }); +} + +function setSelectionButtonDisabled(button, disabled) { + if (!button) return; + button.classList.toggle('is-disabled', disabled); + button.setAttribute('aria-disabled', disabled ? 'true' : 'false'); + button.tabIndex = disabled ? -1 : 0; + button.style.filter = disabled ? 'grayscale(1) opacity(.48)' : ''; + button.style.transition = 'transform .1s ease'; +} + +function updateSelectionToolState() { + const hasSelection = !!(canvas && typeof canvas.hasSelection === 'function' && canvas.hasSelection()); + const hasClipboard = !!(canvas && canvas.selectionClipboard); + + if (selectTool) { + const image = selectTool.querySelector('img'); + const selecting = !!(canvas && canvas.selectionMode && !canvas.rectangleSelectionMode); + selectTool.classList.toggle('is-enabled', selecting); + selectTool.setAttribute('aria-pressed', selecting ? 'true' : 'false'); + selectTool.title = canvas && canvas.mobileRectangleSelection + ? 'Rectangle Select (touch and drag)' + : (selecting ? 'Select (click again to cancel)' : 'Select'); + // Keep Select at the same grown size as hover for as long as it is on. + selectTool.style.transform = selecting ? 'scale(1.04)' : ''; + selectTool.style.zIndex = selecting ? '2' : '1'; + if (image) { + image.src = selecting + ? './assets/flipnote-editor/select_y.png' + : './assets/flipnote-editor/select.png'; + image.alt = selecting ? 'Select enabled' : 'Select'; + } + } + + if (copyTool) { + const copyImage = copyTool.querySelector('img'); + const copyIsCut = !!selectionShiftHeld; + setSelectionButtonDisabled(copyTool, !hasSelection); + copyTool.title = hasSelection + ? (copyIsCut ? 'Cut selection' : 'Copy selection') + : (copyIsCut ? 'Cut selection (disabled)' : 'Copy selection (disabled)'); + if (copyImage) { + copyImage.src = copyIsCut + ? './assets/flipnote-editor/selectionCut.png' + : './assets/flipnote-editor/selectionCopy.png'; + copyImage.alt = copyIsCut ? 'Cut' : 'Copy'; + } + } + + if (pasteTool) { + setSelectionButtonDisabled(pasteTool, !hasClipboard); + pasteTool.title = hasClipboard ? 'Paste clipboard' : 'Paste clipboard (disabled)'; + } + if (transformTool) { + const active = !!(canvas && canvas.selectionMode && canvas.rectangleSelectionMode); + transformTool.classList.toggle('is-enabled', active); + transformTool.setAttribute('aria-pressed', active ? 'true' : 'false'); + transformTool.title = hasSelection ? 'Select the bounding rectangle' : 'Rectangle selection'; + const image = transformTool.querySelector('img'); + if (image) image.src = active + ? './assets/flipnote-editor/transform_active.png' + : './assets/flipnote-editor/transform.png'; + } + if (invertTool) { + invertTool.disabled = !hasSelection; + invertTool.title = hasSelection + ? (selectionShiftHeld ? 'Swap layer ink colours inside the selection' : 'Invert ink and erased pixels in selection') + : 'Select an area to invert'; + } + + if (resizeTool) { + const image = resizeTool.querySelector('img'); + resizeTool.title = selectionShiftHeld ? 'Enlarge' : 'Shrink'; + if (image) { + image.src = selectionShiftHeld + ? './assets/flipnote-editor/resize_larger.png' + : './assets/flipnote-editor/resize_smaller.png'; + image.alt = selectionShiftHeld ? 'Enlarge' : 'Shrink'; + } + } + if (flipTool) { + const image = flipTool.querySelector('img'); + flipTool.title = selectionShiftHeld ? 'Flip vertically' : 'Flip horizontally'; + if (image) { + image.src = selectionShiftHeld + ? './assets/flipnote-editor/flip_y.png' + : './assets/flipnote-editor/flip_x.png'; + image.alt = selectionShiftHeld ? 'Flip vertically' : 'Flip horizontally'; + } + } + if (rotateTool) { + const image = rotateTool.querySelector('img'); + rotateTool.title = selectionShiftHeld ? 'Rotate anticlockwise' : 'Rotate clockwise'; + if (image) { + image.src = selectionShiftHeld + ? './assets/flipnote-editor/rotate_anticlockwise.png' + : './assets/flipnote-editor/rotate_clockwise.png'; + image.alt = selectionShiftHeld ? 'Rotate anticlockwise' : 'Rotate clockwise'; + } + } + +} + +function updatePaperToolState() { + const frame = typeof timeline !== 'undefined' ? timeline.getFrame(timeline.currentFrame) : null; + if (paperTool && frame) { + const isBlackPaper = frame.paperColor === 'black'; + const image = paperTool.querySelector('img'); + paperTool.setAttribute('aria-pressed', isBlackPaper ? 'true' : 'false'); + paperTool.title = isBlackPaper ? 'Paper: Black (click for white)' : 'Paper: White (click for black)'; + if (image) { + image.src = isBlackPaper ? './assets/flipnote-editor/paperB.png' : './assets/flipnote-editor/paperW.png'; + image.alt = isBlackPaper ? 'Black paper' : 'White paper'; + } + } + if (lightboxTool) { + const image = lightboxTool.querySelector('img'); + lightboxTool.classList.toggle('is-enabled', lightboxEnabled); + lightboxTool.setAttribute('aria-pressed', lightboxEnabled ? 'true' : 'false'); + lightboxTool.title = `Light-box (${pagesToLightbox()} previous page${pagesToLightbox() === 1 ? '' : 's'})`; + if (image) { + image.src = lightboxEnabled ? './assets/flipnote-editor/lightbox_pressed.png' : './assets/flipnote-editor/lightbox.png'; + image.alt = lightboxEnabled ? 'Light-box enabled' : 'Light-box'; + } + } + if (cloneTool) { + const image = cloneTool.querySelector('img'); + cloneTool.classList.toggle('is-enabled', cloneEnabled); + cloneTool.setAttribute('aria-pressed', cloneEnabled ? 'true' : 'false'); + if (image) { + image.src = cloneEnabled ? './assets/flipnote-editor/copy_pressed.png' : './assets/flipnote-editor/copy.png'; + image.alt = cloneEnabled ? 'Clone enabled' : 'Clone'; + } + } +} + +if (paperTool) { + paperTool.addEventListener('click', (event) => { + event.preventDefault(); + const frame = timeline.getFrame(timeline.currentFrame); + if (!frame) return; + frame.paperColor = frame.paperColor === 'black' ? 'white' : 'black'; + canvas.renderCurrentFrame(); + timeline.updateFrame(timeline.currentFrame); + updateLayerPanel(); + updatePaperToolState(); + }); +} +if (lightboxTool) { + lightboxTool.addEventListener('click', () => { + lightboxEnabled = !lightboxEnabled; + updatePaperToolState(); + canvas.renderCurrentFrame(); + }); +} +if (cloneTool) { + cloneTool.addEventListener('click', () => { + cloneEnabled = !cloneEnabled; + updatePaperToolState(); + }); +} +if (selectTool) { + selectTool.addEventListener('click', (event) => { + event.preventDefault(); + if (canvas.selectionMode && !canvas.rectangleSelectionMode) canvas.clearSelection(); + else { + canvas.rectangleSelectionMode = false; + canvas.mobileRectangleSelection = false; + canvas.setSelectionMode(true); + } + }); +} +if (transformTool) { + transformTool.addEventListener('click', (event) => { + event.preventDefault(); + if (canvas.selectionMode && canvas.rectangleSelectionMode) { + canvas.clearSelection(); + } else if (!canvas.selectBoundingRectangle()) { + canvas.rectangleSelectionMode = true; + canvas.mobileRectangleSelection = false; + canvas.setSelectionMode(true); + } + }); +} +if (invertTool) { + invertTool.addEventListener('click', (event) => { + event.preventDefault(); + if (!invertTool.disabled) canvas.invertSelection(!!(selectionShiftHeld || editorAlternateActive(event))); + }); +} +if (copyTool) { + copyTool.addEventListener('click', (event) => { + event.preventDefault(); + if (copyTool.getAttribute('aria-disabled') === 'true') return; + canvas.copySelection(!!(selectionShiftHeld || editorAlternateActive(event))); + }); +} +if (pasteTool) { + pasteTool.addEventListener('click', (event) => { + event.preventDefault(); + if (pasteTool.getAttribute('aria-disabled') === 'true') return; + canvas.pasteSelection(); + }); +} +if (resizeTool) { + resizeTool.addEventListener('click', (event) => { + event.preventDefault(); + canvas.resizeSelectionOrLayer(!!(selectionShiftHeld || editorAlternateActive(event))); + }); +} +if (flipTool) { + flipTool.addEventListener('click', (event) => { + event.preventDefault(); + canvas.flipSelectionOrLayer(!!(selectionShiftHeld || editorAlternateActive(event))); + }); +} +if (rotateTool) { + rotateTool.addEventListener('click', (event) => { + event.preventDefault(); + canvas.rotateSelectionOrLayer(!!(selectionShiftHeld || editorAlternateActive(event))); + }); +} + +// Touch long-press exposes the desktop Shift variants without changing any +// normal tap/click behavior. +installFlipnoteTouchLongPress(selectTool, () => { + if (!canvas) return; + if (canvas.hasSelection()) canvas.clearSelection(); + canvas.mobileRectangleSelection = true; + canvas.setSelectionMode(true); + updateSelectionToolState(); +}); +installFlipnoteTouchLongPress(copyTool, () => { + if (copyTool && copyTool.getAttribute('aria-disabled') !== 'true') canvas.copySelection(true); +}); +installFlipnoteTouchLongPress(resizeTool, () => canvas.resizeSelectionOrLayer(true)); +installFlipnoteTouchLongPress(flipTool, () => canvas.flipSelectionOrLayer(true)); +installFlipnoteTouchLongPress(rotateTool, () => canvas.rotateSelectionOrLayer(true)); +document.addEventListener('keydown', (event) => { + if (!editorAlternateKeyEvent(event) || selectionShiftHeld) return; + selectionShiftHeld = true; + updateSelectionToolState(); +}); +document.addEventListener('keyup', (event) => { + if (!editorAlternateReleaseEvent(event)) return; + selectionShiftHeld = false; + updateSelectionToolState(); +}); +window.addEventListener('blur', () => { + if (!selectionShiftHeld) return; + selectionShiftHeld = false; + updateSelectionToolState(); +}); +updatePaperToolState(); +updateSelectionToolState(); + +// Flipnote Editor Page 4: Music + three Sound Effect tracks. The supplied +// DSi-style sprites are the controls themselves; Shift temporarily swaps the +// record/play controls to their import artwork so MP3/WAV can replace a track. +// PPM stores mono audio at 8,192 Hz as one ADPCM byte per two samples plus a +// four-byte decoder header. Keep imported clips within the actual byte budget +// (rather than just the nominal 2s/60s duration) so export can always fit. +const FLIPNOTE_BGM_ADPCM_MAX_BYTES = 245408; +const FLIPNOTE_SFX_ADPCM_MAX_BYTES = 8192; +const FLIPNOTE_MUSIC_MAX_SECONDS = ((FLIPNOTE_BGM_ADPCM_MAX_BYTES - 4) * 2) / 8192; +const FLIPNOTE_SOUND_EFFECT_LIMIT_SECONDS = ((FLIPNOTE_SFX_ADPCM_MAX_BYTES - 4) * 2) / 8192; +const FLIPNOTE_AUTO_MUSIC_MAX_FRAMES = FLIPNOTE_EDITOR_MAX_PAGES; +const audioControlButtons = Array.from(document.querySelectorAll('[data-audio-control]')); +const audioEraseButtons = Array.from(document.querySelectorAll('[data-audio-erase]')); +const audioFileInput = document.getElementById('flipnote-audio-file-input'); +const audioStatus = document.getElementById('flipnote-audio-status'); +const audioTrimModal = document.getElementById('flipnote-audio-trim-modal'); +const audioTrimClose = document.getElementById('flipnote-audio-trim-close'); +const audioTrimCancel = document.getElementById('flipnote-audio-trim-cancel'); +const audioTrimBack = document.getElementById('flipnote-audio-trim-back'); +const audioTrimSave = document.getElementById('flipnote-audio-trim-save'); +const audioPreviewButton = document.getElementById('flipnote-audio-preview'); +const audioImportSourceStep = document.getElementById('flipnote-audio-import-source-step'); +const audioImportSourceCopy = document.getElementById('flipnote-audio-import-source-copy'); +const audioImportSourceOptions = document.getElementById('flipnote-audio-import-source-options'); +const audioTrimStep = document.getElementById('flipnote-audio-trim-step'); + + + + + + + + + + + +const audioWaveform = document.getElementById('flipnote-audio-waveform'); +const audioStartRange = document.getElementById('flipnote-audio-start'); +const audioEndRange = document.getElementById('flipnote-audio-end'); +const audioStartValue = document.getElementById('flipnote-audio-start-value'); +const audioEndValue = document.getElementById('flipnote-audio-end-value'); +const audioSelectionDuration = document.getElementById('flipnote-audio-selection-duration'); +const audioTrimFilename = document.getElementById('flipnote-audio-trim-filename'); +const audioTrimLimit = document.getElementById('flipnote-audio-trim-limit'); +const audioTrimTitle = document.getElementById('flipnote-audio-trim-title'); +const audioAutoFramesOption = document.getElementById('flipnote-audio-auto-frames-option'); +const audioAutoFramesToggle = document.getElementById('flipnote-audio-auto-frames'); +const audioAutoFramesDetail = document.getElementById('flipnote-audio-auto-frames-detail'); +const audioTrimVolumeMeter = document.getElementById('flipnote-audio-trim-volume-meter'); +const audioTrimVolumeFill = document.getElementById('flipnote-audio-trim-volume-fill'); +const audioTrimVolumeValue = document.getElementById('flipnote-audio-trim-volume-value'); +const audioVolumeMonitor = document.getElementById('flipnote-audio-volume-monitor'); +const audioVolumeHistoryHost = document.getElementById('flipnote-audio-volume-history'); +const audioTracks = {music:null, sound1:null, sound2:null, sound3:null}; +flipnoteSpaceAudioTracks = audioTracks; +renderFlipnoteSpaceMeter(timeline); +let audioShiftHeld = false; +let audioImportTarget = null; +let activeAudioPlayback = null; +let activeAudioRecording = null; +let audioTrimState = null; + + + + + + + + + + +let sharedAudioContext = null; +let timelineMusicPlayback = null; +let timelineMusicGeneration = 0; +const timelineSoundSources = new Set(); +let timelineSoundGeneration = 0; +const AUDIO_VOLUME_HISTORY_COLUMNS = 17; +const AUDIO_VOLUME_HISTORY_INTERVAL_MS = 100; +const AUDIO_VOLUME_SAMPLE_INTERVAL_MS = 16; +const AUDIO_VOLUME_LOGICAL_WIDTH = 92; +const AUDIO_VOLUME_LOGICAL_HEIGHT = 84; +const AUDIO_VOLUME_BAR_START_X = 4; +const AUDIO_VOLUME_BAR_STEP_X = 5; +const AUDIO_VOLUME_BAR_BASELINE_Y = 76; +const AUDIO_VOLUME_BAR_TOP_Y = 2; +const AUDIO_VOLUME_RED_BOTTOM_Y = 18; +const AUDIO_VOLUME_GOOD_SRC = './assets/flipnote-editor/goodVolume.png'; +const AUDIO_VOLUME_HURT_SRC = './assets/flipnote-editor/earsHurt.png'; +let playbackVolumeAnalyser = null; +let playbackVolumeSamples = null; +let audioVolumeTimer = null; +let audioVolumeSampleTimer = null; +let audioVolumePeakHold = 0; +let audioVolumeHistory = Array(AUDIO_VOLUME_HISTORY_COLUMNS).fill(0); +const audioVolumeBars = []; + +function getFlipnoteAudioContext() { + if (!sharedAudioContext) { + const AudioContextClass = window.AudioContext || window.webkitAudioContext; + if (!AudioContextClass) throw new Error('This browser does not support Web Audio.'); + sharedAudioContext = new AudioContextClass(); + } + return sharedAudioContext; +} + +function initializeAudioVolumeMeter() { + if (!audioVolumeHistoryHost || audioVolumeBars.length) return; + for (let index = 0; index < AUDIO_VOLUME_HISTORY_COLUMNS; index += 1) { + const bar = document.createElement('img'); + bar.className = 'flipnote-editor-volume-sample'; + bar.src = AUDIO_VOLUME_GOOD_SRC; + bar.alt = ''; + bar.draggable = false; + bar.dataset.volumeSprite = 'good'; + bar.style.left = `${(((AUDIO_VOLUME_BAR_START_X + (index * AUDIO_VOLUME_BAR_STEP_X)) / AUDIO_VOLUME_LOGICAL_WIDTH) * 100).toFixed(6)}%`; + audioVolumeHistoryHost.appendChild(bar); + audioVolumeBars.push(bar); + } + renderAudioVolumeHistory(); +} + +function getPlaybackVolumeAnalyser() { + const context = getFlipnoteAudioContext(); + if (!playbackVolumeAnalyser) { + playbackVolumeAnalyser = context.createAnalyser(); + playbackVolumeAnalyser.fftSize = 256; + playbackVolumeAnalyser.smoothingTimeConstant = 0; + playbackVolumeSamples = new Float32Array(playbackVolumeAnalyser.fftSize); + // All editor playback is routed through this analyser so the meter sees + // Music and overlapping SE1/SE2/SE3 as one Flipnote output mix. + playbackVolumeAnalyser.connect(context.destination); + } + return playbackVolumeAnalyser; +} + +function connectAudioSourceToEditorOutput(source) { + source.connect(getPlaybackVolumeAnalyser()); + startAudioVolumeMeter(); +} + +function audioVolumeIsActive() { + return !!( + activeAudioRecording || + activeAudioPlayback || + timelineMusicPlayback || + timelineSoundSources.size || + (audioTrimState && audioTrimState.previewSource) + ); +} + +function readAudioVolumeLevel(analyser, samples) { + if (!analyser || !samples) return 0; + try { + analyser.getFloatTimeDomainData(samples); + } catch (error) { + return 0; + } + let peak = 0; + for (let index = 0; index < samples.length; index += 1) { + const value = Math.abs(samples[index] || 0); + if (value > peak) peak = value; + } + if (peak <= 0.00001) return 0; + // A dB mapping keeps quiet material visible while reserving the supplied + // pink/red warning band for genuinely loud peaks. + const decibels = 20 * Math.log10(peak); + return Math.max(0, Math.min(1, (decibels + 60) / 60)); +} + +function currentAudioVolumeLevel() { + const recording = activeAudioRecording; + if (recording && recording.volumeAnalyser && recording.volumeSamples) { + return readAudioVolumeLevel(recording.volumeAnalyser, recording.volumeSamples); + } + return readAudioVolumeLevel(playbackVolumeAnalyser, playbackVolumeSamples); +} + +function renderAudioVolumeHistory() { + if (!audioVolumeBars.length) return; + const travel = AUDIO_VOLUME_BAR_BASELINE_Y - AUDIO_VOLUME_BAR_TOP_Y; + audioVolumeBars.forEach((bar, index) => { + const level = Math.max(0, Math.min(1, Number(audioVolumeHistory[index]) || 0)); + const logicalY = Math.round(AUDIO_VOLUME_BAR_BASELINE_Y - (level * travel)); + const danger = logicalY < AUDIO_VOLUME_RED_BOTTOM_Y; + bar.style.top = `${((logicalY / AUDIO_VOLUME_LOGICAL_HEIGHT) * 100).toFixed(6)}%`; + const sprite = danger ? 'hurt' : 'good'; + if (bar.dataset.volumeSprite !== sprite) { + bar.dataset.volumeSprite = sprite; + bar.src = danger ? AUDIO_VOLUME_HURT_SRC : AUDIO_VOLUME_GOOD_SRC; + } + }); +} + +function renderAudioTrimVolume(level) { + const value = Math.max(0, Math.min(1, Number(level) || 0)); + const percent = Math.round(value * 100); + if (audioTrimVolumeFill) audioTrimVolumeFill.style.width = `${percent}%`; + if (audioTrimVolumeValue) audioTrimVolumeValue.textContent = `${percent}%`; + if (audioTrimVolumeMeter) { + audioTrimVolumeMeter.setAttribute('aria-valuenow', String(percent)); + audioTrimVolumeMeter.classList.toggle('is-danger', value >= ((AUDIO_VOLUME_BAR_BASELINE_Y - AUDIO_VOLUME_RED_BOTTOM_Y) / (AUDIO_VOLUME_BAR_BASELINE_Y - AUDIO_VOLUME_BAR_TOP_Y))); + } +} + +function audioVolumeSampleTick() { + const active = audioVolumeIsActive(); + const level = active ? currentAudioVolumeLevel() : 0; + if (active) audioVolumePeakHold = Math.max(audioVolumePeakHold, level); + // The trim dialog gets a smooth live meter, while the sidebar history moves + // at the deliberately slower history cadence below. + renderAudioTrimVolume((audioTrimState && audioTrimState.previewSource) ? level : 0); +} + +function audioVolumeMeterTick() { + const active = audioVolumeIsActive(); + const instant = active ? currentAudioVolumeLevel() : 0; + const level = active ? Math.max(audioVolumePeakHold, instant) : 0; + audioVolumePeakHold = 0; + audioVolumeHistory.shift(); + audioVolumeHistory.push(level); + renderAudioVolumeHistory(); + if (!active && audioVolumeHistory.every((value) => value <= 0.00001)) { + if (audioVolumeTimer) clearInterval(audioVolumeTimer); + if (audioVolumeSampleTimer) clearInterval(audioVolumeSampleTimer); + audioVolumeTimer = null; + audioVolumeSampleTimer = null; + renderAudioTrimVolume(0); + } +} + +function startAudioVolumeMeter() { + initializeAudioVolumeMeter(); + if (!audioVolumeMonitor) return; + if (!audioVolumeSampleTimer) audioVolumeSampleTimer = setInterval(audioVolumeSampleTick, AUDIO_VOLUME_SAMPLE_INTERVAL_MS); + if (!audioVolumeTimer) audioVolumeTimer = setInterval(audioVolumeMeterTick, AUDIO_VOLUME_HISTORY_INTERVAL_MS); +} + +initializeAudioVolumeMeter(); +renderAudioTrimVolume(0); + +function flipnoteDurationSeconds() { + const frames = Math.max(1, timeline && timeline.frames ? timeline.frames.length : 1); + const fps = Math.max(0.01, Number(timeline && timeline.framerate) || 12); + return frames / fps; +} + +function audioTrackLimitSeconds(key) { + return key === 'music' + ? Math.max(0.05, Math.min(flipnoteDurationSeconds(), FLIPNOTE_MUSIC_MAX_SECONDS)) + : FLIPNOTE_SOUND_EFFECT_LIMIT_SECONDS; +} + +function audioTrackLabel(key) { + if (key === 'music') return 'Music'; + return `Sound ${String(key || '').replace('sound','') || '1'}`; +} + +function formatAudioSeconds(value) { + return `${Math.max(0, Number(value) || 0).toFixed(2)}s`; +} + +function flipnoteTimelineFps() { + return Math.max(0.01, Number(timeline && timeline.framerate) || 12); +} + +function audioTrackReferenceFps(track) { + return Math.max(0.01, Number(track && track.referenceFps) || flipnoteTimelineFps()); +} + +function audioTrackPlaybackRate(track, key) { + // Flipnote BGM is speed-linked: changing animation speed changes both + // Music tempo and pitch while preserving its frame alignment. SE1/SE2/SE3 + // behave like original Flipnote Studio sound effects and always play at + // the recorded/imported file's original speed and pitch. + if (/^sound[123]$/.test(String(key || ''))) return 1; + return Math.max(0.01, Math.min(64, flipnoteTimelineFps() / audioTrackReferenceFps(track))); +} + +function audioTrackEffectiveDuration(track, key) { + if (!track) return 0; + return Math.max(0, Number(track.duration) || 0) / audioTrackPlaybackRate(track, key); +} + +function setAudioStatus(message) { + if (audioStatus) audioStatus.textContent = message || ''; +} + +function renderAudioEraseButtons() { + const timelineLive = !!(timeline && timeline.playing); + const recording = !!activeAudioRecording; + audioEraseButtons.forEach((button) => { + const key = button.dataset.audioErase; + const hasTrack = !!audioTracks[key]; + const blocked = timelineLive || recording; + button.hidden = !hasTrack; + button.disabled = !hasTrack || blocked; + button.inert = !hasTrack || blocked; + const title = blocked + ? `Stop ${timelineLive ? 'Flipnote playback' : 'recording'} before erasing ${audioTrackLabel(key)}.` + : `Erase ${audioTrackLabel(key)}`; + button.title = title; + button.setAttribute('aria-label', title); + }); +} + +function renderAudioButtons() { + audioControlButtons.forEach((button) => { + const key = button.dataset.audioControl; + const image = button.querySelector('img'); + if (!image) return; + const timelineLive = !!(timeline && timeline.playing); + const liveSound = timelineLive && /^sound[123]$/.test(key); + const emptyLiveSound = liveSound && !audioTracks[key]; + const recording = activeAudioRecording && activeAudioRecording.key === key; + const previewPlaying = activeAudioPlayback && activeAudioPlayback.key === key; + const musicPlayingWithFlipnote = timelineLive && key === 'music' && !!audioTracks.music; + const playing = previewPlaying || musicPlayingWithFlipnote; + let src = button.dataset.recordSrc; + let title = `Record ${audioTrackLabel(key)}. Hold Shift to import an MP3 or WAV.`; + button.classList.toggle('is-recording', !!recording); + button.classList.toggle('is-playing', !!playing); + button.inert = timelineLive && key === 'music'; + button.classList.toggle('is-live-sfx', !!liveSound); + button.classList.toggle('is-live-empty', !!emptyLiveSound); + button.classList.toggle('is-import-mode', !timelineLive && audioShiftHeld && !recording && !playing); + + if (liveSound) { + if (audioTracks[key]) { + src = button.dataset.playSrc; + const number = key.replace('sound', ''); + title = `Play ${audioTrackLabel(key)} now and add it to the current page (${number}).`; + } else { + src = button.dataset.recordSrc; + title = `${audioTrackLabel(key)} has no clip yet. Stop playback to record or import one.`; + } + } else if (musicPlayingWithFlipnote) { + src = button.dataset.stopSrc || button.dataset.playSrc; + title = 'Music is playing with the Flipnote.'; + } else if (recording || previewPlaying) { + src = button.dataset.stopSrc; + title = recording ? `Stop recording ${audioTrackLabel(key)}` : `Stop ${audioTrackLabel(key)} playback`; + } else if (audioShiftHeld) { + src = (button.classList.contains('is-audio-hover') && button.dataset.importAltSrc) ? button.dataset.importAltSrc : button.dataset.importSrc; + title = `Import ${audioTrackLabel(key)} from MP3 or WAV`; + } else if (audioTracks[key]) { + src = button.dataset.playSrc; + title = `Play ${audioTrackLabel(key)}. Hold Shift to import a replacement MP3 or WAV.`; + } + if (src && image.getAttribute('src') !== src) image.src = src; + image.alt = audioTrackLabel(key); + button.title = title; + button.setAttribute('aria-label', title); + }); + renderAudioEraseButtons(); +} + +function updateAudioFillBars(recordingElapsed) { + ['music','sound1','sound2','sound3'].forEach((key) => { + const fill = document.querySelector(`[data-audio-fill="${key}"]`); + if (!fill) return; + const limit = Math.max(0.05, audioTrackLimitSeconds(key)); + let duration = audioTracks[key] ? Math.min(audioTrackEffectiveDuration(audioTracks[key], key), limit) : 0; + if (activeAudioRecording && activeAudioRecording.key === key && Number.isFinite(recordingElapsed)) { + duration = Math.min(limit, Math.max(0, recordingElapsed)); + } + const percent = Math.max(0, Math.min(100, (duration / limit) * 100)); + if (key === 'music') { + // musicBarEmpty.png is 1920x300. Its fill window is exactly + // x=24..1896 and y=24..276. The artwork is rotated 90deg in + // the UI, so source-x becomes the vertical progress axis: + // 1872 / 1920 = 97.5% usable height, inset 1.25% at each end. + fill.style.height = `${(percent * 0.975).toFixed(3)}%`; + } else { + // soundBarEmpry.png is 768x300. Its fill window is exactly + // x=24..744 and y=24..276: 720 / 768 = 93.75% usable width, + // inset 3.125% at each horizontal end. + fill.style.width = `${(percent * 0.9375).toFixed(3)}%`; + } + }); +} + +function stopAudioPlayback() { + if (!activeAudioPlayback) return; + const playing = activeAudioPlayback; + activeAudioPlayback = null; + if (playing.timer) clearTimeout(playing.timer); + if (playing.source) { + playing.source.onended = null; + try { playing.source.stop(); } catch (error) {} + try { playing.source.disconnect(); } catch (error) {} + } + if (playing.audio) { + try { playing.audio.pause(); } catch (error) {} + try { playing.audio.currentTime = 0; } catch (error) {} + } + renderAudioButtons(); +} + +function stopTrimPreview() { + if (!audioTrimState || !audioTrimState.previewSource) { + renderAudioTrimVolume(0); + return; + } + const source = audioTrimState.previewSource; + audioTrimState.previewSource = null; + if (audioTrimState.previewTimer) clearTimeout(audioTrimState.previewTimer); + audioTrimState.previewTimer = null; + try { source.stop(); } catch (error) {} + if (audioPreviewButton) audioPreviewButton.textContent = 'Preview clip'; + renderAudioTrimVolume(0); +} + +function stopAudioRecording() { + if (!activeAudioRecording) return; + const state = activeAudioRecording; + if (state.timer) clearTimeout(state.timer); + if (state.raf) cancelAnimationFrame(state.raf); + if (state.recorder && state.recorder.state !== 'inactive') { + try { state.recorder.stop(); } catch (error) {} + } +} + +function stopFlipnoteEditorAudio() { + stopAudioPlayback(); + stopTrimPreview(); + stopAudioRecording(); + stopFlipnoteTimelineAudio(); +} + +function setAudioTrack(key, blob, duration, sourceName, decodedBuffer, referenceFps, sourceKind = 'unknown') { + const candidate = { + blob, + duration: Math.max(0, Number(duration) || 0), + sourceName: sourceName || audioTrackLabel(key), + buffer: decodedBuffer || null, + referenceFps: Math.max(0.01, Number(referenceFps) || flipnoteTimelineFps()), + sourceKind: String(sourceKind || 'unknown'), + + }; + const projectedTracks = {...audioTracks, [key]: candidate}; + if (!editorDirtyTrackingSuspended && !flipnoteSpaceAllowsProjection(timeline, {audioTracks: projectedTracks})) { + notifyFlipnoteSpaceLimit(timeline); + setAudioStatus(`${audioTrackLabel(key)} was not added because the Flipnote would exceed 1,024 KB.`); + return false; + } + markEditorDirty(); + const old = audioTracks[key]; + if (old && old.url) URL.revokeObjectURL(old.url); + candidate.url = URL.createObjectURL(blob); + audioTracks[key] = candidate; + updateAudioFillBars(); + renderAudioButtons(); + renderFlipnoteSpaceMeter(timeline); + setAudioStatus(`${audioTrackLabel(key)} ready, ${formatAudioSeconds(duration)}.`); + return true; +} + +function clearSoundEffectTriggers(key) { + if (!/^sound[123]$/.test(key) || !timeline || !Array.isArray(timeline.frames)) return 0; + let cleared = 0; + timeline.frames.forEach((frame) => { + if (!frame) return; + frame.soundFlags = normalizeFrameSoundFlags(frame.soundFlags); + if (frame.soundFlags[key]) { + frame.soundFlags[key] = false; + cleared += 1; + } + }); + return cleared; +} + +function eraseAudioTrack(key) { + const track = audioTracks[key]; + if (!track) return; + if (timeline && timeline.playing) { + setAudioStatus('Stop Flipnote playback before erasing audio.'); + return; + } + if (activeAudioRecording) { + setAudioStatus('Stop recording before erasing audio.'); + return; + } + if (activeAudioPlayback && activeAudioPlayback.key === key) stopAudioPlayback(); + if (key === 'music') stopTimelineMusicPlayback(); + else stopTimelineSoundEffects(); + + markEditorDirty(); + audioTracks[key] = null; + if (track.url) URL.revokeObjectURL(track.url); + const cleared = clearSoundEffectTriggers(key); + updateAudioFillBars(); + renderAudioButtons(); + renderFlipnoteSpaceMeter(timeline); + setAudioStatus(cleared + ? `${audioTrackLabel(key)} erased, including ${cleared} page trigger${cleared === 1 ? '' : 's'}.` + : `${audioTrackLabel(key)} erased.`); +} + +async function ensureAudioTrackBuffer(key) { + const track = audioTracks[key]; + if (!track) return null; + if (!track.buffer) track.buffer = await decodeAudioBlob(track.blob); + return audioTracks[key] === track ? track.buffer : null; +} + +function stopTimelineMusicPlayback() { + timelineMusicGeneration += 1; + const playback = timelineMusicPlayback; + timelineMusicPlayback = null; + if (!playback) return; + if (playback.source) { + playback.source.onended = null; + try { playback.source.stop(); } catch (error) {} + try { playback.source.disconnect(); } catch (error) {} + } +} + +function stopTimelineSoundEffects() { + timelineSoundGeneration += 1; + timelineSoundSources.forEach((source) => { + source.onended = null; + try { source.stop(); } catch (error) {} + try { source.disconnect(); } catch (error) {} + }); + timelineSoundSources.clear(); +} + +function stopFlipnoteTimelineAudio() { + stopTimelineMusicPlayback(); + stopTimelineSoundEffects(); + renderAudioButtons(); +} + +async function restartFlipnoteTimelineMusic(frameIndex) { + stopTimelineMusicPlayback(); + const generation = timelineMusicGeneration; + const track = audioTracks.music; + if (!track || !timeline || !timeline.playing) { + renderAudioButtons(); + return; + } + try { + const context = getFlipnoteAudioContext(); + try { await context.resume(); } catch (error) {} + const buffer = await ensureAudioTrackBuffer('music'); + if (!buffer || !timeline.playing || audioTracks.music !== track || generation !== timelineMusicGeneration) return; + + const referenceFps = audioTrackReferenceFps(track); + const playbackRate = audioTrackPlaybackRate(track, 'music'); + const frame = Math.max(0, Math.min(timeline.frames.length - 1, Number(frameIndex) || 0)); + // Buffer offsets are measured in the track's original/reference-speed + // seconds, not today's wall-clock seconds. This keeps page N aligned + // to the same point in the music at every Flipnote speed. + const offset = frame / referenceFps; + const animationRemainingInBufferSeconds = Math.max(0.01, (timeline.frames.length - frame) / referenceFps); + const trackDuration = Math.min(track.duration || buffer.duration, buffer.duration); + if (offset >= trackDuration - 0.005) return; + const duration = Math.max(0.01, Math.min(trackDuration - offset, animationRemainingInBufferSeconds)); + const source = context.createBufferSource(); + source.buffer = buffer; + source.playbackRate.value = playbackRate; + connectAudioSourceToEditorOutput(source); + const playback = {source, track, playbackRate}; + timelineMusicPlayback = playback; + source.onended = () => { + if (timelineMusicPlayback === playback) { + timelineMusicPlayback = null; + renderAudioButtons(); + } + }; + source.start(0, offset, duration); + renderAudioButtons(); + } catch (error) { + stopTimelineMusicPlayback(); + setAudioStatus('Music playback failed.'); + renderAudioButtons(); + } +} + +async function playTimelineSoundEffect(key) { + const track = audioTracks[key]; + const generation = timelineSoundGeneration; + if (!track || !/^sound[123]$/.test(key)) return false; + try { + const context = getFlipnoteAudioContext(); + try { await context.resume(); } catch (error) {} + const buffer = await ensureAudioTrackBuffer(key); + if (!buffer || audioTracks[key] !== track || generation !== timelineSoundGeneration || !timeline || !timeline.playing) return false; + const duration = Math.max(0.01, Math.min(track.duration || buffer.duration, buffer.duration, FLIPNOTE_SOUND_EFFECT_LIMIT_SECONDS)); + const source = context.createBufferSource(); + source.buffer = buffer; + source.playbackRate.value = 1; + connectAudioSourceToEditorOutput(source); + timelineSoundSources.add(source); + source.onended = () => { + timelineSoundSources.delete(source); + try { source.disconnect(); } catch (error) {} + }; + source.start(0, 0, duration); + return true; + } catch (error) { + setAudioStatus(`${audioTrackLabel(key)} playback failed.`); + return false; + } +} + +function frameHasSound(frame, key) { + if (!frame) return false; + frame.soundFlags = normalizeFrameSoundFlags(frame.soundFlags); + return !!frame.soundFlags[key]; +} + +function stampSoundEffectOnFrame(frameIndex, key) { + if (!/^sound[123]$/.test(key)) return false; + const frame = timeline && timeline.getFrame(frameIndex); + if (!frame) return false; + frame.soundFlags = normalizeFrameSoundFlags(frame.soundFlags); + if (!frame.soundFlags[key]) { + markEditorDirty(); + frame.soundFlags[key] = true; + } + return true; +} + +function pulseAudioButton(key) { + const button = document.querySelector(`[data-audio-control="${key}"]`); + if (!button) return; + button.classList.remove('is-live-fired'); + void button.offsetWidth; + button.classList.add('is-live-fired'); + setTimeout(() => button.classList.remove('is-live-fired'), 170); +} + +async function triggerLiveSoundEffect(key) { + if (!timeline || !timeline.playing || !/^sound[123]$/.test(key)) return false; + if (!audioTracks[key]) { + setAudioStatus(`${audioTrackLabel(key)} has no clip. Stop playback to record or import it.`); + pulseAudioButton(key); + return false; + } + const frameIndex = timeline.currentFrame; + stampSoundEffectOnFrame(frameIndex, key); + pulseAudioButton(key); + const played = await playTimelineSoundEffect(key); + if (played) setAudioStatus(`${audioTrackLabel(key)} added to page ${frameIndex + 1}.`); + return played; +} + +function playStampedSoundEffectsForFrame(frameIndex) { + if (!timeline || !timeline.playing) return; + const frame = timeline.getFrame(frameIndex); + ['sound1', 'sound2', 'sound3'].forEach((key) => { + if (frameHasSound(frame, key) && audioTracks[key]) void playTimelineSoundEffect(key); + }); +} + +function startFlipnoteTimelineAudio(frameIndex) { + stopFlipnoteTimelineAudio(); + if (!timeline || !timeline.playing) return; + void restartFlipnoteTimelineMusic(frameIndex); + playStampedSoundEffectsForFrame(frameIndex); +} + +async function playAudioTrack(key) { + const track = audioTracks[key]; + if (!track) return; + stopAudioPlayback(); + stopTrimPreview(); + try { + const context = getFlipnoteAudioContext(); + try { await context.resume(); } catch (error) {} + if (!track.buffer) track.buffer = await decodeAudioBlob(track.blob); + if (audioTracks[key] !== track) return; + const allowedDuration = Math.max(0.02, Math.min(track.duration || track.buffer.duration, track.buffer.duration)); + const playbackRate = audioTrackPlaybackRate(track, key); + const source = context.createBufferSource(); + source.buffer = track.buffer; + source.playbackRate.value = playbackRate; + connectAudioSourceToEditorOutput(source); + const playback = { + key, + track, + source, + timer:null, + rawRemaining:allowedDuration, + rateChangedAt:context.currentTime, + playbackRate, + }; + activeAudioPlayback = playback; + source.onended = () => { + if (activeAudioPlayback === playback) stopAudioPlayback(); + }; + renderAudioButtons(); + source.start(0, 0, allowedDuration); + playback.timer = setTimeout(() => { + if (activeAudioPlayback === playback) stopAudioPlayback(); + }, Math.ceil((allowedDuration / playbackRate) * 1000) + 60); + setAudioStatus(`Playing ${audioTrackLabel(key)}.`); + } catch (error) { + stopAudioPlayback(); + setAudioStatus(`${audioTrackLabel(key)} playback failed.`); + alert('Editor could not play this audio clip.'); + } +} + +function syncActiveAudioPreviewPlaybackRate() { + const playback = activeAudioPlayback; + if (!playback || !playback.source || !playback.track) return; + try { + const context = getFlipnoteAudioContext(); + const now = context.currentTime; + const oldRate = Math.max(0.01, Number(playback.playbackRate) || 1); + const elapsed = Math.max(0, now - (Number(playback.rateChangedAt) || now)); + playback.rawRemaining = Math.max(0, (Number(playback.rawRemaining) || 0) - (elapsed * oldRate)); + const nextRate = audioTrackPlaybackRate(playback.track, playback.key); + playback.source.playbackRate.setValueAtTime(nextRate, now); + playback.playbackRate = nextRate; + playback.rateChangedAt = now; + if (playback.timer) clearTimeout(playback.timer); + if (playback.rawRemaining <= 0.001) { + stopAudioPlayback(); + return; + } + playback.timer = setTimeout(() => { + if (activeAudioPlayback === playback) stopAudioPlayback(); + }, Math.ceil((playback.rawRemaining / nextRate) * 1000) + 60); + } catch (error) { + // If a browser refuses a live AudioParam update, the next playback will + // still use the correct rate. Do not interrupt the editor for this. + } +} + +function chooseRecordingMimeType() { + if (!window.MediaRecorder || typeof MediaRecorder.isTypeSupported !== 'function') return ''; + const choices = ['audio/webm;codecs=opus','audio/ogg;codecs=opus','audio/webm','audio/ogg']; + return choices.find((type) => MediaRecorder.isTypeSupported(type)) || ''; +} + +async function decodeAudioBlob(blob) { + const context = getFlipnoteAudioContext(); + const bytes = await blob.arrayBuffer(); + return await context.decodeAudioData(bytes.slice(0)); +} + +function audioBufferSliceToWavBlob(buffer, startSeconds, endSeconds) { + const sampleRate = buffer.sampleRate; + const startFrame = Math.max(0, Math.min(buffer.length - 1, Math.floor(startSeconds * sampleRate))); + const endFrame = Math.max(startFrame + 1, Math.min(buffer.length, Math.ceil(endSeconds * sampleRate))); + const frames = endFrame - startFrame; + const channels = Math.max(1, Math.min(2, buffer.numberOfChannels)); + const bytesPerSample = 2; + const blockAlign = channels * bytesPerSample; + const dataBytes = frames * blockAlign; + const out = new ArrayBuffer(44 + dataBytes); + const view = new DataView(out); + const writeString = (offset, value) => { for (let i = 0; i < value.length; i++) view.setUint8(offset + i, value.charCodeAt(i)); }; + writeString(0, 'RIFF'); + view.setUint32(4, 36 + dataBytes, true); + writeString(8, 'WAVE'); + writeString(12, 'fmt '); + view.setUint32(16, 16, true); + view.setUint16(20, 1, true); + view.setUint16(22, channels, true); + view.setUint32(24, sampleRate, true); + view.setUint32(28, sampleRate * blockAlign, true); + view.setUint16(32, blockAlign, true); + view.setUint16(34, 16, true); + writeString(36, 'data'); + view.setUint32(40, dataBytes, true); + const channelData = []; + for (let channel = 0; channel < channels; channel++) channelData.push(buffer.getChannelData(channel)); + let offset = 44; + for (let frame = 0; frame < frames; frame++) { + for (let channel = 0; channel < channels; channel++) { + let sample = channelData[channel][startFrame + frame] || 0; + sample = Math.max(-1, Math.min(1, sample)); + view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true); + offset += 2; + } + } + return new Blob([out], {type:'audio/wav'}); +} + +async function startAudioRecording(key) { + if (activeAudioRecording) return; + stopAudioPlayback(); + stopTrimPreview(); + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia || !window.MediaRecorder) { + alert('Audio recording is not supported by this browser.'); + return; + } + const limit = audioTrackLimitSeconds(key); + let stream; + try { + stream = await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,echoCancellation:false,noiseSuppression:false,autoGainControl:false}}); + } catch (error) { + // Some devices reject optional audio settings despite granting access. + // Ask for the default microphone before reporting a device failure. + if (error && (error.name === 'OverconstrainedError' || error.name === 'NotFoundError' || error.name === 'TypeError')) { + try { stream = await navigator.mediaDevices.getUserMedia({audio:true}); } + catch (fallbackError) { error = fallbackError; } + } + if (!stream) { + if (error && (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError' || error.name === 'SecurityError')) { + alert('Microphone access is blocked. Allow it for Editor in your browser and device settings, then try again.'); + } else if (error && (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError')) { + alert('No microphone was found. Connect or enable one, then try again.'); + } else if (error && (error.name === 'NotReadableError' || error.name === 'TrackStartError')) { + alert('The microphone is allowed, but could not be started. Check that it is not in use by another app, then try again.'); + } else { + alert('The microphone could not be started. Check your browser and device audio settings, then try again.'); + } + return; + } + } + let recorder; + try { + const mimeType = chooseRecordingMimeType(); + recorder = mimeType ? new MediaRecorder(stream, {mimeType}) : new MediaRecorder(stream); + } catch (error) { + stream.getTracks().forEach((track) => track.stop()); + alert('Editor could not start the microphone recorder in this browser.'); + return; + } + const state = {key, recorder, stream, chunks:[], startedAt:performance.now(), limit, referenceFps:flipnoteTimelineFps(), timer:null, raf:null, volumeSource:null, volumeAnalyser:null, volumeSamples:null}; + activeAudioRecording = state; + try { + const context = getFlipnoteAudioContext(); + try { await context.resume(); } catch (error) {} + state.volumeAnalyser = context.createAnalyser(); + state.volumeAnalyser.fftSize = 256; + state.volumeAnalyser.smoothingTimeConstant = 0; + state.volumeSamples = new Float32Array(state.volumeAnalyser.fftSize); + state.volumeSource = context.createMediaStreamSource(stream); + // Deliberately do not connect the microphone analyser to destination: it + // drives the meter only, avoiding microphone feedback while recording. + state.volumeSource.connect(state.volumeAnalyser); + startAudioVolumeMeter(); + } catch (error) { + state.volumeSource = null; + state.volumeAnalyser = null; + state.volumeSamples = null; + } + recorder.addEventListener('dataavailable', (event) => { if (event.data && event.data.size) state.chunks.push(event.data); }); + recorder.addEventListener('stop', async () => { + if (state.timer) clearTimeout(state.timer); + if (state.raf) cancelAnimationFrame(state.raf); + if (state.volumeSource) { try { state.volumeSource.disconnect(); } catch (error) {} } + if (state.volumeAnalyser) { try { state.volumeAnalyser.disconnect(); } catch (error) {} } + state.stream.getTracks().forEach((track) => track.stop()); + if (activeAudioRecording === state) activeAudioRecording = null; + renderAudioButtons(); + const blob = new Blob(state.chunks, {type: recorder.mimeType || 'audio/webm'}); + if (!blob.size) { updateAudioFillBars(); return; } + try { + const buffer = await decodeAudioBlob(blob); + const duration = Math.max(0.01, Math.min(buffer.duration, state.limit)); + const wav = audioBufferSliceToWavBlob(buffer, 0, duration); + setAudioTrack(key, wav, duration, 'Microphone recording', buffer, state.referenceFps, 'microphone'); + } catch (error) { + updateAudioFillBars(); + alert('The recording finished, but Editor could not decode the recorded audio.'); + } + }, {once:true}); + recorder.start(100); + renderAudioButtons(); + setAudioStatus(`Recording ${audioTrackLabel(key)}.`); + const tick = () => { + if (activeAudioRecording !== state) return; + const elapsed = Math.min(state.limit, (performance.now() - state.startedAt) / 1000); + updateAudioFillBars(elapsed); + state.raf = requestAnimationFrame(tick); + }; + state.raf = requestAnimationFrame(tick); + state.timer = setTimeout(() => stopAudioRecording(), Math.ceil(limit * 1000)); +} + +function drawAudioWaveform() { + if (!audioTrimState || !audioWaveform) return; + const ctx = audioWaveform.getContext('2d'); + const width = audioWaveform.width; + const height = audioWaveform.height; + const buffer = audioTrimState.buffer; + const data = buffer.getChannelData(0); + const styles = getComputedStyle(document.documentElement); + const isLight = document.documentElement.classList.contains('theme-light'); + ctx.clearRect(0, 0, width, height); + ctx.fillStyle = isLight ? '#f3eef4' : '#0d0c0f'; + ctx.fillRect(0, 0, width, height); + const startX = Math.round((audioTrimState.start / buffer.duration) * width); + const endX = Math.round((audioTrimState.end / buffer.duration) * width); + ctx.fillStyle = isLight ? 'rgba(174,48,31,.13)' : 'rgba(174,48,31,.22)'; + ctx.fillRect(startX, 0, Math.max(1, endX - startX), height); + const step = Math.max(1, Math.floor(data.length / width)); + const amp = height * 0.44; + ctx.strokeStyle = isLight ? '#5b4f5e' : '#c9becd'; + ctx.lineWidth = 1; + ctx.beginPath(); + for (let x = 0; x < width; x++) { + let min = 1, max = -1; + const offset = x * step; + for (let i = 0; i < step && offset + i < data.length; i++) { + const value = data[offset + i]; + if (value < min) min = value; + if (value > max) max = value; + } + ctx.moveTo(x + .5, (1 + min) * amp); + ctx.lineTo(x + .5, (1 + max) * amp); + } + ctx.stroke(); + ctx.strokeStyle = '#AE301F'; + ctx.lineWidth = 2; + ctx.beginPath(); ctx.moveTo(startX, 0); ctx.lineTo(startX, height); ctx.moveTo(endX, 0); ctx.lineTo(endX, height); ctx.stroke(); +} + +function autoMusicFrameLimitFrames() { + return FLIPNOTE_AUTO_MUSIC_MAX_FRAMES; +} + +function autoMusicFrameLimitSeconds() { + const fps = Math.max(0.01, Number(timeline && timeline.framerate) || 12); + return Math.min(FLIPNOTE_MUSIC_MAX_SECONDS, autoMusicFrameLimitFrames() / fps); +} + +function musicAutoFramesEnabled() { + return !!(audioTrimState && audioTrimState.key === 'music' && audioAutoFramesToggle && audioAutoFramesToggle.checked); +} + +function updateAudioAutoFramesSummary() { + if (!audioAutoFramesOption || !audioAutoFramesDetail) return; + const isMusic = !!audioTrimState && audioTrimState.key === 'music'; + audioAutoFramesOption.hidden = !isMusic; + if (!isMusic) return; + + const duration = Math.max(0, audioTrimState.end - audioTrimState.start); + const fps = Math.max(0.01, Number(timeline && timeline.framerate) || 12); + const currentFrames = Math.max(1, timeline && timeline.frames ? timeline.frames.length : 1); + const requiredFrames = Math.max(1, Math.ceil((duration * fps) - 1e-9)); + const targetFrames = Math.min(autoMusicFrameLimitFrames(), Math.max(currentFrames, requiredFrames)); + const pagesToAdd = Math.max(0, targetFrames - currentFrames); + + if (audioAutoFramesToggle && audioAutoFramesToggle.checked) { + audioAutoFramesDetail.textContent = pagesToAdd + ? `Adds ${pagesToAdd} blank page${pagesToAdd === 1 ? '' : 's'} (${currentFrames} → ${targetFrames}) at Speed ${timeline.speedLevel} · ${speedSetting(timeline.speedLevel).fps} FPS.` + : `No pages needed — ${currentFrames} pages already cover this clip at Speed ${timeline.speedLevel}.`; + } else { + audioAutoFramesDetail.textContent = `Off: music stays limited to the current ${currentFrames}-page Flipnote. Turn this on to use a longer clip and extend up to ${autoMusicFrameLimitFrames()} pages.`; + } +} + +function updateAudioTrimControls(changed) { + if (!audioTrimState) return; + const duration = audioTrimState.buffer.duration; + const minimum = Math.min(0.01, duration); + let start = Math.max(0, Math.min(duration - minimum, Number(audioStartRange.value) || 0)); + let end = Math.max(start + minimum, Math.min(duration, Number(audioEndRange.value) || duration)); + const maxClip = Math.min(audioTrimState.maxClip, duration); + if (end - start > maxClip) { + if (changed === 'start') end = Math.min(duration, start + maxClip); + else start = Math.max(0, end - maxClip); + } + if (end <= start) end = Math.min(duration, start + minimum); + audioTrimState.start = start; + audioTrimState.end = end; + audioStartRange.value = String(start); + audioEndRange.value = String(end); + if (audioStartValue) audioStartValue.textContent = formatAudioSeconds(start); + if (audioEndValue) audioEndValue.textContent = formatAudioSeconds(end); + if (audioSelectionDuration) audioSelectionDuration.textContent = formatAudioSeconds(end - start); + updateAudioAutoFramesSummary(); + stopTrimPreview(); + drawAudioWaveform(); +} + +function toggleAudioAutoFrames() { + if (!audioTrimState || audioTrimState.key !== 'music') return; + const previousMax = audioTrimState.maxClip; + const selectedDuration = Math.max(0, audioTrimState.end - audioTrimState.start); + const wasAtLimit = Math.abs(selectedDuration - previousMax) < 0.025; + const nextMax = musicAutoFramesEnabled() + ? Math.min(audioTrimState.buffer.duration, autoMusicFrameLimitSeconds()) + : Math.min(audioTrimState.buffer.duration, audioTrackLimitSeconds('music')); + + audioTrimState.maxClip = nextMax; + if (wasAtLimit && nextMax > previousMax) { + audioTrimState.end = Math.min(audioTrimState.buffer.duration, audioTrimState.start + nextMax); + audioEndRange.value = String(audioTrimState.end); + } + updateAudioTrimControls(audioAutoFramesToggle && audioAutoFramesToggle.checked ? 'end' : 'start'); +} + +function extendTimelineToAudioDuration(duration) { + if (!timeline || !timeline.frames || !timeline.frames.length) return 0; + const fps = Math.max(0.01, Number(timeline.framerate) || 12); + const requiredFrames = Math.min(autoMusicFrameLimitFrames(), Math.max(1, Math.ceil((Math.max(0, duration) * fps) - 1e-9))); + const pagesToAdd = Math.max(0, requiredFrames - timeline.frames.length); + if (!pagesToAdd) return 0; + + let added = 0; + for (let i = 0; i < pagesToAdd; i++) { + if (timeline.frames.length >= FLIPNOTE_EDITOR_MAX_PAGES) { + notifyFlipnotePageLimit(timeline); + break; + } + const previous = timeline.frames[timeline.frames.length - 1]; + const frame = new Frame({paperColor: previous ? previous.paperColor : 'white'}); + if (!flipnoteSpaceCanAddFrame(timeline, frame)) { + notifyFlipnoteSpaceLimit(timeline); + break; + } + // Seed the blank page's undo baseline without repainting the history UI + // hundreds of times during a long music import. + frame.undoBuffer = [frameSnapshot(frame)]; + frame.undoPointer = 0; + timeline.frames.push(frame); + flipnoteSpaceRegisterFrame(frame); + added += 1; + } + timeline.createThumbnails(); + document.getElementById('frame-counter').textContent = `${timeline.currentFrame + 1} / ${timeline.frames.length}`; + updateAudioFillBars(); + renderFlipnoteSpaceMeter(timeline); + return added; +} + + + + + + +function audioImportTargetLabel() { + return audioTrackLabel(audioImportTarget || 'music'); +} + +function setAudioImportControlVisible(control, visible) { + if (!control) return; + control.hidden = !visible; + // Editor's shared .button rule sets display explicitly, which can beat + // the browser's default [hidden] style. Guard each footer action inline so + // page 1 only shows Cancel, and the trim page shows its own actions. + control.style.display = visible ? '' : 'none'; +} + +function showAudioImportStep(step) { + const source = step === 'source'; + if (audioImportSourceStep) audioImportSourceStep.hidden = !source; + if (audioTrimStep) audioTrimStep.hidden = source; + setAudioImportControlVisible(audioTrimBack, !source); + setAudioImportControlVisible(audioPreviewButton, !source); + setAudioImportControlVisible(audioTrimSave, !source); + if (audioTrimTitle) { + audioTrimTitle.textContent = source + ? `Import ${audioImportTargetLabel()}` + : (audioImportTarget === 'music' ? 'Trim music to the Flipnote' : `Trim ${audioImportTargetLabel()}`); + } +} + +function openAudioImportModal(key) { + if (!audioTrimModal || !key) return; + stopAudioPlayback(); + stopTrimPreview(); + + audioImportTarget = key; + audioTrimState = null; + if (audioFileInput) audioFileInput.value = ''; + if (audioAutoFramesToggle) audioAutoFramesToggle.checked = false; + if (audioImportSourceCopy) audioImportSourceCopy.hidden = false; + if (audioImportSourceOptions) audioImportSourceOptions.hidden = false; + + + + + + + + + + + + showAudioImportStep('source'); + audioTrimModal.hidden = false; + document.body.classList.add('flipnote-audio-trim-open'); + setTimeout(() => audioUploadOption?.focus(), 0); +} + + + + + + + + + + + + + + + + + +function backToAudioImportSource() { + stopTrimPreview(); renderAudioTrimVolume(0); audioTrimState = null; + showAudioImportStep('source'); audioUploadOption?.focus(); +} + +async function openAudioTrimModal(key, file, buffer, sourceKind = 'file_import') { + if (!audioTrimModal || !buffer || !buffer.duration) return; + stopAudioPlayback(); + + audioImportTarget = key; + if (audioAutoFramesToggle) audioAutoFramesToggle.checked = false; + const maxClip = Math.min(buffer.duration, audioTrackLimitSeconds(key)); + audioTrimState = {key, file, buffer, sourceKind:String(sourceKind || 'file_import'), start:0, end:maxClip, maxClip, previewSource:null, previewTimer:null}; + if (audioTrimFilename) audioTrimFilename.textContent = file.name || `${audioTrackLabel(key)} audio`; + if (audioTrimLimit) audioTrimLimit.textContent = key === 'music' ? `Flipnote length ${formatAudioSeconds(audioTrackLimitSeconds(key))}` : `Sound clip limit ${formatAudioSeconds(FLIPNOTE_SOUND_EFFECT_LIMIT_SECONDS)}`; + audioStartRange.min = '0'; audioStartRange.max = String(buffer.duration); audioStartRange.step = '0.01'; audioStartRange.value = '0'; + audioEndRange.min = '0'; audioEndRange.max = String(buffer.duration); audioEndRange.step = '0.01'; audioEndRange.value = String(maxClip); + audioTrimModal.hidden = false; + renderAudioTrimVolume(0); + document.body.classList.add('flipnote-audio-trim-open'); + showAudioImportStep('trim'); + updateAudioTrimControls('end'); + setTimeout(() => audioStartRange && audioStartRange.focus(), 0); +} + +function closeAudioTrimModal() { + stopTrimPreview(); + + if (audioTrimModal) audioTrimModal.hidden = true; + document.body.classList.remove('flipnote-audio-trim-open'); + renderAudioTrimVolume(0); + audioTrimState = null; + audioImportTarget = null; + if (audioFileInput) audioFileInput.value = ''; +} + +async function previewAudioTrim() { + if (!audioTrimState) return; + if (audioTrimState.previewSource) { stopTrimPreview(); return; } + const context = getFlipnoteAudioContext(); + try { await context.resume(); } catch (error) {} + const source = context.createBufferSource(); + source.buffer = audioTrimState.buffer; + connectAudioSourceToEditorOutput(source); + const duration = Math.max(0.01, audioTrimState.end - audioTrimState.start); + audioTrimState.previewSource = source; + if (audioPreviewButton) audioPreviewButton.textContent = 'Stop preview'; + source.onended = () => { if (audioTrimState && audioTrimState.previewSource === source) stopTrimPreview(); }; + source.start(0, audioTrimState.start, duration); + audioTrimState.previewTimer = setTimeout(stopTrimPreview, Math.ceil(duration * 1000) + 60); +} + +async function saveAudioTrim() { + if (!audioTrimState) return; + const {key, file, buffer, sourceKind, start, end} = audioTrimState; + const autoExtendFrames = key === 'music' && musicAutoFramesEnabled(); + const blob = audioBufferSliceToWavBlob(buffer, start, end); + const duration = end - start; + closeAudioTrimModal(); + // Decode the trimmed WAV on first playback so a non-zero trim start uses + // the selected segment rather than the original full source buffer. + const referenceFps = flipnoteTimelineFps(); + + const trackAdded = setAudioTrack(key, blob, duration, file.name || 'Imported audio', null, referenceFps, sourceKind || 'file_import'); + if (!trackAdded) return; + if (autoExtendFrames) { + const added = extendTimelineToAudioDuration(duration); + if (added) setAudioStatus(`Music ready, ${formatAudioSeconds(duration)}. Added ${added} blank page${added === 1 ? '' : 's'} to match Speed ${timeline.speedLevel}.`); + } +} + +async function importAudioFile(key, file) { + if (!file) return; + const lowerName = String(file.name || '').toLowerCase(); + if (!(lowerName.endsWith('.mp3') || lowerName.endsWith('.wav'))) { + alert('Flipnote Editor audio imports must be MP3 or WAV files.'); + return; + } + if (file.size > 50 * 1024 * 1024) { + alert('That audio file is too large. Please use an MP3 or WAV under 50 MB.'); + return; + } + setAudioStatus(`Reading ${file.name}...`); + try { + const buffer = await decodeAudioBlob(file); + await openAudioTrimModal(key, file, buffer, 'file_import'); + } catch (error) { + alert('Editor could not decode that MP3/WAV file.'); + setAudioStatus('Audio import failed.'); + } +} + +if (audioStartRange) audioStartRange.addEventListener('input', () => updateAudioTrimControls('start')); +if (audioEndRange) audioEndRange.addEventListener('input', () => updateAudioTrimControls('end')); +if (audioAutoFramesToggle) audioAutoFramesToggle.addEventListener('change', toggleAudioAutoFrames); +if (audioPreviewButton) audioPreviewButton.addEventListener('click', previewAudioTrim); +if (audioTrimSave) audioTrimSave.addEventListener('click', saveAudioTrim); +if (audioTrimBack) audioTrimBack.addEventListener('click', backToAudioImportSource); +if (audioTrimCancel) audioTrimCancel.addEventListener('click', closeAudioTrimModal); +if (audioTrimClose) audioTrimClose.addEventListener('click', closeAudioTrimModal); +if (audioTrimModal) audioTrimModal.addEventListener('pointerdown', (event) => { if (event.target === audioTrimModal) closeAudioTrimModal(); }); + + + + + + +const audioUploadOption = document.getElementById('flipnote-audio-import-upload'); +if (audioUploadOption) audioUploadOption.addEventListener('keydown', (event) => { + if (event.key !== 'Enter' && event.key !== ' ') return; + event.preventDefault(); + if (!audioFileInput) return; + try { + if (typeof audioFileInput.showPicker === 'function') audioFileInput.showPicker(); + else audioFileInput.click(); + } catch (error) { + audioFileInput.click(); + } +}); +if (audioFileInput) audioFileInput.addEventListener('change', () => { + const file = audioFileInput.files && audioFileInput.files[0]; + const key = audioImportTarget; + audioFileInput.value = ''; + if (key && file) importAudioFile(key, file); +}); + +audioEraseButtons.forEach((button) => { + button.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + const key = button.dataset.audioErase; + if (key) eraseAudioTrack(key); + }); +}); + +audioControlButtons.forEach((button) => { + button.addEventListener('pointerenter', () => { button.classList.add('is-audio-hover'); renderAudioButtons(); }); + button.addEventListener('pointerleave', () => { button.classList.remove('is-audio-hover'); renderAudioButtons(); }); + button.addEventListener('focus', () => { button.classList.add('is-audio-hover'); renderAudioButtons(); }); + button.addEventListener('blur', () => { button.classList.remove('is-audio-hover'); renderAudioButtons(); }); + button.addEventListener('click', async (event) => { + const key = button.dataset.audioControl; + if (timeline.playing) { + event.preventDefault(); + if (/^sound[123]$/.test(key)) await triggerLiveSoundEffect(key); + return; + } + if (activeAudioRecording && activeAudioRecording.key === key) { stopAudioRecording(); return; } + if (activeAudioPlayback && activeAudioPlayback.key === key) { stopAudioPlayback(); return; } + if (editorAlternateActive(event) || audioShiftHeld) { + stopAudioPlayback(); + openAudioImportModal(key); + return; + } + if (audioTracks[key]) await playAudioTrack(key); + else await startAudioRecording(key); + }); +}); + +// Shift-click is not available on phones, so a long-press opens the same +// audio-import flow. Normal taps still record/play exactly as before. +audioControlButtons.forEach((button) => { + installFlipnoteTouchLongPress(button, () => { + if (timeline.playing) return; + const key = button.dataset.audioControl; + if (!key) return; + stopAudioPlayback(); + openAudioImportModal(key); + }); +}); + +document.addEventListener('keydown', (event) => { + if (editorAlternateKeyEvent(event) && !audioShiftHeld) { audioShiftHeld = true; renderAudioButtons(); } + if (shortcutAction(event, 'dialog') === 'closeDialog' && audioTrimModal && !audioTrimModal.hidden) { event.preventDefault(); closeAudioTrimModal(); } + const liveSound = shortcutAction(event, 'playing'); + if (timeline.playing && !event.repeat && /^sound[123]$/.test(liveSound)) { + const target = event.target instanceof Element ? event.target : null; + const typingText = !!(target && target.matches('input, textarea, select, [contenteditable="true"]')); + if (!typingText && (!audioTrimModal || audioTrimModal.hidden) && (!editorInfoModal || editorInfoModal.hidden) && (!imageImportModal || imageImportModal.hidden)) { + event.preventDefault(); + void triggerLiveSoundEffect(liveSound); + } + } +}); +document.addEventListener('keyup', (event) => { + if (editorAlternateReleaseEvent(event) && audioShiftHeld) { audioShiftHeld = false; renderAudioButtons(); } +}); +window.addEventListener('blur', () => { if (audioShiftHeld) { audioShiftHeld = false; renderAudioButtons(); } }); +window.addEventListener('beforeunload', (event) => { + if (!editorHasUnsavedChanges) return; + event.preventDefault(); + // Modern browsers intentionally ignore custom beforeunload text and show + // their own standard confirmation message. Assigning returnValue is still + // required for compatibility with browsers that implement the legacy API. + event.returnValue = ''; +}); +window.addEventListener('pagehide', () => { + Object.values(audioTracks).forEach((track) => { if (track && track.url) URL.revokeObjectURL(track.url); }); + +}); +renderAudioButtons(); +updateAudioFillBars(); +renderFlipnoteSpaceMeter(timeline); +window.flipnoteEditorAudioReady = true; + +// Flipnote Editor information / shortcut reference. +const editorInfoButton = document.getElementById('flipnote-editor-info-button'); +const editorInfoModal = document.getElementById('flipnote-editor-info-modal'); +const editorInfoClose = document.getElementById('flipnote-editor-info-close'); +const editorInfoDone = document.getElementById('flipnote-editor-info-done'); +let editorInfoLastFocus = null; + +function openEditorInfoModal() { + if (!editorInfoModal) return; + editorInfoLastFocus = document.activeElement; + editorInfoModal.hidden = false; + document.body.classList.add('flipnote-editor-info-open'); + if (editorInfoClose) editorInfoClose.focus(); +} + +function closeEditorInfoModal() { + if (!editorInfoModal || editorInfoModal.hidden) return; + recordingShortcut = ''; + renderEditorShortcuts(); + editorInfoModal.hidden = true; + document.body.classList.remove('flipnote-editor-info-open'); + if (editorInfoLastFocus && typeof editorInfoLastFocus.focus === 'function') editorInfoLastFocus.focus(); + editorInfoLastFocus = null; +} + +if (editorInfoButton) editorInfoButton.addEventListener('click', openEditorInfoModal); +if (editorInfoClose) editorInfoClose.addEventListener('click', closeEditorInfoModal); +if (editorInfoDone) editorInfoDone.addEventListener('click', closeEditorInfoModal); +if (editorInfoModal) editorInfoModal.addEventListener('pointerdown', (event) => { + if (event.target === editorInfoModal) closeEditorInfoModal(); +}); +document.addEventListener('keydown', (event) => { + if (shortcutAction(event, 'dialog') === 'closeDialog' && editorInfoModal && !editorInfoModal.hidden) { + event.preventDefault(); + closeEditorInfoModal(); + } +}); + +const editorSettingsStorageKey = 'flipnote-editor.flipnote-editor.canvas-settings.v1'; +const canvasSettings = {grid: false, zoom: 100, scrollZoom: false}; +try { + const saved = JSON.parse(localStorage.getItem(editorSettingsStorageKey) || '{}'); + if (saved && typeof saved === 'object') { + canvasSettings.grid = saved.grid === true; + canvasSettings.zoom = Math.max(50, Math.min(800, Math.round(Number(saved.zoom)) || 100)); + canvasSettings.scrollZoom = saved.scrollZoom === true; + } +} catch (error) {} +const editorCanvasWrap = document.querySelector('.flipnote-editor-canvas-wrap'); +const editorCanvasSurface = document.querySelector('.flipnote-editor-canvas-surface'); +const editorCanvasGrid = document.getElementById('flipnote-editor-canvas-grid'); +const editorGridToggle = document.getElementById('flipnote-editor-show-grid'); +const editorZoomInput = document.getElementById('flipnote-editor-zoom'); +const editorZoomValue = document.getElementById('flipnote-editor-zoom-value'); +const editorScrollZoomToggle = document.getElementById('flipnote-editor-scroll-zoom'); +if (editorCanvasGrid) { + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('d', Array.from({length: 255}, (_, i) => `M${i + 1} 0V192`).join('') + Array.from({length: 191}, (_, i) => `M0 ${i + 1}H256`).join('')); + path.setAttribute('vector-effect', 'non-scaling-stroke'); + editorCanvasGrid.replaceChildren(path); +} +let editorCanvasPanX = 0; +let editorCanvasPanY = 0; +let editorCanvasBaseWidth = 0; +let editorCanvasBaseHeight = 0; +let editorCanvasDrag = null; +let editorCanvasWheelZoom = canvasSettings.zoom; +function saveCanvasSettings() { + try { localStorage.setItem(editorSettingsStorageKey, JSON.stringify(canvasSettings)); } catch (error) {} +} +function renderCanvasSettings() { + if (editorGridToggle) { + editorGridToggle.checked = canvasSettings.grid; + editorGridToggle.disabled = canvasSettings.zoom < 400; + editorGridToggle.title = editorGridToggle.disabled ? 'Zoom to at least 400% to show the pixel grid' : ''; + } + if (editorZoomInput) editorZoomInput.value = String(canvasSettings.zoom); + if (editorZoomValue) editorZoomValue.textContent = `${canvasSettings.zoom}%`; + if (editorScrollZoomToggle) editorScrollZoomToggle.checked = canvasSettings.scrollZoom; + if (editorCanvasGrid) editorCanvasGrid.toggleAttribute('hidden', !canvasSettings.grid || canvasSettings.zoom < 400); + if (editorCanvasWrap && editorCanvasSurface) { + const style = getComputedStyle(editorCanvasWrap); + const available = editorCanvasWrap.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight); + editorCanvasBaseWidth = Math.max(1, Math.min(616, available)); + editorCanvasBaseHeight = editorCanvasBaseWidth * 192 / 256; + const scale = canvasSettings.zoom / 100; + const maxPanX = Math.max(0, (editorCanvasBaseWidth * scale - available) / 2); + const maxPanY = Math.max(0, (editorCanvasBaseHeight * scale - editorCanvasBaseHeight) / 2); + editorCanvasPanX = Math.max(-maxPanX, Math.min(maxPanX, editorCanvasPanX)); + editorCanvasPanY = Math.max(-maxPanY, Math.min(maxPanY, editorCanvasPanY)); + editorCanvasSurface.style.width = `${editorCanvasBaseWidth}px`; + editorCanvasSurface.style.transform = `translate(${editorCanvasPanX}px, ${editorCanvasPanY}px) scale(${scale})`; + if (editorCanvasGrid) editorCanvasGrid.style.strokeWidth = `${1 / scale}px`; + } +} +if (editorGridToggle) editorGridToggle.addEventListener('change', () => { + canvasSettings.grid = editorGridToggle.checked; saveCanvasSettings(); renderCanvasSettings(); +}); +if (editorZoomInput) editorZoomInput.addEventListener('input', () => { + canvasSettings.zoom = Number(editorZoomInput.value); editorCanvasWheelZoom = canvasSettings.zoom; saveCanvasSettings(); renderCanvasSettings(); +}); +document.getElementById('flipnote-editor-reset-view')?.addEventListener('click', () => { + canvasSettings.zoom = editorCanvasWheelZoom = 100; editorCanvasPanX = editorCanvasPanY = 0; + saveCanvasSettings(); renderCanvasSettings(); +}); +if (editorScrollZoomToggle) editorScrollZoomToggle.addEventListener('change', () => { + canvasSettings.scrollZoom = editorScrollZoomToggle.checked; saveCanvasSettings(); renderCanvasSettings(); +}); +if (editorCanvasWrap) editorCanvasWrap.addEventListener('wheel', (event) => { + if (!event.target.closest('.flipnote-editor-canvas-surface')) return; + if (canvasSettings.scrollZoom && (event.ctrlKey || event.metaKey)) { + event.preventDefault(); + const units = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? editorCanvasBaseHeight : 1; + const delta = Math.max(-100, Math.min(100, event.deltaY * units)); + editorCanvasWheelZoom = Math.max(50, Math.min(800, editorCanvasWheelZoom * Math.exp(-delta * .002))); + const next = Math.round(editorCanvasWheelZoom); + if (next === canvasSettings.zoom) return; + const style = getComputedStyle(editorCanvasWrap); + const rect = editorCanvasWrap.getBoundingClientRect(); + const contentWidth = editorCanvasWrap.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight); + const cursorX = event.clientX - (rect.left + parseFloat(style.paddingLeft) + contentWidth / 2); + const cursorY = event.clientY - (rect.top + parseFloat(style.paddingTop) + editorCanvasBaseHeight / 2); + const ratio = next / canvasSettings.zoom; + editorCanvasPanX = cursorX - ratio * (cursorX - editorCanvasPanX); + editorCanvasPanY = cursorY - ratio * (cursorY - editorCanvasPanY); + canvasSettings.zoom = next; + saveCanvasSettings(); + renderCanvasSettings(); + } else if (!event.ctrlKey && !event.metaKey && canvasSettings.zoom > 100) { + event.preventDefault(); + editorCanvasPanX -= event.deltaX || (event.shiftKey ? event.deltaY : 0); + editorCanvasPanY -= event.shiftKey ? 0 : event.deltaY; + renderCanvasSettings(); + } +}, {passive: false}); +if (editorCanvasWrap) { + editorCanvasWrap.addEventListener('pointerdown', (event) => { + if (event.button !== 1 || canvasSettings.zoom <= 100) return; + event.preventDefault(); + editorCanvasDrag = {pointerId: event.pointerId, x: event.clientX, y: event.clientY, panX: editorCanvasPanX, panY: editorCanvasPanY}; + editorCanvasWrap.setPointerCapture(event.pointerId); + }); + editorCanvasWrap.addEventListener('pointermove', (event) => { + if (!editorCanvasDrag || event.pointerId !== editorCanvasDrag.pointerId) return; + editorCanvasPanX = editorCanvasDrag.panX + event.clientX - editorCanvasDrag.x; + editorCanvasPanY = editorCanvasDrag.panY + event.clientY - editorCanvasDrag.y; + renderCanvasSettings(); + }); + for (const name of ['pointerup', 'pointercancel', 'lostpointercapture']) { + editorCanvasWrap.addEventListener(name, (event) => { + if (editorCanvasDrag && event.pointerId === editorCanvasDrag.pointerId) editorCanvasDrag = null; + }); + } + editorCanvasWrap.addEventListener('auxclick', (event) => { + if (event.button === 1 && canvasSettings.zoom > 100) event.preventDefault(); + }); +} +if (editorCanvasWrap && typeof ResizeObserver !== 'undefined') new ResizeObserver(renderCanvasSettings).observe(editorCanvasWrap); +window.addEventListener('resize', renderCanvasSettings); +renderCanvasSettings(); + +const editorShortcutSettings = document.getElementById('flipnote-editor-shortcut-settings'); +const editorShortcutMessage = document.getElementById('flipnote-editor-shortcut-message'); +const editorResetShortcuts = document.getElementById('flipnote-editor-reset-shortcuts'); +let recordingShortcut = ''; +function shortcutLabel(key) { + return key.replaceAll('ArrowLeft', '←').replaceAll('ArrowRight', '→').replaceAll('ArrowUp', '↑').replaceAll('ArrowDown', '↓'); +} +function renderEditorShortcuts() { + if (!editorShortcutSettings) return; + editorShortcutSettings.replaceChildren(); + for (const [id, label] of EDITOR_SHORTCUTS) { + const row = document.createElement('div'); + row.className = 'flipnote-editor-shortcut-setting'; + const name = document.createElement('span'); + name.textContent = label; + const button = document.createElement('button'); + button.type = 'button'; + button.className = 'flipnote-editor-shortcut-capture'; + button.dataset.shortcutId = id; + button.textContent = recordingShortcut === id ? 'Press keys…' : shortcutLabel(editorShortcutBindings[id]); + button.setAttribute('aria-label', `${label}: ${button.textContent}. Click to change`); + button.setAttribute('aria-pressed', recordingShortcut === id ? 'true' : 'false'); + const reset = document.createElement('button'); + reset.type = 'button'; + reset.className = 'flipnote-editor-shortcut-reset'; + reset.textContent = 'Reset'; + reset.setAttribute('aria-label', `Reset ${label} shortcut`); + reset.addEventListener('click', () => { + recordingShortcut = ''; + editorShortcutBindings[id] = editorShortcutDefaults[id]; + saveEditorShortcuts(); renderEditorShortcuts(); + if (editorShortcutMessage) editorShortcutMessage.textContent = `${label} reset.`; + }); + button.addEventListener('click', () => { + recordingShortcut = id; + if (editorShortcutMessage) editorShortcutMessage.textContent = `Press a key or key combination for ${label}. Escape cancels.`; + renderEditorShortcuts(); + editorShortcutSettings.querySelector(`[data-shortcut-id="${id}"]`)?.focus(); + }); + row.append(name, button, reset); + editorShortcutSettings.append(row); + } + for (const [selector, label, id] of [ + ['#undoButton', 'Undo', 'undo'], ['#redoButton', 'Redo', 'redo'], + ['#eraseBigButton', 'Clear active layer', 'clear'], + ['[data-tool-family-picker="pen"]', 'Draw', 'draw'], + ['[data-tool-family-picker="paint"]', 'Paint', 'paint'], + ['[data-tool-family-picker="fill"]', 'Fill', 'fill'], + ['[data-tool-family-picker="eraser"]', 'Erase', 'erase'], + ]) { + const control = document.querySelector(selector); + if (control) control.title = `${label} (${shortcutLabel(editorShortcutBindings[id])})`; + } +} +function shortcutContextsOverlap(first, second) { + if (first === 'global' || second === 'global' || first === 'modifier' || second === 'modifier') return true; + if (first === 'dialog' || second === 'dialog') return first === second; + if (first === 'title' || second === 'title') return first === second; + if (first === 'speed' || second === 'speed') return first === second; + if (first === 'playing' || second === 'playing') return first === second; + return true; // Frame navigation and drawing shortcuts are active together. +} +document.addEventListener('keydown', (event) => { + if (!recordingShortcut || !editorInfoModal || editorInfoModal.hidden) return; + event.preventDefault(); + event.stopImmediatePropagation(); + if (event.key === 'Escape') { + recordingShortcut = ''; + renderEditorShortcuts(); + if (editorShortcutMessage) editorShortcutMessage.textContent = 'Shortcut change cancelled.'; + return; + } + const pressed = shortcutFromEvent(event) || (recordingShortcut === 'alternateActions' && ['Shift', 'Control', 'Alt', 'Meta'].includes(event.key) ? event.key : ''); + if (!pressed) return; + const current = EDITOR_SHORTCUTS.find(([id]) => id === recordingShortcut); + const conflict = EDITOR_SHORTCUTS.find(([id, , , context]) => id !== recordingShortcut && editorShortcutBindings[id] === pressed && shortcutContextsOverlap(current[3], context)); + if (conflict) { + if (editorShortcutMessage) editorShortcutMessage.textContent = `${shortcutLabel(pressed)} is already used by ${conflict[1]}. Choose another combination.`; + return; + } + editorShortcutBindings[recordingShortcut] = pressed; + recordingShortcut = ''; + editorAlternateHeld = false; + saveEditorShortcuts(); renderEditorShortcuts(); + if (editorShortcutMessage) editorShortcutMessage.textContent = `${current[1]} is now ${shortcutLabel(pressed)}.`; +}, true); +if (editorResetShortcuts) editorResetShortcuts.addEventListener('click', () => { + recordingShortcut = ''; + editorShortcutBindings = {...editorShortcutDefaults}; + saveEditorShortcuts(); renderEditorShortcuts(); + if (editorShortcutMessage) editorShortcutMessage.textContent = 'All shortcuts reset.'; +}); +renderEditorShortcuts(); + +// Flipnote Editor image importer. This is a three-step flow: choose an image, +// crop it, then preview/convert it into one or both Flipnote layers. Nothing mutates the +// current page until the final Import button is pressed. +const imageImportButton = document.getElementById('flipnote-import-image-button'); +const imageFileInput = document.getElementById('flipnote-image-file-input'); +const imageImportModal = document.getElementById('flipnote-image-import-modal'); +const imageImportClose = document.getElementById('flipnote-image-import-close'); +const imageImportCancel = document.getElementById('flipnote-image-import-cancel'); +const imageImportBack = document.getElementById('flipnote-image-import-back'); +const imageImportNext = document.getElementById('flipnote-image-import-next'); +const imageImportApply = document.getElementById('flipnote-image-import-apply'); +const imageImportResetCrop = document.getElementById('flipnote-image-import-reset-crop'); +const imageImportTitle = document.getElementById('flipnote-image-import-title'); +const imageImportStep1 = document.getElementById('flipnote-image-import-step-1'); +const imageImportStep2 = document.getElementById('flipnote-image-import-step-2'); +const imageImportStep3 = document.getElementById('flipnote-image-import-step-3'); +const imageImportDropzone = document.getElementById('flipnote-image-import-dropzone'); +const imageImportUpload = document.getElementById('flipnote-image-import-upload'); +const imageImportPasteShortcut = document.getElementById('flipnote-image-import-paste-shortcut'); +const imageImportSourceStatus = document.getElementById('flipnote-image-import-source-status'); +const imageImportFilename = document.getElementById('flipnote-image-import-filename'); +const imageImportDimensions = document.getElementById('flipnote-image-import-dimensions'); +const imageImportFinalFilename = document.getElementById('flipnote-image-import-final-filename'); +const imageImportFinalDimensions = document.getElementById('flipnote-image-import-final-dimensions'); +const imageImportCropCanvas = document.getElementById('flipnote-image-import-crop-canvas'); +const imageImportPreview = document.getElementById('flipnote-image-import-preview'); +const imageImportMode = document.getElementById('flipnote-image-import-mode'); +const imageImportColour = document.getElementById('flipnote-image-import-colour'); +const imageImportSecondColour = document.getElementById('flipnote-image-import-second-colour'); +const imageImportSecondColourLabel = document.getElementById('flipnote-image-import-second-colour-label'); +const imageImportLayer = document.getElementById('flipnote-image-import-layer'); +const imageImportLayerLabel = document.getElementById('flipnote-image-import-layer-label'); +const imageImportSize = document.getElementById('flipnote-image-import-size'); +const imageImportAlign = document.getElementById('flipnote-image-import-align'); +const imageImportDither = document.getElementById('flipnote-image-import-dither'); +const imageImportThreshold = document.getElementById('flipnote-image-import-threshold'); +const imageImportThresholdValue = document.getElementById('flipnote-image-import-threshold-value'); +const imageImportSmoothing = document.getElementById('flipnote-image-import-smoothing'); +const imageImportInvert = document.getElementById('flipnote-image-import-invert'); +const imageImportReplace = document.getElementById('flipnote-image-import-replace'); +const imageImportReplaceText = document.getElementById('flipnote-image-import-replace-text'); +let imageImportState = null; +let imageImportLastFocus = null; +let imageImportCropDrag = null; +let imageImportLoadSerial = 0; + +function imageImportAlternateInk(primary, preferred = null) { + if (preferred && FLIPNOTE_INKS[preferred] && preferred !== primary) return preferred; + return FLIPNOTE_INK_ORDER.find((ink) => ink !== primary) || 'red'; +} + +function syncImageImportColourMode() { + const dual = imageImportMode?.value === 'dual'; + const primary = FLIPNOTE_INKS[imageImportColour?.value] ? imageImportColour.value : 'black'; + if (imageImportSecondColour) { + if (!FLIPNOTE_INKS[imageImportSecondColour.value] || imageImportSecondColour.value === primary) { + imageImportSecondColour.value = imageImportAlternateInk(primary, imageImportSecondColour.value); + } + } + if (imageImportSecondColourLabel) imageImportSecondColourLabel.style.display = dual ? '' : 'none'; + if (imageImportLayerLabel) imageImportLayerLabel.textContent = dual ? 'Primary layer' : 'Layer'; + if (imageImportReplaceText) imageImportReplaceText.textContent = dual + ? 'Replace both target layers instead of merging' + : 'Replace the selected layer instead of merging'; +} + +function imageImportOptions() { + const colour = FLIPNOTE_INKS[imageImportColour?.value] ? imageImportColour.value : 'black'; + return { + mode: imageImportMode?.value === 'dual' ? 'dual' : 'single', + colour, + secondColour: imageImportAlternateInk(colour, imageImportSecondColour?.value), + layer: Number(imageImportLayer?.value) === 1 ? 1 : 0, + size: ['fit','fill','stretch','original'].includes(imageImportSize?.value) ? imageImportSize.value : 'fit', + align: ['center','top-left','top-right','bottom-left','bottom-right'].includes(imageImportAlign?.value) ? imageImportAlign.value : 'center', + dither: ['none','bayer2','bayer4','floyd'].includes(imageImportDither?.value) ? imageImportDither.value : 'bayer4', + threshold: Math.max(0, Math.min(255, Math.round(Number(imageImportThreshold?.value) || 0))), + smoothing: !!imageImportSmoothing?.checked, + invert: !!imageImportInvert?.checked, + replace: imageImportReplace ? !!imageImportReplace.checked : true, + }; +} + +function imageImportAlignedOffset(space, align, axis) { + if (axis === 'x') { + if (align.endsWith('left')) return 0; + if (align.endsWith('right')) return space; + } else { + if (align.startsWith('top')) return 0; + if (align.startsWith('bottom')) return space; + } + return space / 2; +} + +function imageImportSourceSize(source = imageImportState?.source) { + return { + width: Math.max(1, Number(source?.width || source?.naturalWidth) || 1), + height: Math.max(1, Number(source?.height || source?.naturalHeight) || 1), + }; +} + +function renderImageToImportCanvas(source, options, cropRect = null) { + const width = 256; + const height = 192; + const work = document.createElement('canvas'); + work.width = width; + work.height = height; + const ctx = work.getContext('2d', {willReadFrequently:true}); + ctx.imageSmoothingEnabled = !!options.smoothing; + ctx.imageSmoothingQuality = 'high'; + const sourceSize = imageImportSourceSize(source); + const crop = cropRect || {x:0, y:0, w:sourceSize.width, h:sourceSize.height}; + const sx = Math.max(0, Math.min(sourceSize.width - 1, Number(crop.x) || 0)); + const sy = Math.max(0, Math.min(sourceSize.height - 1, Number(crop.y) || 0)); + const sw = Math.max(1, Math.min(sourceSize.width - sx, Number(crop.w) || sourceSize.width)); + const sh = Math.max(1, Math.min(sourceSize.height - sy, Number(crop.h) || sourceSize.height)); + let dw = sw; + let dh = sh; + if (options.size === 'stretch') { + dw = width; + dh = height; + } else if (options.size === 'fit' || options.size === 'fill') { + const scale = options.size === 'fill' + ? Math.max(width / sw, height / sh) + : Math.min(width / sw, height / sh); + dw = sw * scale; + dh = sh * scale; + } + const dx = imageImportAlignedOffset(width - dw, options.align, 'x'); + const dy = imageImportAlignedOffset(height - dh, options.align, 'y'); + ctx.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh); + return ctx.getImageData(0, 0, width, height); +} + +const IMAGE_IMPORT_BAYER_2 = [0, 2, 3, 1]; +const IMAGE_IMPORT_BAYER_4 = [ + 0, 8, 2, 10, + 12, 4, 14, 6, + 3, 11, 1, 9, + 15, 7, 13, 5, +]; + +function imageImportLuminance(imageData, invert) { + const count = imageData.width * imageData.height; + const lum = new Float32Array(count); + const d = imageData.data; + for (let i = 0; i < count; i++) { + const p = i * 4; + const alpha = d[p + 3] / 255; + let value = 0.2126 * d[p] + 0.7152 * d[p + 1] + 0.0722 * d[p + 2]; + value = value * alpha + 255 * (1 - alpha); + lum[i] = invert ? 255 - value : value; + } + return lum; +} + +function buildImageImportMask(source, options, cropRect = null) { + const imageData = renderImageToImportCanvas(source, options, cropRect); + const {width, height} = imageData; + const mask = new Uint8Array(width * height); + const lum = imageImportLuminance(imageData, options.invert); + const threshold = options.threshold; + + if (options.dither === 'floyd') { + const work = new Float32Array(lum); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const i = y * width + x; + const oldValue = work[i]; + const dark = oldValue < threshold; + mask[i] = dark ? 1 : 0; + const quantized = dark ? 0 : 255; + const error = oldValue - quantized; + if (x + 1 < width) work[i + 1] += error * (7 / 16); + if (y + 1 < height) { + if (x > 0) work[i + width - 1] += error * (3 / 16); + work[i + width] += error * (5 / 16); + if (x + 1 < width) work[i + width + 1] += error * (1 / 16); + } + } + } + return mask; + } + + let matrix = null; + let matrixSize = 0; + if (options.dither === 'bayer2') { matrix = IMAGE_IMPORT_BAYER_2; matrixSize = 2; } + if (options.dither === 'bayer4') { matrix = IMAGE_IMPORT_BAYER_4; matrixSize = 4; } + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const i = y * width + x; + let localThreshold = threshold; + if (matrix) { + const value = matrix[(y % matrixSize) * matrixSize + (x % matrixSize)]; + const spread = (((value + 0.5) / (matrixSize * matrixSize)) - 0.5) * 96; + localThreshold = Math.max(0, Math.min(255, threshold + spread)); + } + mask[i] = lum[i] < localThreshold ? 1 : 0; + } + } + return mask; +} + +function imageImportColourDistance(r, g, b, colour) { + const dr = r - colour[0]; + const dg = g - colour[1]; + const db = b - colour[2]; + // Weight green most heavily so the palette choice tracks perceived colour + // more closely than a raw RGB cube distance. + return dr * dr * 0.30 + dg * dg * 0.59 + db * db * 0.11; +} + +function imageImportDualPalettePixel(r, g, b, paper, primary, secondary, paperPenalty = 0) { + const paperDistance = Math.max(0, imageImportColourDistance(r, g, b, paper) + paperPenalty); + const primaryDistance = imageImportColourDistance(r, g, b, primary); + const secondaryDistance = imageImportColourDistance(r, g, b, secondary); + if (paperDistance <= primaryDistance && paperDistance <= secondaryDistance) return 0; + return primaryDistance <= secondaryDistance ? 1 : 2; +} + +function buildImageImportDualMasks(source, options, frame, cropRect = null) { + const imageData = renderImageToImportCanvas(source, options, cropRect); + const {width, height, data} = imageData; + const count = width * height; + const primaryMask = new Uint8Array(count); + const secondaryMask = new Uint8Array(count); + const paper = resolveFramePaper(frame); + const primary = resolveFrameInk(frame, options.colour); + const secondary = resolveFrameInk(frame, options.secondColour); + const thresholdPenalty = (options.threshold - 128) * 500; + + const readPixel = (i) => { + const p = i * 4; + const alpha = data[p + 3] / 255; + if (alpha <= 0.001) return {transparent:true, r:paper[0], g:paper[1], b:paper[2]}; + let r = data[p] * alpha + paper[0] * (1 - alpha); + let g = data[p + 1] * alpha + paper[1] * (1 - alpha); + let b = data[p + 2] * alpha + paper[2] * (1 - alpha); + if (options.invert) { r = 255 - r; g = 255 - g; b = 255 - b; } + return {transparent:false, r, g, b}; + }; + + if (options.dither === 'floyd') { + const wr = new Float32Array(count); + const wg = new Float32Array(count); + const wb = new Float32Array(count); + const transparent = new Uint8Array(count); + for (let i = 0; i < count; i++) { + const px = readPixel(i); + wr[i] = px.r; wg[i] = px.g; wb[i] = px.b; + transparent[i] = px.transparent ? 1 : 0; + } + const palette = [paper, primary, secondary]; + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const i = y * width + x; + if (transparent[i]) continue; + const r = Math.max(0, Math.min(255, wr[i])); + const g = Math.max(0, Math.min(255, wg[i])); + const b = Math.max(0, Math.min(255, wb[i])); + const choice = imageImportDualPalettePixel(r, g, b, paper, primary, secondary, thresholdPenalty); + if (choice === 1) primaryMask[i] = 1; + else if (choice === 2) secondaryMask[i] = 1; + const quantized = palette[choice]; + const er = r - quantized[0], eg = g - quantized[1], eb = b - quantized[2]; + const spread = (index, weight) => { + if (index < 0 || index >= count || transparent[index]) return; + wr[index] += er * weight; wg[index] += eg * weight; wb[index] += eb * weight; + }; + if (x + 1 < width) spread(i + 1, 7 / 16); + if (y + 1 < height) { + if (x > 0) spread(i + width - 1, 3 / 16); + spread(i + width, 5 / 16); + if (x + 1 < width) spread(i + width + 1, 1 / 16); + } + } + } + return {primary: primaryMask, secondary: secondaryMask}; + } + + let matrix = null; + let matrixSize = 0; + if (options.dither === 'bayer2') { matrix = IMAGE_IMPORT_BAYER_2; matrixSize = 2; } + if (options.dither === 'bayer4') { matrix = IMAGE_IMPORT_BAYER_4; matrixSize = 4; } + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const i = y * width + x; + const px = readPixel(i); + if (px.transparent) continue; + let paperPenalty = thresholdPenalty; + if (matrix) { + const value = matrix[(y % matrixSize) * matrixSize + (x % matrixSize)]; + const spread = (((value + 0.5) / (matrixSize * matrixSize)) - 0.5) * 96; + paperPenalty += spread * 500; + } + const choice = imageImportDualPalettePixel(px.r, px.g, px.b, paper, primary, secondary, paperPenalty); + if (choice === 1) primaryMask[i] = 1; + else if (choice === 2) secondaryMask[i] = 1; + } + } + return {primary: primaryMask, secondary: secondaryMask}; +} + +function buildImageImportMasks(source, options, frame, cropRect = null) { + if (options.mode === 'dual') return buildImageImportDualMasks(source, options, frame, cropRect); + return {primary: buildImageImportMask(source, options, cropRect), secondary: null}; +} + +function mergedImageImportLayer(frame, layer, importedMask, replace) { + if (replace) return importedMask; + const merged = cloneMask(frame.layers[layer]); + for (let i = 0; i < merged.length; i++) if (importedMask[i]) merged[i] = 1; + return merged; +} + +function updateImageImportPreview() { + if (!imageImportState?.source || !imageImportPreview) return; + const frame = timeline.getFrame(imageImportState.frameIndex); + if (!frame) return; + syncImageImportColourMode(); + const options = imageImportOptions(); + if (imageImportThresholdValue) imageImportThresholdValue.textContent = String(options.threshold); + const importedMasks = buildImageImportMasks(imageImportState.source, options, frame, imageImportState.crop); + imageImportState.previewMasks = importedMasks; + imageImportState.previewOptions = options; + + const layers = [cloneMask(frame.layers[0]), cloneMask(frame.layers[1])]; + const secondaryLayer = options.layer === 0 ? 1 : 0; + layers[options.layer] = mergedImageImportLayer(frame, options.layer, importedMasks.primary, options.replace); + if (options.mode === 'dual' && importedMasks.secondary) { + layers[secondaryLayer] = mergedImageImportLayer(frame, secondaryLayer, importedMasks.secondary, options.replace); + } + const previewFrame = { + layers, + colors: [frame.colors[0], frame.colors[1]], + paperColor: frame.paperColor, + }; + previewFrame.colors[options.layer] = options.colour; + if (options.mode === 'dual') previewFrame.colors[secondaryLayer] = options.secondColour; + const pixels = new Uint8ClampedArray(256 * 192 * 4); + const paper = resolveFramePaper(previewFrame); + const layer1Colour = resolveFrameInk(previewFrame, previewFrame.colors[0]); + const layer2Colour = resolveFrameInk(previewFrame, previewFrame.colors[1]); + for (let i = 0; i < 256 * 192; i++) { + let r = paper[0], g = paper[1], b = paper[2]; + if (layers[1][i]) [r,g,b] = layer2Colour; + if (layers[0][i]) [r,g,b] = layer1Colour; + const p = i * 4; + pixels[p] = r; pixels[p + 1] = g; pixels[p + 2] = b; pixels[p + 3] = 255; + } + const ctx = imageImportPreview.getContext('2d', {alpha:false}); + ctx.imageSmoothingEnabled = false; + ctx.putImageData(new ImageData(pixels, 256, 192), 0, 0); +} + +async function decodeImageImportFile(file) { + if (typeof createImageBitmap === 'function') return await createImageBitmap(file); + const url = URL.createObjectURL(file); + try { + const image = new Image(); + image.decoding = 'async'; + await new Promise((resolve, reject) => { + image.onload = resolve; + image.onerror = () => reject(new Error('Could not decode this image.')); + image.src = url; + }); + return image; + } finally { + URL.revokeObjectURL(url); + } +} + +function setImageImportStatus(message = '', error = false) { + if (!imageImportSourceStatus) return; + imageImportSourceStatus.textContent = message; + imageImportSourceStatus.classList.toggle('is-error', !!error); +} + +function imageImportIsMacLike() { + const platform = navigator.userAgentData?.platform || navigator.platform || navigator.userAgent || ''; + return /Mac|iPhone|iPad|iPod/i.test(platform); +} + +function setImageImportControlVisible(control, visible) { + if (!control) return; + control.hidden = !visible; + // Editor's shared .button rule sets display explicitly, which can override + // the browser's default [hidden] rule. Keep an inline display guard so the + // image-import footer cannot leak buttons from another step. + control.style.display = visible ? '' : 'none'; +} + +function setImageImportStep(step) { + if (!imageImportState) return; + imageImportState.step = step; + if (imageImportStep1) imageImportStep1.hidden = step !== 1; + if (imageImportStep2) imageImportStep2.hidden = step !== 2; + if (imageImportStep3) imageImportStep3.hidden = step !== 3; + if (imageImportTitle) imageImportTitle.textContent = step === 1 ? 'Import image' : step === 2 ? 'Crop image' : 'Image import options'; + // Step 1: Cancel only. Step 2: Back + Reset crop + Continue + Cancel. + // Step 3: Back + Import + Cancel. Continue/Import never coexist. + setImageImportControlVisible(imageImportBack, step === 2 || step === 3); + setImageImportControlVisible(imageImportResetCrop, step === 2); + setImageImportControlVisible(imageImportNext, step === 2); + setImageImportControlVisible(imageImportApply, step === 3); + if (step === 2) { + requestAnimationFrame(() => { + drawImageImportCrop(); + imageImportCropCanvas?.focus?.(); + }); + } else if (step === 3) { + updateImageImportFinalFileInfo(); + updateImageImportPreview(); + } else { + imageImportDropzone?.focus?.(); + } +} + +function resetImageImportCrop() { + if (!imageImportState?.source) return; + const {width, height} = imageImportSourceSize(); + imageImportState.crop = {x:0, y:0, w:width, h:height}; + imageImportState.previewMasks = null; + imageImportState.previewOptions = null; + drawImageImportCrop(); + updateImageImportFinalFileInfo(); +} + +function imageImportCropView() { + if (!imageImportCropCanvas || !imageImportState?.source) return null; + const {width:sw, height:sh} = imageImportSourceSize(); + const cw = imageImportCropCanvas.width; + const ch = imageImportCropCanvas.height; + const pad = 24; + const scale = Math.min((cw - pad * 2) / sw, (ch - pad * 2) / sh); + const dw = sw * scale; + const dh = sh * scale; + return {sw, sh, scale, ox:(cw - dw) / 2, oy:(ch - dh) / 2, dw, dh}; +} + +function drawImageImportCrop() { + if (!imageImportCropCanvas || !imageImportState?.source || !imageImportState.crop) return; + const ctx = imageImportCropCanvas.getContext('2d'); + const view = imageImportCropView(); + if (!view) return; + const {width:cw, height:ch} = imageImportCropCanvas; + const crop = imageImportState.crop; + ctx.clearRect(0, 0, cw, ch); + ctx.fillStyle = '#0d0c0f'; + ctx.fillRect(0, 0, cw, ch); + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = 'high'; + ctx.drawImage(imageImportState.source, view.ox, view.oy, view.dw, view.dh); + + const x = view.ox + crop.x * view.scale; + const y = view.oy + crop.y * view.scale; + const w = crop.w * view.scale; + const h = crop.h * view.scale; + ctx.fillStyle = 'rgba(0,0,0,.58)'; + ctx.fillRect(view.ox, view.oy, view.dw, Math.max(0, y - view.oy)); + ctx.fillRect(view.ox, y + h, view.dw, Math.max(0, view.oy + view.dh - (y + h))); + ctx.fillRect(view.ox, y, Math.max(0, x - view.ox), h); + ctx.fillRect(x + w, y, Math.max(0, view.ox + view.dw - (x + w)), h); + + ctx.save(); + ctx.strokeStyle = '#fff'; + ctx.lineWidth = 2; + ctx.strokeRect(x + 1, y + 1, Math.max(0, w - 2), Math.max(0, h - 2)); + ctx.strokeStyle = 'rgba(174,48,31,.95)'; + ctx.lineWidth = 1; + ctx.strokeRect(x + 3, y + 3, Math.max(0, w - 6), Math.max(0, h - 6)); + ctx.setLineDash([5,5]); + ctx.strokeStyle = 'rgba(255,255,255,.55)'; + ctx.beginPath(); + ctx.moveTo(x + w / 3, y); ctx.lineTo(x + w / 3, y + h); + ctx.moveTo(x + (w * 2) / 3, y); ctx.lineTo(x + (w * 2) / 3, y + h); + ctx.moveTo(x, y + h / 3); ctx.lineTo(x + w, y + h / 3); + ctx.moveTo(x, y + (h * 2) / 3); ctx.lineTo(x + w, y + (h * 2) / 3); + ctx.stroke(); + ctx.restore(); + + const handles = [ + [x,y],[x+w/2,y],[x+w,y], + [x,y+h/2],[x+w,y+h/2], + [x,y+h],[x+w/2,y+h],[x+w,y+h], + ]; + ctx.fillStyle = '#fff'; + ctx.strokeStyle = '#AE301F'; + ctx.lineWidth = 2; + for (const [hx,hy] of handles) { + ctx.fillRect(hx - 5, hy - 5, 10, 10); + ctx.strokeRect(hx - 5, hy - 5, 10, 10); + } +} + +function imageImportCanvasPoint(event) { + const rect = imageImportCropCanvas.getBoundingClientRect(); + return { + x:(event.clientX - rect.left) * (imageImportCropCanvas.width / Math.max(1, rect.width)), + y:(event.clientY - rect.top) * (imageImportCropCanvas.height / Math.max(1, rect.height)), + }; +} + +function imageImportSourcePoint(canvasPoint) { + const view = imageImportCropView(); + if (!view) return {x:0,y:0}; + return { + x:Math.max(0, Math.min(view.sw, (canvasPoint.x - view.ox) / view.scale)), + y:Math.max(0, Math.min(view.sh, (canvasPoint.y - view.oy) / view.scale)), + }; +} + +function imageImportCropHitTest(point) { + const view = imageImportCropView(); + const crop = imageImportState?.crop; + if (!view || !crop) return null; + const x = view.ox + crop.x * view.scale; + const y = view.oy + crop.y * view.scale; + const w = crop.w * view.scale; + const h = crop.h * view.scale; + const right = x + w; + const bottom = y + h; + const near = 14; + const nearX1 = Math.abs(point.x - x) <= near; + const nearX2 = Math.abs(point.x - right) <= near; + const nearY1 = Math.abs(point.y - y) <= near; + const nearY2 = Math.abs(point.y - bottom) <= near; + const betweenX = point.x >= x - near && point.x <= right + near; + const betweenY = point.y >= y - near && point.y <= bottom + near; + if (nearX1 && nearY1) return 'nw'; + if (nearX2 && nearY1) return 'ne'; + if (nearX1 && nearY2) return 'sw'; + if (nearX2 && nearY2) return 'se'; + if (nearY1 && betweenX) return 'n'; + if (nearY2 && betweenX) return 's'; + if (nearX1 && betweenY) return 'w'; + if (nearX2 && betweenY) return 'e'; + if (point.x >= x && point.x <= right && point.y >= y && point.y <= bottom) return 'move'; + if (point.x >= view.ox && point.x <= view.ox + view.dw && point.y >= view.oy && point.y <= view.oy + view.dh) return 'new'; + return null; +} + +function imageImportCropCursor(mode) { + if (mode === 'move') return 'move'; + if (mode === 'n' || mode === 's') return 'ns-resize'; + if (mode === 'e' || mode === 'w') return 'ew-resize'; + if (mode === 'nw' || mode === 'se') return 'nwse-resize'; + if (mode === 'ne' || mode === 'sw') return 'nesw-resize'; + return 'crosshair'; +} + +function updateImageImportCropDrag(point) { + if (!imageImportCropDrag || !imageImportState?.crop) return; + const {width:sw, height:sh} = imageImportSourceSize(); + const p = imageImportSourcePoint(point); + const start = imageImportCropDrag.startCrop; + const sp = imageImportCropDrag.startPoint; + const mode = imageImportCropDrag.mode; + const minW = Math.min(8, sw); + const minH = Math.min(8, sh); + let x = start.x, y = start.y, w = start.w, h = start.h; + + if (mode === 'move') { + x = Math.max(0, Math.min(sw - w, start.x + (p.x - sp.x))); + y = Math.max(0, Math.min(sh - h, start.y + (p.y - sp.y))); + } else if (mode === 'new') { + x = Math.min(sp.x, p.x); + y = Math.min(sp.y, p.y); + w = Math.max(minW, Math.abs(p.x - sp.x)); + h = Math.max(minH, Math.abs(p.y - sp.y)); + if (x + w > sw) w = sw - x; + if (y + h > sh) h = sh - y; + } else { + const right = start.x + start.w; + const bottom = start.y + start.h; + if (mode.includes('w')) { + x = Math.max(0, Math.min(right - minW, p.x)); + w = right - x; + } + if (mode.includes('e')) w = Math.max(minW, Math.min(sw - start.x, p.x - start.x)); + if (mode.includes('n')) { + y = Math.max(0, Math.min(bottom - minH, p.y)); + h = bottom - y; + } + if (mode.includes('s')) h = Math.max(minH, Math.min(sh - start.y, p.y - start.y)); + } + imageImportState.crop = {x,y,w,h}; + imageImportState.previewMasks = null; + imageImportState.previewOptions = null; + drawImageImportCrop(); + updateImageImportFinalFileInfo(); +} + +function updateImageImportFileInfo() { + if (!imageImportState?.source) return; + const {width,height} = imageImportSourceSize(); + const name = imageImportState.file?.name || 'Pasted image'; + if (imageImportFilename) imageImportFilename.textContent = name; + if (imageImportDimensions) imageImportDimensions.textContent = `${Math.round(width)} × ${Math.round(height)}px`; + if (imageImportFinalFilename) imageImportFinalFilename.textContent = name; + updateImageImportFinalFileInfo(); +} + +function updateImageImportFinalFileInfo() { + if (!imageImportFinalDimensions || !imageImportState?.crop) return; + const crop = imageImportState.crop; + imageImportFinalDimensions.textContent = `Crop ${Math.max(1, Math.round(crop.w))} × ${Math.max(1, Math.round(crop.h))}px`; +} + +function initializeImageImportOptions() { + const frame = timeline.getFrame(imageImportState?.frameIndex ?? timeline.currentFrame); + if (!frame) return; + const activeLayer = canvas.activeLayer === 1 ? 1 : 0; + const otherLayer = activeLayer === 0 ? 1 : 0; + const primary = frame.colors[activeLayer] || 'black'; + if (imageImportMode) imageImportMode.value = 'single'; + if (imageImportLayer) imageImportLayer.value = String(activeLayer); + if (imageImportColour) imageImportColour.value = primary; + if (imageImportSecondColour) imageImportSecondColour.value = imageImportAlternateInk(primary, frame.colors[otherLayer]); + if (imageImportSize) imageImportSize.value = 'fit'; + if (imageImportAlign) imageImportAlign.value = 'center'; + if (imageImportDither) imageImportDither.value = 'bayer4'; + if (imageImportThreshold) imageImportThreshold.value = '128'; + if (imageImportSmoothing) imageImportSmoothing.checked = true; + if (imageImportInvert) imageImportInvert.checked = false; + if (imageImportReplace) imageImportReplace.checked = true; + syncImageImportColourMode(); +} + +async function loadImageImportFile(file) { + if (!file || !imageImportState) return; + if (file.type && !file.type.startsWith('image/')) { + setImageImportStatus('That file is not an image.', true); + return; + } + const serial = ++imageImportLoadSerial; + setImageImportStatus('Loading image...'); + try { + const source = await decodeImageImportFile(file); + if (!imageImportState || serial !== imageImportLoadSerial) { + if (source && typeof source.close === 'function') source.close(); + return; + } + if (imageImportState.source && typeof imageImportState.source.close === 'function') imageImportState.source.close(); + imageImportState.file = file; + imageImportState.source = source; + imageImportState.previewMasks = null; + imageImportState.previewOptions = null; + initializeImageImportOptions(); + resetImageImportCrop(); + updateImageImportFileInfo(); + setImageImportStatus(''); + setImageImportStep(2); + } catch (error) { + setImageImportStatus(error?.message || 'Editor could not import this image.', true); + if (imageFileInput) imageFileInput.value = ''; + } +} + +function closeImageImportModal() { + if (!imageImportModal || imageImportModal.hidden) return; + ++imageImportLoadSerial; + imageImportModal.hidden = true; + document.body.classList.remove('flipnote-editor-image-import-open'); + if (imageImportState?.source && typeof imageImportState.source.close === 'function') imageImportState.source.close(); + imageImportState = null; + imageImportCropDrag = null; + if (imageFileInput) imageFileInput.value = ''; + setImageImportStatus(''); + imageImportDropzone?.classList.remove('is-dragover'); + if (imageImportLastFocus && typeof imageImportLastFocus.focus === 'function') imageImportLastFocus.focus(); + imageImportLastFocus = null; +} + +function openImageImportModal() { + if (!imageImportModal || timeline.playing) return; + const frame = timeline.getFrame(timeline.currentFrame); + if (!frame) return; + imageImportLastFocus = document.activeElement; + imageImportState = { + step:1, + file:null, + source:null, + crop:null, + frameIndex:timeline.currentFrame, + previewMasks:null, + previewOptions:null, + }; + if (imageFileInput) imageFileInput.value = ''; + if (imageImportPasteShortcut) imageImportPasteShortcut.textContent = imageImportIsMacLike() ? '⌘V' : 'Ctrl+V'; + setImageImportStatus(''); + imageImportModal.hidden = false; + document.body.classList.add('flipnote-editor-image-import-open'); + setImageImportStep(1); +} + +function openImageFilePicker() { + if (!imageFileInput || imageFileInput.disabled) return; + imageFileInput.value = ''; + try { + if (typeof imageFileInput.showPicker === 'function') imageFileInput.showPicker(); + else imageFileInput.click(); + } catch (error) { + imageFileInput.click(); + } +} + +if (imageImportButton) imageImportButton.addEventListener('click', openImageImportModal); +if (imageImportUpload) imageImportUpload.addEventListener('keydown', (event) => { + if (event.key !== 'Enter' && event.key !== ' ') return; + event.preventDefault(); + openImageFilePicker(); +}); +if (imageImportDropzone) { + imageImportDropzone.addEventListener('click', (event) => { + if (event.target.closest('label')) return; + openImageFilePicker(); + }); + imageImportDropzone.addEventListener('keydown', (event) => { + if (event.key !== 'Enter' && event.key !== ' ') return; + event.preventDefault(); + openImageFilePicker(); + }); + ['dragenter','dragover'].forEach((type) => imageImportDropzone.addEventListener(type, (event) => { + event.preventDefault(); + event.stopPropagation(); + imageImportDropzone.classList.add('is-dragover'); + if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'; + })); + ['dragleave','dragend'].forEach((type) => imageImportDropzone.addEventListener(type, (event) => { + event.preventDefault(); + imageImportDropzone.classList.remove('is-dragover'); + })); + imageImportDropzone.addEventListener('drop', (event) => { + event.preventDefault(); + event.stopPropagation(); + imageImportDropzone.classList.remove('is-dragover'); + const files = Array.from(event.dataTransfer?.files || []); + const file = files.find((candidate) => !candidate.type || candidate.type.startsWith('image/')); + if (!file) { + setImageImportStatus('Drop a PNG, JPEG, WebP, GIF or BMP image.', true); + return; + } + loadImageImportFile(file); + }); +} +if (imageFileInput) imageFileInput.addEventListener('change', () => { + const file = imageFileInput.files && imageFileInput.files[0]; + if (file) loadImageImportFile(file); +}); + +document.addEventListener('paste', (event) => { + if (!imageImportState || imageImportModal?.hidden || imageImportState.step !== 1) return; + const items = Array.from(event.clipboardData?.items || []); + const item = items.find((candidate) => candidate.kind === 'file' && candidate.type.startsWith('image/')); + if (!item) return; + const blob = item.getAsFile(); + if (!blob) return; + event.preventDefault(); + const ext = (blob.type.split('/')[1] || 'png').replace('jpeg','jpg'); + const file = blob.name ? blob : new File([blob], `Pasted image.${ext}`, {type:blob.type || 'image/png'}); + loadImageImportFile(file); +}); + +if (imageImportCropCanvas) { + imageImportCropCanvas.tabIndex = 0; + imageImportCropCanvas.addEventListener('pointerdown', (event) => { + if (!imageImportState?.source || imageImportState.step !== 2) return; + const point = imageImportCanvasPoint(event); + const mode = imageImportCropHitTest(point); + if (!mode) return; + event.preventDefault(); + imageImportCropCanvas.setPointerCapture?.(event.pointerId); + imageImportCropDrag = { + mode, + startPoint:imageImportSourcePoint(point), + startCrop:{...imageImportState.crop}, + }; + if (mode === 'new') { + imageImportCropDrag.startPoint = imageImportSourcePoint(point); + } + imageImportCropCanvas.style.cursor = imageImportCropCursor(mode); + }); + imageImportCropCanvas.addEventListener('pointermove', (event) => { + const point = imageImportCanvasPoint(event); + if (imageImportCropDrag) { + event.preventDefault(); + updateImageImportCropDrag(point); + return; + } + imageImportCropCanvas.style.cursor = imageImportCropCursor(imageImportCropHitTest(point)); + }); + const finishCropDrag = (event) => { + if (!imageImportCropDrag) return; + imageImportCropCanvas.releasePointerCapture?.(event.pointerId); + imageImportCropDrag = null; + imageImportCropCanvas.style.cursor = 'crosshair'; + drawImageImportCrop(); + }; + imageImportCropCanvas.addEventListener('pointerup', finishCropDrag); + imageImportCropCanvas.addEventListener('pointercancel', finishCropDrag); + imageImportCropCanvas.addEventListener('pointerleave', () => { + if (!imageImportCropDrag) imageImportCropCanvas.style.cursor = 'crosshair'; + }); +} + +[ + imageImportMode, imageImportColour, imageImportSecondColour, imageImportLayer, imageImportSize, imageImportAlign, + imageImportDither, imageImportThreshold, imageImportSmoothing, + imageImportInvert, imageImportReplace, +].forEach((control) => { + if (!control) return; + const refresh = () => { + if (control === imageImportMode || control === imageImportColour || control === imageImportSecondColour) { + syncImageImportColourMode(); + } + updateImageImportPreview(); + }; + control.addEventListener('input', refresh); + control.addEventListener('change', refresh); +}); + +if (imageImportBack) imageImportBack.addEventListener('click', () => { + if (!imageImportState) return; + if (imageImportState.step === 3) setImageImportStep(2); + else if (imageImportState.step === 2) { + setImageImportStatus(imageImportState.file ? `Current image: ${imageImportState.file.name || 'Pasted image'}` : ''); + setImageImportStep(1); + } +}); +if (imageImportResetCrop) imageImportResetCrop.addEventListener('click', resetImageImportCrop); +if (imageImportNext) imageImportNext.addEventListener('click', () => { + if (!imageImportState?.source) return; + setImageImportStep(3); +}); +if (imageImportApply) imageImportApply.addEventListener('click', () => { + if (!imageImportState?.source) return; + const frame = timeline.getFrame(imageImportState.frameIndex); + if (!frame) { closeImageImportModal(); return; } + const options = imageImportState.previewOptions || imageImportOptions(); + const importedMasks = imageImportState.previewMasks || buildImageImportMasks(imageImportState.source, options, frame, imageImportState.crop); + const secondaryLayer = options.layer === 0 ? 1 : 0; + frame.layers[options.layer] = mergedImageImportLayer(frame, options.layer, importedMasks.primary, options.replace); + frame.colors[options.layer] = options.colour; + if (options.mode === 'dual' && importedMasks.secondary) { + frame.layers[secondaryLayer] = mergedImageImportLayer(frame, secondaryLayer, importedMasks.secondary, options.replace); + frame.colors[secondaryLayer] = options.secondColour; + } + timeline.currentFrame = imageImportState.frameIndex; + canvas.activeLayer = options.layer; + timeline.updateFrame(imageImportState.frameIndex); + timeline.setActiveFrame(imageImportState.frameIndex); + closeImageImportModal(); +}); +if (imageImportClose) imageImportClose.addEventListener('click', closeImageImportModal); +if (imageImportCancel) imageImportCancel.addEventListener('click', closeImageImportModal); +if (imageImportModal) imageImportModal.addEventListener('pointerdown', (event) => { + if (event.target === imageImportModal) closeImageImportModal(); +}); +document.addEventListener('keydown', (event) => { + if (shortcutAction(event, 'dialog') === 'closeDialog' && imageImportModal && !imageImportModal.hidden) { + event.preventDefault(); + closeImageImportModal(); + } +}); + + +//Playback +const buttonPlayPause = document.getElementById('play-pause'); +const buttonFirst = document.getElementById('first-frame'); +const buttonNext = document.getElementById('next-frame'); +const buttonPrev = document.getElementById('prev-frame'); +const buttonLast = document.getElementById('last-frame'); +const buttonNew = document.getElementById('new-frame'); +const buttonDelete = document.getElementById('delete-frame'); + +buttonPlayPause.onclick = () => { + timeline.togglePlayPause(); +} +buttonFirst.onclick = () => { + if (timeline.playing) return; + timeline.setActiveFrame(0); +} +buttonNext.onclick = () => { + if (timeline.playing) return; + timeline.setActiveFrame(timeline.currentFrame + 1); +} +buttonPrev.onclick = () => { + if (timeline.playing) return; + timeline.setActiveFrame(timeline.currentFrame - 1); +} +buttonLast.onclick = () => { + if (timeline.playing) return; + timeline.setActiveFrame(Math.max(0, timeline.frames.length - 1)); +} +buttonNew.onclick = () => { + if (timeline.playing) return; + const insertAt = timeline.currentFrame + 1; + const sourceFrame = cloneEnabled ? timeline.frames[timeline.currentFrame] : undefined; + const addedIndex = timeline.addFrame(sourceFrame, insertAt); + if (addedIndex >= 0) timeline.setActiveFrame(addedIndex); + updateAudioFillBars(); +} +buttonDelete.onclick = () => { + if (timeline.playing) return; + if (timeline.frames.length > 1) { + timeline.removeFrame(timeline.currentFrame); + timeline.setActiveFrame(timeline.currentFrame - 1); + updateAudioFillBars(); + } +} + +// Slide-out Page Tools below the filmstrip. Erase/Insert deliberately delegate +// to the existing page controls so their behaviour stays identical. +const pageToolsToggle = document.getElementById('flipnote-page-tools-toggle'); +const pageToolsToggleImage = document.getElementById('flipnote-page-tools-toggle-image'); +const pageToolsTray = document.getElementById('flipnote-page-tools-tray'); +const pageEraseButton = document.getElementById('flipnote-page-erase'); +const pageInsertButton = document.getElementById('flipnote-page-insert'); +const pageCopyButton = document.getElementById('flipnote-page-copy'); +const pagePasteButton = document.getElementById('flipnote-page-paste'); + +function setPageToolsOpen(open) { + const next = !!open; + if (!pageToolsToggle || !pageToolsTray) return; + pageToolsToggle.setAttribute('aria-expanded', next ? 'true' : 'false'); + pageToolsToggle.title = next ? 'Close Page Tools' : 'Open Page Tools'; + pageToolsTray.classList.toggle('is-open', next); + pageToolsTray.setAttribute('aria-hidden', next ? 'false' : 'true'); + if (pageToolsToggleImage) { + pageToolsToggleImage.src = next + ? './assets/flipnote-editor/closepages.png' + : './assets/flipnote-editor/openpages.png'; + pageToolsToggleImage.alt = next ? 'Close Page Tools' : 'Open Page Tools'; + } +} + +if (pageToolsToggle) { + pageToolsToggle.addEventListener('click', () => { + setPageToolsOpen(pageToolsToggle.getAttribute('aria-expanded') !== 'true'); + }); +} +if (pageEraseButton) pageEraseButton.addEventListener('click', () => { + if (!timeline.playing && buttonDelete) buttonDelete.click(); +}); +if (pageInsertButton) pageInsertButton.addEventListener('click', () => { + if (!timeline.playing && buttonNew) buttonNew.click(); +}); +if (pageCopyButton) pageCopyButton.addEventListener('click', () => { + if (!timeline.playing) timeline.copyPage(timeline.currentFrame); +}); +if (pagePasteButton) pagePasteButton.addEventListener('click', () => { + if (!timeline.playing) timeline.pastePage(timeline.currentFrame); +}); + + +// One-click to start editing, make sure opacity is 100% while editing +filenameElement.addEventListener('input', () => { + markEditorDirty(); +}); + +filenameElement.addEventListener('click', () => { + filenameElement.contentEditable = true; + filenameElement.classList.add('editing'); + filenameElement.focus(); + + // Set opacity to 100% when editing + filenameElement.style.opacity = '1'; + + // Select all text + const range = document.createRange(); + range.selectNodeContents(filenameElement); + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + }); + + // Stop editing on blur or Enter + filenameElement.addEventListener('blur', () => { + filenameElement.contentEditable = false; + filenameElement.classList.remove('editing'); + + // Revert opacity back to 80% when not editing + filenameElement.style.opacity = '0.8'; + + // Ensure the title is updated based on the filename text + const newTitle = filenameElement.textContent.trim() || 'Untitled flipnote'; + document.title = `${newTitle} - Flipnote Editor`; + }); + + // Stop editing when Enter is pressed and enforce character limit of 32 + filenameElement.addEventListener('keydown', (e) => { + if (shortcutAction(e, 'title') === 'confirmTitle') { + e.preventDefault(); + filenameElement.blur(); + } + else if (e.key === 'Enter') e.preventDefault(); + + // Enforce character limit of 32 + if (filenameElement.textContent.length > 32) { + filenameElement.textContent = filenameElement.textContent.substring(0, 32); + } + }); + + + + +// Initial blank-project setup is complete. From this point onward, real user +// mutations arm the close-tab confirmation. Auto spin-off import temporarily +// suppresses tracking and resets this baseline once its import finishes. +editorDirtyTrackingReady = true; + +/* Editor mobile editor layout: move the existing drawing tool picker directly + * under the canvas on phones/tablets. The same controls are moved back to their + * original DOM position as soon as the viewport returns to desktop. */ +(() => { + const mobileQuery = window.matchMedia('(max-width: 767px), (max-width: 1024px) and (hover: none) and (pointer: coarse)'); + const install = () => { + const stage = document.querySelector('.flipnote-editor-stage'); + const canvasWrap = document.querySelector('.flipnote-editor-canvas-wrap'); + const picker = document.querySelector('.flipnote-editor-tool-picker-inline'); + const toolPane = picker && picker.closest('.flipnote-editor-tool-page'); + const tabOne = document.querySelector('.flipnote-editor-tool-tab[data-tool-tab="1"]'); + if (!stage || !canvasWrap || !picker || !toolPane) return; + + const originalParent = picker.parentNode; + const originalNextSibling = picker.nextSibling; + let dock = null; + + const ensureDock = () => { + if (dock && dock.isConnected) return dock; + dock = document.createElement('div'); + dock.className = 'flipnote-editor-mobile-primary-tools'; + dock.setAttribute('aria-label', 'Drawing tools'); + canvasWrap.insertAdjacentElement('afterend', dock); + return dock; + }; + + const apply = () => { + if (mobileQuery.matches) { + const target = ensureDock(); + if (picker.parentNode !== target) target.appendChild(picker); + toolPane.classList.add('flipnote-editor-mobile-tools-docked'); + document.body.classList.add('flipnote-editor-mobile-layout'); + } else { + if (picker.parentNode !== originalParent) { + if (originalNextSibling && originalNextSibling.parentNode === originalParent) originalParent.insertBefore(picker, originalNextSibling); + else originalParent.appendChild(picker); + } + toolPane.classList.remove('flipnote-editor-mobile-tools-docked'); + document.body.classList.remove('flipnote-editor-mobile-layout'); + if (dock) { + dock.remove(); + dock = null; + } + } + }; + + if (tabOne) { + tabOne.addEventListener('click', () => { + if (!mobileQuery.matches) return; + window.requestAnimationFrame(() => { + const target = document.querySelector('.flipnote-editor-mobile-primary-tools'); + if (target) target.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + }); + }); + } + + if (typeof mobileQuery.addEventListener === 'function') mobileQuery.addEventListener('change', apply); + else if (typeof mobileQuery.addListener === 'function') mobileQuery.addListener(apply); + apply(); + }; + + if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install, { once: true }); + else install(); +})(); diff --git a/assets/flipnote-editor/editpattern.png b/assets/flipnote-editor/editpattern.png new file mode 100644 index 0000000000000000000000000000000000000000..68e4679c3598eea005282045659a13329e5ffc67 GIT binary patch literal 19090 zcmeHvX;_oj);6|KMMQ8w>Hs()GALF>kSR{61%Vbyh{zDNiiN|V2nYzNQ9+?X)gl!I zTAV5g5JhArMh;?CLQ_vfnIa%WgNzYENJzf5pC?S}Ip_WHeLvpsve(rXAGueWb;QBwMXuB7x?_LMK+J1*Otwc&r0LR_|OQp#a3cmsb> z_TA{XQAz1mlA3h43jA3$c>B%}C8gE((7%(GfA3MRq-1aK^_Gp_9r5mLtS#`Wjeah% zZZo88$lRmldUeI=%Gp-;yf;vlNE;YR3pdPMy75-{;UCG}X_=9WDxJPv>=s}gW>SB9 zse{w^>%$i8JecKhDEj=t`p3~0B9op+XTORG_e{6pdxki&ogWOos$3TnHO#K_?<`|l zOQcuC^6-XIFWA+Sx9i})``6vcvI)v*hcDru->8c`REhZ4-z_0c!oK<@nT$@Mq_n|} zs)c=_>5~aQnE*24 zr(E#K1pk-4fV`A2T+3W5$|FRJ8hm7z!fS>lgzl$INnUvO3zpB5?v@*rbKCCtU*{9# zZ@rppOx@P1k%{ttLRm;nv|pi*o#>YCv|QJwQg^0RX%~BOof-J)TSo@aP5wo=D=q_`zJjS^u`fpd;4#n)j1D7Kd_9%+OABKD$~M#nL|3<_=M=Kr);s?2!{?XCDobY2`)1MWzFkbxDSICG+Xio{+gwf#Kn`*cRqLlMj7QE4 zzip?ywgaj4jNz*~WO}h<~8r7FFxLrPl`~lanuzxrsAbf)lOE#tThZ zSw9`9g*P+ISyQ(jk`+qfg};8mJN9+y3{Re)x|?yfgwStr#Fu-%R?WQ(@|`hp1U}^3slHQ;0>X z#LgoNosRpP313D2UJN;TtNp!Ei2u%7r0`j~5y>V%IuJX6Ep`RX_xfudu_7qZ_rH!mtcENmhB)-)lfID{cCD*_bPMMxMq^T&2gB?^5&4FeFWii0(6F zWt|vjXmRZE3WXgF^#0VN6Vm}0M??$uPJCtSHT;$7ktP&|M}FU5!E^4>ZHEs|T&!{* z{wdpmBQkPm*LrkBUqY{ObARmGvHicgGGB$(0B&bJe1fd9gU7cuBqeK(xlT@TOYO+r zI$K|q``-osD5#-$FpL(BJKit{Of=*_Jyp^i&b#z#8>OeIh$1c%v4?5h%r#?9iWlK?#A|zhvb(LOY}#%=qRC@<@t%(C&eEI%U*GBY&eS^3$3ji%$6q0?3}?^- zOS^sxp0mTz0rxDS%bhv&%)Q%tIgeG0>%K7}+2uv{Qe=$;weWL-Gw$%c;JS0tT;sa| zbLjS`!qNoSOuOc@KT}$WVi;oNQjRwLz;QA+oKbw}3fXQxgtfg2Ew_9bi4*&DYH7~% zTWvoE&-o$K>qew_Jpg|4B4y(*zlPX-tWMPm(4-GdCsy>ErmDCuA+@<1k)nqmOHK&M zeollH@{ahldS%*7^k~1z_(wY&%cq}MH-)IKLgR{$bIshR67OteJj(r!G4!AVRwdnC z3lG=dRYM-`)L)kX9PHkvDG&@ZS$@K5?7ejnIo^fKD2(DRKUO@fB~G47l+${Ge7DjP{_f z;(mxlrA~?B1jAb6PLLmP9y3)~I!!xUvi-<3!@yD}cr_#pPJ#;Ufa%Q`VFz;o`}ocE zA$H);{6NgxZ#E5{1769{gd})%3!|>QQdIl&3szPdIka~OT=IGcxdjxnfT*!Jqc3A9;FZ*-{Fl?(q3s(`)Diy;YbUREc+5F6U{e*EG)- zHA05g?6gFrn6XN+`7&f{$g`~WKK03QRX^_u*<9t!Ls>V8u=p}pjTPUN?QxMnvI$Ni zyKMqLMwfUWzeGWzS2;5sZmm=K(wUTY4dSjKE&74Y;8lO0rn=opfzM`J1i8t`{V&V( zruv>;Ru_uMCV98Q_xJ($9jdN#_e>{{0_sh`TcIBOa$Xr3U)k8>ASwgK;ChIW&e;m} z4Xn9?;b-vNv*;^0ZFyyF%HQya-=BsfFD!fsg(1Y^Br0TMyPe63Plwd2OrzyIq-29l z?O>O6=PD|8mTA&oH>Rp7(}>_@NE0ym39EdsFOs=VjN&1CR(wqI6rzLktdZNO<9>Wk zB(JDsvK0Bhv>istoelO}K7&p)A(-Gd<8`NNN%Q*4n;1q*#+mjs*{%kXE0ueuR+ae90hQ^@aP3@e zy2q{8e;nZ%0?q&N9Ul!1M1GrSVo>38K2iP_@d8XV7&E`4teZzYMC=%*&S|TS9&Z6oG6Bh2F1Y z86M=YEqJ_Gu81dZiW`U#q$yDM7n3+^u#)ZOK&eP5hn*-sD_V)iUU}c`k=Wb9$n?b# zf}^<_mU_@Z!J;30?WR}TpsPYEp09Fwtetv%^0FyL9rLRcMcaoAOLNRd3Xk(pj@Jy|b(7$=?P3s8| zLit{(s)lB1r_Vq!wh521x3j2jYwSH~u~X($Q*UB8@O7gKUuRx=g<)9WEG&;91&Q<} zerb70ghizMmMr5wA6ib4%cy~^Xk`f2J;MKJ5Z_J ziu-FtNAl|hPPd}-CT1OEHkke9MX=EWm&AEZb&jL-B+ znd@)HQVJf1+6v=s?sd|JTVuopw z6B4Ihff^)@?Q4`72Y9Z+R=rY#qf(pG1FP+7V%Et!vNoB5v&A^fFq!*R&soCT@kSG9DmSy+cMgQQPlNW)bE+X}3Z(+>K zG33y_e*w636jp%c5!Hcom|3>tEZHrV%>9Bjb-%$VNT4Af0C1%*vlwD$LjhB!l|xyp z2nyJm7YP>}-5Lt^q`L8-azH^p1qX)6q`J*lHU^G`L00>tQ=H&CG1pMd5+8RvU1&10 z8OwmvWUWqpZY|SCw!`thzMWwN+dEEH$wSEql5^r`K5MormYMrlLydp! zdD#*<@6s&H9>m3@730oH*`kKt=3t`2Fkn;FD!sXE!AbJ_UI07GZzE1z zORUZ%)Cn6IOfAvhKV85=eMs3LK_ z2P1yqoIS%YZoHRsvX6ePNPnY7*p#dq+~$;jd#6} z_A%lOf?^tF#i*tH-PK06qn?T2=Y%v6*}D^JC1eVqu@yU!izAnsi3I>U%t?P`8X#G? z9FS%vKKUkLUpkFL|E_E@%_>Y4$q1{guwb93fg}z>er6>zNYq)*CEcffl{TC zx95)YS2EwdEHYkcb)@UjnaMR?p_RF3{C-{kq{%}5q*&Bs3WF96T&4--O~E}tqo&9eRkk$_uK7AY$VYYxu!j(T`WBoIe6n>NwR*@Ph5$cVJ4{#D_> z5*lc6RcowsntQ#h8)$r=UGz(oC)zTpw++Rl;;Uh)cbT6o^v@>Iz1ILm>_~$)G zVz%Zr#W(x9^pzL$`EoT8SKdky`!?seN(`I)>q|Y|3?Z_Ui%y8=xDe*Go z;jxq;i^(xE&og0ZezgOi-{%Ks9qLe5V8%;ORur*t5|Gh#5pz#_+4$6Hqc+R2V=pPm}vOXC}wxk^R8RO?D;M^*HO# z3VxT^&Ss}(LKI8dM=&`i*a#{$5sS2Vb7IN;on?bbglPA|lOn91j3A&XdzAdBgk5@yt-2u7udH& zv4OLb>r{cV0zyW45hFLrd{n^@U}X6XR{wSY)m0t+rreCsSgdq2hsw#7^%)pn>jPS7 zBbogSuQ=q)1#_k z=!r5IMRhwot?a;?;Yj0n6`GzZv2+;A&)8{%KG^lbt&o8BwKALQ6xtN6ze!t_lr(2nATPLo4UeDivlx=McD~s4+-tKpn0v&BS&h65X#G%uI zLJk;%mz(Rg0v%tU#Q7X6YrvWETUk+Hy=Sw%!v17|T>Jd6@EAsLQ7_@$5=#S@=%F`Y z@%6?U>Pd?#dsutcVhS1nhCm*$u4RgSdkciAfHoBM5?dI>aF;&FNDtcP!*x{`OhFO@ z2rXvAr`V^zTLDS6-~RA4Lyt%=&N*MFMLHy~^9>lK7GuhP49h6h z%x=CZL?$Ag9FPr1cA7t%&fb+wAgMEpbgLITLbicIj;;|3`93hfajsp5?Mwvoa4d}! z2{$XmBEhblZKgG_WPt6$%Sgi=b!rGPG5^98qC$Q{W^Dv@WOPaW4h+|V+@WwYNMn@= zEfI!roFtp2AVXh&{pbw5dtk)`UvmT9x?dc3qUvW{d_oJCg}2K!JsC6U_L}rQf_t2Z z3&A*MRFIW*$_5_>d&k3PTQ$^(9MGEt@zPo&jG&IVTGlmkK6Gv{(G0E$NXJTr2V~Gv zybc9hK#Cc`7>LkREm%Qn8~EoEy=kIzu$R_UV~kLbx4?*CC$J%@=N`L)(k95k5K}eW z$H3Atk#!^(0J0=n5gCs&w7YdK+=4w-Zq;Z4P& z#isIFNIOs!sL(2_XAYl+i=UV3^smUNb7P9^8JW&_ss->rgYCeHVnhz9V%}x$8TAcl z*`Pi`rWna?;0OrDdx{l4C+orCWB0U75Gkrt?X59ES_&r zs+qV~K}$bSoH;sL-Bxx0!Z?$$md!P@TWB0%h!lkxiL5h-X6G54Z;^SXWh z^wbj2)Y{iI9)8)-zH9pB`a>PZLUx(!SQJDHCxuwu)m>E(Qa>rAAb>y)6eSMx19|!K zhO~lCLU?W0Fu$vOue>O#YcNneK?z^M~TTzXZmavalV?CJ1e$o6~anuOD}w>%kMc7 zC+T}8j_U6z-fMBa`FZT#pTZi?`?lB6d}ajV~od!$VBZO^riZ6`>}@1jrs!+A|NneKZv}My=L6m@p0IsHl>(XUj zH_82*S@8wHIkvhiCaL>0YE>`BL^OR!F90fx?N6~);T_1+v7A;e(qUzRviFl-T!7DyLbx|?wg?HA3oSyyK$atDYJ(x#v?0<_jKQrPBhLK@!A zXz6suRf%1JGq6NB3qB93p(9Z7*UR9{>8I{10}1H)vo{FlNPMvK9!p&}dBDdGvdW1VA_{<4=tV zJ>!lX_K*G#9<PU?R(Fx21{;uK*Nqoino&VV;K$<~> zi-E9EnBU23F{%H(JI81{c=8C}oj&2N9h_~fFGF=uX^mPO^?)`=v8-S(qs8Q08AQ-zxQ94zvsp7<`3Kv9!&H` zzV>gQJWI~^{J?x1dzfbZQsMBpwuDKLfR`XsqkcdOMcQd~JAs{0;K%uPTeVKv>pzP2 zCfXjVi91}S7xk(E3gG(i7S(OD*GMH*)irb5)#dt3-q{4ZZj#NU7u?q7 zYIB?bEaEv3eDmCS6{LUR_l&7pTaf+*B8nnwB3)#k$;zHksv-ybfV3z8{{u-1cUnxh z#bleYtck*D?l#jzgq3ih1_IlK^vEL?ND~BY>*H>Th3_Ma_SZ`)#?TWqI-f#U6i^04 z&7)irW9aNC=|U?Z>3bpRB!Y;UxK6e@5pqg=;vzuRv5;LMB}xzfPc~eo2gC=1n&?Ga z7#_EtMCbqaa~do#o$we;%1Hb`_zr`5HY$#xy^rJsEGw>dvR)+bFj7Pkyv?#>nuz<~ z@8a^&OtO{^lnRimjhGO6Z9BVY*?4XEXsj!YdhOom%errU#u8l4dt5qsAu3J(CIZi3 zD(Fx8H>MCdUm*zqask|;ecAuyDc6(*x`@``3D12oDXjp6RS@Q|%<$3pgM{ez;BQwI z+UqZYXd@~DjRWn=#z?TmUGgKzatW!w^HMHw(91EY6U~BXL*B2t|M7eULD6muqur=m zf?yhLH4^ugY5Ceyi2uc+k~6u(hX&W|;6{O$?3mzep;#{-X%fn`GgLPaUZ6LM^==gb zgJpTNadYNMQgZMEX~;q(fZ!<=xm^MD=l`>*>I;}K%M{!`m1Tr81ayarrpmwdxtNY! z5cj@mhGX99tmvU2r54lMN4!>%69x4_@?x^v{YQDqW7mSo!XL!&2ieO<`an{Vu^Umg z^}c%ZkT-DI9r3^t#WpKRp^BNk_Y)Twtv(!J3!o9x=x>pV4r~h=6A<809Ofo2*u=VsZk2O4LM8cE=QrXPz)LGtmPfZwAz>&}KSW$R0vN85)3S zGiVe9QjK^HCOd#~{;0>C4pgX6nYy}yMA2>%dg20Cqq9Cg!JRwYI97*z#@9Cu;v&DL!c{ZXTzvk3tOS%MYbul zvhGO_q3ev*oYC&xGsp7%v=g8ZMBf;^e?uW$tqv?*37z%DB&=Lxi}yX%&=8%=1TG)b ziRS1E_rhc;8Xqzx7$9G`<#&zD1v8Gzao||H0u^Fq$f&$oC`$#cE*3i9P)h)O8tr}Y zHyq@oKWH+IApB!_6e96_Ye(^=Gf?_Q~$-J0wK#DP`4FJkZSzix(Dmd z!MXzBIH43|2pi%;Qd(&2M>Y%NUp$ZHXdt8MIM*P3Hc|?{Yw-wG$fDR*VNCIU+0_Gv zq%=y`?@0B!1<6wJ$Px|cfBMpcKiKfmDMy9skY;doAw8z$c(XT3=blBqFMGapnO4gL zmZB&vu1*3gC=5nu!b)K~*v)1UG%}4Z6y@IiAnF`p(g+Q!K-L|44#TRzuyYEnLC_Nb za{{kML1*#ipJ0isWN^v5j^%SoVF(q8W*SYGjIdboP2H*l{Fn^btunTfS)U4NPMZCqOCGAe7Bx z=6{1c5-qJe15;#P=)>l;Qeb9oBg(&S4|=O~=631Zf#CjjjfPqh0xoE(3?7-OM>Vx^ zd%qljTXf{$6Pie3dA?bUR8^xZ|LoOZ<|9ThgFZB{i#OEPb$vln*;X7TcTH1vmB;Mg0_pt`wUr3FHwP z9OCtFESP~C7hEZujwLqz6)mYom3u*%MUz83VBfxdaWEM; zCiJBhfFsfuBeZ-VpHzEI7u+3n* zv}jIRTu^0tNT`fACB*?yHQ=^mqkImlJM>HWqWTcW zc{$D%p?-zl>b2Rsy$=V_ekfd~QOj*Mv(M@9dsn(0q{?_ustcM9|JC5niZ<&HZL8^~ zHP=uc)C%NnzTHzrtL-2g1n^wiZSE0{0?&Axfm zpi0m0n_$c(Ql=IyPx%W?3&t{iW>shE0c}lnj;43>JoOR@Fl;b9Kjm33Y(l9|3+KjQD~UN9+PgbB*U*U?7=(lM|^au1$My8JU_n@(G#{9@4tWt zr$z6F6pz}?2TWa;;^QU5YjNhCnnVEjHetd1Bb;y1G~^WG{FPY4YAA&V4NQ$DC%lgb z`bI$>`+xzdF}g~I4GQ1M1h&!nonplsdUA4M2Po*B7}!|ej-x2 z3sdcoZj%{`jinHzIf^AgV}9hs5Y)%-+ehW?pF8MJ5RvERwC}eCO)J>}$h+%5*3r?Sf|VL4#=(O>|YyPfI&gE4zOhQrRmdr`;iQgA^9H0 zVc$WdjmT9%s6f;}18W6VVNh3S#PNMII&+o0_9swq1@)&5(BL7q-iiYB8JH~-)ce|= zC6HElzdD{gsyTu%0Gw+sBnf}hc!PvI+IV7I2~laz+7BG`!;)?4#YiDwJKSh zeKLe?YjQbd4;tOVFKXb;4IyPp=>*vp=qCgk>be8pKDGbs{@MY!hqj&Yi#kADFwy8w z)Ng{{UE(EL-QB^kl(irn2L*w$N))z5u(AWTx%G9+QWYa)~1Wccg=1O z9mS$Fs7lv$87VCSM3YP4j<&s5$#yZE^_bkJupgR4fazj4DrNK8DQkO9@&%X@)z-1&rOWePoBKJ1-=pC$} zYKiR>y8oDaAmZh5u)`6KLXph^qW!=2uLStC@$WvF;FAfIKAGT?36wsW;FAg9cewxG z=7OLOa_C!*{7B6t{BPD-<+b_gX%FW`tzRhq5}oG>|3CiSLm#QKQB>|hVeA3;8;9=w mBi7scJxJW}H^I(YeFGB9~F Nc)I$ztaD0e0sxIE3=seT literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase1_Adv.png b/assets/flipnote-editor/erase1_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..143bd56066737515f252841ff537f08d635258f0 GIT binary patch literal 773 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`cJxJW}H^I(YeFGB9~F Nc)I$ztaD0e0sxIE3=seT literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase2.png b/assets/flipnote-editor/erase2.png new file mode 100644 index 0000000000000000000000000000000000000000..92669616ee0969e2c35668b964707f772233c960 GIT binary patch literal 793 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`+}p&7%K{#fh>A8zUR2l5NwpKnt`(u0VB vkt8_f@6(DKRcteQdEp5V+52bLJrQTy#Q$t#)ITF&l4tOA^>bP0l+XkKyhRuY literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase2_Adv.png b/assets/flipnote-editor/erase2_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..21f172e496ed2062945296db3c446736f99d23a2 GIT binary patch literal 789 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`rQ_w`S zNzL`b5{_9Sj+2dg?B(;n=le4GTfR3_UT=mhr-V(za1lIwhxyOs!~YCERqc$k`=Wb( z^Fq{sf%&sgbIyaQ)$^hsl;3~`8B7KijypvB9?U;U^nBhof-ILh46=c-*=5M$POzsSxu6{1-oD!M<$MhAw literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase3.png b/assets/flipnote-editor/erase3.png new file mode 100644 index 0000000000000000000000000000000000000000..31f0454adc73fde134fc3620d239c79c403e7136 GIT binary patch literal 818 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`F1;bLZv%Hs{`~D%ciO7K0|8ax?%N`LJ15V!FF#}5f3gJ6Nd+}`g#Zm9>4u_ j%U9V1LmUCqJ}G|51r4pX3;}mwUSRNa^>bP0l+XkK5eqGe literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase3_Adv.png b/assets/flipnote-editor/erase3_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..22a05d90d880956ff07aa54efc4b35a933589df9 GIT binary patch literal 828 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`rQ_w`SNzL`b5}(iW?f2DRtKz)#^z+}`XREo9B{{I^7=(g8i{&?{PXCr?LPwnpE%y6wla zKwNf|Z5PS=_`Nc^=Cg(#G?3Bl+8xa`|MUNvTTIg*Ibp;ISn;OaOAD^_WpKx|gTe~ HDWM4fCWthi literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase4.png b/assets/flipnote-editor/erase4.png new file mode 100644 index 0000000000000000000000000000000000000000..cedd908f51a19d7e5629274138c124c3b88eb09e GIT binary patch literal 876 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`RNvF@C%LDt6vJi~INV)mK!+{$_nJlf}1dfv2E} zW|Nxhg(V!bL>wn8WdxnO+;6|X{@-ig2a56X`NICb89`XpQCCp;@BBlHCD$F!tUmU8 z$2aXaZJgNL#^UQ$5Nwftd;8hBG4H#lB|Cu}jOma~r+puOdA4S|^z6^BhYy}R)q?Cl zg!0tIvkxtPyM3Cm-@MTGb4d4RgFb~Nps#I*3otKj!uan)&c`^$3`dC!E$dgboFf9^PQ$e*a+j1TICOf;L+ zTrVu)m?h#kSt%ok#kXsLXQjM+{QULvtAu{2e?OgBkmUrI5yYmPI)Z1u)N`e8{u9Tx zyx+c`w{F^bAE;~4jlY-o{PBYyziMQ^rEOb#JLmrQI!j(ZUmS|dDwh{*yL)5C?XLMV z(R>LrH>V`n;&$EZ-!^Kum1p5rv*WnzRk^Qq>B7h%1v7(V)|9Jet$T0h*q-LQKamp@ zhzKcI$ezlYeJJ;-F&gEV9(-L zZ@2F|Z*((16-RQ|bkXK9*5O@s`7Hm$J7{-M9y`r{hopE#jLC6Z?m u@jK`Q^{;=<{KEIaL=TkuFkR<;lE3I1|1%N)d6mE<&*16m=d#Wzp$P!YlN;0k literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase5_Adv.png b/assets/flipnote-editor/erase5_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..f130ec673c308b73a1b8bb5ad450a9fec878dc75 GIT binary patch literal 832 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`}xs z@$FjRDQKeEq~>~I$(sK8_V42BO*#LZdj9)%bGS4%MI2NXd}jYQ*y8k#o}0=an%{2I z{hjQL;V!V1n=X4l6wD8?ecbRpiY^WVg$T7hJHTe9#`0yv9D5^pLlZ{`PMI6Wwf(kP zH4ER#HoCaAa_2n%&FKxxH!g&yLgX-y^xDVB`JDe*!%nNsz+}(h>FVdQ&MBb@0K}jl A$N&HU literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase6_Adv.png b/assets/flipnote-editor/erase6_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..65089a8dd5dee24cbdd2992e4968f0a1a6c9faec GIT binary patch literal 896 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`m-{k~tU58{PPG@I01 zFD&7hCE_?)DI*&KTmOznx!dO5 zZn}RHcQ{Po^y7Y)?Tb4g((@M1IFDQZhyQn5e$VDzs(v1qJ3f_JzA^7R|NS_3JX_uS zsy{a@OmPGXBKVNfCfS1fF8>!f6on&-%T(v<3-~LPmVTH5%vlVcu6{1-oD!Mj#WAE}&f7a1c@H^=usSy1 zVV{x^w4%w)S5$2&tJgy2?gNJ|8BLD+)O3|Uf6nsD45G*PvLBen;@h>rQ_w`SNzL`b z5{_9Sj+2!#f{uNC{`>Xc)v6z+9)G&$(<>*C7*>TO3;zG|TjT!j*nI8vr;C3+oc8}I z70pKV1IbPs(b^y1W44<1eA9Q9n2abogxr+FHgmt-W0+mI5F_n@Y?t)1E3xA-s^;Yo Q1}1O@Pgg&ebxsLQ0JK07Gynhq literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase7_Adv.png b/assets/flipnote-editor/erase7_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..52b63cc131d931b144f2a07b02433ea8f8ba7c1f GIT binary patch literal 824 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`8v0<0SPYl!91gb)zxGUzD>`^xW%13wPb+RX9(}&LowCof+m_xYOWWSyxDI5|IWYpU)4WMxc`2A?|L<;I0rVxqzQiZTi^d_$LE)OK7RdG zUi)bO9(`dPhR!}@Sk|a*un_Jl6pJ=$$~<0Hz>3RdX06F@n8FjBFuVpfBq`Y9nD0X_ zA2e^HSO5zP6v0BE#;Xr-YRus}UQt_u(^25CMKKR%$qt}zvhez5kKHaNTp@)SfiO$% zi1Zbj?!c)L7{qb%v5lO_aSzjqk|59p^SF-hkcuHgBkmwTw*sN^Q=z3?++Ti!V=bVx fiJ>knyoi1FNwsSx9$Zs^IfB8{)z4*}Q$iB}6YwaK literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erase8.png b/assets/flipnote-editor/erase8.png new file mode 100644 index 0000000000000000000000000000000000000000..7ea8530a42c945871f79d8ab0d7d6030ed29bcbf GIT binary patch literal 771 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`q5CCEp(;J4RC`Fol7 k^l)ROMNq(;UH3%1saXEb=|6HTz$DM$>FVdQ&MBb@04sYRAOHXW literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/eraseaudio.png b/assets/flipnote-editor/eraseaudio.png new file mode 100644 index 0000000000000000000000000000000000000000..a922dc7d84c34b99cf0e2d8062542e47f100e46c GIT binary patch literal 3893 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 aQm#{Wo(OCxD8~O_Dqwc7U(T$klz#)EO}PaC literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/erasebig.png b/assets/flipnote-editor/erasebig.png new file mode 100644 index 0000000000000000000000000000000000000000..5f6c54311d7b3fa96552e0b6d1bb7f96c1596fc8 GIT binary patch literal 8397 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/eraser.png b/assets/flipnote-editor/eraser.png new file mode 100644 index 0000000000000000000000000000000000000000..09f72a394049c07de2ede3a4a68512a49b4e90d5 GIT binary patch literal 9937 zcmeHN2T+sg)=olLgNgzckQ(kPC@NhL5fVT_K#GEZxCkrK6a^OvC?&*#>!Pr$vMMzz zDn&qwNDYayfQl4FdJCc;9Rw0;xQBe-?mu(qpF6Ynzccs0Gw49RSI+mAbDrm%=e&5` z+R{XLrQ}Ks1|w{GVBcX32B!pHDui#~UmO?4;5!EEci6-blUXC(4}&Xi4yNuF78tO? z5HNyRF^s^H3H-%iB{72hXAH&+EA{8|VeD4a28Y31^TOa!8%OwDdNf=5_UESn9s55s z(s6%SJ_Fl8>~KeIgqwZd ziZPpfAH^8iEn=HnZ^>P`;*qp(1rKjjVYxp36DcrvukW4fqk6A4U#1JJ_-3E9pyHty z#2*EOcKeE)dr;D7po+hxuvvKKdWKy?P_kSfB{?WyVRyiAP*drgOJqch!DwP|Kx$}L}O&nPeiqIo&8mk_MmUDCvjEdcNpDcBr9r~#vgO| z3vTqUdSZ!AcTa_w@;2X30eQvxDA_u7b^2apr0PtzzVFj_5Lus|6GlJ0$>9Praw!d? zt9RAhN47?G!x}tN&-#jtSix8Wx6*hGA^xsuZ*Z5)++04RqZ!<#H^I6dV#tA_1@X~u zSZ~%LqX`(zP--?KXH{B7P>QBSD1yvBMU`DPjbIl2ABZ z5RDKV{EkyP>RKT@J{oUSheIa^hrLm@LxX^akX(}Yq#-Wa3a)h%e}XQ6!y0UPA@U5x z+Z_l=PQ(%P5VSD*Ef<0#3dW_twv;O&3;awbTbrulccMsL0>sdXXk>u{L}JQjiAx(1 z2-22-w0t}qemQ!};9Tb3a%%a1oH=(?RYA@0+|RzQm>_Zd!slqa)`mV&+}zw;nMUni zjlGj~Mkdmqx8XvB2udNst*d3tl`;WR0a8738Vjjk4#2u&+tluQrlF3KTH&SfIT)gW+XQk zMjnMqHX0DNz}F$P-(mEz+w&%d#=}=oM=PbUxF#s3r#H%qB9-qoj7|!?L+iG$1=+gL zy;~Hmg9o7wYKaV^b?{$#u`WL>@n9QwewgP@`&?~qM-cHerE}I2CSG2c=&-IYaUYHi zIB}I63Wa*{#`yv)joU?~PIq!kwV!eRmhG@3T)Q(cDW+~_qVHPqXlb$Ig59dc?zL12 z$>Y(*Njg+O~O4J~P9v*u7hYI#YGY%z_>8@Z}oXHJpd#JSC_@(YaH?kSZmz( znXyY7E>wN^xgvWO4`S)xup$yzgK|AoAwAq6V*5VQ*cCZr82zIxDv#a+)VxfR)NpO$ zV?GPs)Nl3>>xx-90hqa{4F!SYAID|~pH^<{Am;^ig#@I`JeYrN6`FHu!Es+-P5hH1 zMJ+`=A?h`L*`A|wB>|lmJOW1<+_Yxr(%=h$T^G``!zYgOduxlu4~~ zbBKl&hRh)>Y>uc;x;7Z*UvE?41H^&krzW@XK z&wS*V^LHXCCSofo`75Gz$v$A{vo7D6pE>G&28h>XJwv4R#dRXK7h?++kR6A^j>E$r zAyT(477W{dAHK^6;$Jvg*3g-G1WIO)I{GXn!}}T@mbN^| zI|Ju>bW9i)l@^t8lhZwsu>&`!d`c=Ncs-zZo}JBRNhQq?9j3LLD2hyQfXQ8wEp7k{)KSmL3KREjMZ0bc{4YndRkY> z=|+nJ;^tWJUaVjJE`B_|K%VVC@m34z!CKdg*tW*SW+H~-U?}eT+!S&1WAI*Y`*{Qz zMUdEwByuefH)}z5*6MqF8Zo>IhUNFp+~T|W**0RzGaYRwBnvA;Jd`WEZzBi-d_Sr# z?2q9ao(FuMS96a=8pSifvokdoO^D$tFpN-Z+l(N{1`y)Au?@SUD*V(Q1~28^7#&R{T3q>dL1c2)FL@9b?dF41ob6fp`SxxTHXUVUW$PM9!{e^+Za3;55?Iq(#5$3^ zoATIiZSkffUUuj}X6Repfku zAa3=FE=t>(GW*i6(XP%!^~tnrJ%SuA_nNqK@c6XAZ5g%FX3{kKa+yp(`!DIaJ#9Kc zob=knp8L{j;MV)J-a9$0>5h(|QHFNORO7MF0%A`)L+6IC7uUB+)3m#S{a#J#u2F0$ zd-3AMSW9gvrC>0#*z$)0SHgLD_UL~O{r?rjuF`62^f#tKu)EhKPXF#Z(q4FDSf;H$ zadE!SHR?5m<;i1TUb`m0D>Nv&QQ4_T^SjdELbKtR{ag+we3iKJhmw8Ktv!25v+ttp zQZu&F>>y{^Dc*fH8Z=VqAwkXKP?iMJRq^8z72*ZIcCJ~oCfq7yz~;#1H&rUkcCptD z$7|zsG=h7iy1*dR2Typm6WH0BkT{|%?zz*!UthURk?axnv+(dm&+7HQU5N~7%|+zzAD z)DLRkTwhX11wVW?n?7h9@%e`f4O>bAKVRzEdo?}wmoDn!Ligph`UNxBKW)zlIj+#> zovCN=sPGrBf^b$#uE#YKxsYeZ{b{=@#Xl|8?Xor>rc7x3_*LEn0A}odZLmTKM`7hk zCGG9Z`L9)FOE$5Io-X?xyj%`MJFd4?dZE;oK#oxHUa?|@JykrVLVP^cJwkc7BP6yz zFs@&-srtKjM+oUEz}s}6lt1n~Od+}ApX@;5odwsaIix zN+QiivD9BE9ww(<@iE}`m`%~fNb#jYMyG1zyCOC925|IkiYB6QW237=ldLH6%U`uuvT^noz)KOVbk$~>hmh6S1_{PWl8cUDwV_rtCu zO?yMr^>}-B@AkZTjH$cTdT=7SqOWx-@ovO^#;8SQr&a6XnohCjwuji$_D_Bix4$@n z>k+@@yf9!N%k*V!s7)l1g2yAAOC%j|Z?+k@{K}fvnQZr~jo&&{TSQJ$4Cyy_mJA)c zET`GZc&@SPLqya4fDCPLZMr-=U`->-Pl;v&)i@dNw;PR}4&c@bM8RHH^H@f9s z89y`_S~bYl*j&mMgahn55X)^Zq|vt4+Y$gDm$zNi)6!*jlXUg2%?%Rz9tfqkSNtTA^{wHWKGkOk#SZHr*ek@573;Nh_uO39yp(7> zjJ>|)!_tKdVCaZVmErehw0Hm{#r=*Jl29lR-4@3ER#BQNcCpb);*lYq@D}u<`%9fO z6S(7=RlRw*ILRf3Sy>V-b=s+y7qE8gLs14H;dA;O=tkki|773vVu=kjc7 zQ)hV5X?M7@SGO{O`T@z7?gu$xLW;#fDDMvK?Zo*>B$9g|kb9U|??irmIQj2=?bQ}` z*X-+EYdgFFXDv&eHiGkZ=mrV}Z@2+oyp34Qsa#?Irnm4Lh?5DCoqX_-`*2k?T%WlY zILM1ePr5`K=ZmpPrcab#Otf?9! z@SyXJt-_h z6D}`Qw&u02FLXAC0Bv_3?m>%10cdv1G$N7=L|`-fOw*9UPq+e|UiZV5Xu|&MuVtOV zlI~O5*7=6uKmwb^Ju*Yr%z`ynr%9u0f)9T`nug{-JgoVG{zjCT6$0F^<#^Bq{&B-B z^^ymd@RIANUzzBu2M-?WlH3@vSUXZ5@Tq=LPd#MsnKO=w`Uwe%#rpB223@cC{^|Y& zTaP`96;a7OkGc65c@GEc&)G2gi|Q-uC$uxW24=uw=5~U%3@M(<8EWx*9fZS60x2Zr z=p0x4NI_3z_iM$(8`~c9HPaQL>7+z{B3+6Z%@e5PXsd?=lL*&$LP@qiB1-E=pwff= z4d}hpOQ7}gYs)Snl9y4kOq}JUu$(9VY3Xn<7T=rMS?SF_Qd?Qj1Rp&Vh^nlU(7!GJZ0WM$U-yfPj{Y8L4lgM0BNh=X lsBh6L3{=u2;TBg7T#kDdxqa(e_-|ks)BTqFG7ViW{T&Vm*3bX| literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/fill.png b/assets/flipnote-editor/fill.png new file mode 100644 index 0000000000000000000000000000000000000000..6c21db1141c83709c19500e05d7ce1833411d25b GIT binary patch literal 9821 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/fill1.png b/assets/flipnote-editor/fill1.png new file mode 100644 index 0000000000000000000000000000000000000000..aaf3682e766ace0db3d5d8e8a4e09627fb7ade27 GIT binary patch literal 1265 zcmeAS@N?(olHy`uVBq!ia0y~yU{nBM4mJh`29f4<4Gat{J3L(+Ln`LHy}OZD%aXzM zVydfr0Mim5Q4_9E9!=c~oYDgCJ*I6~_v8PZ8`t){IK`P%ZY_K7KLg9Zwd@S_`ix9Q zOe`rZ949ygCI~7hC_6Z~H8k`vFiJuszAYACcm8^dg@RDerLk39B2;Df3ch^ zMUidi*7uiQn#5nd?dImQ^WJ|Vtnp3W^260nn*Osg|LX@5b!~7*;B*~fiOuu>Ha^|N zGdEuT^%7P2Z#kROXWQO?%8JLw5HoMy^=torqV@G>(@d@puirg?e4$bwGjs^sj~<>t z9dFX>6L#u2Kfiw5D)VH!?d>-=)6SPyGQvU}rT^T!7lOprhJ6n=?g! literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/fill2.png b/assets/flipnote-editor/fill2.png new file mode 100644 index 0000000000000000000000000000000000000000..058e3294c29f65d2257019f125515e4c2be487c1 GIT binary patch literal 1135 zcmeAS@N?(olHy`uVBq!ia0y~yU{nBM4mJh`29f4<4Gat{W}YsNAr*7p-reZS%qZaM z=37dS&43N9^L-a7NgpVEt$Rj=KPJHLFrbi;wzzf1@It2#Ki zH8k`vFiJ8q88NY>uyCB<5SSpSpr8zvIP>?SyiI-0zeCv!^#@-+KD_XGEh7%42s8Np zi@#ZM`ts8YpYz}6e|vj7e%Jq^7n$)64jy=H0&1Um`}NN+rBB%3H8I*3it|J`<5wq%^A!{q^}tAIu(-(h9GCIIso!nMdgJ{D{6``g?8x4XI7)+pi+nV#JV z2PDc5Hkcyo#}br<;%D~N{!ac4bU`+9$YOE8V?7B2pbHXsu|@?#!yVSm`S<_b*avih z6lRnm)B#<#gPW~qG$_$iH6nR)yUj+{s{$xbWXIN4}-LF(lOTm!-x4>M^;OXk;vd$@?2>?L- B?@<5% literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/fill4.png b/assets/flipnote-editor/fill4.png new file mode 100644 index 0000000000000000000000000000000000000000..835d663adeada377b96ca539686d288bea7e97ad GIT binary patch literal 1094 zcmeAS@N?(olHy`uVBq!ia0y~yU{nBM4mJh`29f4<4Gat{yq+$OAr*7p-reZS;wa$i z=E|x<%Mqle|>%U?mL%4L9jf-kNqqh zCpZKq2r4KjJ2<#CH1seqN-{DTF|njTBzD^VI{f+hb;&-)n5ON=e>>mb&4{W1i%!1% ztk$-3&lU1LVA><|=N>P98-NBB|Ioeh`}l9mbBr+zyAuvb!EMK4IM7))SecIj@ihTl zj)rJErYB(l#2ebsyo7FO*}(=wAT}is5*xYMdVqM1BGzz17#eGk&;!K0$f1akg}dcV z6Js(EXX6WeptfXj9tj|haK?-p6hnc*CNUaxh;%z#g3j`f*qf)%f6F{)_@`)&KfMEU b?-}{qFU-@b{^ZpI%LfKeS3j3^P6kL%|dV%`L8ySC~R&avgc6g;mAf^?CDq*Nc;#Tfg65KYhP-!vW{N>ff(w+b@5P|8B4S{cZZY|9kez zRNa$CQi$DzXCJmFr2qbQWMA#?=Qsb`eA!~IprAs)#)tU_&wYE_3AE?@=J$FM8`^NW z4Ql1PrkmUI>vQhM8YJ*y^#j6bcUU*)-~V@G-|mD1QkZ^4r~?MV4(_!3`~GgMEkD>` ziWVj)>VSb&D1HX$zvSQVni#W@!wy{?Fi;=s%>erA__w>P%+~~9VT?r`Fz}DX8vOqD zw*T#JZniaw_=Buxcfz-~x8-k_i}OS{`#Z8Ts=$;c8CA~ literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/fill6.png b/assets/flipnote-editor/fill6.png new file mode 100644 index 0000000000000000000000000000000000000000..83caaf912266472a8d1ade62185117172dce313a GIT binary patch literal 1139 zcmeAS@N?(olHy`uVBq!ia0y~yU{nBM4mJh`29f4<4Gat{R-P`7Ar*7p-reYXgh9a7 zk#AxEGvgEn%`L8ySC~Q_)UP;Hgg7K*Uc7wB=gse;FHfFqS$_TZ?a!r{?KKL)B zprGvF;MUO4!@wxX$YjLClET7qf+e8+PB?suIiEt+1{_=xB7u#=puP3pg4Qr4f41U+dn0$wo`I-R! zFgzA(@cY}_{&iKaX^Xsog$aYFtDnm{r-UW| Dv#L;U literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/fill7.png b/assets/flipnote-editor/fill7.png new file mode 100644 index 0000000000000000000000000000000000000000..61b16b275001a0c0c002b654847659bb388754d6 GIT binary patch literal 1297 zcmeAS@N?(olHy`uVBq!ia0y~yU{nBM4mJh`29f4<4Gat{H#}V&Ln`LHy|!^J(@8Pc zi-|4HJQ4y-J4&KjJ|;D4%<}1AoSgq)dx+Co-G6IR?@#L68an&-%Mbt96+YzKGW>Wi zq@bYe;NaHK(8Ite$;f2H#FE0oae_l&f*@ET&HC4a{O$3{>>uhM{Ho%+`A(BJzZCO{%?)UuJ6nkPDQsXCvW-T>L&;9=k32H_TYGI z{P~5|_V_)EZUQ_M&?RzSGv1#i|M|k-l9^#qpAyd-RZsi-Yu@jl7A5c5QNs^iC(y{7 zcl_GFpJ?8Hw{ETOf&Sh5<~e__!xIBoOn^iU7KxkJnD?35KU?_wrP10gPY%yZ{igH# ztL^vCg)i>$BSk6}oj@Zu@0`o`ejhSSKjVr$uZ%KZPzesA5zVE>MqsW2^?|HrjI zGuq!RKfUGBB>w7YH#fhz{{7DRnt# zxesf98uH)upPso?RsNgK=JcEE-|w8?PfSvodG>8dMZe9D+i$PmV*0b=^=nIFQYFqb zg)A{M*Y-j5=VZBE^Yt!!dE32-Ogq0R{(iB&JW)vnRPL;EXZY_+IJwXK&sZwEC$g^J S{xh(sWAJqKb6Mw<&;$U%FQK3S literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/fill8.png b/assets/flipnote-editor/fill8.png new file mode 100644 index 0000000000000000000000000000000000000000..9528a16dbe9cafd373403e3c2450c185660710e4 GIT binary patch literal 1024 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 RJr`-PVp(p^k);JM{R_hRk(2-c literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/flip_y.png b/assets/flipnote-editor/flip_y.png new file mode 100644 index 0000000000000000000000000000000000000000..967d90eebf4b30363d005e91baf1d05a631c7bd9 GIT binary patch literal 13640 zcmeHOe^gWV6~8Y5f?@%sE6#(4Z3^e~h=40U)Ry3{Stk*TfgvVqSJH?iXb2(6`?32%VADUk;{Yw3 z_s{p{{kr#iKi_+A?)ScT-`KEz&12K&Ob39+);_!H#{g3N83dsrNS=?&z%drqu3Elv ze@gX>2v1dn0xtoezaz=SqoTtQ zi6rK`I0=A5p+JoF(%-yLM9F|E(GkhsRx%#hTIM2GfjL0>DjZNYj^Q(LK=}aFN|fY*lGr#rv41UG5-uc8qbiYD77&q9NYBrQZHPWq5tNyQhyA~U!-yl~ z%z|WL=^x->x}+<*0-LdN1<=g%m_IOKI3$4@nwwLlcldLAW zn@=I&P@yXKIiqxFfK%2*WNvTbcS#`Jf1j-1#%glp)a7E{6a+DiW#vj`gvIDfyO06D zv2k_NAicmR(NxW&Yu$TF%V3U?RYgHj$6#=3$}?2<=oI3>Qr{O=u(K4jH}y6bu_Mb( zDuI4UnCbvzTp*3T(rcA=z6z*|jfwRqY{YBdj0qZp_p#8-a)mqt$86l61%;dFAlmM; zx@AN8O$q@j&7vft-p6@ZqUE^VAn2eA%Yxf2q?)Am=zI-=_BKfrG&{6rxkWRz*x2Rt zYNPw-!fG0WA4-=QIIL0tmu;gc9URG#pTn$9bx&S?LTNfGQrIb73<5c(|5Z(bmTT9NrEK3RLqtJi898jfr{uYfX1&)riIo_RIy#Ix>a zWUAZBbM>9Y>ia?$d6HvXu3aX*q9C=}d|!Cco>Y)!>_1$N2#wNu_P!WZd&{6w>G!d| zi}^;+2loa#QDn||0^3C2k`@#SHh8@8~GlmrvulejyXWsH6g;7KF<6Wqbf*ClQsV583W zKJId!%~I7!;JqyO3%Cz_yRs!S7&aWY*Hz&*I=QTTI@B!gR$)q*xy>v27G&l%>32$i zx-<~{Bv5}ZCfjj;BnSc_@% zu<@=)PExNr!+Fj88Xp0c8a>mhkTci$mqW2=y8~07{n>CvsMMapRONg;Y!+&}o3Va- zGYS#}_!7{M9MhMJZ!lqp4U*<4$`!!gY1Vhzs>u$daPs|49s&8+aUx%<6ptiCfYWzwBhk)M6 z-B1czv`VCu`In?UPEaGSoS+@&6(v~bCa#^P!BQfM5kZ?emR7T!%T07Pk9fgcr|iIS z^N8zwRjKG`Wkwuf`NzyhIXxvK-m|nPVsNp3#O1Cfll2vz?tlXwJnA`k;fiC#>x!?) zZ|A8;eCQE~v^3k8&$zpZ{1!6k#a&DJo;rVw$}9h<#OOJ(grbq8k;AbqY(|UbYIYkD zghcPz6$mN)-Novi;ju`#HkCivwMq^;bk67YLOGH>+;EV5V)kok)!Hg)C=$|X@<+eZ zv|oHora(0M;iwrLVpdvfdq9H3%={$~tA=(jRiwxwk$hvPxJZ-1o0iZ;oyBCOHlWMp zJfoFgDx1AXV~=ne14^GO@G>eZT+B1FFR4e+sZeV9-k98T1x<=8*#Q{8IBj#1)zTRi zO7|hpF9LXPNiE~*_EP^mZR3&@frWXqRcGK|4NuyV+AZ&?4XohHodX{IRpRwlo$n$u z{I9hzX;MrwRgLd+jPE4zVmS$%NStCIADimYQe0?pp~d84 zD&;?-M~{a`k1apG zw~G5x%il5MtA&UAqF{X9L*R$&6ZU!gBz>7Q1*g3*9zUAd-KnOuUZcOYg}YOG5Z{MQ zP;Q4Qp%HGm8;|edpk#bHT!gNTn7g&M61G=Pa$O1zj~UWcLHgXEd|N$G!LP>? z^s+JZ(D)_%SG&!>h$L5_sRs4ney^Elsr~c_UyX7svj~d{DK_p1+F{d<&^@v;h{6MG z)`>+jwo50Z7E-!AI)34h>BGIqE%Il1E7qi>r7urQPG7k)WyO-kt5a9}fOj9jYp{0p L`cKzPB5Ehq0 zgM$qt%OlneOdJXV{tS&3jB6N}IF1NAD8w;ne_)uS#4M;b_qHX_4hBzGKbLh*2~7b0 CR4>2) literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/invert.png b/assets/flipnote-editor/invert.png new file mode 100644 index 0000000000000000000000000000000000000000..5b1db543fe69fa4216e5c7ceb442158c1795b919 GIT binary patch literal 15443 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/last-frame.png b/assets/flipnote-editor/last-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..2298eeb3f417f6d29794776771e02c8d5e0ae922 GIT binary patch literal 1144 zcmeAS@N?(olHy`uVBq!ia0y~yV9WrrKX9-CN!{iD{23Tn>^)r^Ln`LHy|d9b)=|JA z(7xL)qmhNvg_q?5gJ7s-5wk!5GgD`a*b0{A0tuN1U+h$uXRdki?qA`nKNoMy znq@R-G_rLF;ip#Qe#`CcUfw?QG2^tIwsv!^?z8aage&^@)>z|#wUwUu8fi&R&0|jQZ%>BDB)^cuF{-^u?y!ed; zjEh*L6ubyfcOE_1d-1m5;ybf`Sie6nOb9^s{i< zz@?5`d3}8G>HiKKy?pr0M~2gPc6Q%g#to-lU;O@)zi~6Ha51fu;}UO85|ruapD9F1`7xX2nq@c4GoQo zii(YmJ%9duVqzi&gOQVy1B1c4cI`4SFi=%hRaRD3P*B*qb?c!+hoq#WJUu<5qoZYI zW&Qm8G&MEha5w}4QBqQpmzS53k;%!)p;D`bacFQ>5{j%_qlWD5)u+TJUoa*;+8F2Y;A4vczk?(d~$MfMn*yczC#zlhf_nx6hn8RaM1cF!t=(lbf4c zSXd|&3a6%~W@ct$Vq!!h(e(7RtE(#-jgE+jAd|_*j~~Bw?HYwb2?+@~di3bcn>X|F z^3>GS%F4>z+}tWFE167YQc@BYi>;}t;q&><&dwtvBYk~+{{H?I6%{2VC7U;IE+{Cd zt*vcnXt;Op9*stGcXzL^um9zjU+&($J2o~}U0qG5)8D;&H$Fc8{P}YU35l?L&JEVp`|R_*$vgO_kIk*|`GS9-20QR&=glDF9v=YVJiKZwHI|4o=9q=ZoOYipx~ zF>)V1j~so^`&64YXU~fJu$G>l@@A2l68n4GXtVLuhsHUcmUGef)7@YqJuf&Y|S*&j^9oBmCd8*}G< zU7j|U?CRjTp@^aflcD9yrBmh2L6hNzzCU4dki7RWdt1B0q%qrUZ;gS|o@&|vDmKS# zxl@e>`8xnWH1BDO47{q2{|vw)*$lTY^bEfapd~suiyaKzP;bnQw-~L>v*4bt#oa+u z!@VdS7h-;AD~aS-w-QG%)v>n$Q$oAmsa8sa24EV#3sY?>c1Nhqe?_9HNdtf6v$uR| z%lDW#2xQ~&Lz5q>`wM=kwEghrr(7?)m?46N;)cAX9>Y_+d4|R*lQngO#)ydm3#z4G>+I00?wKeEgL4(hlYvbh^Jh{x zGm#KMU2eaNYV5h%h3TB@Lppd2gb{J4-~69gZ}yBE2MJ?lf-%)u1xQ{nJdIR)s%scS zvsZjH)hjIJkbk;(XZ|#AZkEFDUf^*cd|G@rX_vR ztOYD;CJTZ9!Ro&S_9*%qnY73jN^v5^lKW&#wLXR#Zbm6*C3=w)Oj>4h`oEA*irqP2 z;6us)f+g5i#MzSp>)5%1{zXJ|L2LjG8eon;V#;@g=gnV&u6$%1`12)8cLcgCEc%vz zb0FtQ36quWdpda!k_t_Dq`ICd{U~T@xI1)Uq42sKgh7`j(J6;lwhk-|eg5?Zb06Y> zGLJ>}QLT}*f9eZc)0${k6(`r7jg7e~awkvk5Z$$Qp;ZKyT5b@pT3J*^p5i<#025%$ z;zsm}FDD5hW*{9$LB@d7j5!3rCVa4e8>TuOO=X~It3h9EI6PvhBhAG+l6EVosRV~! zS@s#}d1%V9M)O~jWBwY<@)_w%)B^^Sr9!`h`4K+(ndZ`IHR0kPJti$E$39|LCLNO< zjC`04Mkj04t*RIgFL(~9G3XF+RQ&dA9;RRoPTFmap-Dp=Xp5!_!5iMMKt=Fa`a0== zBs}dZrTdy@;AYC3r&j-V#ng=tEnNl^1%<>$U2cU&*(Y8_)+#aeI$^u$ zI46_5L8VgInsuB%dPR=Y@{KZH+>o>oA#5>fNJ2~;{7#%;3bIf=;+?Xn{-z+&jCJZS z!;kQ_bETs76mAl_Z7*`sgbU8w+c}4nxSJxR1yC^2wymoc>4X zHhtvx5*%~t(RAwfPYqioMGa_kyMuVx<`*wRZn@JsZ5LME3F}KxxA7NH#eSb}OBKWA zpPn_KD(qMpKC4bE#lxw&*og<(bzk2p`8}9?se5$I5}+QCBF}<2+74f{q<(=B)B(v;t?v?>_^(w>QZ?M4G5^CFc`J&BfbLHzQtZ%F=VyD-!ht$| zK*6n`{w6Ewf88pqPoau%dyo9J_0Y=iignJ?M1AuI`(qgITOgZ&oe;?Rup7GWP{751 E0qR#MlK=n! literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/layer1_inactive.png b/assets/flipnote-editor/layer1_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..3a59c3eb6f009c9feeb12b33236755eab261ad37 GIT binary patch literal 2457 zcmds3`&Uy}7LKB|rAvH2z=$(MWr;;a1Y$8Vc92{s#TEiq5f!L1P@;e+6b8yeAebA% zCA=R&Rh z($W|VMsjlUz`y_qf*cNKXlSUsyj-DBR9042S68>Rv=kQ?kB^T}PEN+g#>U0PQ79BD zm70>0l9-ry|Ni}$n3&eqR;5ze*Vi{XI!dS0A3S)_-`_8j$=cf5%F4<*Iy$&q?%3EE zf*?E|ufD#X&*#_G)jfXvxTdCt&1QFYc6N7n&(6+PRaMp2)(V9}sZ{#>`E#*Y+}qpx z?Af!1hK9kx!G(o|nVA`ZKp>aPo12@XqM}qPReO8;)2C0ny1IILdeYO=qobp@7}HVZivZx#9SlZKsO{@oZ?J+y*fV}9d;|lIEN3zPmO1%502Yl&(f_$X(` z&meVS$Sccox>2D#M9czqLeR0t#;8HdYDf5BnRJ|@B zmAg$T)jE;q^p%yNGy38dAJ~Mh)flSYYLMCkxEd-+dg502G}568vfWVIJJm1ySPs_}V9gGk>pA}%=$;pBLLr+=oIujXO4K6} zV*d^40<6uPcZm>vU*qGVX)a!#&fCk<_4;FZ1r|q$oL$VS zo;!Nn66IykM7 z^~-Ow)Wv#%wL033D$d|&;rIF|sT}a`p40qh?6JVw*Vy8bX&VmU&ECrj@>Ym;QuNSB$!iWxC8lykd5T{362y3#Edb@4)sUk(^5^YI5I4AsPC{_5Aa*r=4LQ6V*^K_T zp5Fvm9QJpQ2FB$cTaTQbob@qtJ1r(XX_c{#wKUj%bZ`{vYaoMX0!ylWsQ?jD#_lI8uhm)%p;!KvA3Zd z*r(JNvp^C~j1Uk&018=9WY=DoIH2dG(+$|b3?U~$ZZ;NjLnRBJYoN3O&0HzLX@Mqd zOWgq%l{}ve;6ke>P9jLSEerv%%|PwJLBta}D;#7$NIfv`kKdC(0|HO!34BW5(d#d7 z_g7Q^`Ck4O1j%~=V~mRbw8=Y969A(T-ArBFBzBYHCeedz3S7Z??}xe<*X?83f#iL= zmDXU{XSQJn^)zii65&Ym*xmaTmS0IM#BMr9>WkQ0W^xtvnZc`s{%$QI+C18}yqLJC z!28pKXSs|1C!A?}wG%d3g`CycSX*&dr<`8AYw8PFINUnA`hKD)wCNZWxhG*BdO6aN zKRivd;4{N%T3j|$Ylbhp*iep)Yhrv?La>M?Z|A&b$@50HAZv7Y^(msiVmv8T<60Jt z;(T|HhtV*_muDgw@04IG$pqZ+2+PgGNFARu+ejC_=Q19B3x}^N*3(EED^M#zvQzk3fb z@C#e^2GHzCurWunk4VZgZ+^V_>C##~3A3wfb191_wlBZdLvy>RD>HtLjA*&zdW;nQ zW9f#IZx$2_Ib8bR;-4vi#`?pDs`-_s$9eNy-dCpPLyp^7FtN7?o!9qQ5mO70VREW2 tdG)xnz5RdPw%WUQo{{>zF+#9@+pjgrKVG>!i9P@@_UD~!%fGoE^B=K~AyEJT literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/layer2.png b/assets/flipnote-editor/layer2.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb583e471d72435774de2702d800ac4f41ee339 GIT binary patch literal 3264 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/lightbox-placeholder.png b/assets/flipnote-editor/lightbox-placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..da7bba7c57f17b9e986c212795076de3e9294d4b GIT binary patch literal 2570 zcmeAS@N?(olHy`uVBq!ia0y~yV0^&9z|_FO1Qa>F%7&GJf%BrLi(^Q|t+#g#{a-~f z9QgR0`@WNK+p!527R{TxTLkwtE3@otJ9zf>tEh7E^b}*G9i;^dbxhx^KRC^@TeF-a z;=%XXPok;tX7xCELrEzPLA8rLc=JOu#j8;alHAhAC4Vn5TrK zJ}noMUCk`BY-)O2?*c|IuLX=OQmE#knG2KznvYO~M=@9t9>tAiTtHXjF#zmgJO-dR zji7BHccYky$7HZ4aVv(Hgkn94VjySKV~|9NCwgfSW58rz$W`aPH7fScoxJV&d3k2L zCV!ds(9X`T?%&McfB!vxoP5GAeA>()+wvzb22E@$54d-&tWk3WBGQchpKeA#w~1Bc1$=|A_q zI6vS1>7O~4@;aa2CVBELyqLjK_tQ*b( z$Q*xY{NULpV5UP!Ip8$eP+G9~lwwtF?cV+S`EMFM=d{z;*S~)Kx}x<=AGK(^0|)rG z-P+hLU$Hi9_wL=l*RH7930 zF#Xp2Y_gljf3tBD+qPA+F1`QGd|&i<;jdo5#o(Z2;yobsbJ7NpL*ap0Tcg&7X{Vh2 zlH{$fuKsYf!{0|*zQ4;i7VYaUZWMTNHy}fF>4Dlbv1qRNcN*!kPS+|n#whGd?P3P! z!3Ns`li6o4t)H)du4vmnnZ4?sM*l6ll$P^9h<=$8?Y`^{XYn7|{y!vNns=MW)&Rq&iVBrO#_}68-Urztb zn6o!-{q@)JdzW6!XnAj6|M*Nkcdm@a{|JW#n(NQ#98|jCozDO1xtP&@-Z>o{H$VH= zlDA*p7oy63qPlAFEh+$FO9d4pYt@Q*0RcfQp&$#5m!I?pH5+Bk*61J zw7+TbA%BIg)ag@~ZvNS}@#S=&qk9%G_ACIV)^9)y5RnbD4MKxc0-@mMfMzd5vOzHs zmW-e#z7+u_DU|p^F@U^D;XmM@(^ksM)8{HOI8!66>#@kIrZ^-vH$xMCwBd+cifo=9m?Wfzmpx dQ06-k&--~TSFX#F%fL1sgQu&X%Q~loCIB&P6t(~W literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/lightbox.png b/assets/flipnote-editor/lightbox.png new file mode 100644 index 0000000000000000000000000000000000000000..67f7606520a3ea4bf44470ad758c9ff3073db823 GIT binary patch literal 16443 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 A!TkXy5Qauo$t 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/loop_enabled.png b/assets/flipnote-editor/loop_enabled.png new file mode 100644 index 0000000000000000000000000000000000000000..e59c803bfe828354173f2ecc951907ee46fc6d8e GIT binary patch literal 7638 zcmeI1YfKbZ6vxl(>=PGxm=;Us zBnGLiks5S2P0B-&ZXY6{)}6+-G-8XU3Pv`KPO{2JHdB%`vHJ|W`=1s0L=kNSfw0$)_8(a0U}B=P06`?^6uXF`i-zH zVWkC2Rq@RSq?_gC4r5%+iX*aBKdO~6#~Qepiptfi$F#RP&lu0U?(DN}^B6Y%e7vHw z_hC9FGc6tG^(LXVIF>gEp91EOlUk~uulC-$WyZn5ZNIpXhb6Xs!S%bPz4CD_nC30Y=Q*&9F4xm!Y${Yk;u+$-5XWWmC zBorVcki$!802MaV%mCVCY)GmYr5UfLfR#R5NqQhDqKonl^9e(j5!;F4n>7Y91)PbJ z8VKqB)s~3dX0tL2?~mg_IPY}fDvl;%Q2%!9Dnlmv`?b>nt|HZq4AL=vguFg66av_@ zU|P2~48x+3WyN?sjuv8$PV$TbXJXnvvm*fNW^j^b0g2_2qqIOlIo0y$>O;j%~FT)xyji`N?jR4 z7T5!&nT;VDv!??cBQC{dl2C2tlup$Tc^{0Q5f(s|D1aiVWpIh2xkZGvUuwCxQX%%2 zOD#hi6!)e%@>xoo0Sc!TL{b?L;w%KGs4QRj{D%KIjmc&u&gSZi?8~(Wh z^f)RSr9gn!Xa$`dwu@MV&ive?Im|=L5Jg<8$zZ|93Lo^XSjM@$y4L$sF9j3u=>$ooe$%_h{>w;tPBm zetg^un-;RMr(BvrO&NI!>~hoA-=LHhBYU^dTt#!G^Tbl+LVy4`ejlkX5v|bm8fS0gZ|!6X(ukH`<(kyGzMah3oB36u3e}Ezw7SKYf~-g%3!g5g_8QSa zxm0(F9FDNYWTPnc!bdfxJp(Qr#IqEsG3Nwxx@fHRvBva;%bx%ltI9%Zd*7kyNgta2 zdh$Xz&GDYNl#sznz|@uv0j&;dp&u1TG|hHqWW+QGM8tW J8dJ>s{|0dJUkCsI literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/musicBarEmpty.png b/assets/flipnote-editor/musicBarEmpty.png new file mode 100644 index 0000000000000000000000000000000000000000..0dc3998cc139f62cb8d7a1972b27280e8389dbf1 GIT binary patch literal 3102 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 UUr>EvmBMrO^m$n)rWNh@4L!SODgXcg literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/new-frame.png b/assets/flipnote-editor/new-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..3502d40bdb3ad47289a63d60bbd4fcc28e2651b9 GIT binary patch literal 9660 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/new-frame.wav b/assets/flipnote-editor/new-frame.wav new file mode 100644 index 0000000000000000000000000000000000000000..630cb8941b8f6505ef55fa8a2b8860bcf0d07b3b GIT binary patch literal 196400 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 @M{Qa`mvSHMW>8ee1GpX=jaLI{^VREwN$XkzGi9 za%nmjKMO{CwPv*RBUi`HIDwsHLXXbX;bHMxD4vJCyWQ1F+LzmvbP|e5 z;-XPLT+qRG)>HPqFsnXZ;d5<*g+lde3tX71`Nu$lD?4;gNljr#GV0HB_QY(kwaV7{ z_j&T$O4&8TQ(pGcY;UvCX3xxion3UkbJ%i|3bR`VkKHogMQnO$Ec7FZV)Wn*obVPb zJR-G5H}C%0l(Q{oBaavC*0n`i&N7>{TU%23r03bYv+GWZ%pVl#CEM%lww2*ODckkm z=NhiGIdCTpS1yS>zNkk!PJ*A$B>0o+vinYIukYRs3^>!Oq$%M0iY7oFNjj~Pdpyo| z9Z%SBlgR5QxZxq#Y}-lQ(+b2BIB&S4Ih%WQ>qU}v{X{xU!+{PsgN06X|FP<@ThEsi zJNNpY211{~LECWBo_l54T^onueWy85PTOhAeAB@Hmo(dKd1>45k46VBVj#mOjSDRt zDZl6F2aAS>9lJQze3r?h5#&2eia%+@XxM1YXrkiP#xu6uG;D0$tB~;3{>KQL=^u;KrEvt@{Mi-xdo;rMw!Ap6xYm*PNv~0@J0tDQC$O|+}pp*@CU+H17X zZhQWEQaE1?J$SLzrztJwd^n>COd3&f;@=Ex{^;P-y)jR-3m154FMiS3(%k+sb-}0g zgclgfm!eHR&8;@U*tD!-ek(`INvn>Z+Es^kQ#)vJA5Y2)5BapbuLgd+a8J99XK7pY zNg8ME!?ixqZdzkB!`-(X%6OjESeg1GzN3LZJ#;8nzws{z2RxMGoj#;}r}a+zj)yek zU{?m+ECIl#F$YswTJG-kAs*A3!;d9N^&3Mp@1En$)xatyy|F?oiAO93;6k&HmaHGL zRDj#YQ@Cl1C4uLzY0;%(*k>(}W*&apl#d>KmZo{e6XVuE_mHL&{uKvRhWzi1@Fu@jg>0ONP$|o@)_utT%reT=fS$;4SrBTHoSH^Elk`;??Bi z11k_wdKx?0i(kzR?QH97{n)&?J@_am`VM}Hextve)3t4Ec8}*Y@%W%D{N=F<$SR@d z=2p+ZL<3&IFZkX!TR*VgFrMJSGQ!%YF1+hcePZmGOYmTsgO8%`X#2B?(xbmb=gE?& zxq?UfN{VJyHH{5@fsa2<9b^B;d9Q)1efVazBGsRbqvj^wz!M%)c&i5n3lga@BZ5$K zTK`wa_`w%*(Y1NjnA0Boj6bRM0&k7oB0@cGXd_C)7GKPwe!k*vThvX^`N9u^iXWt6+U>f1vG6HoA83Qu_WML$}zmH~@Z zOKpp~l@$!D0qxm`p{dCVoUaK6-7`(_r zN4se7L%;IsXHg@CM|{G|_U2ikunJ*Wk|hlM;s-j>2v&GR3(FL|_e`oBzGrO&4nFv{ zyzi(xPt=xjEEv$s*8r;?^;m&`TMGYthr*vV6H6K2S+lHR4FmtajdjM$+Qzrf>iKpX zAAF|`hkT?;v(oVljp)9fZ`4`be3%s8tSs~$O9TDoyMOTDn&nU5*ZWqTUjeX{L9cJ0 z`BP=-!XhWjoxZVW>0xa7{+tDnHns2jcJ%pnKR>VG4i7Z2?f~1l8|ss6JD%x z(4FNAe8>dyMM1IvcV~7uXLW)HEN0Ya#i2e6CRQTbH}d37ES11P%O_t0d>O>&e5isI z9F{FvrR4vWl@2)v>mz43GAU;_vNYC5r6i1S=-YmlELpyQLAC%s$()~S=jZU3(r&;% z-y-Vp?LoK4Zwa{>KLpw*>EvI5?l9U(CDns}zF+tfk#->8?{5WNES(Vh7PdF7}b$Sb_Yk2Q?$r~yRx(hXeLDllT_I~$txZPmn@Qu z5kEX9MaO^m#DjRNJo?F0H#|02@S)v+2e~Dlk%{u_q7@Qy7}89TQ<0XEq?0<6--QF| zIqgRp7LsXwfhPUn9vpIK{3XfNPh@!EtUvL?^Y{ZFw2(AQN&o9-Wug}@m6yWb*hoIS z@sk9ZR)FNT{G`=scE~C9qw(E%gQNb%bJ9Fge0)oquPj`RCH;Z-8+`Ic_i6j^G+l@O zM7wcgEb?o#X&k_!mB15HW>$K%M)fP+`O&nf9}oFcf*t=r#*F!4oy}XuG^C~uV`@KMTy_))34NgN@xVON2jH2%)=P3-V*)MNKY@WaU|3((lWz_iEyjdY zzV+4kDNmcmA3RWN{bM7l{HFaUaW!7ZY>gi|N#g0Bj9S=%6laV=95&Omsa0IUeK0+# zESk(HrExh7;j1)kv<6dYTvb+EY2LjtX;^4hY21{7D+qP<*FK$(_GkdY$5rj?(fDem#4W2&qs>a8{Z&W?A`~A!fGGX~n}wvk*?f(c{|3Gi^0%y_slG%*6V|OoK=Lhi0RsQDdgzjTyx5 z7BApx)#=S_C4;+_+#4MK)G_;e%k>tuvR1aYNBMRk^1bQNikJA0_h8~fBOHDD03LYG zbC&XEQL7UE!n>7K+vxCvpR~GGCgTDg+KP8uS$TPV>rG{L@}>qhZ|3l_N}FZ9rO_SV zc|+5ddp|Vh`AvD(+TuNp9dZ0uNB;&R-ti2F|K6NTP`m zC%bx42c$f<*`@MQW>X@xHb< zDISWCt{we`*E|e9oYNogfe$86zwFQRgDEc`y#4vP1{0pz+MjDQiFwXL-2F-6DTOx} zXgQc`b@v8$_4X!T`{JJG#qnnH-8+?P1O4c668z-NPV9AgXblhdaQLurgcsV1V)3tO z$DB|WY~S4Q@Q9!IjgMfXSO37V(`9_Zo9n5Er`Ib_yms;6?=E`5{V+c`+$=coj|QIj z;00Ixm}EhldBl4^sb@(7^jYH$-Eei<7L{Y;gEsXseAIbAdB%`7q9sp|`bz)m3w&rU zY3E?BNdTl^z(tBL`T~yb@fX~^kL9ZOe)a#xD|qR9<)!8+p5qN3;_Jcu%sZY0%i~Qj z9&1~<#>GD;)%MN6!k_DpeWXv}qJNB6Z5TiJV7$nCH`k3X`G=EF%4B?@`N2rR8t2BR z@n=jL=l@=14+oZe&y7=Ke}Be|@oX%8xl-#w{j5J9O!?*|NeX(5Wqj8+cnLn7!EpV{ zM;fkIJ>`wJ+LNNqSVWuh+A=Tvu7@|u8+X@J2CSR)`*P84?!TXM#+&i|c3>q*1Ly9d zAD(Ylox3A>yfgK_94RO1+TD4s&Ygh?4t|o>+!?8>@3D+G^TK>4&B6D^%r8?8Tv9`A z8q04NOz=s~;QHtJeLj*@-|*d;Yklka7X$x`DW|>9=iWMi*XCKgfRnicM{7Pfq;L2n zZBBuUXHq;O0o~2Y=&NN-Eb6KBQ<#CrM99 z-{3=E=I}|XFYyb_=)v1kc_fqi-8Y;17XL_umA(0h2MHqSDd`vKoIMChX5aK# ztC9+~j*_1Bt_4@#yM>E>AjyP}Q=fOXJ%KX>`!4&}z1bny7uW+j53vJmKi)aQnZW*k zv%7PaP^Nn}n=CuoVBQ;8tTOMdJu2%xr(^fv-JdefC2TmIJvJLoCs~*{DXGcclU*bD zP7yt4ed$z{{jBt0p0G1LT9DbIW*_=+p0US-3tLQ*hxUuvjym1@gD)KR=O-m0fSWUb zw%V6D=YW^3CojzKmA^mOh`6IIp2kUh4(EUHkLDT4g?90dUF^g86DcTrSRSkQ7F-_H zdG3ZkFX{(V?r3Tq&T~?a!?^~F$8es-A5F@Gx%?+nHZSItN6V9e#IANXC3r_yj%V|| zRS$=?aetok!hSe#wU=jixbxhOvnTWX(LC29JikAjr-ySVe}DcC7Y+{xE)V9Y1CKo~ zezBE(FmMm1?*76Z-s-~VaMDNf#}oXMDODsJ-r@I$9`x-^nY$lhpnHGip%<=rb+qd5 z&GSd&MI9Y`lOBy{d!G44GANpX_D|ODXwf0U0XRJMi%xJd<#%Y zeHT19!d;|-+7sR8c-@~4B>WQH;$*Hxp5Un;uHcAf0DloO&~rZc!dnCYJW=Lk?srrF zY<@*Jcr^K<6QGHQfBB+AfFVM`%1Hl9rB!+CZ(_agb6%%4aTCsPI!?`*fa(=&_sY<4HwW(WfGf zJejsn=Gp1Wf3a|SHaLk!@@%4rh;{-m5mJKvmnn~yi)jJfyD6tnQC9Sl-PBj-Y|1>F z$RW??UV1wB@dyuJPK_4>>wJFCr`@w@@#Q>!K2I*E{KfozJ3VPx$1m1^}fFK_XYcOt}o_yzUr51|8nlZf%Eymy`1`|bBzbu^@IOclXo`PXg$C6 zw}pde7ppvcznN#>O+J2K&hKLCeLFN<4n5xvuZ&gvHDmS9r`H0 z$y-_7DU(Gfi%r&wyt%UA%bPDtOxHZYvQq6HetElPCCIvv$6J<>c>`u`#A*|KZG#0) zo?g+AH7x9T!2RSQnAIs?z^qhRzko{4dbk5hgsQRWYvys7fziL%Y> zD^H?g;;IF{l5FsJE6yV=@2dGb&TFpN$YQ~YNX%vx6?v28?UNT}UO`d7c2cjRo>$b( z11yZ>X;lgqyHFlWZ`M5<)i@+_GjFuKy~48?!92k7ehOkfX!8&YGOxuT=W&wXX?biA z`R8S0qj&P`3P!Pk+23lLZIBVneo4=ek&IpVDoca99-?^EjN1D{jm`< z=HbPQE*n2KkoW?QHrV0mFZNjc>++Q>?#k2QDwB7^&3nhH+$=B zBhbzR7d*^#_H)-mTegYn)*g?)XxDdap}}kxpoJ|@{<^ha|ALq2V5>v4!H4am{>`=+ z-`L9evG=g@uxIDdnEkdDij4rg*kt>$QMY2So3=7a*}q%$tT^n|@e!SP%y!-Cz)Oo& z1y5TE*_O-8wmZM@RuDFI>a%li)#b-MFA!D|cz~M(-d7CnvwvsD&n~_dUi~e`0EsR7 z*zB~*Ix(`bV6Vf@gWU?Lknc~5KOrTthG%w5>^{HY_=l!M^oW63?Foo#>?+}3hzuj@XZRoyJrKV z4t~&l@V`KJ2?oBQM>+JMl~kRE3y*2xoX%;h@QRL*?TUQg>iEW{H?*>}WhDD~z0o)E z7O(InI`A-^HR(E@!i_{6p7;xP_D`hn^_l*G1Iawi5j@!3G@kHLyJB(RM|LsB15E;r zA6sGXnFKRm<}qa}4p$3OWrQzQ>0D736eBS;%aERq=b zLY7p-7pdeaBmwM7q--YCbDDSWv~2g86vh{#_#w6#Nd+x!no8}0>-$%y`Xmm%h*d`$ zcCN}erNfJ)g^Y*PfMlSrbxA9n`klxRSG(<R}(gLQ;n%hl%9)Ryww{%+BNFL(#TJnf4gQ1k`QU~Fd$547ed z-m|NDf@;wS@R;xX_#{q-UP4ni+J);S9JO05as!Cs6TnxG=ox!_ZM<3Vm4vb05R-sk z`XUV!D*>LbS6TlTzW`^y9}3~~YW9U97F?}TVimwtIVTBRu&1{dSO153QuF}5rQfy1 zBRy!i249prU4Vg8KTN(B8VT*mi-*9E{;SEU{cXfttu}s||Nk+0xCbwLaFF{$DSEWh zxH8s6QZNRcNA!n$*I;N@Tb_Ks@Or&q-~xO_P}oL-aoP83&92u|0(L(vOq@GFbtW+; zeIuhs?5R7Ogp0Po)o=P!@A+#qC<9lcHrn7XqC!;i=x;1EZ*kQ*#=Ox^ksFGY0#ftP zdCd5CMsj9Qo|M3PVZUvRX~UnfX>=O*QZ=0fjl!sImKovJ31={6%!i_S7cot^CTon<zs+k_@ho|XtblJGabIh3*RmziL<|UhH<8_7tg+* zhq!G0St_M1ly?50W$X;&I|F-p=N0Fr&ScJC&I0!LX$bA_U9((ZHJA02wtC( literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/next-frame.png b/assets/flipnote-editor/next-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..549052e66eb9a28792bb1ac1910204681831e2d5 GIT binary patch literal 1122 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/openppm.png b/assets/flipnote-editor/openppm.png new file mode 100644 index 0000000000000000000000000000000000000000..cb29be0bba5899af7c4af6d39da19648e996ce78 GIT binary patch literal 18601 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 Y!y(SJmA7NT>^~H?IeJso#+@Jh8+ysv#sB~S literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/pageInsert.png b/assets/flipnote-editor/pageInsert.png new file mode 100644 index 0000000000000000000000000000000000000000..05db6b2c284f24ef0ce5ea1eeabf8994c8e332a6 GIT binary patch literal 13215 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 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/pagePaste.png b/assets/flipnote-editor/pagePaste.png new file mode 100644 index 0000000000000000000000000000000000000000..959dcb3106408d33ae448a96e69de9ff8bd579d1 GIT binary patch literal 13672 zcmeHOeN+=?x*t9isnw|Ly|%p-Tx;R%-n(i=MS&#P+G|;XY%B4jtj3nM+*AYJLjnf! zL9V@rdUvp?)lh_nZy^{lI>&HccNXp5s4>${l31V1)MnHQO=;lMv!8vjWdT3L96x6viatQ>@UI>P` z>h+8msA3~?>x0v7{q{m*RZos|MXT|fR-<6vL; zlDeM$n4WMW!P!s+JuV6kw;(Btz~ImSPX@gjOs!KCNnK*5AdDSf83+EDT;7Xhfr*Y) zeTy3^V+$cD=jcC=gZWl8#g4xQEHt=Wr(XbGd~A7d9P@KkblH>O{rlg{5HUl{S>%Xi z#*rDxXXd#+vp$I#PSTe=OxYTCRTTClLtQD&qUhAk+3k?+SOIjT=y`aK{Eed}5#|jj zw94KlSm=0ktt(PqtdH2!AyqHy-SFzbEXPoKRHw=k*i>KqY1`c1ZsqI>e}m^x2L8Tu zaeXcEAVg%3#OZpTwXDcU!5@Z(YWtER>wf=jKWHe6Fv69XcW%--N#|CByQ&bp%&>S5 zsXQw~_P-T{e@cZkff z@;3~-E3lq}{!VwXq@pp`o@4{g6tC=*;UB05edktAGuWV!*drJtKU+Nb6RR&X1^CB0 zcR!|Fl`x?mojdkhC;4tI@St%h-F>OW`K7w38+KP!pviqXJi~V6nDL!!WkJi(#rEtpnhkp3!@JN(Hey&4Tw^0$;A-)v z$@rFiFzWT3-r_m02#0Ous9!GB9v!7hz*S1t8TC>lT0jP_yjOO0DvqD>sm!o2+ox%V zh23pbP9C}j!%wTJ>(B9vrULt&a)Awn8_E%IePuw?*j#YbMO^97lpNQaIA{P?>gB_u zxX5zB4z9^E#L}1C?e*`I2-|bfys6N4=tVxFarkNxxYii$(vg~1NR_b#-etg>r`7^+ z8R@sk-6n9&!rX11tkY@%){m~R5`9yP!;D*~NvYX~fop@SbfB!^iU4+v6jVEvBOEe; zSi0NH#33k%Nrd!&SXfKT$mZ1vf@k@{kg2`IeoWIitk?R8L$-A^=FNLqp!!{zCgd&` ze$7bbSoriP9@j1lnp~z&R1=##lOd$S(2Fv%uAw|=Lz7Cd$s(-iE&tddr++$9r)B#l z+hNglqu*|{|CG2EXC*A0P4@|TCRZs~3Z+8$vW!4vhNEx^!y< zt=Nzj(=>HeigHmA=i5_M`e(qOX53DxUNm*nMrvCP4}M<{Pfs0pV&Y8lEWwmlgJcQ4 z8^ptIdW}_C2#XZRhekL?zt*Jzkj`XZTZ!I8ci52aU`6x`vlV)P8Mc+JE?ifT-7X1b zQ&S!8LW+EDD+qRvXloq~5X!e@AE_MNX&J5N(lI8(m82V^$`xp1Ic3Sk/gY7wT1 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 fyRZbC`W&yg*sG~mR?bcUWd;UMS3j3^P6|Fv ztj>CBFn?L=^Vg4m9-QC!|L(ctt>NMreuikAeTerBQ!dx_;)RfqKsRoqW=e7n*G)mR z02^q{Se-MJ1*aUYnYHgP{|7TYP#6}I#F0zO*&Wyhr(7A$$SVuBow$pZJvsQzd*iBXJ20B+6Ymie_@T}i`&6ced k&hT`J>UOzkc*D*x}m=hR0UHx3vIVCg!0NVvG-~a#s literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/paint3.png b/assets/flipnote-editor/paint3.png new file mode 100644 index 0000000000000000000000000000000000000000..2d3b53bf791436c2971792b64efa392120734721 GIT binary patch literal 781 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`>?CTEwdz4o)B+K!9|8&!>`)~IC#^-}PBFOdM&u_dR%mdKI;Vst0IXdQk^lez literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/paint4.png b/assets/flipnote-editor/paint4.png new file mode 100644 index 0000000000000000000000000000000000000000..6ebb61fca4a884ff5d383ccef1d35869718cab12 GIT binary patch literal 800 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`?Y{ef#zK{NIo9`i=HKZR_^`c;y5U@4}{dU4Jx4YB| z7h>@X(0P*EMf{@AEyE-IYx#z=t*BuD^H%DeAd6hf*~f58znFi>OdK9|Fn!$z^nfWjO_A0#4<>9$^;wmB1*G*&%BT5z9U``_jF8S)=vPj8#O<^`0> V=$STd{{l?;44$rjF6*2UngCJ7A}jy^ literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/paint4_Adv.png b/assets/flipnote-editor/paint4_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..3d94338d18499469dbcfa3d0cb4c61b4c5367643 GIT binary patch literal 799 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`_|2%)`kUesLnG8HHEa8|X;y76; zBZ$SfYk{YriDr{p>i+*fK0p5+n9ub8)7O7x&sTE8rChP8Cq>Za@^$y?J8NS4er3u% z_*Q$n?|~DltB)RGq$xntIsG1L`zu)kzHIU`7NHlFl41&%%X}fI|`mE8VD>!a3VXLIF8&VQP|s zJ-Tp(tFNN>4Ut0(Xwd@EIol~H2Ag?6-{){e9$F^Ah8dwioi{`#J;D*vNx>(C%vuc` zumv?Rz(m9`5(vx>un-4_Ero(lpH|t){^c_`hCS-iw0|IPpowOan(Kun z9J53mCo5$HvG|_K`}Olv#lF}63Da)BH2C<+2_oEuP3@o++Up{acH;2xK{02_!)b>~-&K|!S-Jx`43X6E zY!|6}JTIMl&iPiUBT+r-PAH~+`;=Y5%#&z@a4oQp^YF!!M(j)4^fd87W9e1aj4 dkeWS7T*jhA>mSGL9ANHX@O1TaS?83{1OQKBGt&S7 literal 0 HcmV?d00001 diff --git a/assets/flipnote-editor/paint5_Adv.png b/assets/flipnote-editor/paint5_Adv.png new file mode 100644 index 0000000000000000000000000000000000000000..4efb694c7ec8400cfec63252855399b4e7184f87 GIT binary patch literal 860 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic2}o`}xs@$FjR8QE_yzkYl9U!jTbqI{ZlVPpOl3uIhFPim791h> zaoL48DccViN+X9lOe-)d^103~d)I--UvuAxS)}0!mMgLs@%U@