From 998ad27ee383fe22cefd5b30455003e1a6b7593a Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Fri, 20 Jan 2023 16:30:01 -0500 Subject: [PATCH 01/32] File Import Initial Version --- img/attachment-icon.png | Bin 0 -> 2665 bytes qortal-ui-core/font/WorkSans.ttf | Bin 0 -> 359628 bytes qortal-ui-core/font/material-icons.css | 16 +- qortal-ui-core/language/us.json | 5 +- qortal-ui-plugins/package.json | 5 +- .../plugins/core/components/ChatPage.js | 390 ++++++++--- .../core/components/ChatScroller-css.js | 78 ++- .../plugins/core/components/ChatScroller.js | 79 ++- .../plugins/core/components/ChatTextEditor.js | 657 +++++++++--------- ...WorkerImage.js => computePowWorkerFile.js} | 0 .../plugins/utils/roundToNearestDecimal.js | 4 + 11 files changed, 776 insertions(+), 458 deletions(-) create mode 100644 img/attachment-icon.png create mode 100644 qortal-ui-core/font/WorkSans.ttf rename qortal-ui-plugins/plugins/core/components/{computePowWorkerImage.js => computePowWorkerFile.js} (100%) create mode 100644 qortal-ui-plugins/plugins/utils/roundToNearestDecimal.js diff --git a/img/attachment-icon.png b/img/attachment-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cb43410dec8fdd5a2c794c4f3ada13cdbb50c132 GIT binary patch literal 2665 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 X`3#_Ou{e%>00000NkvXXu0mjfUa9%K literal 0 HcmV?d00001 diff --git a/qortal-ui-core/font/WorkSans.ttf b/qortal-ui-core/font/WorkSans.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c8d05412fe4196c34849fbfaf489c4660c96054e GIT binary patch literal 359628 zcmcG%31D1R^*{dZdr78Anyr(`WZ!1bGFfIOlT7w)vNzMF`;zWZN=r+jfPid@pt2}d z5mdyl3-VR)tAeOy9DT=IZu`R7E75E}5U)-9P|L5HM-kUecq%HdYQj*D>ckj99 zoO|v$=bpPfiAj=_k6$TL&(OeN^m7kis7UO+FG*7N$)VWf)T>`C-zBkIelE$i-x``) z+4qHy48JBR-@ZqKL^&$Q!9E`{O#1U zha~0CJMsP7TQ1wOziv$rla#;Qi|_tzd$#ObwBs|C5^MEJQhNS*c>d9{_xu;0SK|3K z=Wn@WA3k$I&+qWL==|O9Ij{ca-d7}vjZ3WH-t$p@**_l+p!|~(yZIkGcWl{Sc9*YR zV%6_Q`IemkSe{IK5cGTj-<>=6TzbW^dkebp{82D{YWLo4Te>R$9+6n#VoA!lY|oY} z_N7ip|B#sd4V16EXv>}*e;LUtmRRqAB&Gd&-`-0ueR}Vs&q-`JUy?#U+qZwmzMW~e z+=%BLC~uKusTs8WNIobj_$Gw|c@8D7D({B3-ioo0#Ms+2kIDyUc1tK%j9*uxoJGoz zq$-D}%Hm)FC7{Sxu8gYBMprPl>Ha^x=aEO`gX%?WP@0_;HIxs?Zi#x#k}9!LDGooN z!&8m}elL2Hw~&%9Wl4EbkyHx0^3xpE?ognhAkf?r_Jji#7H^jwTi(~VyqDE@eLnX7 z;M53nmaLL~7FdXm zfBw}FkIro?xOGUil7Akzo|cwS(Cw&Bv*52lb3uVEKP_#cc9)kH7w21Z3ep>LLatJu zJ>ScgB&R~D%q}U&v1R90mR8t3h4z|?)7Q+Sst{DUq+<)^oU<3svJaBxlBD{ zHTBAIQ?IN~ZEGlX6&K~^+futzTboK;C51Wp1*wlEC+%}Roz_BYdAauS&)0OilIW7m z`%IIf@{*JWtz076K(E7NafAcV6^EIuKa8weHL`!*2gfEN-!iu@zj(c#^casUtG+$0Tjue!5g|lx&&Meg;lTC8y*uII$;Hge2`8n5P35 z&ucY0zxN+Dh|p=M0uAMoTk;t+oW0vM`Ch+3kN@{PN1Qp9Q8N1#>YvoN9?Ksad-hrR z;MlWc>C8t`<9N80hX(U@U&p5+k<%FA_zR^9Nebk_hQWkcY}}H;hBkBi<_HCvLqUgQ z_v;6CM?&p`*BrR}fuX^{p$9&F%PpUl4{lx2yeyMtu54McV=NL3wT%OhXUwE|9OYExn*?`h#O*AKcKheaJRhFxA(8UR%rdfxfK; z_qQ&qnfl}f7kqNcG1%;CYwx~n#nk29{r%vIx4^k7)3~~nYtCG_!gRQgD1wxE`IOhq za08D=z`2z4df>!(Zh?(4!*?6_4g-EKyf8`iR!DUbe5Ay-Pit0v1l*88cifBjR> z9=-V5YcEEzuYC5iUxELAV74SE8-UJ-}{+RMJ(9F0@M^P`6 zv{V}a7zDf7Z`A8qjrttBjqCfdF^%t0&NnP=-y6aNKoQ6QApm7Qvywl zw>Ub#Mfkd8Et0cSpP$zve4XB;undza!qXv&);xA{KbNeH`HoIqJEz5DeoiMqlj3NK z@N+c=1 zGs;sGBg)Sv;3wZlaQMC!eLTnYk$j)IRq}lT%Skgl#iPMJDIBhihtKJ)&wmaNyh8dM z*|FIoc?@GNKf!ZyyG;=#Jg3X7&;NkyoE-I!wL43N=TtYuCH-nqeOYU?qNu8)w5T-I z5=c*f)#z&`1tHHRVsuwkg^Yjl61AG$hQVEfLH?VuV{mA3Igqy${GK3wH5(BDmUL(f zHxYS`yec`O-arN-#(pi2JUga7j{=%^x)I*VZNvoRni4%zmyDT7s4CCl0jumXIH<|n zHf+7hJyNh~u>bt_z>X2$P^kPVR??l4SG+Gnc&X6|FPzlnQ*ctYnx0zwnEK7Dl&%4y zoBYy^-2Mz{cH{up8UZIQ;BeUK1Kj@1h06xqikOD{n85QI`?#q*X`g@}MtPD8yq%m1 zUSap)jrbhJVsqqyy&!kQ7EhPVN}?CEw`~Z=CRRJWQUBV zAN9pCvf1UJ@i9I^Cb`C^zrlNYwn7Fjv_Vj^yn9a{(=>NKx9}VeJGfV~*xrQlrwA@I zL&78(G)Y#_@@P!7O_0|=QzSpx~#r4mpj{4XZbLDECSU^J-?wpc)IS8+u} zaZzRE4>2JFg+*u ztXF!HOIpBB5uJb|dXpZF)1NXceTc&mVMuQ(WTANZ)YRF_lFH|0-o7l650R8G*9wW* z93eaDgmma&ObPe*f1Z;sOS!W}(l5b?4Y>u$u7!+bXyU;-_1|?Bf%3|Tr?#V^taNc{ zdTy4-6??U?uEG_nv)QZLOAE`=QUjI@4mT*K}m*`T)E&t{9ii*#EGS%`-rJIP2lv?7c+m+v;X^ z2{7-TwaUN%>7GZJQ(mKyFZ5V$;h3c`EL>jTQFf);5*ve+=C$5|H78x(NwuWy@+#gZ z7hr6)a5_1h>jFSLrXp+#x0slbD;y{LgfE*_F0X}-G~^?_)oH3GwGnR3iwwTRN7KQc0uKb z)lYnf6Uk0Y{lBSSk)zTi^cw!ee2FYi*rDJb=~}>Q4}`7qYvbwc@W5e~K7Q3lo8`bq z$Plp4J~;DDdE!BJ3p!)(7r(&npx)gLpAAglcR%lgQ|v27ACP^^;Vm$az|QOePoBJg zoBBqSrHpT5)*+U{aew#f?W!Mmhp*kio)UQ9N%yb+2OxD#hHr^P0-kLq0wWKEKXOG;lOE4Rg7tOSF4VUwFeadHfe{*`! z-x|N@Z!#Q+b)n zBTppCi;P#XWQtxf2e4=^cp=hdTYO${rJ#R47h;a^$eDTZ%5mCg&Oo%0l{yQJm^ENJ z$isxQ(MU6i-ZRs9yZYS)snjK<%`@sGPbz&7Xbz@Jv+BpNTwhcACY^#4mLj zG>4NWUe8sQ3(Z&do{91~a~Jj61?f(r-H2(q#Sq#`vl*e?hX5ywA>c1+aKsV>hc`OO zE@LCc^S6!XhfU8n^5@V%9;XVtTmy}I6z>Z7EQ9Sc;1p939J3w-D~d)1`7=AuxWY|) z;GfT`3LjdxDu1Zy=L<+rk(RCNZVhfdH+kJY#~RScl0YYii;-o}PkagZY=V4=FM@Nv zjM8N1MClU=QrD&9fekvMwT_s}s28_IFDKNacw9&oy%g_7KFcFG%>sEZa@$6;K;Fyq zpJNtynIxW9B=q7N?D*_Eh;0UDUjY0oJfh1)3|otpDG#%=u-(G243v5n6Y{d3P5M>m zCV!ND+&?^|o>-uxx?Su==gjv$=-Mt1I?vBKbAN#%ZjC=Rr@!DixJMC^Nsx5PxF#`A z3Wsy6e?a=FA(`^I&oK@Tu=Dii%FMjymR;5CyQf2;>F%K&!QhUuv2DRNA&+oy z3+BeB%-&3k?*j1bJj-WzOb3D?Jr`+XEBm*#wr=acD|U40@bFS(@|xClb**bsRfH_x z>Fw<5IgPM{$0nV?l#5(|kx(fe&^LuxXFa4f3CYSHpw)MiX8~S?2 zF5@vcAIF{Cf7`HX6*6YsT5!+vz=-qDYAg!^>)IF_V$9iI?dq}?7p{DK0mb>TdqXVU zmLX^4Eoy7E=I4#t?8=q})klxp_khbxa9L>ZX-N>6-Re--c{{GWYKP@NVt=P3`2Eix z+4KYX;Lf#cx239YLTtZi$A53+s^4T)G+rsL+rzc;i<(yU8F88!t}qRbxDHf6t9W_D zb;#N#!INf`_a>A-l~A7SguwGM%HKc>G8pw6w6E)z{SYyEgWP1}p3OZr?Q4y?G)YjxVL%sK?=2y}kUok9gOfTaf96SJ&&A=#UM3 z)`aq}C6uSwo6}8nahca*E@8%h$|w&wpN(^VM0wD~pQBGU&Hhuq1{w*=ACJwoC7Cc^ zLc!2O9|Wpw761bwDo&x+RCEsJ05(*-Rp@(c676XM^e45tC~U^Qmbr( z1*KK3)h%rU&%1hR)F}I2@79*_=V~Xzooi`9SqeiNZiF_7mid|)Y!xCLZPA8D*?i%< z3(Lz|!z-t57?GJ=tS+fzEWEL`e^Vf|buhZczM>$uy9d^6?1LGjw`|QW&52cJEJ^R% z*xY{E8YIf1{pD@T>crSY+m#qG9pKsmLXfAzzmVMVO6(OR3}+sZM|ciUD+5V?moy-C z5o?(JD|(E>d5;OW!YD^5OO=@A5Kb#)TdFY05h)gZx)XHkEwQwc_U3_5MMIu7yP}}f zSkC&)6;HLmjLJy(| zGd=sY@`!~7bSj_V5tX3EqREQ5dcxq!lgi=TL+jYQcutWR)ia)B{KTn9Qa^#3Cls9; zy?!E9E9PN8VepO~Gf^k8pU%F@sk0@O{mTjUD6SB)JedHec$;&BNDX}jYXDg@FYhyF zf~?id*{(GG`my0^e?G0FtUluld(O+@+xnG+;V zcK>7jKUaOZrsm69+P3w5H?UujrNFwMLaT{6SeZjvEB%jJ9*0GF^~Y16|J3?VeSY#s z+YVH-YGkx#9+t-_*Mg+WS3mx7bp@#8_4$ZBtZD8Eu0eC*GBd+zw!+IpIw!b|ol_ot zjPjrlnMAn_^dT2rSI(Z1GSn_ z3!hAZ2~q-fdpImNzY%+5({-WHbr^*gsVbA%9Ve%!Pm;#-8eEr-YkZW?zL5kjGc$Yu zeDLz%qg=YhC{O$e_+cFmdjg6K_$k1Xv8Yzy(=CcJGI7iO ze3n2ngxY?8rkoLNsc)^aE>ae|I{lFj^-Z>sIM;M%l5>Jd7q`b6hakrcH**LI!67dn z=WtGW^Gv~9-aJ#7t)Fl_R-#-l4~fNBPVb;msNv@;r$)ONZ5kXidp_DOfCTb2 zE5v0G4rr@JlqzRXde$~PU1OP8)ZaVS6T4jfO)R5fT~ANfhBfT7w2;Jn?11`Vv_P1) zKpSZ;^A72u&__9q!Czs7Nwaq-d(n~&)()|_%fh!(X<;jNbBKaYF+6$P_i1`qsP*JXhh(f{~w{yAx&R z2PSW4lGXfODYDJ1`iAs04{PK7Qarz3D7%MX16v);y&_K-druO3WlVoQs4f`o3XLF&QLS!VfLCX-}*JQ1dKBP zWKm%KFX6phDSDY!@-b#8GPy*46!N8(uqTAkitj@0U4b>=;(2Y#k77M|V4;?2)f&8@ zKY7cruJ7S3BQlUb3LfJvV-=Sz)H2p}Su~z zmq)+d%Eza`Ll#QF#Y$`{m-aB%*=oc9HBvitN$bscd_ ze8%cl2V!jb5BiVHY%z62LZEn^SXxn9Bs~6g<(jJ4(3%VUT^Ensi;N6=@rw~U5*w^6>7Q^mP!(0V+|xtj~u^Pzp}a> zjLm+U@Tgx)fWJm?vdB>3FLK+*Gvo8UQXo&ktWem#AAOxn)qSxqv)lA;7;A`N9zoU= zs{tcpJcGw^a+-sjW`*5}&lL2OkGDnE-zU`@**aP}iKpw7IROu87m>jBj?_BJf=)+^ zt$eZ5R#@fyGglrHQQ4(kRV7X3xz=!2US)Bahm8r7KbJ_Z9af?Ra^f^%WZVZDxsOiy zG?z!^d|$xkWI=>frYA@$tb9&NM3<15X>ke7Ns*9D;Bjzv;uCzgScOy#MKtXct1|tK zXiz`%#ZDEKmlqY5m#YupS0TQzhs;MAWFLP@52zGAXN;o^)FxjSgr?IDHIEcVo2Gqf z+VqaM>^aLy9}8$r+@sM^v%rdt+{nq)s3EE@I^%kc$e0SLAT~D3Bw&(BcwRP@6>F4S zu2J^+cy@K3R4$yCZe>y@qV`gzlCNQj=f7ZSOH%SH%lyUpizCC1y7Bt4{-1uXsj;cPEL2m-`dd5eo4u`ruCew% zJ5#$e3hJvf6i<`AytBS_B+JuRmEo+c@Z?l9R6<`lUGkgI*A5y}DKD^mZU+SXSp$BY z;275&u3W%R^I;d~{Ih)4e}%g*n~s;!p})=N%&q*a=|G^m;hb3~OWui$QK_u+Nc&xg zN17|13$Hlq@^AA>2MhQ8OT5k;Id@j|Ui&0JmAH0ZuX&Q4N&L5Yn}2w5@?YR|{=r6d z%e&|N3V!l&{anuBhm)LpRQ?qWzTc$53muSD{ssQ$oSvL{;@p`xM)#}6C_b8?*#kJ` zl_B*&D)G~m{}S`xjPF0oI(Hs>_!&+0ws*-9jai+e_?SHlXLN@1U+GqjGupLQ_ij16 zeP;IDS<}bp7W7G}wA|>E(v=?00zW@IQ zqtJmL!Z zPg?o3l;6J-$F6%eI`?C4^-cr(NrEFf?VK&;(TPnOi_cOLPQ3n08uxRNRcUT7(hvA- zJZI6rq#ge$@OiA-`5)Qyt|hP38N9w|mVxq|2B(v?Bm+x|%A8Wrkn|Hqy%YR7AD_wq zBDhv*P?|8#8id38aelt5!R|3OH(08g#9>tt(|N+#iian;MOf#m;s@?u@lW_idcWHn zXc)=w&23xO)VFm>p|yRywtrJlmeo5_*Yq{_+qwa%AG^4?D8#YRM}VfhI1KMtW6o-j5y1@aIvAfD%YM`&jyx2$~H%9hs* z2eUFQ(O5?Qrp~UNUEMpQqf^UA8-~1|p^WmD>g+4!BkKJ*4V_(=E?;(_E4p*ls-2Ci zB5f;?Kx9$`?MaYvCB6WE;sQYnKZLD?$1SWbI2l}qV%V=KvR495; zSJy>Mt?ckQ8w2$#JK9&(7y7G;{e6zE1Iy4PjSt=q=XZ_9FV?`4z>h&E1l&h-K_^7{ z*J+N=%L8uUq4S<%?*D>O5BzX%CF&hbfd9(CcbMQ9haKR*z$|Yg%>$u3ph7alt#72k zIvuNoi2HVTb*<0Ih(#@#me7QwameEt^pCGto&kQRF750IS=gEx_j}UnIxC}%YdhMO z*OAbr6sZfXdC^)ipEbwxrXsQAS3CipQcN?oNu8`*twTrxd~2TW57FqJH;Gjkk1m&OCPJ#-ULin3F&Qpc$LEW}af`7Y^T; z06)o^_}K?&Ov-XltChzoXpy-SaGZ|jcsv3QD|bxU3%p{arD3GG412GSXbV>BE%6bJ zEETPhF#piruCCpK(QRawcC(9plRZsC9`8sXJV@qglC%w@OPHLYMZ-(Q&=aOdA6sdB ztemfnIoK3!%%$;Jn1CN=W;l5~0Y7HIeR8}!jk!9GFR>m)h@#$6ss~-7G4E6c@K*-E z0R;yu5Eas`B1*h_$)qhHfWx?C#3cgV>a)l~8Myrbg4>y#v&+N_UUXV1niag5X-+fI zOkwQ1q4{fwTc^*N=G)cNXQx_|WVx~hUZGUNdNWHpf3&KEDX_epP@1An0Y7Pm zD>rFyI%hy575gdh$@JFeKgWJ0{dr{q)o-xd_>N8*>1i@WaH72v950jF0k^>BVQs?L z+b+76jz)&X-6Fbo*PN*}eQA=;o+a*zWkmzq!%7PKi2AWw zCNJ$CT~R#{^iSjuEQu@+jh`R7Dx>|p-khr3p^EgpMg5c0tGm}W_-m?Ntreic@*! zw>(_c>MeMEa1cAFoXD@|%P}b%Yv*m!FrTy8tE*|Nkca!^W^ow^@1u3_hcpa+;`t4m*oDtLeeFp zWN4B}OL>LcUm-8DvqD~nxx5%Z+x--qrFDCK9N=Ds5EA@aa;i)IYv0yzU{xqK603?1 z^^M^$dG{7JTeGw!GEtlUiJxUwHabF);g*WPIHnqU58Np!xF@Ya>XgRmygr?lPyN3T zKfC%Q+xc$}(mVV6hyML>%A}1jbXG*ZHoiKZr_D&oz>z&ov*m4!6|V}ktO>=2g3jRv zOgn#5@Aa+dRUc!{sov^|9`&!VlsL!FvCv#YqAj4bEI1!mE~5LNP9!3{q;ux~CBq7> z*oa#wKpXo1gM7RkXRGKIirLqxHHG{#ho{QYjZ!l&?@cIwimgIDSsKCb|Het^OzZ-X ztfmzzI-h=HNmW$|ezIcn3jPPaY*rVux1j(e9q9r5sh}RpK7dOf=@HsmfhT|Earo`{ z3oAaL0b_jjPpDSJ<8T2#uEC+v%*kU^0Y9q29|nAp3C~Lemy}uT2xQsvxA`hyNg~z4 zKZun;;^5ih%F1F~N%39WRZ)b$r(<#ye~8N~w9n<7BOyChq1+DH5gl|-1+|B00iSeF zlqM@g1h?@qID-2rAUl-Dckp>i(2cWglJqqb-A6Qd9^glKOe6UG{Vbw~ByxFKD?c*p zW51+R>H3*L{|7H&348L}|pEhkVj+-}zMOM3;g6HR)%3gb=;A1H0C$@;}lqSeTdh zCX_#Al=rc}#LGkDX@|Wmt(e`&B080+(0KH70{1NP%1RTBoBR8fNCJ_B#RFgGsSWb<)T$IVoQRU!KxK=Fys>}EF0w~jeiq82`-%8p z8>6WutNo$rqTI+}MdRw$*g&&qz=z*egMRk*Qh!Zjb9JrFyP^+1q;;UfJ=%i3*!aUS z-lhI8_2b4_FSQ6sKgj#>6g!Sj%~ydEa7L8qNn#Tb8>5!2+?FA?;jh@+`!c;v1#yQp z-lOu|zVLe#i~SdG;|1;X*j)HA9gaPAR!);B{~Gxgq7_27a{DOYhmG>^MW*t)FWQJ% z0N@zMUhFES%}J(_97;{jmTWbTaP{uFTt4zz++3V|WMQ7Md$*3;T23o&1i4V~fRk}% zT2Hc7JS!%6qZy#!?F8`#9iy3HIe0j?8$s%Z_H=da8R7}$XkZvg%>e(cPx{2vZ_rOU zX1|}NeSXN5x551jKTn7q70BrraGD(p`0)fd<$whID9-_6()jIoa_S5;ilo%SRBBXs z6p@b-H-elgBXw4~e!WmKY9T7+n140!v-eOeZ7wUG`CF`X(k)A&^l7u}|z(^xCHby+Ca z%zX|Ws^zzB2;JE@@A*l3F08$BH>`a#WvR{Qj`$_#eEE`c{XGB1S1+}-4(`{|<}P~` zusY!hhW3>ela2ND*$H&64pIa^TZ0dzQ9*t$C^x@lDN?M(`>A5){6DDn&H5r#g^!s zw5S|y7+taaDYiZXtDCS}SnsT8^BkCou^ii#L_}cPN}|^Fc^i8( zZObBA6`9$Y#f25!HSF!>et$~ZNJ@%xq*eVhbP*8+yN-{`7crh_*8Bz^XV?kAK5T*? z)8I9TxzQeUQAv7m4N3ZtvqI{3FYT!7ygwfqoWmC)@6B^~gKX*$c^?9<8a_(&sUF?eVOn%GMu~p82g@6|s%oHM zWk;x^#C^p`H{?BfUQvJExX&3VDfhiM+SqpPjSAM{n(9E*x_qCww)FA}P&^W{4V z3S5hdvwQpvLvFGkoB}SzQ$o57C&_iV@@Yts;2zj!z#;v^_+j`&w4iN4B&VWDaEJE$fPo42=fc!<#m!9`>if3%i;p>pT;o z6i`CDMC(CGE{zv`;hpP%EpKgcM^l+QBR9jAUG0*G>j&K0!okdA(98;G(;e~+sEK_w z`UDQi4A>C2CodIC4^P)rmDS{x+Pkrw;$bXJ{e*mjy2$0s&+E4pmb5prx30jtgJNJ( zfT=yN756#2LR&;hU|H61@(SpE>d9_8Gu_n;T_F;0pY#w5H_VLHxI}b0k zssh;xa1L$wU#_LEGkkEkH39yb2G1i&Vc(5j{*d$)exAPqw!n<%l!0d>`+^DoNha}o zK-NHODD4MJz`Y!7H_gA1RpvV-aEfF}&IVSlfP&C6ic!4#~zGJ|DX~2&sz>ga6qY3aho|h8f4;tkU8E`LgV8Hp= zeWTqto+CziaLmUk(TT_^U#D^(kJtqDuS(By`4d@%p*>nBq6<9XOm7_Ad8aUOq{0FTT3e_C;1V*H?!>rU zgS)v)pd~g5QC6z9q@9>=^bmBu_B=T+?fC2*eM*viJeo|!Xf$vgQ>qR6Uo_J%>vEzf z(WHAkHzsVAA)!0uEpAbNRC@-Z@28o1UVXK27NZ`a^Q zDVJ!#F%#0@hZ5j%JV$u$QRe3=-v(tCo;S4Px-gGN!Ffcy=_U}q_2a2eZ2eUFZ>E?}dxygwhy!=Au)n=-tq(h^ z+X@Qu>kEqg*5c;Ru77WZ^8j+L-)?FvvgSsYi{1dE9s>S32?p@ zZVLn)V_t(FWo9_Wwgx|x0RNEzKWxCgM704w#qQ$ro7?UYqdd5jf2Nhk==$Sx29+2M zTB|1$N<*78_z8kTR&9{camWh(J`iHq%}Cq3Ea8Byjiq(3{R8`y`j4(HuXnOQuRPeP zK7rrVr@R)|0p#jQ7Ic3?68NzMIN}0Xls~M)VcF5T0Y61>WH44Bx5n@;3;m7~exH*$ zzXo5AGpR@bx$wgtF(=i&8|z=awU@1;Uvo0(l$`#@efQmm4FAh#$nfhbL#y!d1b>06 zp}3$bE5}w`QIe40Z|HXCTGGZdYl2AdGmbf)-%r5xfEfo7mrxb?eNe^t+L8>L1i6a* zzKorTKDh`HX(gTJBU`11Kp_!F(9QuYi`X$wKg)s$tDQ+QGz4R;N_JJ{N<$c>HHk^yW{F3 zHEW_x=fIhg@xvdUt~eL=NPh(%q^E-3!wGyKtDwP8az1do9JmoXGx@6G*J4=9aLY%4 ziPkHRl9i@=7$7MX_fYXUEVv!Ptdg&W5#OZ9Yy^Q-43)7~f$Bs_-K>=`8}(nK`hcTF zWP7njA@E|20vRH}$FREhV^jH4dU#Mv+Z;W`B?QQmv`{N$DoUPBOn~xfJDT z#YYNb$-!o!_0pP4ui0M}`_t5DY-*U@$%)i<=Rzxu-O#!k!hW1>ckZI$@f4cEVtcOF zJ7;_Des%HO-MQdU>;)zrquez`D;Ns~oODvaj}sg=gqPP&=6U!z!-VZ6xM0-wUJa+&I9AyLZb09k9zd zR}N0!Dl!PzF`a~gxQxX)x;AMDx|t~Bqnj>P%6 zc3MMExhO z={eAQ`{{3-iC|s-vBC)nmh$~xlD2Plfm3SdU~V*-&Oem)tFux({~X%P*clnnZKT-s z8{~VJ&vD=Hg62}Af1zE!SD%%OWLth0G@^0)ANLC{CHMuJcL=RNWWb?il4~_HtpP3(w{h?B8cK+hU8721W`U1XX%@JPz z9*YuVDs2LIp}a69wcV0d9(J%a>|Y!2DT-~b%{Y*z-o&mw0D5@4e2%hOqZ2)n1a96J z06IaxC~xcwAo@990zj5>PJ6zb4&{dOY(em+;JEak9)K<@T`L)7*mmV~G#>$6lYS+s)VkN7;%= z^(U;WYiMpBayaNHA4FP>=OTeyA&}Cd_l2_|kkY9@}chXmC+-zd2tdws{%xr3OL60i1j|9?% z)Hm~9;#o}0sB~1V|W#wip z$t!hcAF|fBJ)d7NP?&2OMokv;9kc*Dax&v*oS8qTiY@B#PvLh zU7!tXx;#@Xz~C?11SL*S;ZiPEck%VtU+n9%=j7Vzajj@uU6H9@JoRo@z0*3L+t)O< z62}=`VRy!GZe)rT|8K^J-*e@M3X5}!T74bEl}kMBHR^;ZS^ghAt@Rzvo2#7-!5wSG zVaFnWpObxOa1i`~ZzT2biBy5e%%EBBH!sM(+UqV-7iQ>>1;b@^!kJzt4yXvCVoW^&Fl-#CS6*u)oz)` zO;R?d3?3-!U9zgBtG#t>*P6yPo$hXX%3^y??MPkiP@P%OO%AWm=IqNIYN!fTw2xn~ zGBC~3|2HEXsrT2^R+KukmP9+TP?eQkmR0Sub&OV54Yno>}h!%+LuFqL#(+3Y!(k)+HT8> zk1Wb`@XJ!^sM<76K>YK^fqU)=kG;}5k?+WBbq2bkk@4-}DgRD(N!!YX!9$LRLH`{o z6$PD3N}6k1ogLK!fl-)tnev6WSG*CKN9>XYXF66tah}{b z^W67!RdI(g#i#NZUrSSt(uPii=Q?E%%+uV8E(l}vBv`J>r zCPQnktjG(lIBw2HY_l2-W( zaUYo9>g}hmi#Mz5#AoFjTRPtwl(d6C*Mx78%`prSYthD5&)D$eEo?@6dwXm+Ds zJC~NZvzbAIkR&~xMm?^>Wy*?kyMh%f*bjYNV?A7l^Kq@0kJ};aSu){K>b3Hixny!I zWaSL&KreDV;i;+fw6?lKMAcjjLOn`shDlIvcdH-y5i7pLQO zgN_%G-~6G$F#dqWajs~C42WfPslr_Pwd>>($F5+yYr5FI=i|$@42%z5^23tZd^88-=E1+CmM#P z2K;q({(;*1I_x_`um4T%lUwP1eJ;L|5Z@Cdf+}O1mfV*WJ+Euol>-A;O?6E7=8Wfz zMcY<4Hmz(~x+3$=;E*$V{o1wHN2~h-)g5iYt%HMGgOLcN##`XL@e>fK5}V*UI2W!M za34_wS%?!8ud@jrA9X-W`V2gD3LS9XJIV3M20n18(5zD6JB)fA!XrTY$gCDiefAaDkWwm^jpiKD)C}W&=SZkaZf=-dXihtAW3+h){hWZZMqA4h~ z0d8|Rcc0PZ-NgG(z$pgcaQOZjZbRmlS6GcGkN(TyR)NY>|54meF+=itGS!3Z2w$yo z9sbIo{T3{~CtER*&R&e~zy5oi4dpt1F93xve86#;c|iyHB|*n2jgF&4ht^(>slE9A z>V)>b72ivp*q+!vZM3LibSa;KeJho`;2>$v+9L8|32pppp?%S&7UiQ!&*RT+KSvSX z2s_Tsc=Dc^W%`RV?{QWij|-Z|07-n2F7@?uv?xv23W9S_B(&&If;P~o74XA493ugm zYruK$&DTx!D(+V>52umtOhqZ z>Gck9mUD` z^x(M9zj<_QOG7wNRoCn6xn|S!`v){@BZK>N=$J5R1v{=9oGZTK9^i04{5?qs1SlDHT1j)(eKTKR|4*;So*Nuor&T3= z#c5fzgZ96#@jb@h*-qmk%=zPRyoYv7v)$f=@~7As>Ty3JJ;*8GtJ4q0h(5Vdj0k!H zANeEUsZjo(95cSt$dTZQI3<6RPjgR2aF6^9!+s%p!-D)#SbwA3EEs0Ryr+2ml;hF| zsHbM%0KC$KSC&3NcxN907S#k#BRI~=^78WA=WAW5syLy{f7np(kNEdf5dx2z)t~AnnPz}A2jGsOPRfz zq(%FJUQ|33#pa&1%Wt^lsN>^(6W%qL7UeJfGmQVmN{DH}(K;&v?5U zF0}~j=X3G8_Xl=wUAIY*$C(@m4?}^~gPV6x4Q1LF=VTV72ZQ4yY#LGm9=U?c?it9- z0xiDLhr@_YJk$` z2ovs>hTan#@*13f$f<+bL% z+x_kQ*O=T(xOfkb_4XIu_6(@~NQ349C0OnflN zenuV_JYm0v8UINf@1*@2&=A>z@`B3vyWLl+-}nY6lHE1(;K-LiBnaV>`7-{bnNZ^8 z{`?*9(^uT=RGc?oK4Fo+J^F3PuTlNjo;`TG<&$jiUbTe1e6RXFK|9_>?7H`JbHf_t>w@w1n^$>@P%(TvXlw_o6Hcq88>NPfCesK5yiFTTCWM~_LAra zwp7?4!|DmRqQlYq!b);DB)-|`{V*TP0)Ch+na_x~w_}v2^Z~zH=JcRPKd$#^ z0zD_$W_@-5D^KH3(0_vHM^BTt+79o_&uW_duYT*Geo+sf60+FJtNZ(}UMYTetZ8gq z)4_i)67xXudr){?@tgX9x53B4n_3@;TkQp$W)mEaSwW1;bZ&W>-*!*y%Ai$?(&wJl zRN|}PNlBN};&X-i1-Q^B4$q#28!xD!xdc&7Ij$ISm2#A*=R1VvQfZn^oP|pEyZ8*_ z3{+~?0ZXFsobHu7g-4xY6mIOasnh@`a9|0SdJtJ3F9Be2G3ox?%h3kLPnx?Yd=;-em(CsHxL z{t=@-jS-rm5jA@K1gf~LGC!rg>dEW_;zmZ9GsKP9eQd~_$BjY;FPrP}2wcEP)`k@L zOi#4O;T&I#&-8@dp_yL7bIkPg=amV4@do?G>|YTVbjAuUPChON8 zR^w4uulDq^-R}NQRb44Bk1M%VtvAs%wz)-MP<^U7lpJ@%*lN&ri~G;eV97;D3rKOJedr;w9{QN|hfI zjqg4#V53v->Fd2}W$&g6cCGr8x8G)?>LnFhJEpd7o$6fO(6Cx_NPgcWPev@O^$+g9 zVCN;8PZDvMtjLX02EX$|yg|c(@X)HI0&RFt^4`50KNvegDb)|jjT@FtZa9R<;(vn8 zfzYot4C%l?Z-~O4Ry~J^Kd3hy7DSpKc&Mwlx9g!VUbJuDMNh=`4q_`xaB%IamFxCW zumZj4m|aVngY%Nr*y*M5kCz#VMQu7ZV%oivR7u#2f#r?e<-w||a9&~l&^H&*k^8F0 zyX91QaiAsNmNSrR`|QF>BC>A*E}LvxT{QBfNN;;^%0cIJ`VSi}x@f~)%=hJi$JDFt z?81L{%8hF$maci}ooe+I^ENg$HR&-J_LM^Ba8`}$PPGw(nc)i4;E2J{zFyvldZ!Sx znaigc<-G~zji{HGM|>gL#hOGlAMcbwHRBgiFTH8~Z*mrT13_(k?VJZd`a9Lpx7c{V zcWTnc1H1nIAkXHjD?9AlNBq5xqT13cYe(x_c0|Lg`U*RiG<4Suj2G3H-ryUoiJZTv zFBonLTV190%7WCQ^vd=||G2*>=xK4*=eN2G%dKf8i>lk|n@2quJDeUaw^B`R$T1u6 z7r+<6$^KF9na*pvSdQ_WUnFI;qqybRYO33*IhtA7e(R zhvEngPB+<;?<$8(&E@r*?8~9!W;pJk=cgD~L&qDr)iUPW+EFx|N|76{yk!5Cv5Cpp z1T5Vb_Uzrehiy>5wmcSF&Xy4syf*e1xfP5NK9c)qZ`R@Rj1EU6-!IYoLFbm2nW?-9 zPnsDnzi8k&WZ>}<)du_&8{krzgC8C_j{hLVCxTAICx7SCqVflo0u4@a2jD!e(dqTd z@p|#+=Jt}+KVr~H_>1P?M_l95rEyUH-#JeyuF+dZ-xyM%aed$H%_L>%oupEJ*<4S? z8m7kWNi$rz-e`}*!7WMk7rb|>6QueE`}!=!mO`p4416z|@fq~brTZD99nKHejy~)} z5^wq@e?qxnej?Bwz>CWR$2J~c`Edtxd|V_D)oXTLKvRCkcT-Vn0r@a`bGaSMz<8l5 zYI^YQ@?U;sYD?Ws>u#>wGWnm|Ho2K=8mjuhUF;3jdN-Rw3SjD!pHv?J7nRr{G^ij3 zRfb4c5xahq>wth`{-(i?n&EteiSmcca1)**1c&tjb(G6Mt^}D0P>3x(7NrWO3h_pA zYu*dCFRc4@&VRA+eK}7|-?R7-YgqC}^=PK5{xz5RUdvK{20pWZfdK;|v9NfLc|fr_ zjz6`2Pjg_;x~F2#v$c<|UHhnd-}5x~cxi5t^fgcmx55HW{BSttp%%_3hjV@e{E!*W zt*w9`*5R-upvJ&+3h;QlBu7#Hh+dwkR}jNUsBS0Hy7UlJ<;PN@;Uoy&j6Uv+8Bc5N^@R;KxGk!fjqIu9H{IExl zRQa}3eJ7^2X@Hh$?6itOFPNTOan%5pYC6~V6^t%f)z!YX$-ln0so%H6_X@LA^=#JG zYRV#hXLGX%&Py9wik($`mEBwVt05cSCe7uP?R*p;*Ya)xPFgA8#}eQ)8U*~P0k;zE z2K+U;wc5Z>dLhc|c|E}^>9K&H)bK2Y418Sgc7q<+Xz4@R-17(clqEc8$DlWK>j7UM zsnTv^G~PsgUyR+q?^YtUvr5@~A-?VN;C^$BZ45Ezr&@b2?>@^6` z8~(&yk*0Um;q4S+He|uNGEF!Tjq>-~(FSJ7KU=n-2R^v^nyx35CF$9n>uYb#!AY)+fw&SyIEs+e$#zL($c%qxgA_Req$dwzdvG zY-rtNZ0*|Ep zVxDOhOJy`wvQQcbLD!exFMslr_C|MZ_jvX1*w_2})fb8@77y9O4J@cmLaMYPS})VP z6G1g%eB<8FIWONRVBviq_`tr4Z@ls1(1^=565_uz9{kdm9;Bb)&MQ{0zM@n7Cc2j3 zSEYQAdjj0!f#nNaY_8z2#gmLxuWPU^33mpwqwJ+TkFBaHb40c~yjHvw7&UQ@6JF#V zzSAevrRcpX_WRTIe-<154%kMw-59GL>m68biKe%0=?^T6RCIjkvP_RBU!SV9HO+I+5>XvQ%)WV>hm3SMO6W( zwe7r&sS%$mTwWP=Ivedw#_DA!#L?ia>2WqJZE&{L6s5?l&>JX?tP7w&>cD*fa?7IA z4mdRsYQ+0?FyF*ZDpvrV6UOD{7E5CzyGZ>3tKV_pfcoSAQJB2go%_H8?d+@S^xy-3 z{%dhdaZ#83FT`s%c(ni@_V&)1!O=Mii-)crwpaqfZgC4$vF5_ErMdmR{$6KQSAAu; zE;s67m#DujmgT~+VE4vgb=OLqa`pv#tGnwOx+}9v?OE)?u_x1u%0rub0?XTIqQLda zfnIcCyz)C`#P0NLyj?cm7SN>{KUHnj?vfIsceDP`WF&tyr@y_n&u>r3Y#(#>Zx6Tb zh)(T~3>+A&?RC0)eFNjwJ@P$eolOpZsCN9kc6rgF+E7t&b6@Y4;F`O(?Y(oU`iP^` z>+W`q^wxA%K^mAr$k|-FbmuaNaUcN!_v&S@OkL2pJiKozOI?Q7kgxg9&-d+p_9rXz zF|z6HqE751D8sG+I*%{z$I&*Qk#(Ul6Ap>pTAP zeUaKga8q|=Fxu7@9cah#a8I3(Gr-*XSP*ckrjUbib&^b8?#OXTPAA8 zTAQ0%Tbr6&XZ~0gcDci4Wns4~Tn32`f=(|a?%*fA1(osB8*oCwV8{{Q+-6Y9r}NAQ z!A35gT7FsCWbQ~$aL}8VvbbZ+)w{j5<-FnO=+Zuj+}SJNv-+;>kn8wJO|Zu|vAYlB zs6Jd6*w8(&Ej%$X*ggn(*K}2rrCK?ANNJNFmEEbZQIbV@lJ(8DCxQQ#<<8RD@Z9oG zv7*`5B=DyhON!?iW}T%wt>@x-7Q`gO^BfD!)+Q=z7s ztajxLcJpN5#fF=bn9e}4T zZwJd_Y?veyY4&!kcImEAXxCEldm91(1DNU8-B44PyWpw~r;I%3j;(kH zxP_}4%e=W~U>k0? z_k{MbAZ~Ly{p$pr4TWgvwo{kI-M(C$8>Vd^? z9CDMz-xy0~@M9YXB0+2*#?QVczU%#hpWN~z) zroXX>rKE=^YWm=1&x=NvE{)dpx?H_-1O|V{-7CjOJmFsV(Dv^1{49T4NpMSV-{#=J zM6`dYtFH!KzD}6?c#!V;Rx*Fhm)9w#$xs@hgy`}K*-Q&^2X;9ulkc1-G$>- z%;e>2L$2?g?CMMSt7u&w!7QO`i7ZAs@~Bs==eVfLooQ@ThCSRWcDPuGRIIiLx) zIGw)J`+jVQFJX&w$ySnWZ2;~JS{I?1&K%ISgean8yW-)TEc0;Ah~}0!oOuZ62=h4C z%7egsIi(e(D837y|1XWT;bcM9RXfiq#xllQLWqU276f4w!DfrGhWw3)v4U*2XLEPO z@~+EQFIL+SWcA(r(YsgdeeNeKD9R%LV&~(nR*TC`VU<`w(nsIinTb`mwwPNnfAoS# zU)UGew_(G+W?xHRWam&rzth$4ukUx`cT0b7Yq+mJBOX)5Hkyatm^r9oT@+|hz(ukq z%`4I4D2zcH?TxkS<=XANJL#cwyH&xm60^C5-}GW5wR6TSxDp=BjFt5 zkYi2H)!&e0NHp;{MFV_Ns1`&?^T9G^g%N1}9R>7#0G=(E zfBzWooo0BxHsI!CEH(KUyL9dt+a8}XX@d-yR{=9cAZg0S%SB7?;4OwUx<7&RAHB`% z)3}Lvo-b+VQwYPCtp4uLg)ad{VHt56OZj99Vf3SgJWl&vf1sY!WlTMZS$L9&(vbA4 zu$mvABW%rL&J#IWs-Ep+yQF7ji}D*unxVT2GiLjx?@Lbtg7Aq$N@x37jFGGc}9a!TWfi(XElg|TFtx+_6_p3sLZtV3~D`Zf~@DY z4jGVl*e-UPpjrJIr}?z>eVmZyG;>Jl>}eL`Wk55B6yi?HVGaS!9FmSQ%Xux(%pt|I ze`RAF0-8Cb8nxE)TA-Oj@E#e~%*%jgqs%nBRnTle*7I7RSwL7C+r^{l-$- zzc1f~X!%M0y_79xG5-9w{CgqF5A*L&@$cz)zMPkTntv~57B{5j+KwEP*g_q_3aJuiPq`XNSfH5&qb$Hgc%=cde&`cIclO)aCJM!&xiKWu2{ z_8sS+zhnE(w$YK1QTpMr6iyC(A1BwzzSB-?k=((%q^|9XGFA|&Skh2~SDsw*&uov? zR+-w7Qc#XjQ_3bFf#Y;q-x|W4o9w2DkoD99ydeG;fxE}P%fJZ?|D+?}$#uc^1H zqNh2lC_BAA<2LLC%&T1D4%OHzeNA-*odeEDRatwN1AFfo!{&apRtSEF(Hhp|tUPi9 z;<+2hsD8)G(aMRlOGp{X2irYo;3WIg3;7crT!#XfmrFjUuF>G3GbhMvocVL-~!zm#(Z|+SuHe7qPf1 z9ez8@Z+0RT5bJl>*18K_`E{gE1MBNOwdI8s-X2#fezKjU+0*hdyhY1Jc@H5U zEmDA#;;ETAQMzGKAM2ZNR(tDLjtsA=_g1+k`l`L&>T0jY;caN}dKw$#V{Oe94Vf&n z$rbGE3_6?fr@ykfEmTobQdwD2QlUQMC@Xh3%FEC_Vx+By{L1Ktx4FI5C2J8m$TH|5 zJ^c2>D(`3@JeJpIak)#p_6&ukR@4>@jMJM1Yu!aIn_oVD;nu$O{@U8ok_x*oT#;3n z+aKwOwz;dT+}6tcN(7l~_B{N$1F@GPNe&iZceA6DszW|Fvs+kEC4vmKn{1NgQ4ULg z=6RXlve&o`%fL+uuqpGNKczh{#q(dn54siDFsjc!%O8!%WSZ>dG*AotD(y~P~-6QHFBq2b65WRQP4F-&R?{-{pFX^$J#A&t@Z;I1E-@ckY!iuA6^v9*V6Fa^yS8 z3Y>=0sav_yenvP`WBbT8*JO0pNN+)&YX|BdAljD})mkABD5!F!sR^ybm#c~sgU{^K+s zCl%l%!7H+OAlqq8wao77nr%t76=n|<7gg65CONg$MK)Jac6O1=*I1EhtqP$b#i?Zt z4P~jtxU90`42u$*Io%eOmSDC~{|r--*_>p`K;2q^*WXdMDmE95lemm0wAd1MY$w+7 z!WLLu^VL>iAHYr9Bgn)p6=&-Q(<Dv;h^Fhj_g6s8D;zbyTARWdyqTgU zGN_i^L-(_`8U32FT?V?y+q9LwDE6drb@oOp%dmbq;>LcVjozW&fGumldL%Frm9*jSGrL}zd;^{s<3l>uU zAcZ36Uy6kbe0-+U-&zL@{X-M*H`H$!xGOO;EG*LS)QW`*S8OaWM23ZB5`pom+$@og zkON!HVz5$DD4f-PcjPCJ|SWg9eN1jDb5qp~DoIUNdj>h41 z)1R@oo2Q@su@(BiTei%4gDyyWWi|)Vw@|) z9feQnC+InC7^81v3}eiSG@uq^{g`>ZsQJ6Wq2Kri6p{vq}&7-p~$$m9!5 zom62iuGeboi_J4<&*uJN&fm17wPokV0&~wd7hd=+`!*imYNT(KMp1|qB_U*4Gw##J z%mo{FwzTfplyC0-_QDIlk@{K*&A^xFcw&EG5`Yb=LvhBAz-Cu$ngjAc$3in4l^RPy zfkjj4$Ta2Usk3Uh$EBYD4ARi(6A^2+|T95*RU8RXC0TrOE6|p_3{Cd5 zxiBF)Bj*7$pu0MuApM!x7#EuL!5kr;EIgZ3EIp>dI*Ie)j`3y`=^9?|Fmzht?ML-%LK@$}vtE z9cI+K96R)<)YM3C`28o|@cWP7;eA7sIDfi$>>Gb~C0$%ogVd24`v$3Fwf5B9PQ6DS zWVDrw9o@rH66cQCk-zsNa~2c!`TOZ|5zneK+&2S*3g< zWg^EG6B?>IaHfa!p6Jv#t@C6)PNg{GLxwG|+77IY{I{cSu2jMoA#9-NJ>(@3-?L(S zMy$?)fRq<`1u3FVSYCKyA!YPo`ZCTR1a}Q<;8b!qLJkr@8GQuLWtjP~3#ieelxxJ_ zzYh<$uUX^%MGVTG@%#Z>mXfdd1)*}dcG{`07>B;h;;{b5()5#JNGV!RB*NOQnLbKe zxO?FDmQHQ~7Cs2YeoG%E2k;y{M}Z?V4#X64_x$T$> zes1hHdXRs{laen^!tp^3I7M7HQa>>TPw6FG#ZOrM6vC73AxK#g2MzYiXdBP2{>k6z zpA@Ij#Kmf1Oa9drSA69zgqnyQnXYDEqg)R`NhxtPHt^387U1a%ko2sBMu(EqxFCqY0h=0A0sw z$^8lSL!4%;V6;Hi6UWXd)~PUdu9Kc)>@=cZ;~v;CD~3E#@`DcnoCg%2-g$51op;h| zcnThL--Q@VYo(B4C=_7y`6*%yu=NjMW+|nOXp>T`e${ioU_?S9!Qsm|FHz#&m|lvp zPOMj)kk6^DEU}=+6;>JAP>|ncnL6lf5333*>(B38>?eI-rfl?C<5`EhlpH2ybGR%)O9iSIl)f=T=x7azojc zwv~OUH5uM5Z6}p@j>A;vd}Q;DI^ z@^pSh$#a==@}tXYGBe8jrLv1v;w)a-x@u=hi4s+k_dwRhdGhHSv$&|pk-OxaHm5JL z{L!2Zpq1DuxR`6<8o*t}iZx{wvI_(XSjpagks~aja_m>q_RTV{TZ3bGAW}jABIn8cJpoWo2WxCXj1qClQH8eD} zRadpsS0D^y#ISYXosdbHcOG&^vT|f4XY#_hia2Ei71CYd?zMDIxNNK+oi>i>9GB|~ zmy5n|){!*&-RLlVAL|m);<;bqf)1N7c(10H<*LB!!T88wl_4rXouoF%I|6E)<8KzY zH*z2P2URMQQ%Byasgk_*J zu!aJSyBb(SPL>2GaYr0EFjvZSG<4P6xs9}N(J!^M!2RbwDy(oHps6lubRVWEAD6qT zj^VUNEIRXSuW9VyDYY-`iR`Q!v92TPdQcn{&uZBs4oYz!dx>6(V`6LvYEMsKluaBE zL0lJI=)MT>ip3F^Z8AABQZo`#4AiAANYNMAE!McG$k^~C!-TkaMU;O|UtYsNWLc2I zQBdg<8XTt%);r(H&tILN@7|lKk1|C^YP6B*R#@*VwK`>RP)LZ*9-mZ_p|Pe%<#(jl zH5hV>oQ?>cNoDOasS0gslnXwmTo>vm05b$ML(~HL&bAFgX^hwjhXT03IzE4H(=|0! zlX}wK{d5l?pnDnSn~}Tvzs*~Yb8+#5mS%Pwtfe)quUN)PEwM7tSA#w#Q22q=E{ygYMCCDm=oyis`Oy<4pSU z$ZUG=+J1LBO?J6h>#~&gBPAZS5G{^g5D5>BWrs^CB#NPfPVsX0Qze6-7A3nIKd7&) ztiO=n>T-RN*;QEBmFadPgNI!Og+*9?5*V+*-65tbN8Ms?97b#y0UD#Tj+4 z!8>!xeBCcmt8ZD(o%FrEFg66vEEBQd=sSv!sPNTrxWVu{o*Y z$tRy&mwIH;OeW{RNW1q$9m{DK<|AfoR`Co8Z z;7pC+SFGSye1!UsS^fs(p8=YX4ylgK#~xPUoV)^C8pXl^hBD2awoNCp3R#uLv+xQ9 zm1>b$XwGN6LR5w}AuB)|m~lF@5NIw+8sipV$qI^I>!IkNSMboY=^2bSh&YYqT^jDb zo1Pzz-6~rqE>KkZRok=MvId)rCi+(e7WEZ0FD@)vMwQ}>{ar)NSvcRQ(P8YZ_Q&Sy z!|J4{35m(ZG~dC%mQr<6bf6~9V3tn_YF^`RKQUid*RlCJC1q^B=9#rUXY=$(PON;vtX+X#4FKN;Fb7Mqu2(4?CTQL<%FW7{R8U&8kp(iLBTknNKCA z7bNb)eCpvZE9p}A8=1MWS&62+^py1YBpr1nSw_qcKn&9%-^pa@6)k zlm+JHgqL=NkPQYrMHqYiP1s+brTFT9lPiV@hnw($}-)b+M+4Q9qbD#zyVf zpdA~t6EJ2EyyqFR@89Mbv2>O=V0Ve*72_v^hC@=mT#}Tj3N@o2{gwXCU5zWzg3ton z56ER0A>jU*T7BHLnXagDZ;28W5*iI%;kiJg&gQO$l7cBJG$ViWUP0XST9v@M>>W|x zXB)kPR*`WNpOw9vcmJe@(;bLpB?{8i9B0=s&1~sPWx= zY5jo2(qEwmO~!ZPSFu!0f5i?dpa~9A%4s;Olp-{s;sM^YwT4EyV>qijgnswBsK0bj zC8wbSECtFmn;Lfv9l#roGyy|@E=Ed0Z>QMe5Qlyrwz*efqTGm}Ukb!)#BvZjaxB7? zScfb%<+yqSmp)dXESDcW3y^FLm;uZ67Vq*O-QvAsWho*za&h!w2@{sCs0EF4C(`!; z57*SZ0EF?jh!k;gINOxfB6M$Ttxf5PO<(!0;k#?4FSrD^hJFYm{g=zj(X(<%9IVY9 zkOK*p=43X#K37cl8ut{=@$saOn-4yQSQUt+lVa)Fu$IXI}#uw8K>y!nHBY6^O;P9Bvtu;{T2-O`j8Ez0h`5ND_^NnV zctk{Ms^Vf_IQ0Q)GKG9azo)~@9-1=gNkV4aDTVZVAD2U$Wi&>`sC;UDiV8GYCZiJ9 zbhxTEQWa^^R@aCD>l+TT!P9@)+D?SdW=BnM8thmbL!Ac?>R8XfDbjQhZOm4K2{;A? zj**UitM;v?+inBrv-89k?FiA&wE2`S_?c&ilU z5k`>>IxSS;K|Cvr4Q^80cCEu4lU9?avSsHR60Ig(Sfnv3${4ALPs~oWm1UURD^=Mx zN211-Y6y)quvcNK#9VWxD?K@eeH&wT(4S(h)#=X83?_0V<>^9HdCAFnstMX6tFuz) zr$}<9n)38v33*9)>aQubJ1TS&*eB`L)>!n9tkEBFZt6Z0_QYeL0>mFpFE2HL_)V3>LG-SlOSO z+h1w)hN$;ANX>U~KcA49QByb-M^dYJ?4xi16C9^njrx#)Ky6HdIk6<&m>3!mq>aXP zDfe@bf2Dt*$vpC8YMH@v%@w7U8d$qx>SW&COd9~N6KM?;om%9}Fwg5i>>gFBm>cMeXy#4w?>GJNx*`h|ss z3+wCVmIhU#hcHi+9hik=*zcd6Lv5iEdJS))K7G_@R1YIHu_?82g*e^Z z;(n4|jb-Kv_rvi2aK9Mjey*m5x@ZCW5?OM4`7DN+h6i7ZZM7&3ymb2I7tpwR_d(EC z_f6c9=JKooXpMA_7wyrCjTztbmq0=imS zkw#ydQBY?{pXf+;YD|Xi+)0aVj)nfYQ(eVFx!9LL4l7oDX?l8@9`5b^%kA-TIck0L zl{4W7%W&~{m3|DV+Z9XM-A-Owz6ZTcTolE@D&13z>n z_@N6ud{iu52Y%>!am0xn5fgC072TH0_@d=hK5OHW9+OW}_tFNdbEr1MPwSUaJ9M{?pi&7w)auOafl70rC)1hK z(p;aISl`^@zJU%p`}Fzw`aY-oT4Y03kxp&_SC8?W435AEyM4P|&elymt3i^k-$M%7 zdJq&h%KLJDS6R{&&WI30x@xYE&xDBJD0Sa_M^cl)*EZEsyrD$qAL#4XiD>DDkO-$D z&7%6k(3Iqu-=~fWj+o%%Q}5>+=r1eTP;8lM0~VQBvpj@TJj4=HXp)uCvcR&Aw?~+H znFd_G62|X34ITQoh2jRR4NJR|d`vw{8c(kkH>!gFoYctAx zp0~_Y$r@PBni?7$QOrgkJ0VIja>~^BT*)6R3^&%!;i=a6FZ9hxc?-t1bHD_#ou^rW zhYs!hn75tvsA(hoO0b^9%WW!NuK_VN+R+Rey6xYVD(hUGpVjYb^z4P+o~@U&CN{Fh zwu>rpQ3-*vdkbq9y}Iq(LT^K#yDXRfu@S&#Sse)zc70UZPD$YUi%*QTe` zgG}o7OiLmT1O8CHswT@_m{p_7XIJ#kxN_5SZ@ZVLwly~-CN?y;(X6q{)N=3Wr@f#C z5>4^<5?}y@vxosk73+E$FOLhQfVlbuWFkRrz0J+t?97?R za&|xE&C_CGI8Z0cd0vi}0-h&jtSw@By0pl?p-s$qj9jo$kOofbZD62dyr`F#@(?#@ zw!9X0Dr|iN16hoFqp0tS@X<2XhLMMGIUVl3syn$vpJR0uFRHS2ml{&LD<))5X*659 zSGxTCx>{43ObWdseb6EosDIP0sm>WKak)v0%IfFkYl^ya^ZT6sCDy620H%KxF@=kl%u`FO8!^t`!|kz3Vg!rV8LW&*v!Sdt+JLeh z6BAuiv9jA@ZXC+?FYie;ly=*y78MuSaty`E-MfcQ~vc*ef*zqnk5%6#%YY!2@XP^W6tm9&CD!e*JtSF1nw? zipT@7LSR+~_!TV@@C)3gAT8`42|9y)8$vZxM0N`-7wZq~-A|r;pl{TK>ctqBj66xd zbBC~)Wt>0K=tsPX^e1l`{q!dozX4bpC*GlK-ZWT@9#(i3tBlSBr^YgbQ7kmp*ZVT- zBizH+iQVsmxI#x^Y{m=l_~)4472NDL&<)(4k$zSJ)b1YCF5W08g+^usHqGwaP_y`I z^q=@@Cwu1yqks2m_iCWsk93&DYBF1`wwBDy7MrydIzl=wkJEBEDaDgg!FOzLn;q~3 zV@^;?Etm_X$lfqkN3xbEvk**uvL;#tA&2Ml zQwlPyd6SA0OIJ1j9#^s~FfA{xJtm^mYRzp(N@{pZ>oQM>2@D8`@XxZ>6uqV`Y_nF( z%o{mi9c(l^!U~(xxP*}nam7J!jciD&wODIR7|4*^g(}!aE+iYsC9F@#z5nn`3D@bg zHmk*A17~pJMt8k5K`;?42#6GAkhVt6#(tWpJZs8SOW{3|vZ0z<L_(WtRXzbKi9V?(iu~fSe{Xlm6M$}B8!gIDPtABq2b}7 z#s$h)8Wt9;4U4BWs_>}rcz-Rg7W{mg;+4@xLEthF#+to9dOp)gLjwcDLSUWa2+LFE zs&nG=~c#@l;o_e&b*xds-)OV?U4Hs z|4`q!(5#{A^5vbGw!sV9S~vD)rbK5(=psk$eGZdDlaN!Ll3D=qU7})(KWBebOeCTPJb{kq;kD-zzltD^kRuZhl5Q>b-NJxa(o{%f<%?dTRGLj8h(JA_r zB&b$%a-rvn4Ue}JOmt>fCWq-Nb5n}+N@t5S~JHj+td_(E4)s zkJQkqV*?dwCSd1`9MqMOz`jN=`G1^lPA1xpK$H_jS8%D4?8Zb^2zjguC9V-l14+E4 z`!eAWU@E%ARveGbt;%O>3*&0CiYkH(i5C`E#j8pl?U)-^7|~QxQmr-IVlUUIb4|Kf zQ)Ig1+Nwg^;C2;_Fhs|vV=FI{LziBal$Dv0oR|TxY^6`s1XH$F?Jyp9q*x3YSt-$p za?J!|u3l}`3eoaNb!2?zgt)-!%=B6aA<|xh-=Ta=ZzgiNl9uIc^QPVFMW&S05Gc6V z|IrJsntD}R+f`GgUv^yW%BIHiaAbknsRnl+AU|ZXuB{g2HE_OJIVLOD)aAe{PD&fgAmFeRsJ0oCX_k^Ub z1Q|PNo`>a)^j4>f7&{)~Bvchs>?`(+L$~0{WT&paaAIqs!I0SML9_EJhUV_emA7}C zgeqMaZ`IqKn;+I_wHnl>iB^lYKTLm(HE9L<9aE`8hi$X#=TtlsSp78jYA**o|EyrKP~o%|EzfXD|tfnVlN(m}4l`VixjQOaw|;6%3uZb96* zH{fvDJLx^nWP8`Wi_RMPg8d?e2>8n}+chKj2=pqprs0)bKRExnEv~RUv;;yb0rCqAjTuo8CL_3-|Z< zidnV%%zcFtDVdg3)I4jjSv4CUSS^(Jyll1$gB-~i4D`yGS2m3tF)pg=yx5+$qi?~! zw$|O#ie?z-b$aaV(z<`eElKA3iFK7_csy-)t35U8)?1TOC0Rg72XAH|i_sQxelB*S zU~Li({ha}R1ZWc`SH-*k1s4cEANJ$^7w|jr9;NrnRi0Sxe_eUym7b`$x{}Ep#iM&L zL;ehV?gz<$*yelC=HHELbGQcaO?bmjr^tezKn0so#?KofK@?{VRt?ge;luPzV&BEQ zGuF`3v}5OVv1VIMuBFr%qIRV_T9rllIR(n{^o+(hwHmugglJ>6LoRz;h9bEn(~=Qn z)amqos(g#Zm1c_a4Kmgi=G58EQR+Os!=cH^R>h^7u+^RB{H?QK&Lg|eHXH&*WDES zrt9L2aj_whkus;uZi_X>#{?@QW!%fD`KpMR2)(+vR1A<>4_akE`Y)sCAW-|=4{=wN zl(0T>fYZ}O-1k@)AZI>9n=0Z|@sZ(yQ4`ETc71$`G9rc38-wB^LzN+6YIQ` z7NyTXjc)L^49}_*kmed_KMoLEhLvl4t?H!J4(#ZMZno1!lafry)WvF-bFnH#)dIs~ z7^6cNqrjb_wdqiDM>+5R1O^!;zrpA1YHlWBeSvx3!W#Owl+3br`mn?9?Cd~(&%nne ze{?VFj}cz}287GgAK&(NM`o8anoq78y#u4-Z9ElgMOn0np4beozwiPjH6zWJgx1Eg?Nr5tI-amuL{86|U5b(vZMBzo0~Ii~%W> zAO%=jv2|)Vn__f%xJVDjRgqgzQJref4%Nm5#{~ogn%>Ga=M=|Agd2?!F>&xIkh8rZ zM`w>Fke#FV0_P8b^ASAh8J$G-kM6~jqgVy>Lr%Fgl{`mhc-#It_aZujc@VI@=Z<18 zx-hYPSSxkVn@ZfB*sKnF$AC&qhOgvK&;(W=63u%YR4^N38z&l_l?t9hg^*4Y=+b`l z3%T$vw4?O8~BhGFy&JoX(shkUYq8&z@4?Ry-b56P(qf0J)M4+=m zI%UA{PK*(pltO+NpPWvoFB(n4xzX{6^A&}?3%9%$4?597@&=?$Y$%GS0byg~Y-Tk+;q zyfMg_Qqf07?qzvQ#nY?sl-b-!6zQt!#>VQo9o%QLX6-pOhD4FBn%glKkywtiX0g`h zJ|;W)B~ojXy=cLExkIRv^UpDQ6HYr;k~?(T;r1B4nZR$kSRRZeMvXOq^H5$Y^AsAz zMvk9C`?&`dg;*3+5t6Zu?rEt2H+hMVE|Z z!i0?AdG_c(a7GiXC}lAotnq}d5}!a05zB!mT)+4Pnn<=&1Iebpl3zWRmasDW^=U2{ zhL+-Ux=MP^+Lx|!KXT#?*MH&-*N;s;@C`61LXEsY83qYr>QIH_$#$wn9}ruf?F(oD z55yeT>k10ur{j;RN8Yf}r`cH@%g!CwtPC!M=7k$My$i{vJw5Gxn1_yd=q#Svj9cIt9~av+5Y{CN?lt)h{V7 zUQ*w{e(!NiDY3Myh4({dXFg7C$?Wv0&&a6nNVR3B?{+Pzt6S`HEv~Cu;#y6E+&|0< zUgOH@a^!bHEtK!*%G#floRn@hnbJ_2c=8in=NToUM8<49zb}2$4FQf>J#KM0Q0noq zn9PGX3e=DS;wxR}zUIx+(o5XeU_GJ5;c)MR$>crZ|x9&P2-=l_xX(%bah?ODE+eWgx<%k z5l(qNT;57)-!0#-;F46)QL2O*wwYN?f+{LnHS*Sj4?;MfOUoEGOxn9y#AdAro24Q) z;&b!|Ke0|Ms7WYkB?R=)=t`IYAF*eQlM2Ok2~6#x{(@Vf&_pqu+)2^e(0HKw4D9bD zD?I#`PRY)mQd%~coju59M`=QUo-Qv$s|?mfsU&w!>9uKoF~u5JqARN?E8Af&N-aw$ z3n}7rl&Faml_j9yENc^=YgOT)q2VfRu&*v!momXeA(N|O^jN878dk6Wy6A(e$6OeS zeZY*M03IbO8~Gwn&xANh|4LVuoy(0~zik`WyJSi1*}QM1T$vwxyyW+YRWOkvj^)WT z){NLNkwOl1cmT~_ux{M~uP1v$o3R?oO@n`BNM6gNl9Cet^H;3geg;qWl`FaOrAs{} z^?G#~*=%q^mwZB9JeI4`QxWuBzFjfMws1*P(~`o|Gmc%dgub?H88Sj0pXx<_rrgKX zv)6-O@t=6k|3Qy=?jet#JoL5W)*{(ipJNSt#^p$*xC+UJ8kdcg#;);8kXg3<%Ba z+WWEg$YTi-+DdVW@#N*lZxiyGGK&Z8CE}8!z?L7;E?&?0H0(tK21k!}VP-36{&6c5 zn32YHNAE(#Sl9SB?(r!_9`E?FbNk0@twCP*_+7iWmoL7Ujgax)coO&ILGSGNVI@Uc zZ5iuDf7B;06&U_er@VxX8@ZYbVJa()C$GPF+DUzTT&#n5{Ngn1`{yy8&t-O!2;m!18y?SdcJ}EwpIM7i19#WIo#Z$B?xtXFg8xno z$kTjhQNiF;WqIUM$Q{!FjBh>hCy4jquv5r|U(*V*jU2=->@Bp9Zlk;DGdO*q8he8b zaC5np@L<0byRE;8b9ukxj`M0hmG9!u=da*z;P2$0;1BU{@&6KdK_SEnMxjpV6o!Na z!aCs!;RfMO;Zfm`@Rp3oLS(tJm9m4f7i4eBK9YSc`^AU%3HFKgN%3*`%=g*iv%}{W zpJP7%@cCUXmn-B6@@jdTe1UwGe5-t?e6Rd=`NQ&O<$slbApgQQ!dLH`>097i0 z-*>U^Ufp#J-`uA6VM$nEnrc=nt(?Go)35< z;KP6~1AY$l2@DHV1)2j31FHjf1nvpEHSp2ELxJxF#Re4x4FugAbR_7@pr3=e;Gp1` zU_)?L@bch|!8?QZ2HzEYF!+VwH-kS4{yO-0NN&iokZmEmLiUBc67p`yry)OthK9z6 zCWYEUU7>ZMouNaa^FvpKUJ!as=xw2oh8_xiFZ8p}AH(>t;IP;*W0*axK5TB-%COC0 zmxWyy_HfwiVMoHg4EtHZDS{L+Fwe_W6euPt`W15&D-@d)k1775IIQ?s@r~lwa9Mb0 zcy0Ll@QcE)3coS@k?#<9E^D(mc-`8o*(-{TzH&1E;a75xa;HP z@f%gVDp)mJwMz9)LVdzB39lu*pYVCYPYI*y32LQ!o%%xcwdz|mN{vp_t+`lpwdN+x zy_%;ruWH`YhG~1X*J|I_ey;sVM|2^&D&0g~ziy6hwQig44c)OsbK)h5KPHaqC+L-W zojzTkr!Utx>j(4;^=tIo_0Q@@4Fdbz178)U~PSr#_bYm(;_lAE)WkENM&A zUNfhfbIhgYR`V3|eDf;v7V~B1edfE&Pncgezi5J}%hQ|F2hta& zuTMXk5uMSHu{Go7jQcX4%y=o|?Tk+{zRQfvY|k9bT$H&s^Ww~_GjGbgH}i?iLz(Yo zsj~{Q2C^>7dL-+)tk<)SWPO?SvnAGIv{)=oOO2)7GH97+S!vm9x!iK2tew^&>k{i$>z}N5TOYIj#d_HKvrS`5v*p^#Y)!UFwi&j?wqe@^wwrAC z+Mci-vb~k9-uZdoKfoS+KWIC=4l77iJXZ7giQNR`_z^JI*j? zvNPLR>|E;H?0mRLC~7L2RJ66|%A%)TKCWt4n`;0Bd8KQU>r&UXuEVa6UEjM#idDtQ z#ofgh6kl1qzxb}=CyQSye!KXe#UmwtCFYX6lJb(alG!E8OE#8VQnI(?wvsnXJ}C_> ztt-8_^y<=^O7AUwqV!Pd-%5{`ep~7;3o0uqYbd+0?E12I%RVjpq0C+GUmjVmEzd1) zD<3GITfVYhXRv0U?DheyAE7~e%RP3tQSMhAcUn{<> z__@Me8CV%znNgWvSy|ayIk|Fn<%Y@|EAOfNYvl)(UsV3HidF?y6;xGKO|0s#+EKNq z>ei|UtA4BYudc7YrAAg0S`%NBR#Q+@RWq?>a?R|TRW%pXTvKyv&7(DkYTl|jR`X*m zUmIN8Svyp_pmtsDrM3HNKd5!rsq1Fd?Wo&RcWd2)bx+s5TK8VvXZ7jzj{2JV_WEh{ zi|W_bpI?7P{SEc^)<04IM*TPSzcwT_*cx07bq$>j%NsT}T+(n&!_5u%H9XnyQp4K~ zpEvx{=-a4lG&b5BOBx#*`x<98E^FM-xTEpb##b8OZTz(Hhemf(LQ{5AaZ`O$SJTv{ zg-vUkwl(c)+Sl}0(_fknH+|gnO|z~!z1h)R(cIG9*F39vS@Ye^k2U|L`Ec{c&EGWt z+Tz!e*0QSQ=9ar!9%^~JRcO_;Hn;9=ePN<7v0>tjiI26(+Ctmn+mhOBZLYSuw$8Sp zwgqkb+iq*SukB#lGi@)mz1jAD+b3;bxBb+vZqI37*#1QO^BuIKrejLS{T(Bn8J#_y zD?8VBZtuLa^T96K71uSV>;A6KyMww@yRF^%-TmD&x)*eB@4l@2s_y5zU+aFS`)K!9 z-9L4Y^!W4y_eAw*dXjrAJ^4LlJ@q~9J(GK8^epT-uV+Kg`8_*&uI{;^=hmKkdmin1 zy5~^O_mcu9l}y??>A<9qdNsW{y~Djv^uFBtLGL%cBYlB=@qKB11$}jWllnIF?d|*9 zWbNeQ$;&4{KKb2#L;sBawf#5u-`D?S|7-o<4TKET3~U;>Xkh=qu|fHua!@~5I=E(V z%itx0y9ciyJTQ3A;NycY4t_iMuPI?uGN#l`SvBR>DUVHgWy%*r{Lq9U(@^iwj-mZS z4-7p$bZF@9p}!CPF!bA0ZmR!O<-^TmtxH>1xUPNO>UI0q-M#K_>%Lgetq)nRS)aY$xxRM&ob{Ki-@X3&^>?m+ zeEsw5Ut9mq`lIW=TL07fkqtf@f;U8M&}>NFVA)W#p>9LhhM^5hH*DE($%gwk{AI&? z8$RFgPyD+#Ms3t=tP}s+Hui0ty>b1SQM;}08u-xRhfX;aar zOE>M`^u(rrZl;^_H&<@HeDn32KiYEr*2b;pZ+&(fx2=BLrQ1HBzErz+zZ5=ha@09TZtOT(l#j&UO>?e{1 z?l|VZ4g4e6iyFXPNM>_)kkYflU&(Cn*CJr$D~7}UScq$^`ia1;!SnwPSjBV)kp7#% z^GuLP;>G7j5a$R{0az#kHIdnTFPY6k0HLPQ-vH0zS|(kS4qP)x03-nr2Xo;S!|Bpbh1{Kn!QFYY}EjAwSu3znExvyko%dz-rGJK3=@MA$rA$cVDA?{}+Ie z13XxLF9I-pb|X9&tRpGhAB8=nkNXSu<>CepqgamE4bm)ynw2F}#qz0X#V8Q7^~2EPq5^}mBo;?Ms(C;*PD zfzN8BD}~rVhG<0GJ>$vSZ&}DMMw;UQeW3U>cou1n1K~;BGy2B*6>H;cQt;mfel00r zfc`uQyZq*z6&As2YBnk4wj*2uVD|uFn*^K--~RW&0n4R4x((wZoFtwJ=;tI*d_D{1 zm_mdC%em07CKXd!)tg;{X)*uLAD&0?v+^ z`3?!V+9P&%XJ?}tW{(lR0OSa>#|L8K4RMiWsVK_r#lA>P+;Q7jxiMoVCU z=oxGO)leEYC-qVK4RDg60HKpUh_+(D+6y74Bf(w}?j>sBYLdrq1cc!_4p55mx&!bi zQS%c?s-VX86@Ue#mun%)|2FXTM9F|_LVi<0ug(Tr@&0P`-|-=?;dR!Far~b^_kR?B zBXPmjs8^(^jIN#ucH#X2V&qup0a(xWdVvpN*P}lGz6Lz#32{xTJrIg8mc^0c5;g8Q z+`oqYZ4jXWX?vj-*Jl8s(eqpbn{OY|%fO4znL=Urxc7Tew*L!&e-3zw5{Lb3F&EnCDCkNm`bG>kbn=J4fIrEF%-8~`;HQFD3c(h{ zzNCaxq5h-cPjwX5e|L~B&`uk-mW0#$Q8x^vI9cpH{716M(CB+&Tr=iNGpPlzumbg} z!1EGv6#GK<5_aZtJnh5xeI%XskamFPwD$veKXBqbb>nmv0Cy#6=6@#5NK*{Qp2OB~ z?AjBDFprH#MPNQmcHG;@t?E7luRgF5I3uVX<@bL#slz<#!8WLQarF(7_`k@^%$3}#^(P!s^kEkY@ z@cS*KpO1dW4HLbn7uZXJxY=m8?HGSAqW^w{`-6bzFduG5dzeW!d(StB@wBKH*1wGa z13&?Y0>l7R0QNkFwGpSriYfXM#=lL3Hq1c)yl3NqL6aveLdYN&z}hk!z{ZgjFAns9 zU1xw#TZ;Y@K(go|?C!|u$)gyPO!itW(w1;;BE}tP4BtUau;n(f{KP!JMH=6dRe(&u zA^^MJ4A22q3kESv;eQ}HKndq2X#fMuivdnZ2p6H6r1L%4b+7~b2R(rOHUa!}$a=E@ z*jE7cHpLUhpL!3PmLbBssK0kfh43XQ1ypdCk|bd!_>{-dR`U=C_n0$e)Cd*o%}5`nP2Pe-2tYyrFqSOn9kMh@#2C~)rQPW7OjAu{A|3T|0z|Q>=SRWB?JKB(t8Yu+r%c7~{ z?xT0nm*@xdGx`<%5xb(KaTdbyZcW7FD-uhH9Sb zJk=)E#i|{uD^%C1e#OdgggQp8Qft))b%xra&Qq7Fo7MBxE7WV$+te4S_o?@*Z&Kf) zzFmEf`hN8z>c=z+O{4~AM{CTQR!y&Fx8{K6Q7xzS(Zc^<8=;NSsg{$IJYE_~t zS!GsbtMXLEs#;Z-su!!{%T>dwi&U41<=?B8t3%XLYIr4z<*Gu%`KWoP<|Q8AByrvkClIley{$3{vQ1U`h)r>_0Q>F zMEQR=958mH{KLl2Q}if59UYbV3vkili^Y1t5BH>WHM-CHgoUFs0EY2T=wbRM`pC1W zrzhxRIC<-#(dS0*8)f@@{DHq?gGXV9ed6!fp-;L$x$xMJ$98=ZeXQ?T9|#dB!Li!_ z_a1wI#XEMn_k&vVRIQr4i%?OtQ+5rPc+mBk0MiTNNJ@TtJ z{Uh+RI`Z<714m>Zw0+R}-tqT4-nn7IgYwy)(fAw2OC-E5zr}n&_zu{_fXeYR`Puwj zej$G!RuBj%F@S%De~y2hf1m%voARk4|2F?F{}KNw|2h99|26+D{~iAW|C7K8Awrm7 z63l{CC=`m=R|vloevxr99~ox&brdCvrPz@l!Yj^c`}9d`P|!0{P#C zAYrYLApA)4X*=zJUD7laICe>m0o~CH z6NIOP0AaOI&i^RX3cW(TFj@Gs@QCoe@F+h=NEX%zQNlstcj0m2JpQlz>%vdMHsN9Z zXW=;i8b4P^;Xgoo9O3^-!axQ6NHq3&SAwrmlLAmY3r2h|X(Jt^3mzA1$c1E>tS8r! zZNegQHF<&jnLIbSa%qXVXRWN_q+C#ZEdI{(&#R z%l$F>Hho4|O#j9S)Xfo&!w%XP~pf%m)seD`)Thovfn= z$u9aD*+<_eH`0&E&GZ<#i5?|)&@afn^jqxM{tdZ{enlRmzreEWU*u8xGx-~Q`Cf-K z`ZOej!(1r5sRGEG*x~0rPDwuI;=wn53GaZfITQH`JLUaEO3B0IV)`O^k@JDSc^YJ; z8Dt*(r1^x)8k2`QUwak-78$nNM#ci|CzX1-*}~ zg#5FbK18mg@8HDQcgb%0Cb^QnK@QNrliTQL!BB#Z~mzH1+un2oTEP=eR4E)$?$T4%k8_fexHJ{|r1>hm)LPxn9I>IZ+1@w9F zYJVXc=;LG)eUfa(j$B*lpK)s86XX)`>zCm)#2w(ncG6eLrQpk+huruVPDT!Ka`H0g z2d{U3@+vn0=gIk!zeAS%gj2(lPfI@M5`|)+RHzV2gi0Y^P{GD9T8I(iKoygOM8N=y zL%r~f@GLA4FBIkrbA@2g%nO8h!W=;^_zEpTo6smU2@{2Gp;ee9v62io1l1g-eAUuvgqDY!(&@Glea}N@0a?lW?nWvv8Ymi*UPeK)6HLD_r{!9o=*n z+q{--Lw101MIuA=5bmXG3VQL7=bD{=@}lRO$BB?{d#(kHVYW*G`&I_I&+NJOfnKiC zbM1?Bj;DLB{l;BSfGoYz^E_bObs$xv-la5ypqn`2xsHac&b()l7Z|tz_@+wNl=$+{ zHHgo#GXr1bx#l6`w|cIbjjacND#N$Ep65O!grDiTmLnYxK9JSJ`1PLWen@Ah=h`3Z ziPw3qCy)&O&z@_XWeHhJ!Xc0(3sIiyAjodTo@=ZpfQOdigpfqxa?f=rF$<4)uETI9 z=I5Sk1!1$D6d!sESqyXn^T>Qyw=M+bm`0{!#!*8)$bc<+F0NYf1UfTFRPzvH7Oq-I zKbea?an+;|Pv()Cct0q9sgx(jXhVEfI#5|)$!f}*i}%~Jr_D;KCY595Vs*`GN2;5DR}1D-YL8)% zF1F7Mv9`-aj9BZn;A$#TUMAK9YdtB(Y=ncrpS9i)I1Du~nTwE>NG-Mti`#;lszulU z%vfACcoJ4tmU8V$DWu}bsr8YG`d}dSoHOO?#ipMu1%5Nc)`GPF@=^n1seh>PhUM$R znH}tUG3rP?)|;gIT7R1CbJQ;7M_NhX~6fj|D$hCZOLY&IR{wGML%L~ zD2);}GG^de8D!?g$g^Lp9qApbC+VsVUrfQ>5~R?A>p8eCMtZaH)Ek~i!TY3Gtd&8G zx3lAt=4pF|)ycj31ioo!#O_@2PhftoSSOE(bHyBA{&U4UwQkN8*IP^HigS9Mo-6i= z`mRD-_M`UMNI6lDlBADGZ1ft8_%r-L+d#m-|AlgtrvjBxA1Wu$Q(x@u<4-5h02&Bh zGKkzxgJ}qM$qWORsUUY?-VX2 zrJ-7QS?g#b)l&mCLRb7EO`^%<5KW<}G>w{RI?bS&Gz(`YCX-*V9fXzIXg0Oe9B@~; zG>LRbwVp;+&ybL@}8m%B^S_!>x6=e1rS_=-no;J`% zrn3Z(oe4g@1^oI%+9paJo#3;lqE=p`-Q=&dhfboskVGbf^PWZra2m-J@&+Bkrft)} z*Uz9c=`3*fbN(OFz630)>+JvDbMFky3=9JdI}9)&0wNB>s=+#BsMiRwbt6irp6d+tWB*k##-a&k|-`wgNlmaipVDO|DE?PGZ_0l z-}C(S;oRSSxAUHTxpVFntHn9uT+EPuiTUSWFx#)iENTnZ4cHULtZ9Mxg1At8QCuYc z7_;>}@h8|Hy(s=v=%V$_QoK!dnfMCk?fK$=i7T+)St^>@oT*0@i*dH@g2dhBjQo<7*_XRi^s(iSkIgkPl>0+ zGgzUWr8U~OVuN@=yeMA6{>Wvl*sfxi=L`h`(lsi!NT=w_7VMVN17|lvpDZ`sV*rRMXdkj0LW3Vs(BzqiRUVDO#Vx!p@ z_9ULNEkp}_5l_f}%*L^&a1!VH>}foke2Gn9Kg8bQBK9ns$ev@9@XmrMtb$Etm24WD z&Sv1b+3(ma;V0~QR>fuuAEIBrj=uCMoa33p=HlB1HEbTM#j~mT_(j4c_JYvO7P1%F zBKBkKRQ@-1(!b9Zv!Ajh>}PB#dl@^HPYa9LE5Z`CT=*HDUH+7Js#-FoS*)P~C z_8NN~XMZr0iK||#n!N2v$yeP%V)5b`;D-Zt;Ok~b$BoEyZFA%vur)q zR=>yTtv|4h?2l{{`xASQz0WqYKjXyJU)UD*A-)mw5!=Q-X4~0c*$(WT?PPz$4&vXj zhxQM)oBfmRVV|+R>~rj>eZls#f3XAjTEap0B|C)ASsun)vyZZ4IIZ+GJI+qvZQm#H zCaKfx4Bm}*mYu^Hrf*pTyTC58ORN#^6xl8OlU-p~Srgu#*34Sib#?=%oNlq(><-?y zd>3yZyN7o&cd$;@g?A#~XFbe=T|7}@l7!t%1NQU$B!9^ynI#MMHUlLq_V;Yq;R}&M zCA$}W2Ns&hBMEd5kkBK=HSD!nW% zlU|XQOaCRUkp5d*i4$S3O23d+Nv}z-OTUy>OTUupq&K8DaZYTF^lRyD={M3^=^bgE z^jqm&>37n4oF3aC{XyC&{ZZP49pLw*_odC!pQR6QrfiG!p|n-{2zzKBOWUQtN;{-a zq@B{=uuJ@R?56!g+AaN4+9Q32eYMZAv-X9wU;3AH0O!ySN?%Hcr2oMl4)%1UW71dB z*V1vEPOFzrVn63JcHq9juFg5^w0$czNEf7w(k1NMti!(BW$fI9Vuw3S2**xOBzAnF zg%~{jydqu2j@&iu$+cit4*OctE$qzQky@p@Qk!&7YL_~&-_<2`OZTN7$s-H0h+R8L zmSux%q!lRk#;_X|AO~VUi?2UbPZ-v?oL&LO;yQB59A40K|g~P^hEhN?4?b{`!4V+6k!scU=EX~$QANbocfz4 zPnT!NGX)z~ZCizpBS-WMUSz+T--`R7>W|3X+M zuM+-_w{g5CzbSj=WC(E!OeBlh@;9 z;s*H-@5&VUH@)r3+d8_=9yiNXC-Y)+YyP=<8m+^1dTl_or7XKmd#y;d8`7_}Y zzDFVNlfRJn%m0!O;7sH}>?s|>-qK<0B^|}y(pT73I*#3?dif-tR+)qq_)5vog_Xjq z!pnSjNqA1Uf@hjbg+173I*VPV^D^IUx*%VaFUgJaW%-JHRc?~6$<1<$d|keQUE*8V zpSpvc&AYU3)sDTYPV9JgW5=pT_TWUOh+SmKAR7$WOZF3131_fc{snel>xFOeOz<1* z#h$`$?0M`4o3I~j!A^3Z!DGi=kYVU=$TSQvxC~haw;|h*W5_k+8S)JUI2k&~@Q9(% zFxW5zZ;BgcC^8fqN{mHQYAP30`pvJNnN?O)rd*5j)T>Lqy47oraxK!XCF;E@UsRyp z7xAmh?NarNv@6*G-R)O2dvZn1oNB+KIn(DL z6*Frp=Fgs1Rk_eyGIdVvRO5+t2|4&mgE>qD<;DrE;Vx|6E~~OrTXWhbh)z1 zmH4wta*bt_Y-1TIDPCu((#2{nl&GmHQN1rwbD@M%k5uHC%X+z!RaUHKK(Uqqd46ST zLMb&_s>!SzzcMA^iu(mx#+Mi$C66qRdK0aPxZUPQd&TB*=TPFa%G@q__>{>tN-Eq% zmf^j1A&|o!8)1A*FKPI$I_lQaN)Bh0l@J~LBEQE}#~)K1H9l55vubLkWkhe|uB>cI zlPjwv-#C&A&Nx!fRB(&sk#Jk_O&3*T&sJm2(aNRBI7%@w%Ev@buA-eo`JPpllP8r` zPgioKq*&z@7)R^L%PK2Xi>y?Qs8lVwQq@GM;- zQ)gDzRL+|@&-nf6HIo-qT7KYVDtcPuEYC6ifJ78>xtjagYF3pO7$=Z<^MqdQG^=c& z+gz48?}d3YGF`5$Afku@9kyY_;q4PndYTV3ib9qIxVP-{L}AkYUiU9%ujgX zSRDo_b<5|zw@WtOnP=tZ=>oRLD51}d$gG-EQ8j90sS(EJxw0*qA;PK=-~io#c7wN> zlLqHxmE{)cH>f9W{RSLl9CVD2@=K28QL0ZW*6rume0ob_DiONLQcjS!-_)7B)1(`} z$42mLZrFoKBB~LNq1-G*lwK99K7ej3dZ4VVMZL<&Qsga<0CmJ8BLV8Lr&^>+O4z7W z6pK$Ku_$8{v271l4r}j<5%fT%u=rGn0N=_GpbkGuq~*$GBwZ{@FRo&5O;GYQyI5sV zm!;?_mC86u;V3=0iXT+Z$yFIXNmeSNUUztYE4>_vdg~4t#_uWWO1GwBZybT&tEikh zv#M&cR}(!O-5AFy(n`nWZx3p0SeBXRP#*&Qhg|)#R3_ z?w6=;mZ-@sp=83{Tyv!!uM%Ogn$lt|rTKo9zCC)DYBDR=uhKhX!c2>zc4d_+S9k6} z<8<=OGToa>9)&B*oo$}pD^|>$)IQstCC~8AoQf?odh4Q>$TiN?%M}Swy>*wHW>Vib zZtUYqI<$#M2}wY*n9ZE!H&ac@Omz-5Qy-38?(BTatlrUKwv-?6{Pdd2%Id1g)l+9y z7^|oNja7P{L!?BmLTZ#)vnh8l_fq}J)}}+*#%e{i+DA2qMl4LAvC@h z;~YJ~m`JH5S*k`*s+MS}YNAwe!0l3t!L1f!wpIhw8d6ejnByJnOI0_^G;69$AQr&^!L6$y-cYNiZS zYkZ#SdS1D4zFr~_QzI|auK9-f-kdE{qgU2NS!HEfPOHYs$^++rH)nYx^Uc|OXsOev z`8181uh-kGviyO@7br6?Mk5-r}N=F%QBrZQpHpT zgl#^31`*WxLpI$6d3z6?iJl>gZj4p@;oH+5W~b9E+J0ND&zh_SCJ_h zsNaBtQI@Q#qb#rygW5b-o@Fv+fr|Cy|+6}p@-B7}`8w|BDPM}Du}NMg=d+pPLp)ki@XlPf_kHlbc((@ z+|iAh#yo;S-rL8(z>m5xKj59;M2 zH~0hv<%^3&Y2bvF;>|-Mv`Em@72y{Z;TP2+1>gFjH=A%PjeBl}R6&2W(zNHM8SwYq zObV-{dC%3F`MpvD{iZ2bGgYmDm`7H~s4^8Mt-=7Q)ulp_;gv;&ysBsrWuig&tU*+R z3Tv+_qH=fz(IBda2E7J()zGL&H$kOZR0Q}`3QdGZZ_+A+M${ZpDno@?uL^)lM+Gf^ zwHj0e`c?uMQ;V{~Fu9V)uTF~qNAbPZJ>81 z#f?oxV63d2fs09*SSg5aN&zW3@A77pODhBn;;4ERGI!CWOxy@*WoA+)Ub>AeZo^vX z+h|qm#hOWM=Xi^u#9OWaWQOKFx!4pl@HTddGX} z3YnLPAD`^us+5A8du{cZ@_9MjM|ma!3wrFT%6aoRHYuYIQ0Rl>5OKAVvd;WYp&)m8 zssd*EVJ61E3bQ)PB&fFrlQP!?QRYI{ zUSnc~{5+TDqbBB@fI+^~POgK_q{5^PYCu}nt2!9aD*Tk;Om&#dC|uo5WgvsLI*e7w zv!SOXFozeHj4rvtq;<$Zy`$lsu_mrnR7_vF!$!vcSki#^;7G;E95!gnC9q> zTbbK}W}Wk$LgKSs-A<{N)u4KtOR3k3Qmsm(YX^p)xS+KZIOc8WW!%24*Q8ms1NTTwojU8*bG;^WHRt#o8UiL|8VzLi znNSl$>q^zk_WF{e`GRC~s$mZA$mF>Q{A>CAl;8b`2fP8Aw6PSZcaigPl!_8FUCphm zvRo}IK=tvP$0^U__LS+ls+v`n$IHny&(}1F+%#tFdZ%vnnZ2v3u8h};B?FULEuHYon~QmrqWRp)e7(*p zvwzU6^S@JPVUs5{VEzM@4^@V;V7^w({ArN@QKjU{ys0errjklu74SM$#+|bDS~jS5 z5WA8LW$~a}&sWl^_eK7+iU8ZBEF`>w+zojF4_@GGWnlqi^;uisHb7rqkd&4vc_D7) z7dcIvLseLP79L#FXF{c>y+Es~S!LyvNy>5r!b;N1wW7g_!dvX+y;qumey_Fz{T|uRqtNfc=Sib`6Ym{4 z%CYJLsCw%gZ%%0Mp_D`vUuZm~`;cCPsxG3efs{xhA2K3x!CW&9Q3Uxi(3P!hZMm|G zX~V&tUqBoFxYB|dSK6AvmDYo}(&GYLX}yapZDQa`>qcA)Op7XO=4A4~k5JV#BOWPc z;!^v<99+=LFLTpGitd&^-MD0Pxj`_o94$2 z@+-H9t4T2lS}{gkWc&l7y+%b7oT# z2=F9upeBJsB?$`RNgz3%1d>ydKu}2n2~rXWQ4*@>&#s|32=F*KP~+fGi9 zkem_+K_w0nq&PU@Ex_%{r+jg_l&w!(sm9}~)Oc6EQsZ6uN{x5rD>dGguhe)~zM3!j zwAX_HK+(&}%`u^XDc%_YRIm!3XW{im#R24^dZV-#Os06nz=fxm#%tbQFrrbF;8&hf z@Oe7HS5gT;X1E}w5VOe2g%wqkXHT8;LUm@vZ) zK9(6%Nh;<68h@r#QZ$Zu;ScBiCl&NKZj++I8%V{R*|R4r0Gn?BNd4@Y6?2rEKorT8 z%9?pIr*oE4HU+d#hB`;jJ5g5ZeSVpB?&KQm%T`XSCC7s3wuU+ko>c1;bnlg3Hr;yJ zHh%wnCDszEey~dLict6HDk?Qkn>l?x@&-?Y6`zzHKWwC^?I|nEGULU$Gtg!6h-rHP zYII|%tmb+uo~`4M>GSbeq;fW2%%~28>LjlVBsLGvO(E+mMkKG8NhxL&e4tN(V{WAt zH#k8WrlOx$5&S?wlEj?aO3a`rF3d1~G+((_BEry31eF^|HySJI0c%>7As-Yg@WDzt zWGlCg?xac&@0 zrrBsewUcXJl&4~gS1y~6k%(+5EgL_kD3z;b)=XB3YDDS)=qk!H&aIpWeU+_@gD&L( z2iAFN{1`WRt3mXZiupCDiWM&co2Jge5Jj<>0Q*IYs z(VwaPSAm*fJe;3gQ#0p<`EwQdf%(|D!#x$MN-8iBiB}qxGEkxLYM@f`6v~u2wKEhn zf9_OOTA5tnF`g1-mhw9s%)1qzb4$vNd9<-WzIr{qdaJH z=c5wr1w!>1tYX@rxlG93pihXa~ zzBeVlH+ggu(kp4c{BoZgEG+f00M7Z|C}!kQPmLUD8a}gn8XtdaOvC5Rr!KU{G+L*P z)@Xs$(P?z$PUBgXo>ylgakD4;++k?d?o?4t6I~@leeNKt-C30ZP1jV0G4CynWcsZl ztm`Vm-dhsZZbN+rUd@2rckI=OzJi)^FF}nMQd2p7ChtTmr)nhY)XCGaA)ws`E4O@e zK+ENs)l(}M@<(Z5-d~8|>%k?DmyOHB4qUXC%70pr>B9>1{_4Po8ttWiN5T8M1YZR& z^+APDR7>8Ycw=?thdfBjb#XBaC%L*9e#U8|AxVn{giv*i@)e5eHAdTV;@^d731;H#5Z17dK>rwTr4@1R;&V{;v8%w>a28m^xqxLjON?KL zBm6@C0%5(3FSX-W#{T>?F8&0(K|#Q8s|B1|6$PXIYi0aa8NM;dH{#tE9)pp*XYytI zA{hsGa5jv;lcE5BcxwlKmn;a^@YZr-|mr}b{zz4ngIuI~Fi9wx~Kqo2RYYzYXo z28G%qB4Zu#&ZN{#ccFl{Yr&SdV)fdMoBzD!EwU zk|oWMEjT399u^T96%*%3NKEdNlG-n$eT%KFO#;2ALefLYz_M;ZCk>}x!dn;_uuZ? z{@7oi+c9Lv6FZ*SG3gWelcZ0QKS}u{^OGT;EZTW*XTP1Uosa*`^0xthbNwxU*X(~R z{Kt#Cn|7ObTX%=-PT4*6pNIZAd(XH%3qHH}S?6cLpGEDp?oHWSw0G3!t)It!p7?pc z&nJHV+!sk-JoUxX`_Jz0*>B%p@~?~kavf+qaN|Juf!G6y2T~4X9w<35^50?qPCv*F z1|Ez&7=1AP;OH;E{L=r+pf97o%>Q!ap+kqR9=dj@`B3X2+oA0Li8yRN9CA43aPpDc zM*@xn9|=7Yb|mVE<4F9G0Y@G`GU{m8(V(M|NBbWwJX&(B^Vt1k0mp)l1s@ALHt^W6 zV^16#ee8!{g?ttD)r7BGzV>`AejWIA(AUvlr+nT2_@EP)PFz0GbRytH$cfMs_7hIXs}HY_s86mRSpUe$Kb~wjdHoKPdQE{p6YvQ#Hop=rkuWY z`p#+VY1?W0>6p{Wr&CX-o$hyf!0G(c1*Zp{Euyk9{-Ztaw&B zYdq_J)^yf<)^axbtmACP*#T#>&gPvRc=nOAg=Z(8yK?U4x!dR1Ir*INocUbTxtMeD z=Q7U?IOjSy=-eaczdV2FeAjuu^MU6>&qtmg`t4WW9{)D@Tl=@M-zI%K@Y_eeEpOP_ z@J&NQLsLUb!>xw4hIWVH>Imdm!wA(tJO$6o%ym49DpxYB&3^-BAd&MU?%epiyOq+IEL zCFjbJE5ojgzq+$wYR}bxtHD=8uR5-#Ty-@yH{EQy)pWb5t*N_-HAzi! zlcCAjxPFYqzdR*W_#d*UZ;!*Fvs^UyHn!e69aA zSM$#1L(TQg4b9EXt<7!CUCnZ{q1o8%*KBGwH(Qzmnyt-2&GzQ7=BQ>zb9}R_Ik)9x z%juRYEiEl=EuAf0E!{2mTRbg>7GsNFi+_u$#oS_PiEfE)No;XmKX?85^{(sUb#`68 zZn|#19&p`y-F`jddffGXH%{MZz0r2#-i?kM(hd2B;fCK0(~aO8_8Unz(r&nKJa*%I zH^05vdb9mz_syQ0hMT6F=9>XGtv78q2i$buEV@~KtNzycTg|s_-D+QDN=G&Ir0k?y0+i!>8 zj=!CFyU*>k+nKkE?p(Oja;NRiy*ur9dhW10@*TfB<~!CqVRypsMBItI6Llx~PTC#U z9rvA_)Tt+77kV zw>7jix7}=OYZKf2+e~fdHhj~o&Dv&b3vR;~wA$=#VQmp@k!?|Jj5#wb1(K@%DvQkY4@`4{jhys`@!}@?Z?_Lv|nz& z(SEbNy}hHov%RamyS=AfY&W*!yJYR=_JH=lc58cRyS+WEJ*wT&p4gt;-ml%=p50#9 zKDhnS_KJ=_bsX=w*m1j~qocE8dj?i4$vPGhHkr>WE2 zY3U5?v~~t{+B$S87*!m#fR&mEAR}>$&bj-Iu%Dy4$<)KJ{+a zEq5Ebjotp;=59+jzIoOi(H+wr-<{aquRF7Q(f#B1oA2Mb-+I6Oe#ia$_j~Re?)%+0 z-8bJ4yl=g4yB~Vreji^SyB~Q!`hM*FxciR#@%Iz%C*SXTf53hB{qmkqdJgs+>8bB& z?77m@)N`%pX3y=OyFG0^?LA#R_j`JJJUw`SQjgqY?D6aI@4+|UdIEc_JwZLeJ)u4J zp75T?o~WMa9!F1nPhyX=C%Gr3C!^;P&qmL7&%Zq%dMotr=bop-)9LB*bbEU6nS0TLlYSn9$LR6%_T#rA3>EEkgg%mv&L`lYd_LMz9h=?HWjOP~YKX>`mPN1SoMIS&Vz%W;tMf_el}Ir^v^eC$#W z!2a)tU-{8je(=>v$6fQ~x8>j9sM`q~TvLvvb>P6HjDwOU9Fh#g5y@aW9H||RjKPt} zMD;i%KM0v2KWTX1aL{nnaLjPR&|tV^Xf)h3+%nuT+%>ctIt|@8%;rb0FH;Vxh2Vf% z6pp6F;ZRx<9Y#wx3^E=wer>#Jykop;^ca~DAEh^1@F~JzV~8=-7-5VuM&q-DamGYr zUt_8<&6sY?G!8JjjRXDu?RU!Wyx%3iX1^PLxBTw+$pWNqz==w9w>d@w4I6 zg?7Ivzi7W$KZjqEUmw2|zf`|;zkYrL{bu`L@^ALP<=^Vx=HKb>@i+MU`JCD(*;wLsoB(Gx@BrJwVOIk-6o^S&tx(Mn1W2f_|RdP zDZ&(GiZ;dKlZP%-x%qSRm*zv}W9E8ugSpAvY`$r}Wo|RyGq;<2%tn0r&|(fU2b)98 zc5@g$gBWR!GRK%5=45kUbE?^GE;NrakFo5soV1*>v{-IiM2o-0Y_V8EEp|(|#bHUX zBw6}e`djiX#g_2_Cj%}7v;Lbr^|7FOd?4{wP-l=i zC@9DgloXU4loFI0v&94-4n7%tC%7%xFW7?5Cpv zQH9aX(ecq~(e9Yom>j%2;AC7!T#uvK(dIDY6^`K!e2W~faV(7YPY6f|PDo5hPUx4A zooGqy>+ItkkaQyHYEn{iY@f@0ZuM>Io0s}^sy#I_H8r&`bxdk?ntxhQT2fj{T3Y(~ zenT>@XM|=%W+Y_v?H|=YrGGj;8CagVC)1NDWhM`}=VGoQSyEQNtf6kZ+wGp3eLlN1 zrzxj3w=4I4Zb06Z{O0_Wd{;r&pwWfq!mz@^!A-;ZmlT$lk7#>5f6T5ig=5A%IiYIy z!a^LurL{=UUQdCi%Ci_pPdSBFB%ZSAp2}FykIbGQ&GpopaYC3guxt^wtQ3Ctn(%U+ z@as2)m)8iJ{~-MCkHX3igttBvUfU`x-7frTyRhaHVcDm`{@uc__6V=<71n$%)a?`g z`%7Wlm%`5v2`?WOUO6nRJSDt!vFFb%J-@pv{Jc&0L#ObrD7-vCSm74lt;E_9>qEXY z>bdH%c?RMC9N`syk3Tckhmis+Egv~fD5#oTTaA^XAKsEPRmj7+v>_T-x9g{s-l&wd_nLYb;s5%HcInebdBn_I5M>1P|Ruj5WZt8u5GI(TEk zs#JpQDR@7W;t3DAN8DGw+Ld6@01W}_am6;Li#RcjgSYS-aj;Cmz_v(3fHw+9@k)VB zII~Te{!~3(#er`s_g~1kPQiZ>EBSEOGf9nHHd#)#LjHeWmBW8^m3{?l_8?lThYRMUQ%P5n zL-_xsHrNSCPDsv#Ei=7~VwUn23cP!Qzj@+wo*zjY1p%=s*GT0G8WUt#zlc46M(hLx z!aA1rIGxP%+@917+hdXi{F#&O$&TdIWOs65ayjl_O`d@NQ9eHFOoKm08vx&t9R|3Cjl`^X|soL2-fX^eWEAh6^D1%oam+yBE~GSL~{OTc$; z1nl07!*x7r==bo3sYQ4*(@*f;WxT}$`#E>{eh#J(*w4W`X0V@wx6G`-J7HeJJ7Kot z{k%KaH1R#`cEXjFkO**$#Lq7KK4pqf6r>VL+&G`_NgdHkH zgQxgG-J}$amV^yDFIUyTo0T4MRsFN%?AUQka#+l z^;xYUuf2z%K1jLh0aD&)OP`$xy$Gr7v$qd&s1HZ=9Q8YzV(!zH;z&sqgcLK+TkNNP zs?oe$Ld{&RH!VeYKo|B>?3|V%r?^uJQ_54uq)dg?2`LK^a#YPxF-o=gY%xkRW1NmA zYKU_)W^iuGQeRritDMHUDQ}W)jmE_npO^Gs*}ty;y1tPbN_kg9UT(^JzO8aRQk@)>Q!IlxKhKo<-V`?eY^j<^u=gBwS6}rM0-#X+JcHW zcRX>?0(@w*i00MhXa%QboJSnT`?dn@&`{qGG?cMNLw$8FY3v~luP#RiIjwJRT9Zyw zP!Ar5_@{;-jKyDyfPcle@z-5qOnusmQeEGrac=4$O)j-mN256Ezp4M${=53`gG`-{ zrfA5ATcy!b7ifrcQ&*Di5{;I+PD4Jrx@8{?o#xd@-Su5vA1hu@xBjnkx|P&@nq2B( zr0-PfMTFM{AuS*+93j(?mzLVD(Y)NW1klpcauANi->S4V2&d`DOBO;1kGL}=4c8hRjwmZo#lx;0w5 zQAfH)dbCE%$U~hTmVtJqw3+k*AZjy&sLc@KZDG8QYSUNiavUjg94T@fO~Rid$7!=j zgCj3(1<|&E_WwOf_H$t5aQ7>Ot!5pyX~@g%hqk21^)u_pt|Q$_zf_&J6zJ7{Zz3$$ z(HI?hxf68SR2^08XrYe08t;PkUcYS!kM=v$Z-2j={W|-7sv|FrqejlnkTa~%enmrfm_;t6vC(RNAr8{xZv#tGSRj&=VmT~%wZj!(h*v?%H^~K zlJnB8>)dt?WilNF=!nxuH`7ad7)sYQ!ga*`Q*^!DoL*e=K6A)JXk&HSGde;~qogPk zeMIJ(%=HLYX~>0|lDRo^yC9UHC6`2(B;)>oj&^H^b2GovxGFlUaWm0dD7piZ2NVym z4Ja5eGV?Yxx(A@A@X{VY(OiybnH)i~SinDDG@u^$%>&Tt28`FyEFDeK5vSFX++v-! zLPwmok+@#k!_XF8W3`Uf>WKTISny6S!rshHI%V=Z)=Eou=!$-t?up-gUi)duXT#z9@*- zV%Jdt{|J3bXun3wlC!L?n_14R4A&VAd1-_iIrjltC#NAFt=yWz&59(sysTkaBeKS2 zP0XqRXGRwKIYKXSRFIB4%KL(F9VO_9b6@8d&%M}dKa25FYO`79jj|J>8MplJsKhp$evfj?XPhK;XfeK_>WvA zMY;z*sZ7!A;dfD5%Du>&8(yU6i+hx=@wASn=%`9Z>)_WWUc&CJ_}k~+g&KB{&|!q9 z@OROD9W@x`p|lToyN0rvj=;?h;B`D3Wv<9^4W-_Db%~Zka$I&<_GW}AUlk44(O4a! zA6L0tV;X79?L}Kev^BkG>vfufwu8Gn`vAhP@OL)*iXh|^7i=zAQ?NT{eD>|^?i^!| zEhpMb<0vPYbBVT{P^P9q+yaf8Gm_BGoQ9m{oHkfqtfNUfnx&()I;zzX*IhxnUhe8% z+y^cH?;3}6U9UZ_e;ak~79DxHdv%)EO1(zQMNP>y=h_iE@HZwGePnK`j@&vzzpZl1 zb%b6tw>lU7X6~o>JCoap5Iv!a(Eq9EO&z_fBd+ltX`ruD<-At*>$Ia9%8NuzwC14) z%e|?iP7USBI^uFpE|-^wKV=LU1~f5m2Eq|K8mFVzfj-FFfv`qLFX;$@HI|18mp`Tg8kAp}KPvxe zAhg;7RuBL*MMqUSTA(A)^0$(lm$pRbuGCSTj@Ic2G*#D2dl=fKYi!aH{8M$k+t zwEcnu1^)@5j{x^8XxuL7MtD|7S2Q%xC1EUeH#ox|>dl4?yQL>IQb;N14BeLWfCM%Cvby}p3 zbWa~~>a>i9Ag|moojVcp?C~X&aQ_niHazkH!WlZM(UF(S(Q=Y|UFW{7qb8v9kMtni zfxkVE97M=z$BE|Ua@5LcB_lNtyr|G$qZNi|$cG#I5L${xD|BgyYZMORX)7$%XoaIR zN*yV*Q-v1`_Z7B-vsFi48b`Z`+naWn z)1Y&ahq!X?bxs@11_uldADjS`gTM5_!&Nkd&@$YuAG{giSRFm1BhH;h+_}AIi-^X# zs|c;pxI@+s**Ij@klG=8hZJaN@OBM(xq}aAw86W9j9kNq=H+_PSGvYo9bM7UZ4FTz zWY6otkmz1q@^5f=FPfrZ(`jCfVx8tqn-?YP+{}j{ubda@`7*?7d8Dp8UPqI3v{(>^ ztl*_OWHql>94YmWXx#FaAv=fcElv=Gp~6r*udZboli9}VY22Q zM_leOm*e%XIJ`Iq_ZM|^T|=B()XurZ%$HUiKr~&KtfUhSy;o2xe#|BB<@Juh$A^arMh1p(Bo23 zVtu~?;*Lz2kg<=@^pr7)TL}$LDNp=>Bb2yu_+OCMZCu9Qr{)-uSwHW-%Om#X?S9WzEF^)8Lm9KtJ@PC9a9T!fC875hX_4C-Eg}p0--(QXKg5 zs?b-lHyc*gCbWV(0bH~Jpr1f@79oU*(+K?#2t5M0i^UQ6w)ax5;Ym%L5WfUyHZQV6ZYr$rQ6ZbBPb9w+-JH1`YP zTvnGrZ9q&1I!pc~2toq-C2+qdO-MK%e+p&|MIZ+<@-NBx5|M{R*DWh<72jKOtUf0lZ5|m_#1r0V#de ze4wX^HW_l`2u<*WCX9*u3jU!+B#b25XwZho?S?&+dBPZ?&4LGm;@$;q1ax7Ob3M-{ zxZ_qsm&cJwwAaKb3F(y2Ga#2jv=#W(5?N-5Ga-^_e}J*AHmW?~~pY^bL=KAo37zCI?zXArks zPEHz?`Znas<%Fct)Hi|pc+%qSF}z<+@`T3QV$K6$jEq-uyj*%RDK;*a(>$q;o*3@^ zuLZ%;N%l~m9o-I|L#X49+eAAJbe&Kot5eEXtVBr{6D?D!NZTH}2^xsaaWJL^C=-_V z$2>#m*|aq=Pje&|r>%1E{F@@BB&~Juc&A9Cl2$u-u1%4WlU6u*9}Ak}J;xS~U}bYm zxGFcx!Q+@B{U~XYV*{bsr16e-34NY4(yQF|w!H#i`;gEB%C5~!GDMv`ruxLWk_Z$-(g`6fWaEx*AvBx1LI?5e6KygyJ z!{x~1G+1_1Y^Wg)rRBNdUtSFAf#{Yh92qeggr0PS$CPsfxe(HD!&9q*5U)83BO=52 zi*~}j#~9%7kG{zft3zE^XoKTgqj_7HEj<}`F`Czd+0yj5^U-?<<;ES4z5>MS^8wQ6 z7KFI{9I>UI_u}@(9RRl&8b_mFCsYx)Kl)Xmv7XSltx@Au?zZR&K(jrmaT}s|Dp8-~ zHj=$!_@b27Y|pcCtD_vSJR00p#GNk z+_=0bjOX~;U|k%wJT^p{5EmPTn#l7sj`}(YZ3ev+x10%GTNF|bR1s&4S^+cwmhDl~ z2t66w9))_!;-%@at+Aa%%Z+V|TFDWsi)|(=s8_L9IFkART_hStu-Nk)@fI3;hR|C; z^&DZWm>zp7iuaf=f<|S;`k(AZBS_5rPj+)m;i7RSeKk39t| zTcNR)Xd|HUK@`t%9>;q`+YO|Y)^%vC=WU-YhsFlZWy`^RnEp*t=z9B7PZZR`;8Ilt%g*g@gEW|zZC9?^1p{u1jB=QaD+ zkW<>$ThMhyAP>Yp!g2!1y#{{b$QG`#y?>^!*s zTthkqB$F;iQ*H#h==EdTIAV){ZWC>R#%0N@E`~~4TmUZB1M!?-j5!{22D%Hx5$M^u zl_~7Uj`1;H!AcD@4l6XYZlzZ~D_COoP;X{ud!27aSfOFZ{Q1=g8%I)^b1BWQ?9yA# zg*3kkW52@*AKp^zY?E_pcq^gJ&IyS-2)*MR6VAsxyC*fKHf#ye8l8pVJ2}Gq-yOc1 zXm2=EDWC0_qoel%Em^Y0jEWgfXlqPq%m|=Rwlk(Y9Bn~FF2<10_-3dSGbD!Bh}~jq z%rMg3&GyFRg^vMk3*_9K#y$bcOhW4sp%=T0td&(+c28(HG~kY858*4h(c2t6*OJ)Y=v`sx1%S3i zZw=#hD2dfYZ=#$G^`u3wwxPzx0l2=N3TraBSaE=CwfU(CZP|b=Y~xJ%0Q34 zE3}hnjnPxWc)2A>LD5f#@wS@8-iRI*#@l(4G$Xn+ftP-g*cv^E^2*6}M(5d^KtoT4 zK9rZyD#0&0llsXpvDL9FhWmoji%ub~g>AF9+Iju6AlD-8X9>MwKX2zP#p20~Hj~^h zJwv07v;rFMiH}|vit-R%Vf$hm6YB}>jXh&uMyM|KD9=gZ6=`nle#d&EHO78wuOeDd z>^3{<0d(Jpeb2rCXc^lXwbhOs7Y2ATqdwrV38OtZQPd+rZddHmsEzO?!4n#_KIA%3 ztk@d0j=XwA5Td9@5`HEvj#?Ybz1j@9S3~vzeF1JA+4}Mp~n0kmW$JI%+EK+l9YCZc+lTFUdg9@U<1b4=F@FO>zZ5;}l<@ zF^0y$bD)hS+BtZNehj+jJgHHoA<5`BCWAXHsvNXO;AwG4EYJ}4VbqY&DL~^PHz>H9 z(6do_!R;JD1LY}5EEFh}>|w4F6B`A4==nFrghV9}+8pB_#qDLFUiHKka3l?hZjG`M z8XJ8x%1kII`h1j;BTs7dnP8ss4AvNZFp|fXff}|alJ^lAs9`%IZ*qhh_CaJbarZ`V zh`a*SkL`?Xi@eB@^oz*m$n%6MA{!!kZR#ieFtR?9*Cw?1Nc5FJyiO{)`?e4mc`))Q z(K@4*{A-8YUXp7UgvgzQib2~>2)#h$)_C+Z!Yingb9LH=c-}r-kb9SC*FBk$YYDvq zjWzM;z41NWy>U~58wqWTdph_!&@4}APd?nV&h)Q~7SDE$g z1=s8t1k0a+YjjKkdJ|kmKL1G&B6}D$? z+9P&>>t^dBu1E6P>1Mx*xEPs0XlKN!NM8Tku=jRQBuA1VVqe6Y#61(Si$-=g`+3Ax z^4TrrL~M%SDR+xa5$lj9t|3Rvjevh*7TXe0gA@ZvFGtLXs7CvU5?do?Ma<=L$b%~G z6$=1TYMl)3RPq$*kC;FRWf6h)1&#IKDy?=s+Z!=FXgjB|O%X$a_7Zw0A}44Wp~i^x zpmiKUH-YQ29|I}#2s5lil0Eb#7@N7}Rv@JpmDtXR!U*2-B;=4gf_pC^hf>3N+9dYJ z@J<}Z<6kq1I}C&~Rhye*vf5sYmKZzt}r1@O48@U|xtJdV2A-tcxS zukGDzTljTr8=()wo2+Q(0%XFeUE&;EYxrSB1MwaVYphd<`$l-Rbs|s>?9C$XTb`le(_%)#@_$K-!>5Gvn)nJk6+SD5qx(P; zVxYlKxQ0_%3V#L~8-o@qRCz+fM+NKv`Y)aPUr6P!fHj;3-QpN0kO#;e6Uk|wXTviC zmH_<)8tE~pvBK}c&4}iC9|IK2*H-xMuYlPGPw&5h?9mP2_UAPx@FhZZ5kn{^`%7gJ zIpG^PO?oRLJ^WpuO!m95E66Rbu_+=vkf*#q+Z+)PnL_BD2o?xmu zJusCcDJc9Rx)Dx$Bm7h#?^XJ1B?zuH?CpR9BzHFK^)TMHGSE{W3it{rNf0nofi4RN zT1L4$gzXJm60jfKA#7XNf`G#uiLGJOdiWR>Hp8+Iw83m=*wlbkK*-~;No3^*o-JX| zgu#2^46IBISW0MH*o1(UKyO2C1eX)ez+O3_pQ1eWSdb20Mqve<#`n1}%i(hGvr}Qz z@ACdC3~duM?7xSlno)N}^jBfYVd*!v?4rHL@*d~1EB4m#YLb)eO&0Wtpq;dzw~Xh=lWPCe%sow$B>O?jI6{xx z_gIFI+!6Z@3vUZ)%w?y!4@TX0?W^r;Kucw9_Bu-}=fc0&xqqw>=vDG`8#Jga#ci;> z*vxC^HgIc++lRT7)-7UvskCm)Sq1wu#C?K!>=Vt0pz#EB$C*C`%JHPxOHI7)J^{;+ zCDdXsH1pc@1ay_sdJ~9BOY8$H#pV^@A`X>X0Nq&9g|t0{&_E!iC+vgVFq<4)19C>< z_VGL$+HJCdh8d~-C8A|XQs{LvY6)`O6WVR&BS|_t9D18_GQyJ`2&Nf3yYk%55RL z{b#|KSC}hg7xzVI7KD%;pA%>>q`c`HUWO<$g;DTtg5MIvF^YLY#`y8pQwC25 z8Ow>*5|Zb~>&qlhs^3H-FY{5bmrC{~!M}84Bxp|{*P{J$xEw5pb1pBf5T*5uVTVJi zi93c}44LYe2e~oqO2~v@-r~o=gE7WNqMZyWH#QJ$QAlC1f>s4z3_eRVAtY7ddQyWA z8{Q_GGsJG>EoF@Kc!=4ES^~>QLIk6tu>r3O;jIF31UDPG#%R_SyxF*tBk5nk>x22| zGFtj=@S5P&gx(5X6k{0z`gSfRGX`?X-E*kdiW9s^p6IMkDB>y&wKHV?0KY$L86mSw|8 z(CnT}TPM-nKxZYCs?gyHwVjd~=dzQwGll?;pnFu&g}przS}nLP+ovk+S=%;=ryMD? zZBb~V)wbET6|@qz%hqJ$Wm&>**v{K{Zj`XkZO3gqZJc({ww_S4ZI2DTfLMw+-j-@P zO|sfPuEk@9UDY`mmP*xzk$+j!baq-nO-Z7YcuYg=wZNrRSWTS44n)}V5WrH>UZ zLyNajJrGObsnTjc6il`yw&l=G0auw5qIa`Vs}<4f*_2lM5x7dL{YVmQ(_~&AAF+$J zXJlSlA0e;CO6NI}oVMXSC&iDTJA~&3`wh?_%9mhRK_3HdFlZUXMd{g;cIhWcHkY&+ zTt8S&k@gWfX^X{^Tuy7T871DU_`!-lS@H9v2HnPPCTNl~s8w1@=<%Q?X#&X|2|6$F zd@-`Npu^HgP7{QngQPnEXg{GaNebF4<$xQ;JV84p7onD*?a~O2Ah%JGW3HgJ3PQYV zMD%~W=L)JLno|;j)-s-QC%YN6THcjm;|yBN8i3F$f~v(jj-adLDOzLD zB(j3`5%i3R-i$?q`!vx;vBN==7|)kcusj~UAV(L2MlwFPMZXqQOmZU;TZULpw3eV$ z##26u9SKTiD}biMUINMe3MiJ)v`yk z?3DFPkP*IY2DhGbg|C3{2eh2ItcMi@+CDrc5exO;vfhJC1VN?JK^yi^`hlhqh9?L*16U}kh6j|jcBO*SZi@zGtxNG>I51C+61DZT&&0=&{l#w)`}jM z<$^YfXwL$TAY=o#Jg^a5l#>;`4p0G5K_Gf2_DkeX4yTDI39E~^nLrspqLN;zX9@Vy zR|rMH!7;1ip?H{w!dMEEC_GB^M+pZ|_%(&cDBMip>l7C9P<)oc4jvkQNc105_(vYH zA5eIKLWWQjsEmZ`oFWe8p>T=9;Y1M$|D1>H4@B`$m_cEG3j6U;P&|Bxa2rCS5KXw5 z!YLG9?TZghM9@7h=+m_ zenNOB(aQ<*8i+k%!jwOvlAkk)9!vDsD2yk{dlZ)Nkku0|rZAX?!p8`WLKNY-6prU1 z+rvXKnZiB@jRMKDa840^#zRTwA>-wXw-ytow1^uCpW~tMHwt%9=;EPFB_&ZR1d^1O zaJob(V%#5z{guK3gm{q%LhOz4P>iH7jKax0H2j{2LMw%{D14jfr+CPyyu|%Puc9!7 z!e=Oqpl~)1MamgLvCN462+`jlJc`1*2=Qhn!at!fkB1_sqhBP-1PZMbK2G!(3ICAr zG=zB9GvQigUf`ift&BzlNuY96T8c>dEXI*c1IbfO5GijN*_U~J!1GETvda|qCCUy8_wrEY zQCp8z7ti9A5W5lp)5;>*b1L?b|PKd>t zM*0cTr<>tBr2dqEr9)#)Z;~0dZAtM?A%6sEj?+*M={lD01#R_Tq+c_`eMt9~DIWdA zr^SMO5uQ#No9cVllBVZ-BPnm^6!mfGd$fPJgQde)nxO{dhjAl0j5$8F`9VAX#qz_Q zDDye#U?iDCPOv5|EFJ1d&RYBozrbJM^~nD9&aiaoc{4nl^oLBXLe4C5&c)y3rTAyl zTa3?{;Z>yjnV~(@LiCo~W%nO*^D#su$ma(*c* zm5gRPnc5tK{g^_TtH^1DHOc9YgUNZu46|;bG;;c)wXkkwkbW4?Hp6zT>GK+LenpzG zD71|nj*HM%@>zQ5JxhB*&tFbH$83oH5W0rBzD4>~Xf@?InnEkcUrEj;oPa^gL@XT| zkG1e}WKVf>E$z|rp`Vhrc24nV$q>CIbQk%P(KADLlHP;Ukp3BZgsE(`x1an2GR3P( zeiZM;d+-6%qknp=e~5MVenS2!X4og)5Sw6Y@|o(LMb2+bKdApDa@ZH%4oiowX6id; z*w!=T(VxS&Gj%u8E4(3=_G)9$R`)YCSo({k*|VW~l$lP>NX$iAK144KF{*eS$Zu&z z>G`2xDXYoPGre{AJ92{Kt_?ZWa4P->Q`w>j`JteNgRy0UO%0z&$)=|N5amB5=MUsO zMcTGE#bfV>t|A@GMX&SPTbU5;9BM`SJjw)1>_<+f=>;vr7!vADz8$Szu&=#8TaLHL z3|)^LN8W>^*@ND1Df6gI@hX$l3~}U!f~&rgYqmPdS5!=y^D!SULOU)hdBJ$g8hTOkt$dVx{gGVK z9{ILiQEE%^B1)%FM$acvI&{)Hubp0&*33BGe|72vC8LX@iyKa;)UNEmYR&a$qia-e zXaA}-uhu-JOG+0jcYOaUuSlsLUEF6vm2&@5svrI@+FRwumCvm-Dy4el`IPFF7FJqV zV`)nD8cSd%({S9NJc|Ee`VbxExerT-$& zf1iIHcAmU6`LETII=fHnbojp$&b9Wpf3}sSYjk*vMxL*k5nXI0YK^FwQLVY&uRh*? zr|b?&p4jTdRwvw0WmDOI{SVjLDr2M2#rIbo3Csj6sf03bw|Es^s zpPT%boEEJgq)r^APh7*gqv&N%i-hF!diS7SpR@1xWl!)Tx*zLhPmWZP(<4>olt^_s z$%``A9Hu|W^v9Y0DAPxIQKk&`zf$Q5OdrnlkxU<>`Jd9CNV|zr-I$)i^xjPG=v8J) zGp1b1l#7|tByz4-EBnF7dGc}HpSN^VOM8{QLbo!zg}+xzY~>%2t^Kdu#_V>HD$37^ zRCPt!og&rU$Jsq1ueqJsY2FF?jAws#M)>FMVD@GHLFKho**)1ESzbQN8x*NYPA%1! zAE_+|>91lJx-YYvXbUdOt{=83%~)z1*7;If=d6)dN7itwL!Rk1$?m{>E3?LGzM5I9 zY>uq)ww_rNZ9TIl*?MNpwe`%JZ0ngd#nv-xx>v>4GizGt1+C{;t?idtGqtw6vU_We zZ?aafr0uNjDy{7|S!*I%r>sr3wprUt=DvlwdolM==B~}Qb||x+BW+u=W-#|`=6;j8 zw`Z-=-0QN|g=@LhSzA=cwyd3g2e&4x*dMH|AIMgG%^HMhqi|%VE6(a2EIV<)mYucC zmYw+4mYwyuEjw!g%Z|`SRao`{mK|Z)RlF){=dD@qu=Ukd$M&qD)Ff8 ztN3bLkNBsy9#Z=%@icQUOU%|5?@7FAchnQr{k^LF9shvVzqZz3d18^)V0o5$r`w!Z zsx?@Wb+MMcIV(}J>~&h_{aLNU=c(RYt@&43^lGhh6}L4p(UzLnqWRV)c1GH3UsgkXD1ffwzz}wd$lcF6IHb~>k~D#9y=3t zv?uo_GSnVh5;v)3EnVHMN;J?`Y)Le>HHeMTUfrEI->a&*x5t)fZCWRKsg8BALOUX2 ztHSl%>O{Kg+ZJ1AtrFW}TNjV9T{~@yGFR9Z#fqt~BQ+r;Nv+r%eZ z+r+2X+?g?JoA`9ALus{5e7e;XpT*vpWi`d;dR4VuOA^`MDz`GxfEH>TuB&Z4U%ho> zqPh0YrbJ8a^Sz17w57`uHp0E1&Vhp_IaXyNlm?}DTA7_sVSeD z22s;Z)MV?iJwAe(x>-$`OQ~s?)f8V+Qd1RbszFV)si{6SHKL{_R#WUrYPyt~no-ke zYSIy@nnrom)K0tO?d-@(bWvM>8SkV$Rh%(Kt+FSf^NiaR*YV)?#0RR5jqx-c50<0j zUpckOnQ8l!`f{l6<9=(jW_#jwv`z=(YJFFn*-EXyFWy+KzdL@uT7OTxImd2GEoW8y zGA-w$e#^9+&GEKc&WCYrsoN6oNPUZA->SZC@n@~S$0Mq5cf5+~+ZKOR^%ccysJ?yi z+E!oYI@R}SyuRvN5^tpXHpH7yUo+~vnEHz2IjZlo*cP?iwse|a6 z#oB5u_r*qsUeFOYSFN}wHeao{C$oyy_3PLywdLN}SZmAJc$S>5^(c5nFSTFxk|DON*utdBjYI(Ee# zRUKPmkE5QfI9;YQN*BDicoZ95PrZt`5on+T&X@!iHH`!S)R@JV@(pK5E zUfL2n_r)~kp+=TiRcr59O|^G}%uUwbN;f5asnrv?47L9<^;`_K$rqU$LfP7u?dl0% zW)`VUiZUlyYxHgD4|Z!acdMm0WggHt_EF}yYVXyth_+!v<}=!cqD(tt_x7uzZLpm7 zTJI`q@6DOdc{RLQZcFBTEn#QoTx*%kd~2D^>DDrtgREuJR$I%YjkA`?yvbT7eXg}k z<`8R{^!e5@nZvAQl%tkWj#@@JYMIOtT7pJ{%xi2kNNcX%T$K5swqaxD4cdlXnYXE* zY|p%tJ)mnPl^LNW?8&gR=C+JhTEf1J7OeNZT7u=&BB!>t0zBR4K_lbF@XgN#L8UNE6|I-=&(;5HM8UNE;s+IPpGybPD{_Dun_^%^Q z8LkW$5DBQ#uCeb+G)qhpp?7zAFMC70SG;ZBi(aw*ZSuZSXWOi&byV|qgldOsdtZjm37z8|)I+^~qG=b0 zF4lLMFA0tGz6segJUZ(c9+N|f&~y5CZRq*XOQAt}p2w-7+q_!Z(h2WvvSw6|tu)K@mo3SC(3E>)@O9lzPv7@SILq`*pmBcRMC=~*pmC%wfM$< z<88_PCfbtwO|m8Tn`}$&H^r9RZ@MkH-z-~lKkK2}`^{&`J#5MS7TS`PV@vK~OID7S z+;533xnH4In{Br1|GoWc*phosVac^wa($NEh$T1iZ`YY6*Z&RLS~O!VE@mw*Wi8sU z79CiNF18k_%UFwEtVI`F3+32a^kOa2S&M{MTi;hKOkbz9Sed>hbhBHPc0gAyThe#x z+S=&?>6Q_rWeOJG_Hd6LlNL$S7*HYs_QEH*qWm~_-TF#pE z#cGky(=EE#mA*nPvM7CxT4ZJVChd!t(zmM>iu$!xD@;k>t5$e2{h%Fh{p<+X-OtnZ zujrSe?YA7AZKYK;o})lpl_>eqm^n3Jw+Hf?7cEqO;D#Uz&2 zGIyuf^&i&Sb@4yYcr@Ez?B=C6^p}wSNG*7-ztk1?YUeK_{R#PnxYw;qzd-A_D7}T& z@x63i$0|qHv2I&xBdz1I^ch-<&FSs57N4a*Pl)Til# z>^zaqc_MY3?XT1c)*|Ucw0G9057Tq03f-K(VU2XX`hIKe(yN)ZOYhaXDqfo&vDWF; z%vz^!Wow;Yms;!eea>2^Z*^-OrPVrpYgy~`Y+08%Yr*A`RoxbN< z>-4?ATBlbHYn{G!{@C2Ng|$xKab7+BF8%NOj@I|#e&2V5`mWi7oZfPX+G|GNvFgX; z`i@tt)$BVl^r+j}cUWkmoTOHJx9?>2-En=Vgx0uM`)<+JywTUrJ+u4HQfn^iJJ;`^ zem`Glpu)bz>R*N357>1~--T+gxB4#8QC8Sj5uID#w^04~!@jH3Qg8KLXZ^V66n)dt z>(R@ruU1>xeT3a@(cRLAa4bHI+MCLM1RurU;&^-vwU<@qclbE|9(4>V{|S5&pYodY zO%MIS(&6(>I}*E(wzcj)R&Bbm`*^kK%I>eUe&pXZOio18dE+%d9oi zT3TyLwWe~^nrYLmHPhN!Yo>M7sPJmqtk77uA#JX;Y+5(9>^Euit!2}CTg#?pSj(nm zTg#?R4DE4i(iU3#rsZ4vrVX<8O}ok3H*JWuZ<>vzh25UBHBB34?W=RK+Ba>IwQt&J zYu~i7*1l=ut$llBTl;p8cn#IQOL}(HXtAG-h85VOw#`NsLy2WkB|Fs2cXY%+aG;qd5yI{=A{m? z(Ysq+t@XCle2o}sz3tx6n$((Fm(Nq{XvEmp``d7Rx42IvdXQJ zQyYYHN#|j{X=jAg#&$+XwQHi6Qk!ebic&||86nl8vL{n5D%;wpiuS?oJ~p1L=u_Ly z2&tp(jF8$kqBBCO?g+cbQoGq1A+@)*wJ+Z zqjxvkXQ|6BmpY^t@%8z@e*fG?*s_nDhHN7UUYA}LVRf=Q zv)g+;t0iyh^?c+sx1-mzNF6*KwN1*YhxPFcY=CECLp%!`;n~>OmK8Y%&()gDv}i## zLB$M8YmDzTFLD7kL&XouX^t&Wu|&@c)Q5|E&9PXb*F3Lz=rUJ))xgltu{Hhz+u-Hc z7O%i|*dDcSH1Czz5wF5d*cr9wRHiF-!|vDvdtxt4#opKl(@gb-y*(F(&v9ROy)%3+ zo`+4asi~f^z2_?R409LmRcqDN5wW8uQEw+*{c7tA?TF|(U;RKO)elUyXvbNKg|sYd zQKg%b*4?(AW4(*)OmgLz(9`Of4MWf16#OgdQBJtN>~|Bz$$nms)bZfHB>t( zeG=Bdld&eAf_-g$!ZFkPX=u1R*s)e<9~R?&Jb-%0w5A@!ukdSAquZX&t;46f;_f=` z@HDK0r(<0m<#TkVu#)xh3~YdBVnaL&8{yg57|(HET~$N9$2!xjhxvF7 z`PbqgybcHB4S1tzBSZITHZn+!3{r7^hsAc5={{3q(w^=%GVJR9e5A2WwPU&aH1E=o zTCn~3p$Bj@{u;;NZ&3ZO{UAHj%dz+{>a4FET`$TWm|wz$a06EtI3{ML9{HANnKyNv;b$i~o(2@z3}i{so^$oeeeb9@H_|UgNkNWZzA1 zf4=?BnEX#{g_q!^c$sM}*IwsmsfbPLuBmKkcdFV?*2veS{VKatrF2u$%`|mgM5CS5 zxvbk*8{68i^KRDRQQS4h3!{$_jA9B_!pe98R>2cd5wmKjhSl*Ttbr$EO*{n^PpiCo zvs7=E>djKUS*kZnT_JWo*QRys^=HTl%7j-|fYb$TL`>N|e?^FB? zSKvxq<#u#!?S1ZwuA1nr#xHOUuElk@9yj1d+=QDoYG!*|a4Qy}o%ymum)QD;?r~qW zzb$kx-iIUbejJITOpV;d?Z#L%*tK1_z_ucMGdZ{5t#}*$5{KaJI27-|JMk_YhIiv| zya(^a`)~x_k0bF{I0_%Y(fDf|*!oM;7+qJemd>wgGTit$?9o14twN&5i*hSYH z`#QGKvHeBYG#%U9I(E>py}oNq*Bh2oi=5iz=xA?0%8quKW&0?i@xQ}Z>!)3Aik@Tb z89f&-VGsUFeYU7`E%jN`#|TC-1uJ1?JOOogRpn1a#dAtm!|Hev*1(goCZ2-2hpY0Z zVr@JP>)`2FS64W*ym~mxeb8x%`m8w{U&A^0I?lyHx4qL4_1WUib=7Ch&u|5<#8qxd zr@890%R9|ipEbY0HMkbn;dEnb1`uswFbE3qS9g`Kc7cEPUL4ZC9x?1{ZF z6?c3+)p!kF zi-Yhw9E{iF4R|Bogg4_Ycq`t9zr-P^@lk6#6z{-0@h%*OcjGJs01=H}Nf;hkwWU z_%^`@JNWJysGOQy;Hph#w1^yH-#-CAVOZ-o4g_q!^cp2(AYW-}u zHFNy}bsVXF9Y?Y)UV%D}l%wNF>Nt`*j--wwspClMIFdS!q>dx0<4Ec_k~)s0jw7k# zNa{F}si@;fX&pyW$C0Fmhw0(E2eS5x96LjItYc?rxkG=oezLz4cagK(w#e5fl2;7W zeUgu^7_2)$tFO34@p{dUo~|%Y>}cPt`KF`3S>yQ~YudGW$2xYlmgjo)J2ueO=E)r! zYm~BdQ_{`6N*$YPlrr@R<`qLDY?sEWgB{P;Sf%_Z`^DR0vGo-bEw+}5t)*gXsn}X7 zww8*mrDAKT*jg&KmWr*VVr!|`S}L}dimjz$YpK{;Dz=u2t)*gXsn}X7ww8*mrDAKT z*jg&KmWr*V#-uAI+n6LXP-BwPG1Qo(bON(bqmgoQZ~*3F9%?kwREG*bFb)M%viFuWUwt1m3n zwQKPelXUHB>RM6iT2bm+QR-Sz>MBt_t-N9#o%q{c0&aZ764 zl8f*IT#O&$68s34;>WlQHEwC%LR^lYqQ))dufUaT*XN{Hll}tN;96XV>v02a#7(YP zZMzw_;8rZcZMYqG;7;6yyK%28z9bRar>C++LdCcr58#)05Wm8&@f()@EjlyoVF>j` zIX*@(iYZtLE8_`R1y97PSPiS=Nmv6<#+rBv*0MfnSEaHxo`!YsbUcTW=i+(T1e;8ufoQW5J8X{~@Jj56SD{99ElVT1)W|2hVmIuLJ+LSC z!c^>yeJ~CCvK2ARw!LFxyz0CfHEyV7G;Tq&~FOdlf{#T2ZBmGK0uf+u2CtcKO`B&>laV@*5-YvHL_8&AVJcskZ~&$e!^ z=-Getsj)$7Y>*lo4kZmXzjPwS3~s?0BN4X(v?xE?p)M%;v(-NDw~6jhm9u?V-BimKGAcIACZj$L_6 z^)IRZCDp&A`j=GylImYl{Y$EUN%b$O{w3AFr23as|B~uoQu|Y?e@XQ(ss1I^zohz? zRR5CdUsC-`s(;BeOn3WR>->fpn29lrV*;}<+wE%IJJg?a4i3Ov%)@*vz=3!*UW3=- zAiNF-Gf?>Had#&_^tT!8Q4LVO<=;Rm=FKg1>Y z5iZ4#aT$Jsg}59)#m{gBevX^i`vMUWJ{oGj_qQ*bTd5 z5A2D(Fco`aA56mn8&AUY;V^wTd_CKKvqqt( zJ$NtPha>QQ9ErceQTPCk#$V$Y_m|fBx+7seNL_u%J*aCCrT1Ym?#Bc8B_71D@N4`A zzeQ*2SKm$Dv6i}HEp^9Q>W;OHVhZYxwQ?$>?pQ0WJJwQntflT)OWm=Sx??R*!WwuA z=3*Y^V*w7ttMMAVP2=Gpe>84$540YuyFlhH)c3<%+qb$mw6^bH?rJ?u_kXsw9-;d` zmZR@gDyKF%r|En&TAzoSr(<2LhxPFcY=CECLp%!`;n~>Omae-=rml>BHrehY$@8_u z@sSI#IkrH3Q>gXC2**n#%g&Gbmc?!rplg_XJsD|HuE*0rb8+Fe+w)@r%VQ)@}JmQ-s=wU$(CNwt>LRpW)-yemm} z#H+9qcE&E)4RbXrwD9uqZgPg>J$NtPha>QQ9ErceQTPCk#$V$Y{0%;c58+sR7{}ow z_$dAs$Kzu-0e^>&Sg39O~_J!G@OG#hm z_O~3OdkUt`DpF?^sk4gISw-rsB6U`gI;%*XRiw@;QfC#Zvx?MNMe3{~bykr&N~F#z zQfC#Zvx?MNMe3{~bykr&t4N(yq|Pc*XBC<5K5H>e*IZ@>W?~HEn7}O5cT8Ij(>0fs z&%pthi+Pxj1vn6|#%u6e9E8{5V7wl0z#H)du1Hodu~o3sQF$r0y)phupynJBFUdXYd8Lyv6gODWw03Q}J&& z4PV6R_!7>*mr>sjSDmlmEY|8(oQ?X9q}p~4zK(P84SW;d!g=_2oR4qgJNPax!1r(= zzK@IW16+(B;u8D_m*U5`3_rm_T#ldOXSf1avR$8(UQPN7T!U+I9j?a>xDhw$Xq}|H z4CWTxibc2$x8n}niMwz&?sG*g$A|O{GPxfQ;Fow1^$jFV{Tjb<8(WSDeT&WvdlM4cFk6;v2uo70r6R--Nh*hx~>Nl2D!%0{JPsW;f3f6L~T8`E|l{JC4#a;&a`t>iDT4PK6I z@d|8*?Xd%1i5>AO?1Y`M3wFhB*d2RdPwa)M*csC|WjF#i@5quPXi{mN( z7*4?7;p6ywoQO~0llT-qjeo#N_(yyO|Af!tf8%8QGd_oZ!RJwtqSk*3{uQU<-*6hf zh|^i~mq^bb{W8wPS8x`-inH-G%Fn^qaW1}rZ{k}x5C4wy@ojtu-^B&^9xlZ9aS?ui zi}6ETf*;{B`~(YeIqI`(wSYdmmMd^2uENi8HGY9>a4oLG^|%2y;wIdTTTpjDRln|j z%5Auv_Su0uaTo5!J-8QbjG7X*F=|TK#;7S_8>6O#ZH$@{wlQi-*v6xGDSL(V~>bh6zx>xGDSL(V~>bh6zx>xGDSL(V~>bh6zx>xGDSL(V~ z>bh5+Y}@76L|ykPrxxi`u{NHDb?|hoi}kR+E4rYUe+D+dGqE9_g^loRY>fK!Saa#q zV|gBS&^1*z|4LWHZM%huT%8e~0H?5V5~8 zs*=B=)f*E>N0MEpRcosIov+;blyyg7JPi(o0+bn${>E@&_BHe>a031gAIIOLeiudMpTH;aDSR6LfY0CyZcn4$iVMxZ;#B+_PQw>*I=+N6 z@MV03dK4F)vBBa(skl%oE|iK3rQ$-VxKJuCl!^0V(51JGzF0}GFH~@1o5A(4A2jbOu4PJ|b z@H!lf*W(R%Bi@8JyCqFlg>E5zE8d2`#36V)4#hk0PP_|;;oUgg?QhybapAls>l7E7 z_u&Y6rQRB)p^rUoPjUnOne1rvCgmJY~2io`SkAJL{lg z#{-Q##g67_SO-r>{bokf>iWjy{-)V9IC4Yf!@N#U6S71AAj~(zz?1)!kC+v(}uq$@M z?$`r+VlPa^-q;7zu&?zf#g}Hbwz{F>OKU5Anke;YqGaq+d}-^i&k>upP<&|)LVb>? z^kBT+dY9r$%efJ6az%|F*SAPDLg+Ia^H#hKe~Clzb{vX#;GK9E4#T@~INpQz;(a&* z@5hn&D;$Ln;As3cj&T#4j?rf}=7X%~LvBseR*HblhjAP}f{)^FaXjT8!wL91d>ns| z6Y&Xr5}(4S@eeo&|A^1vpYU1yZ=8&O#^>-a_&mOVQ}C}i75|3Q@I{=?n!iMP2I-e^ zCcc8R@Kv0RuTg#uzK(P84SW;d!g=_2oR4qgJNPax!1r(=zK@IW16+(B;u8D_m%3*g zT&hpM%w^&B z;cnFL6{|<>#eGb zbvy}c;K{aKiiXWo$kC@jN}r0g@ieT1r(<2Lhl+;J7-`Y4R5UCV4NFDCQqizfG%OVj z%f_gY$sD?(k_w4Fc1?Q7b} zALLs;eX2q#Whi_G! zI;-9U#i^!`5sYFAR>I170#?Bju_{)>>Ua{?z>~2io`SXTRIH7sVI4dj>$>rEwAHuQtFpk4V@KO9Nj>pGv0{#vk$KT^bd;*`ur|@a~13rT<=#O=` z_NL%paVq`|r{Rk@9bdv3sQV+D_Z6#0v8EZt6s&}m@dT`bm)O2jtZC&hbMMzJR;+2Z z#$R9?yd2x&71$2jV+XtvJK|N?2|HsK?26s6JNCey*b7s!H}=6aOn2|rcwVulnSq%Y z!#E}|3$xvab-z}uY2|Zp0P3EHa&*r_=3@a4#H;ZdycP%HbvPKW#~biQya{i{TkuxA z4S$J4@OB)Eci^3P7Y@U_ak$%8ubpDecTXOzSkt@@N8tT95=Y@f?vr{KSgcuhm14~* z^}-fq)_7d;rR6_^`dc#fDl5LUoWJ5!{2NZg7jZhigfsAEoQbdCEY|Z?oQ<#H9DE(; z;v4uTzJ>Gf?>Had#&_^tT!8Q4LVO<=;Rm=FKg1>Y5iZ4#aT$Jsg}59)#m{gBu4KDD zC%u~V7q|x3;yPT98*n3Ta_`q`t@zU1f?Kf&x8Zi&fje;*?#4atK)vdUFZb1Jq4?6= zkNO*TN`Hw5QNOLIoUid4x9rq!63ZO4i3SumPTl4e=~& zglA)8JjZ=huc6{g^E_;VO|hBnJH?k~bG!&!;7{>l)ZfFbcdp_~EBOm-gO_7lyaL-{ zd+dN$Vn@6RJ7H(+f?cs2cE=vr6MJDQ_QpP#hJCF^DZVtbZ7(anv=+<5d}O>*d}-@{ zjmorBd}$8C>u@k$Z@o+LrA@sN6<^kiS$tVLV)3O^d?^)QO2wB_@ugIJDHUHz#g|g? zrBr+=6<AcR(vTHUrNQ7Qt_o!d?^)QO2wB_@ugIJDHUHz#g|g?rBr+=6<
    %}a-l!`Bvqxe#3#g|g?rBr+=6<+4y1nL zKwggejRU3i8wXOqaUk^@2U0Pvwn#CqRE#SX<4VQ2QZcUDM=`Ecj4Ku6O2xQRF|JgM zD;49aykcCb&tj{uv(I8{JgrD~eZ3s}%vCx1%vCv~$=T)}sy9?|t+~Uktv66{t+|Vw z1C%`I-l;d(;@aw4ETWaCx#jh4QABH=j&-pf*2goj0iKBs@hohFXJcbqmLgj7JT38A z`%9zpe7CyZa7DDA*Bhyb*3uV}Zch3l(k)2;l(fD{TW^daT15ehXmvJGL~H5p7E>sq zwe-twYwaqEXg{qt&Z5|p$0~kR?4bD7JkQD~el;&d&ccdcEk}K$W^2W-O6xf@BZlj? zfY(!oc%qB`0;$r$^YnseQF`8)8DgD-CTyej+PU7j+_)ci<~uhFV;5w zyJg6$L(U6kXe0U0kaH91yUdhIW^^d|KKY+W&#RABWhi(`PxJ!Pt)!oFI@U9zvv8*A zzhs7~Ei{1i7Be!;jJCwFDNg=YKFU#Ze#kVk)CIU z-;jD>E7p|0|7SCD0(!VYhP+hL+pry4{rZ@S^wVf9??+ydCC{bt7b@*}7ooL-4oVsF zg6*oIv>&+^t&AW3wHZA?4($-7O(O4+|1kO1{vofErBlu}L(|QuZG|7b4cp0(XT752 zxs=f{miA|u(UVDAuh5fJu{-&7NY9of&&~7Rwwy??RvDI#4#p3p=lzGJBL$>w-jH`b z(hkuc{m^RK}06 zm%~q3Iuw)odXEeRPg#t7p|l@i=@IMgA+Lw!c=Q?Hj$A*|3F(KCLehUV{a$AHTGG?W z{{`t^;RrK4*Nj@r`;iSYcxr2i@!9vkvHVaqGZO5PKU+E)jH~}`X+PM5l_-+QL4IvB`ZLm-ahe%kE>k!Q_#w)NS6Y4wC8JMTI&vL`OutBm zykH&vNSZOw?@!v=Arw5%&|j>yA7!iy-*0K3z7*nkiM~kAFf%3C*R+8DFmru^{mjTZ z(`OGxpC;!qw6VnxPcfq$v(aMGe=t)TnUTJxzlr=U_%YHZew=hiGh+KXL|^iwtW~5f zQ-i%}y~PjH1H&9m(H!Rb8tLsJ>vbj1ij1aMew4iyp|yQ0?|G+Keo6$-H^T?;8Du<( zG8%ZSv!7=9p-E=c_N*Vd-t^h75as<#$v<7{Da*r!cY^ezZOlj&GtBmeZzbn0a^{&) z=8b-2>698~h|TG~%H!+T_iXP5a=dRp`~%a8h43vHzGBcEb-+(-U}*a$fyqCaIS zeJ^Ckg1%HHL!PZ?$&+cLUn=dVFy=&mZt3WDq<^NIYUv2;5MDrfI8)b};b5#7s&vR} zjh9(|#Ex{|pK69|WYFcCrF~mNU1V8062}+KFulkRw(&mlBgmHeRVl;SA@ZN(pJ+zw z%aGTcoM4Z$?O|(GFLUb3EF{{i9D~I zkk`)g!*+z~iFB3@-C>3oncjz{PapH|v~=`5Gt%A+EhFa=45r$C4|$a>Cmf76^q*)? z%L&<3Kl~2qb)^47nwF2uB5j6(=f_4KAx+Ep=i5}D@_wPEqe1WOOn%Te*wToNBz`35 zd)+J@9!&Y&l)MO8Vl%;z1fxgLCK<{P@svw_jfr$ME9o!6{btl^_9GL`l*Z(+?;>nh z$XZFim>2VmT4EH*8xJ9@xR2 zop{}*s~3BzUI*_eKYE5pmtfMsCZ0F&QukF(ZnCJQgZDPg^W0Yj)elW8O60hrg1U#^ zZ^^p%Q2Ids)t1&%E>^kKIr^P}{IUE$nAOJe%l$-ii##v8(5)KKxLp2my~%mjis{Ka z>v-Lj`RQ@ZRzacUb|+t{uS{E1FiVA5?(Li#%1&^v=G>Ni@3!RJX=$}ZVKVQZ^P0aZ zs5klg&sj=o-#J$OLEo_wt|&L>@REAkM!B}I{zBQk12TA3A3A)VvhQ6{EK>IU(bL%_ z%PHSyj$Z!gQ`~{0_fdSCR?D}pV=a4uZ*2XTY+TT=!sIr$ue$cB6cXfKjUjRF6&OG zSIcv&GQ(=)%5HOJbyqZCklQ(+4*3VN8YSl``|7r5*7Cf(GGk!garGHJ??=BMqOog9 zW^? zC9_r8#Ft{8b7@x(x#wxHa*XytneUZ*4zs$>sd!9?x%{oOV2~?zPM`E9V}|q|drXG8yOG zyv%{BcXVkTrDI`f-jS(_#uv9ar?!<;UmonVHHAFtYm8ri+n#K7vnHRj~ zjMCFgH3km6v}|4KEBXt4neUX8X%oC=j@VH}fY4>FnuleWpIe>#s2+ z_)))lGDwvlN0M!sELZxP`AlirQhF{~xu0Mkl*{^_Z-Vnq=}him!JB%vYi?=Ik*Q>x zyG@x>lT+PunKKThcV*6Y6Efd)U-kdi@{;Z6_GK>OT5)gY(x7Zko8Yy)+DD&iTUVFQ z{qA>a?+sSUeU_=7uGcjMCHvjp{qP?>lWq6$&TakWc*~YAmp#4wd#!(Q=C?M!9G-uy z_ebwb$+`ZKK2z~ElRJ=ona-1m3tp4QPjIa2DTx=CpMLCjt}tj*H$Qf%Tca_2eyoivjOlld z5^2Yt^Z2G7>-tvLoxRG|V&@}!Z|A0DzMD1hi0cveQufZ|bY68nvR5~6K(@WwkJ~js zpZo{;LC*I#c@A@r#nP$sk=QZ!>-S$m=^V*(Q)$jWo+^7jI-c6}WLnNk$5Zy7T_!oM z{v*qAJ7Y(T-N#>V?3#aM*+0g-$#qS>TAi*;JanvO=y|Y*#tHXI?9e%0*CqG52V&#UdeunZ4c&7#@OGlF1Mrqxyd=)!Pv}- z&V9CRI)BVb-lICSt)IsA?7Nu$euLV;%4~@(u)31huzJ$*xCUyo15(|Ey*EOma=E-z3vzU%!7JDm!;Y>0}=} zyw6lr_Pg?8drM0Mah;wdO&jYO*`<>zNV!e@51l7eHbqY$uUfX4dm~=6^u5&`ucK#U zH*hoJjZ5=`l-n6U|L{ERYyH+ly!ny&$H-|=5{ot%SpOKMe$XVv&RlOKuRl3Pbevda z$zv#}IDeI%`+Zq*A=7za>VIQZ@A%-g z?9^mBKHgGpZ+xQrBtD6GrpBkZsqyJr#w`7x>;4{}Pnq5Eg|1Lfy8nScN4rN*QFS^d z3J=eByW*>k$SX7c2GdH$-Cg}JNS1Y5b6Y3V$NxI8_OZ(*$KGR?`XN)3$5h4h9(m6_ zIF`znE_*x$Q-b5jj;-<~D}LvS65)!cd{=JrSo*GV6;1!)$DiAkQ!cjCc2+LGgq$|z z-v{s9{`?^o$->#bldIsY8v^OMqT$$8eTNiY~IzyMIlce@^H(AreE~+lk@s zy~Ien*K6Cxv~3CfrDC0bRwgEJ9Xvhpr011ho4Pf*F?;XUC7!YJDpT7{O+3fz?Kul{ zF5UoLPmS542+nUhdK}>AW|01`&zsmljKZ)HH*O0fs^0ht(%1(D165l4@ySZ5r z_j*>9itnjZG*%ow4pdYoS^KeGgLOMriSIY9eU)pDq z<;wOW%X9bTEDok6KeJF@T9Z}76%H6(I!|dzpU>4!_LtIJ)$?rDkA6*?tl#PLzT_C| zzASTHU-FqovRp;4?xQS@spK@bIICGQZLjXRtV8E3w?FIB(rHKTQ@3rvmeT2EQ{VG^ zS!H0!GLvmywyqzRE_+RvY^!6{6In)Rs0ipOVAHQ8Tto&Q4C^KM?=0{4f!dl_ffWKC0*nN@ zT-ydQvzwQ7&|Q;lvzGh}tdUFwuchOY+gH%AbXviN()6)XrPr)(XLd@mq|#OO-JF`q z_xcRALuuMlIc?ms>^g2^c7tTTD=f%4lrG9{>^{go|Iqu*+0Bz_)!WkTu+JOpC%dhw z_Z<~KFL2wlyYYT=c5izv{V9z>OY@FQxz*Vjmg_#x&OUPL56^L%v-6KOw_B7wsO&WN z-~9&$@00gEv_AKh9fO0Z+OI>(&gXgkC)j)UWcILRzFXg4=kvUB<5hCn@xHnT@@Y$V zTmRWMj~m|q&EuW-@N()GBV2Lz=(724e)d>0b-Wluiz zUdOejPf>rEZh87VYL@!eT{z~TbO*Ge09^ZR|V`X^|_ETn})BOW`Yhz7mc7^BZx~0=qenQUnvhOYJrVf~| z^ECV8h-nl1hc&*`KO+B6{VN}ySN{ERwq^O5c&B@A)r0JTHQb(@drM;)yT6vx#-`bK z9^AJ6<+hFdH*+3z@AhwK>C(^SEv?V_OYR%HviA}WT?gp%z%Bh-S;^#&+t9zYt^udI zt^M02^Mcok&$qf?+NpfGANQTsyGOD{x4FOmx@*bx-Om28WWK$+ef{-a%d*!U1)tjZ z;+`EqpW)p5oBo5{)Vy4cAGf%r{fD}{`|JA={q^*d{$t$i{^LsP>p#K0-2X}4e^`;s zOO7vEhv!&c$ykw`e)y}d=cksWp1tN#JFvH!PjTTaBvBx|zIg@Sutr?)d^E_aDGT&a^TLrtdj3I7nP9u%U`d|9vigKDL z|Iqpzno{e>zps0!?&C}xe*eX0a|d%SJ#zM(Hf8gU_x?+A9{cQCZGL1MBn$l8y_W4C zwsIxk6JtH?dME#U<&|9bFkY2j8y_>y$m!*kT+geW)4$hdH_1s*zQK{-ANXGNsBrR{ zKly6&mra+iFXx)_?@QnPZ~k7hos&wp^zc;K{-Czg`RMQx$M^ooW5R97dDPBF$2Whn zRK@421O53(HJt%>Tq@+PWM)HuF&5D%30woKKv+WlU>I-I|tdhqE>JpK-X~{$+H>B zD}O!c9vGlsYR~=FCLem`O@J%T*<1Qn=flxC2W=jAU;Y&Sj~I~R?Ejw!=owoB4#jzH zQ$bPbd`C|uzjtjV4t>6;@7D5u-_gG#&-9(8-)ndK@|Psu1^rm$}ae zw6!(~N|fq$4_HrPrm;# zUX$O^|Gsr;y|j-?fAqUZWn#^exNCK8!_pGRO64`tXD#O*tHiOVCBFj`e3o+TvPVvJ z+jB2S=DMA^Es|+_EqgraI=o5QX-r>`dnhJ$`*K^mb-CqYVfR++4N-#1tL_?hFKN88PHm@Br=(2_sp&$r3kieF2Ax6Q52U0rdR zW95F=vX0!B$EvODG=0BfVCnnd-tnRD2k*`uTskee4y9wfd$#m?&+W;*rL=6>ROz*? z`?O>nejxv{vL!6-R^_)1#@f<*neLYSZpnL-N0k3l?(oC&e$4l7S?_lej+H7e6@GUeVErv)6|yr{^>f(|od3ug|BvS!elO|AYYw*~?}qZVC9fgM=l;L&&h5^-(|u*1p_cx*rFo<5 zo$gB?^Ztr^Gw;#TxpW_VZ{DH%4{lN3({6j-vpQa$56UR!EzX;!WzBSNJmKk${>aw~XmcO7E#3v;6y11=*+mr9z zp1i$5I!}M!I6s+v)K|ACKjo+sk1@f`%dcva^;+{7#s1GvvhT-T94=%&#aXd0ww*+R^gtGYIvU;^g#-uM3k?|98CpbIzr%IJn1G z_WsI|?N$E!HOW}%$a2~zW#XfXrXMX&G0!pLqa)|CIO)h-E&n?{tF36Bz3!wPcAe0Jv28wf1%~M&-0h?T48@FXyK;juRApFhxuEQ>EN||TiEBSI(P1* zzB>w9B|m3P&f`8TXs7+!N&EEBi0OWl|Fs)Zkd`cyeAWHnup3oSIr(1mRWFl{1?iH0 z%6+P2f3d#v!+w%&lbqWPFE}@uKK#{9Ew~`aD`;`}v}3(@)-Lvw{7gg1(sp4=Io$OH zJ<4U2ys4$U|cd)_SHRIFrn=G^6BI`r{GC#*E8;og6D$i6`yb2qQgJCEtpYKUiX9kJ$}@# zZ~l9%iLFKX^XmWPdbvFX@BB|L|KDZVI(8P7T^F^>(z5S$9DG{-{r~Ile#rH=#+Ypd ztIKbB+4}~`DZ1ZqoS&VS-rq~kd;G6%mwk3?KNXKl!L)ys{|@W$uiDoC3(NVR|E}V{ zWh)>0-JE~RwfonvRILI%b!K419T-@pbm9L#=l9Ze`d6m(J934J4eh%!Ee1ByJ%%Pf zMqKr;+=74lvi|?$=qOEG{+_~73jLqS*4Waf!j1LA?S|W5!{)Eehu^bwc!~Bi(Xg5noTTwE2W&wZG+y9dM6Y*>_w4U^JSQ|H+;k~9mh@88y zxf!|B49zzEs#5Vc=_xYgT}=8EGrU*^e_cO#T8$sQ(DI|bu%_u>hWy2KzZvOo%y3&7 z@=hnGx*2%_uO?@v8G2uq{Ec_Nqvb@I8r?&g47`&3H8SJ{OTU7gXYnQcPwZ<(mYLyV z%*Ef!kjHvP$dB;%@JpU;!C&n68(2wyni=u2A30M=GjDVb>8AKH2CYgvN5+%0!Yui_ z?g>kWTapv}h4u;Lq??h)$X|-f$mwndPlX864q>K7E}%>c%G9Auu>NzE4te~w`3Qf} z-OskE;b1SnYH8m|ujE-yzp|W&^*296+eVl-9IWSV%lE5E&#O%Oa{PlC;V=BBuq`RH zc9=E^x2DWkGdzrRobqi*v&a2=ln(y-y&tVX{s(4w1L@WHTl`#>JjEm8Sx)qKc()mT z+w?!gTr>KH8NMIc_Ry1-PC0|AA@XgH1kb1NebzQg%Y@mY$aE_cq0R_%l{|^y9BOEQ z^oQspq}j_p{WH3fdFz?py{5m5oQGw|t8eM>UF6Rv$BwCzr}p^8mLDBrrm(-lKeu!^ zjr0r`_GKk|$jz19o|tzk8Uyvr>oVl|gMtt0%3<@jCjE2g$G z!;Q@ly)Lqle8%2LRnoLl_)<&z`Q-c!*&{x!9X*w)Tg|YIZ=v8R4B=^(6FrUmUZ(%w zX6Q9p@~jY_F(iDZ<%c_Cdo%KY89WWgdyo7Ad>v=w4l_#02rU^rjl%yEQ*XxKm=Q+T zFh`}g%yL5HgzhH&6jIW+-s=U=)bMGs&?cK24c6^>ONSnkB~K;u&miYIGfaOD4YzcJ zqcx?WrBhO5$rFWmVo}Lca(uSMzs1V4%Zh$s>F^+OtY-#KkMca5D|o_1$#Xh_=aqzQd;Q4MX7s;Ezi)<4H2o|yyiEqr za`MCU=kN&SlsqplZ0)1pTadw%uzbIXrGqDH_;*@5e1ns3CVGioRVxITaqLRIn^#(c3YAp2?;rc z#<8L#TT-cQmu!{eU>q99h8bgKTyv5pA;xJiNwR*A*HioU-QVBm_s8dU`|Y`P-*4}0 zovyXs@7L?S*0t7kU9qx1*bFpHkgjq>Hj$5+k@;rKL#D5z6SJSP=dQQvSymq$(lnDa zjrB_3AFCR*bKj1IA9KA8543Vn*Yxf*{SoL~eJlU*3syf_**|ASv<>=YG|$~-HP?S1 z#Mf5&H&Ej_^_uIygcZ>?L^f&()>?dD$1$e5m7Q9+n`dSHx{&9tN0OUW^P;BzM(#4N z|Gtc`ee%aIx%_VR5!-uz{LY;}&1&>pIi9;ouWGMGCae608LX6wx50WV$NWoPufMEU zuh+z@UcK^wUFAS_llP+UX}yszt?U=!_1|ao zBdt~Qcl6wYD(ff*?X2wWH?RNVnCqe%Tc+=;)p_NueEqj+BG#8Z*GjMIow{$W?6glJ zomJmaszpWiQU2q%q9SUsk;V3EBv~ezfqG3O#matZbl2NhV!X7pnk+cc3Ub9DOc9zWK% z8c)3-wvm-%ukVM8R*so&1~I0egYJ*fzSa^^JB<0K>Q#;{OWBW6n~bqNfBiQT`8_vH zqxTZA7Ujnzn7+0rqP`Pb-D4*zW&>1*MHg2kI7X{2lTW(zOObPvtIT0%l7!6U#HTs_HFIHZ!PDKU&Hjb zs$3cUV4CT7!asgbFtSpwwzYcCmT>*ICja>DQSZ3wkKy%SJN@IAG$X61x&F(ie#~0R z5qBR`+)Ujq?g9S)LD$uFblu!g*ToIv?>RTzjdMfXYwjaA+AVX3+&^5l%Xg{ndw1Gp zxMKc(biaAd9rXgQtUKvd@v69s-mTuP?vmHYYvhW(7G4YYi`UwF%3b#U=1uo%c?sSk z?*VVIx7>T&`<%aLycPTn@>Y6lyl1`jUXk~rcPUcM`#f@Uq@kA@X&PzeeIIEPY2%%Y zw2id&PDR>99`gz#10yd*Vk0j{=0qw--i)k{+!0wD$&2)f9QG?j#`~502O|IWAM_uN z6!?$$10u)$f&S3QZ~h4W0)M1GjvsQH&Rl#XsO@`#1Tg{quh9;HKa% z|JI;&(AMt|v6|;h=ZW+wU6m4f^_z1pR}j{BFU}V5r|C*cfc` z9}TtyTl`)zbzEW7cYmj5pnJyM?gqIDlqYhW z0{5}|gz_@}0{5v~LHSF!&XsrT`D^Dk@E5p5x7Ag3+c=^nbVP6Fh@Nnj-On7?TihA` zBJLOdB09Pej&7U_ywYB27xBt?Wn2lbwpW{SeXl;Y_FB7GuZ`D+Y3}jv;nlWYUsuw5 z%zMn0<0wDnJdXWDoa9Y%)x62xbXS9RkbpD1IZQLx`_Ps3mU=vS>3!sVMEPUy6Y7_F z%c%L(Tj8qEJ~mKJ^zv9vzL!t=uy>egzW2VT=7@KkniF1;tK?nqE>M5byU1LZysPde z@ApV0S2}WIq?)TmtBQ9wL~f1T?&?GuMjE=Zkw%dwu0HLoh4UhRinMX%tes)INMC9m zi#+DyBK;!$a6n{$yNQ-Jh$TE5dDc~p430d2xk9K$XulTRHrv4azjBDe+>c8q* z`(ypFt_gi*oNI2qg?ZoLm#Xig&m=JK41Wgm&h%$8?<{{7^Un5XGw&RK4)f0S=Q8h` z{+rC3=BK&)=v@cg1Ae-n?mGGh{e!N(pXq11`~56G%XRP%`G?%S^uKJ^m>zh>)%4H$ z=UgND;(1q--dN972;zfVDc29`yFUfD@r%@V2MvM-lyB#EsqYRN1`S=kpi$7s#RrXp z#*~``O(-`F?r^sS&4Oms+!@?SP4l2R<-3BrD7Ow;yOu$lpp9!4+!NeGxoyyvGJW2) zpwB<-f}m^A)p@}q!6TIE|E|3Cf6DZKcPIV7KRy*abuyO+hQ8HAm;X%hOPt=p$X-tF-@Ikae*>%!IihTK$*BeB#5{j{}mTXDk4;Mx4QcL z*Vr|2HC6^Y{FPLUsGt~8K{3K7Mhv8%Kf|9-q@7F{Qt&-j5o;DB#XSploNi9s!iAa_UZcC8}(Q6~GLMWt&06_v^> z5>+A+4R+-t&k>1g5Q&Dkdm_(Ao+o|{jSMBmy%2eU@-QM)9Yv@be?%yYO=T3D;uMt{ z5S9MnB9X`0!g8#x^L0SkyzF~q6zF5*`p zTE!_&MHHv1C{Ep^I2ErrRaSB84#lZk6sH;~PE}Eys;D>>qd3(?5$YjDsLqN|e^!KQ zs|fX=B2*_us5XjF_bNhlQEYlhv8l6S)1MWa+A20ZsMyp=v8jz>)4i7Yi9{BgdMY+O zs@T-c&+s$cBZ^Qx6rs8+LUr|{ew67gPPJB?YN9xGx8hV2#i<(=rsRtCN?pK_8Kym6m#i^=_ zQ?(SQswz%ZR-CG!IMq&Zs-5E0ZHiN^6{ngjPW?%7s`(#r>i>}d z%5fd9uPv@4e^YL58&k2uR{Jk*iY+m2>kVW6*MBvxPZ@JD=3>*aG4K5MUx@{!>f2w5 z1tk{5R<N%t|NAet#sB<^$@rgj|5YJR#$IUy;-G>l{%K%Uuyq<=B`k^`da7t?}f$rn;3W0;{Q4G z@UMi4`AZo^f9T5R&tpXYj&p}g1|;tM!$kFW!}IwkG*2G?nosR9)#dQ7Y*#b?Bx4@5 zf3dv&!BA8GO3v@czmR{u%6y9%8KR2>apAfAMM1gnVm@amK}G&GJE+3HmN4e29v;qb z=x&zR=c}vv>avtAEUmh~-8IN>XUhyv;3t>p60)UdF=bbynt71>VE!vxGcDhx{kr^CZqk6 z)Fs+>gF8k~Qe*Z2CDafP-cE8rW^a@X>@y|SCFJ)gIP z`G3!wuJ^HDQ)OJAKX0q1nyLAR>Xk&zKicMJJA3MtHug&H_MjD8Hr!s%d;E{(57zSA z+1|(hFR;&>!(**1uP}6RWJgpyI96BF1Lo3)mi&Z$2+eg=L0Uc z3TFbyvZCSs+#|G@L%GKy9PPXyQawDDd(H0@7UUNBU9Fxmc9iu;++pT9%In2B{q6O< z`hHid@h^sjxo-t6O;>{L{xmm_eXuw8g#R0_4-Ec<>w~+)#km)Qb>W%Z0`}LL+%t^7 z`4ybp4F4iqJlytbZhikZT5=8cU4GtDj%{Hc`zg%KOAa=N=W=sgN!DAG*O%=&p4Y`c z8J^F!qj@~{F!L4W9%a7lys^x8GH*8X<>xKY92aw{GsnfeLAD=qnydY^WZIwdx>@_l z&9YXXn-`?;>JjER&a%3&+ts{@^xsW@6b)3i@#5ykI9%dcKbBEeGa#!-sIfmI2>=@>JZpScZA?Ke~CXVDR z)cQu+@yr=%$1{hk4z!K(?4PdT+1$SVG3Fk^cFxOf?ElOXJK9>aSFp_qxy`kwTQSGW zY-t&PWOzC!!5cS5k9 zEvrInJ)6TfY%zCM&M^Abg`AO*>MSu(U#UxfKA)4rGO}~V+TO|S#4>))C8n@fCo%WW zxtwn>_pdqUSnDr2og=r=3c9iG-*OJyKFT`7e5csAY=6!Z%=cr?fZze=4$Wi>c#oW| zJfc0>O?H<(?M24KC-XL+&{A1b{0Ldq8y)9Ijx99m$Fa#r&vch>#MYoE8(}< z=jac~IbG-v2eOOl58q~AWy^^a(d`_iXm*yxysRX)_E>fv@A7DeU)Pnk z77|Ue77|@SUbONYm1!-}ui1`sQS0xgqU)`-P^Ps|rnN+qt+iw=aHX~VmW89)RhZl4 zU1VOz$g#-Fj{jpjnsODx9ct;0NHPQE0?Bcv%yCB6gRBS1lz2jFe`L00 z|NjH#;(ld*qli0FL*+bZ}OVR1AX6Dxx4KqLUiG~N`{nO!*Xa)a_JTHllnYNXMneAC>c4n3> zHIorln4ftih_`8iTTQl)o^UY9T2iLP+n><5v9?N)~_fU9n3Gd-zdRyLu z)jSa99~y0$DQli9&-+bGdy5ucn03`M#ew?%(QtECH;YRL8v7^1Raw1R-b(h&Z{d?|!i(6XCp*>PHS1uc7S=2rhlj@A{H`dQ{V z;^~>pV#{rrS6TMrtcZUqEKXg(KDjS5k@ZZ^D#v;TWZ8Q-lU1Gd@G9%c&l=2n7H8FF zJ-27o|6|z$wd^HY_B&ekA}xEVEt_ay%Wk7(udrnkC0KT*y@xZINm{mz0rIoT*s?Q| zZP}T7bu8&i*5VF~upY5LYCY`W8GnY#WGKo^kvfd!6=giZ@_x*)ap#$X7g^r%gIBD# zQA2N|#@gT&iw3D(wCtZVl5N?URcVRmGHderiOY;<4f`_(u)Gr)ds$vVWDHq*u$ z$7vfCRa03tw!9?)Biht>Ep?~m(bS4s_PLC)^t|I4eJ%259HY;j&9JkJ;{7(3El#y_ z$1@rISWjWb5!REPmcn{YrrCHoKkXdru^N8)f|@F-*&JTXc-mF4D0Z-}ZFg!9+b8>y zElTYF*rEioXQ-K_8uC9i#I=JKaf=RG#65DbE9*IZus`cLdT=!Bu^QqUHN>@pqqT-h z=`$@3rLWc+p4J-LYYpwShNrcL_BO)U-`*lq`eD{^HvKKua5#MdYbZQek~I{hFJ=u^ zLm#Au$dta=Rj~0(>LULUiwu4@*JbJLPH;Dq^Z0^HkXX$4z}}I+rI7orEu}V zj_kv&2cNdl%)Vj%@0OM9jB#Hf8TQP+%d96FUQFrAF}b*Zq4y1{p7%E)>DvPnf07a@5p+NrzNl+tD&Dz zLqDO0ev(qo`bo+v>nGn`Wo_)ey_a3omET*$K8&U}V;^=(e}aA3JiVp2*wsuQz@E%Y zZ_A#{OYcBms+r!K{b}VpWOMoF@ML;tuBJ>#D`)#Ny&L{`Qj!>k4FizjNGNsqJ9etHG}C-!7rpWnbruR;V%Os_!%J4MYYvTp;wketxo#%auZ zB}_`E7qJA|8}FxqogwdwCu$Yqy6|9nQ%0eg=`CC(ThHDrwDgMy(rM{tciBTeDtpRa zvbXFb`%1PhWtD%D$f5VM_ab}y#QqNK?V|6}+1sb~ceZOAdlT&1#@+?wQ6-m;JEYjUSSVz~Q2#BG$D*HlsmYbWLh9#-1-m=3wnA?Cc`uCajgUJJrS+f{M$@>?!S?p!nD0n? z+FD-PV6x`^w2{`U_voBwZ-%R4>)+d(cka@5`Xj=^vP<@F~Lh=URrO?2Q$;22wo1)rVR*2 zhPi2jf>GhAw4uQSTNm#?nzn%VziM~$n5H%_TM-SDQ|+vzaQ7HHi%1>I-Y!U;#J>1G z)y`Var`lQT{t%vVo z)Q;Ee>Img3DnAgO*i)5#aentj_Qm<#S1fw&9`9~qU;LKZ#-iu01#HXl)X}#7Jr`Mj z&HasS{i!w*|2ef2Yde$Ll}{=w@0-adl?_t)tkS08E()v3P&rHGLozCD>sqjNIjM|Q zDG$*$er(&gcdPcuaJJ~DT_+u%(S}3zrxOLthO&`tESt!t@($T4oXhx%G2-r68^?Wj z)y8ot5gW&)l%y>#PAO;OxE&vJ?CwjcY~#3;>KwZPDYZFvXHx2O?0A)9m!C4y#&Ic) zZ5)@<+*S483JZ5sp#3NBA81#;cU0k;x4A&R7w+G)+JB!OaWS|F>jqV2HCbKWENjSH zWKCI1)|Pc-U0F}o52O3`b1gmkU2@Ps<=d_7s@e7VxVY$=b4xtSjrucrEi*No!7?z#Ss= z>7A3TPp1@FpHBJQ`t+{#)~A#ATAwBt(5H7a^!c?(UY)5|8QbvcY`r>HuhQdF)>@BG z8EZX0Ws~*zZwsx*r!27^pR&w)e99#2@hQ`-$EVD;9-lJLdVI=4>+w4$S&vWIZaqG^ zzxDW(C2V6NTes3}l$)fD9#*;}xkYZ3ypLT=xFgLaXSf`hEAwQ&q_-s3x9c=1tL-`s z5;>9UG{~6{S`J)o*J+UJG{|)tu#<(+5fm6ucM+BKP!8uY~clm^79wJA;MkH=F6M&7fzEOKxEoR~E$<#F;ibNMe2 zEjtjuinm{;6@Nx~fpa@r5xG~Tw4l!vrL>{Xe3H_hK2x01i9T~ar7L~rKuS-a2>)#h ziz_K{^qGW|{uaeq0%MasX*P05iQ`U=m&13zON@+^qvUA$iX093gX*94%jwW8|xHtQ;rD%h%)tIZ;lMljRiox|}Mf z$?5V9nILD#nR1qCS4y{4wJW8`EA0qvdyBo2KxE?xZQo=^h%Sju@?E1{cF}oUglU>d2Q@ttjbvac|lhb8_{Lszb!#QqvVi#98 z!+pD^L~dsE)h|**-Xd$tTC%pRBkRg~GF}dp&&WZNzPQ!;BHL!8s9g={e+A$6wNVuH zjH0&KnC*{I)Hdsdhjt~{C~DVS8%6DUhxV~!*CHE5?OICz+rNuXi&$n~+aKG@aKD`w zzPo#%i%4GvGDgPA60)Q$CF5jiSw@zXH_589nq*X)G}6wD zl7@M$t%k@I-Lb;PwmY(HY`f!Y8{6*4BZ}qiSa0V?JCbZ{yCcock0^6~gq$BC=SMq| z*-sO`ooVMsJND8h3U(COKKQn(jcs?Fh`H6)5>sDmZ^mAkw5vCJ#q?z$V`Qu>Axp|q zGESD3Wn@`dPL`JybAdq{hRJ9?{dx3RPP+1-u0qpvo1^gTu_AIRNwf9AgR0dB3& zUFu$I?tdHSb>M!sx4f?YJpUcy3Ov0 zsJwkF5u{)T_b`W-$oOaEdHGwIxT85ar(oMo#=d8_6_a_6Z*9Y;7);5h7-s7*c}JI^ z4YuQ&td-lU+%DX|qhoNd$^DmnV!ff5++vQBqvb1djN}s#UL7mP$?@_vIYCa8ljLMM zMZPZi9Z;4qO-`45B2G<$oFVyMMQUc5mdg{jTP{ahI+DwgT#n>&B$p$(9Jx1Q!#TU+ zv1um9=WKEVj*r=0_K=Utp0bzhE&Is6@>p2Na=5~`CDyL+B~IroV^wlf_VkZiCa|Z^ zY$;=VdP_yybIxsui;R?`uwnffu3>tdA0%5ieMd`&XW<<*IDlAJ83$k*jmIZaNN zZ%D3$G4BjHQ!)~#X13%?+=e*+LGsKMzq5Quc99QD_EusC`^mjPxmhO3Epn^m zn&8HvE<3!mE$VV)uFRA9l2QDIl3dlv*_Ogp9rFiSAb*rc@IuAM`cghOZJw1WM9cA zQtR!$H1nj%vkGqg0-jPZZ9JRKGYMuJ)wGrERCBL<%C>~(2bd*f$K*g# zeLL@6qw8s#58HWflI}la3BM7wmf3ml=EK2Lwm<0)wx1ucEVyZ`WkDnhB3Tg0f=Cub zvLKQLkt~R0K_m+zSrEyBNESr0Ad&@{CF%0{xWY$Ev;Twc9Hb_y?U9#3@oZSxeOllhSBA|IBVU2jgHw-l~_ zi{4^#^&PqTj$D1mo|3EYl)3tjTzyBbz9YS5!z$}7NN+)U3({NgBu90wPnP()*s=tY zCGZNzXDU79(%N$LkRLYBwH~t8J}3HV^E;M9zAonJEHi~;KHiQG|7u|I3W--pyuw!` z@rv@Ra;zLDiC5GUuaJ0!#499TA@K@{S4g}<;uR9Fka&f}DV&WCHW|{jf&wO=+r`xPdw)yIadxZW|xNZ>r$K;b?94%jwTsx$OPl=H$ zh&WD;m#@hQa-y6hCrf?>oatYeQ{^iGw&ksfj~5 zK4wGNNH&&DWK(&E>=d3!96|pnPTWfWF&~m$qnDjHfum=3mp$a8vZw4N zd&@quuY4>#kvP?VT>eEqA)l0|!|jR7IacO*c_sWgk?SepsYLs{J1fyX?>?2dm}8Zb z_%X+dDLGbVqpo!{v?k{G6^*&kUGO<~{`z9>L&{x$ zmHQpeZHU;__LWn7MtzJMiDgVPF1)y*9G`#NlnI)0hNdJ2F(o5IrX-&;<(r!FsHXf$ zQy$Zl^na#g^vIN_G$q%hHk9M40`G#W3bt?i*jQx6UK@*iQN&o}^>wvvEV8bZjYW{L z2r?Ey#v;gAWK~NWi>#|}V-aL5f{aCwu?R92LB=A;SOgi1AY+kr^=&M&W(ZgOny+ij z6+iQJIaN-R(`AC>U9KHuqZVY;f{a>_Q42C^K}Ides0A0u_avhhYTlQNj80d>48w0IbYGa^PEo}_6YNm~WR(L3m~5hAfF2$p9>(L3m~5hAfF51W8sxG zPZQ70zsM)#lal94)(j>C&sZ~wsBWH@SHg@n!-(g{KJQ69Kfh)k@%+@9k$jGDV$E1S zM=-0&>hfk;L*625%389vtRw5ndNSVDMRqV7vX<$bo14vKb4hmiGTHJ1rqEhv^F+57 zMpw-y7nr^bWQ>fJC1goiO2*03vWzS%%gOSxg1kXil$B&BKnm2U#G0lt<-H@|Zj>Pncv1w%?l+p8jTucPT9VX0}%>f038vukwoIt~{o> zY7%#juZWM_7@qvHdW1Ws@FrPRR+D_PwEQz-)A4WS5u40gWKCI1)|Pc-U0F}Y%K=)~ zK$V}7gXFWemm))K%OlUrq4EVeOb(YXndGv<&-;BG6t8t^S9Cg%sp zd`RX(=0+j{5)qJyfJ6i&A|Md~i3r$C-YJ{QyJQPVmS!5_B;GAsNqPu1jDeAUf{cOj z&yrCTWkylhUfw4=$ou63vXksCx#Gt3k4mn%QSK#s%RaKNd`vRhXPUprC*+f|pZu%r zFQ1YF zz$_kUz?!@+Wyr9+xL1BgD@J+6WODAtED0WQ2%kn5F^Wdt?44ugc%$H5r;7 zZ5~U@+OocBBgB7?wr3Fjx!Uf8{B$?puVS(fzp8J~BT%29ni-PwoUiJ;8RYTAm9gY; zbCet{Uy)bdRbG)ijlwoxmA}htGBkZBJsFX{3}lRql_g|JSxUyq(z1*!D|w=sC6|{KWF=X{ zxsO-+x5%2ZmZa@aUq{xJ^(66}b z=7E(p8NNYsgz< zO<7CUmUU!ZSx?5>S{TWgjAT}} z;i=_O#yL4FJ6peTZpksb#)R}VBz__B3yEJy-$UXT62Fl6g~Tr;e&IwpNlunil$@8*EUXT~%C0Q(qcg)4{$6w_YNd%>(eOJ!LQ1TlSHCeYlrvMfqQ}uyX>+ZB=d;=6%tIPms~gCEx5>UXpxcj*_G0 zD{_o{RgRV8mXSN$vQ~ZS>DRB4w7|{tb=47BmXSN$vQ~ZL9z~#b+7^NF5b?!v7u}v8_OoLsk}pWa&B3oeY%X@ zWOvy^GS;P@u`c$Ky=5QSSCXGtFZl_{Pe^`3@)MGuko<%{hK0*UkeR+&UfHg7FP_Jh zkiunS?D>hsMdYfa<+aIGCh=@ZXTA&0Jg*w=R$Vs1?!H+zl`NIAygFIRtR}0=n`I4o zi>xVY$=b4xtSjrucw0VM%A_YPZ*Eu4m)EzRM7gcX?c}|Z`(2mKBpW3!Z|oM(s|w#= zOs_JzqK;fqN3N(NSJaU!>c|y!k5kI74zpo$@S6uUdA(dKJDI9$%C}uQK13^W=Q_j(k_X=iI`9^evlmtz0M9 z%MCJ7Zj_s3hI8*9B`29@RC89IljmiTydW>iOR`x0A}`BdCC>n}4SeGTUX{PgYce$H zRi-B+(wBjZk+HIbEGbJ#zSo6$OUp8{tmK{%>dVUtvXZP3URu_|zeU!RwPbBsN7j|~ zB=`3)J@@xueR-RtpDik~eunfjq@N-E4C!Y`KSTN%($A27hMf7cZJhZdXa4xC94w!c zL*$QkEG$F3f6_*S@3*AS6)ihscYIQRN=qxG+}8RTuhP$uZ&F#zGp+Q?4)m~Hi#yZ9 z%xbc_yjj+ex5%2ZmaHx7$hxwgY#3%QyU2Gm@-7(rS(!LQxtYp$s?7a%)Ze9YOWPiv z42XWzg1e)N7xpK&on4d@+@o?^l^HF)-~Q@@V>y>H-wcc1dxBnQzAfj;`SKn4u3RnG%5{=w=$UJSOq3htCYcfL`DhZk&E)e! zraUXpNj^KIrbu3pJk?FjC0Q(gk(cGKlFu5MhI=FMs{CDElc7ofGd&rRz6@lHjFly1 zNm)w9$y1ZG|khjR1 zvX-nZ>&UvYo{YD(kmpS9`C8D4v$28&OYP1t%1u<}{x8aRs?1$rlTY(L(xS!Mn}b>=Dj=u$LYgoi~#nX^xVkk`H5U6Kb4=!f6C?ZU-EPLgNp6u_>&5-U@yr%JCys#zVflKaNZz3qcZ;@pO8<=e%kWC%Kq{xIY2%yhnjAk_jvf*+pE04 z$S35JvY-5`>@S~^f0IwkzsrI0897KkD+kNxDkT1)Ta+Dk` zUy)o7^tHmC164+$nd- z-ExoIE5DN|a-ZBUQ)QYwAk*bRnISV}mOLbRI+P=oEpue9%#+{COL~vw6W-}BlIu8> zf0b9{Z}O_ivkh);#|XWFGQ9!m4M=Z5dIQoA7A@h49P<`gQ`VBTWgW>i7pAEv<7HFZ zDxR7!o5?$6b9t9+A^#j+UbHaMPUU-LdwHMiAn%tQyNW z*i}9vyUFgdhkR7_l)Yqc*+=%3^c(iWK)oZbzwjOhNwx5ZS~%acY}3%UkiLa4$YFB0 ze90u!|NQoTGQD|TUI|YxTE=I`XBU0WXUD~J?0YEAELuyBKfP!bIo_-$tIL~Z4S9>K zDQn5vvW~1P>&bXq7dhUn&vEZ!cOhc~dAn?=wKtYcsmZYWlCha;noI4^V7x`1q%0-lWNBGOmX+mXd09ac$5}FQ94krMFJ;;<(teTl zi?m;){UXsEX~mfB-24^xEGgzn@(^Y65R!*x^4$W?&5pPuk~~D2JcQ&SBo85Z2+2c8 z9zyca?1maT)(6pO$}@v^DB!Ye-weXXRk|oTSB3 zPm4oZ9Ma;D7KblN+8t%u9gdJM%aL-F94%jwW8|xHtQ;rD%h%)tIZ;m5yPhInms905 zIb9~mL`}a@Zjzg2lB6B6g4Q&Q#Gf103+6i7i?mz22o|LC#p*$_m$g}dCJTHsPNK;!* z;XZr|czr z%RaKN94yr)`Na#~k=-pkFURf{M&2o2WSXu)yp`>)VB{=gZlaxEAZHfHIR$Ea>}l-z zD`J>(!SI;+QhUjs&!(SdSfrVH%_0r@GLSJcR+f+@WhogaOUp8{tSl$X%L?)aSy5J! zl_imeH4w5s3}!7^Th@_vWj)F9VH%DPa(s~EgB%}Z ze;`K&2fCWG2l2ZN=1b1a_}EQU{UkYAPLZ$6sdAc}F5i#|a)z8KXUW-ej+`sslyAwm zupp?qI1lK+s4xJzun1gl$+#cnIyN!tuh*B&MU*6M1}Keawn0QEAwQ&{675l?e^S5Wd0xvG z{7D{@$K?t6GskOyJ1I}eLP=klF~FnsPVeB+dXd(Pv|gn3BCQwWBt2m2HLs1zJfkoz z;5m?*b0XYPX}%@jmhbr7*d`~Wv@5@Erhv)Twz@xd+O; zMb?zHWNleT)|K^SyliOO78#&54phxEa*%x1jxu*bvB$U*${Z?Rklde1&2agWY4>bQ zpUIt2tELs$eHxVKSlLEScw3nNc2^rYQRZng$~-mwcF%yPrcpgNc*NcjzYb&emQPtL z3bxoZ7Tcy?v)G2W*{d-u4V%bjvbk&_TVHR1b_He15`H_#d^60M)0yjr=G$_foG;&z z?@D?n)6+YV-ih>1q<12{6X~5u??ieh(mRpfiS$nVSbieusnmQbKa>BI%jLi1=kg1= zLejIDi=K`2Y@}x+JsauSNYBRA+H!g}<+UoWlk4RMnJ72PO_EUs(l5w)MEF;TGo|Rykmd>=bUB)0V4NX>&So)yjNGc99Ruu8#k4)hcmnELW|} z?y`q`RQ8m;WN+C=_Lcp#pZ_ZR%ctZ3IZ#LK897KkD+kNxa~%MyQq-6sF1s;kh`dm zyQq-6sE{_u^t3^w4I*t28_3&bL)l0+mQ7?+d53H!@088uU9yGzlWZyPmaSxK*+$+Y z+e*Icoo#3*@0IQ4eX@hRUp^o^$p`h0I?IP-7x}R4DtSVScg-FBET232kvsa4JNl73 z`jI>Okvsa4JNof4`FMCOp`!bX%1>Zb_oT}Gff`IKZN!MuNyPs_i{fnjz+P3}}T z2dU;+Iaod?hsfvUQ2Bx!CSR1p$Qg2`oF!+=IdZOiQ@$nNmhFDgP~3$*<(s@*BBYu90gc zpJ6fY2Ca8nSd`G#ZI|E5WVu7`l)L0^xkv7m-^moYPwtngGEE+k>GGh=keM<|9+GGD zz8HVgN*QhARr$NTCPUM6GO+FUVq~l=Axp|ql5Y}bn$nt{&t4PSdSpMApxDB55JWJq zji$NBUgbOHj!f#oQv;_Gn(+kOxp6Ic0`5dYOTKk3BcY-Hil!M8=1*SAx6TpYcn;29 zovyjw&|C?cYliA)YOYzDYqsW^tGV8^SA!eE}Umw}9tv9g3L zDND&XSz7Ys5A&9lHsw4Tqtl`|K9^{9U zukg!;vX-nZ>&UvYo{ZP@oNF)_=NfpM>(eOJ!LQ1TlSHCu_%W|X~B}dCwlNkTc~hIa|(=b0y!M%9L-(x8*!J zU%n&Xl?&v1a-n=*E|UL{i{%G$iTqG5l^@BE^}arl%T)8J%Ad)9%H{H3@^krxTp_;< z&yA`~elY(nSIMvB*YX>=TCS06)jR>PfQ{|nBU4|xkK)hyX0=UNA8v1$rQOy z?w6@DO&*Zx@}SI+nKDZrl2Jy+W9<_UT33$Dm3cB>e(&5XQFlaghDo_V{wO)aq=qw0 zJSLAz&M>Jtqb2+zf0I|`@A8@qP0z`|_7C~TjFly1Nm)w9$&hFxZi7H68Y!!Sbozhyfn5mndick7G$2nvEA&OPH}8_cHDGw&V|YQ$vHOV8=7*4 zrsSSTrkO40YRWflO7cw6s4ip})tUjiJyxq2nJYzOd zO*7ftmO!4dDHRpHTgWzfqvFXnrY{2-BV%O=SyGmgak8{5Bg;y@^O)t7mlfmE#Bp=i}>MS3UUF5^EtL!GpHq1q~L9z{!ZIEn(WE&*gAlU}V zHb}O4ag1df%4C~yDVA+0_mh8>{Ut4q`T>$`Lz!%Yf0qNptd|0^jX6j)&q}fl(~xbD zY=dMQB-x~L9z|LD#XH90{})Lxn- zC(9}Fbvac|lhY;Nl0ZvKkTc{=IZHAwO#V}nZKxsJAlU}VHb}O?FT)e#&XH}*f6G;Ed?vu=V6Cwt;+QcDEKnzOpjW z8URIDd$cnO(tSoPoRpd>ws;nlfhx12uC105X!{kvtiG^mO zbI;Fn8|5asStiLXa;rQX77S0ZRyicz9g(z3%Ct(PRU)ktX_ZK;L|P@%Dv?$>IKx^c z(khWwiL}bW8Jsn<$2_i#4K47vGKO3kL#~V=SH_SlW5|^;Uv$>T&J_H2DT2 zmwb~0ukuX}I7mJ#2g~Q=5c#|uDqoPpkKS^w0uR5k*~_JlB*3&GhV(XC&-DCJBEkW^|)ggxnmf)V;H$(7`bB@ zxmOtHIXASax0Iu~%3CH^%74pM@+?|LWUF5@(@19}GM`Sli zB&Mc^d{p+7y<~6MNA{JE*?x%hlh0aoim2CckD)E+MZL$DEuL`qyg5pamaoV$@>Mxj zj+5i%YjT2|C@0Cua*BLiPL=W`;#zo;pE$=J5G>_RJxY zXRwiMESt!t@($V6qOm=5I3(Vl#lY^8@ik?}*Vt1szNXCh8W~?B<7>2im|@?hhkucb zuPHw%Plu~s>cZWZ=6RF5Ecd^d%w3jdHCbKWENjSHWKCI1)|Pc-U0F}YYniu7wFh!9 zM~d8Qs(pH0Ij1W6TQcWVW-VD;){%8(J^7&QEFY3x?U~z<(aKyKXamLCdtWi zihNy8mDA*O$;g8(Nsu$-Ov$xa>SxP2a;|(+z9o5LiD`IZ3Fk|mSfb1mOSnM3Cl|{1 ziD8`?-IzQp zfjldL{p4R|f5}}s)DH-M9#)gljg_C4f0qO0Gjfo8Rt}cW$szK2IaIzNhshV^aLF$c zu>28{r-dnV&VW2Ej9kk?o)$)~XyL1pr-do=v@r6tF!Ho8^0YAWv@lK%e;wGB(T(}K zoGPcu=`uln85Rt>#uE`X*G9QXZk9=Mi`*)=$?fu6nJjn6opP7lE%(U1@;k|snXGG{ z+%HpQnmi!W>>B;A@}Uz_wqz|>HlHwKES1@(zWkbtyR6r zd-o2KBy5nZAUP;G=Omzmh$u*sBsmGFh^QFGaTGHmVpc>zL?nqM5l{h9F#!T1A|eP% z==cBJy~lAz&wGyVneVzj>f%?cS6A2WuC7}3)T*kd2URDgFb~OZV&LSyfh()nyG?Q}SzN!^&_EgH72;HkM6fQ`t;5mn~#V*-EySZDd>7 zPPUiaF~pp?V+grp2)Sbjxnl^qV+grp2)jw{7^2J_LpZ=%GolGAYb_<3;M3W)y2Ho< zBY%+OE9kSFyAcizv)^n!e$7tqM%X>9C;Q3whFSg>vxwvuTPU9|i^<}$ge)nuWhq%& zULecZ-?D3gkz?sTx*K6weY>WPvK^P!=NjSt*Q~HCsC}AqrLdLBg-W@q%49^PTwP`I zqEcpgUiTlaM$GiIb2x35k=is;nlf%Nnw#tR-v9IzPfvWaXeo5>ckrEH~7)LOQYZDl*zUXn4NWkL+c-y()XVmKs*Lt;22hC^aF zB!)v`I3$KcVmKs*Lt;22KE|tLZ^^exO&@uUyjJ#={eqzX16((4_E!zDHPa82gXCa& zy&NKk%3*T293gLzBjqSLT8@!p2GM{-wUb^k}X8hu;;qg;({ekym%&*bNUZ4_T5mi3=Y#Akjb z_sG3+pZr?xm;5#@Qy!GxXgME}-^#<1eF9qt`vg2GPs=m%tUM-a)=x%hsoh`guFqH zl%wQmIYy3^*mv;0M#mS^PI z#CQD{h390#bb$<|lU_zLCR1cCnOo+Od1XGy?=dj90y0&m$+%3H88TC5(W)#XQ|->H zc9E&pEF=rdBJ%to_{$Y`FEAFDC1gp-woH8~Sz2BoiFv56VoQlQ+9ZO*YLW>|6$%Vam%UG|WD z>|3*Yf%!y46i7sY18fgMd~NSp+pJ~Xz4AWGx{GApMY8T9S$C1FyGYjEEBQ@hB9qPb z8*+cS!eZh6AK0C_NKA+oC9xl6Vn0-TMTE=WBEqH3B*JAcNrY?Vt8MQPt+XkLZK)x) zH7i=X%C!k*W!2~#A-1(=tJ@riZLM6-=D^*O+*{mhAW^N!y~W7A#mK$I$i2nLy~Wr{ zww7&VTiH&wmmTF)Nn66JZjv|4TjX?ktK^Cernybdl()+}za*Nz5x5@YA2l7L?UG9)OeXLD?t?VnwLq`pH=#V^g zNFF*Q4;_++4#`7@*UKSts2nDT%MtPhIZ}?2qvaSmR*sY7#_UQm&G#*RX5L2i_rsf@En#vNR%D8j&oGm@YFUXZ5McN_=#g zW1sx_Wx3fWn}uXySwxvRaTSL zWer(V(!%Q|W3Dg$Eay7b6s+bn$|K~j!~0{5{W1= zRT5EBj!U9Q$`x#BbN8EBNnR)~l9go@d9kc&?L2qCnblsw*ZCsfTDFmGWjooy_GR4nW_FUDWf$31 zUM9QA?y`sMrzLZp>@Nq%fpU->EV&QpvQ(}f+SRK$R|);lt1WjRFYndR({I#qELREH zvt%*j*;#t_E84q`qUHYBauE5=_8Ro66qt6J`(98kvzo;r+W%_O0VzLBdVZs zMU|^0R&`q&T`cPbLHGP*aD1;Cf@tYXtEQViXn-1*?x(3i4BNz&8sak z;cjZ?Mx09xajsd>zEk2{v$AT`z7glzvo&Qsdu@1*YnmQ!x14L5OfMrDlPNNn%q{cC zyfUB6FAK<2nI_{hU1rEknI+GY1!W;wSQe37)6`{+UDJfc6NkGTCI&Z4%4}ImmX;St zjv1OgOeAhoavlLWkAR#>xYJPO`J?BD>1V zWH;Ge_K;U3c6RxaDBJ8Quav#yRkF9dTJ}kN+U0SgY@7aC*;jHcGBsR_jQ!;RIZzIg zgXQ&dh#V@1$>DN@yg`nXqvU8gMvj%^!yvr;i z{S&Ra5$T(SWMNrE7PWPc94R(sF7V2%Uk6Pd7GRm zZx7OA{MzAl%_H{>$;rd%$! zTg<@qedbqMGDlSZy*w&^kjLbY^0+)9f08F9xy5;(Kg(a_X?aGTmFHx_kWrITJp zGA2`GE}2{Ak$Gi4nO_!=sWMH*Wu_#zdzbx|+a1a6j^uVna=RnB-I3hxNN#r|w>y&C z9m(yEe)N=>?}RYRe3zSSI>T}XAkJvZ}jZ9dX^)AHV5qp067A{UlLb#xzCOOT6DG}0OSY& zIRZeA0FWa9b6gfLU3p zH6!Y`n(FpiVt>l+C!+pu>h&k;H)-9G)*Wfxk=7k)-I3NEY2A_59ckT>)*Wfxk=7k) z-I3NEY2A_59ckT>JRnFO5F`%>k_QCI1A^oM!J>)1^*a*vo5d18HZMl(|8cz`#Qr9E zK#)8jNFER*4+xS61jz%!^yC3S?$t-~fFOB5u%aXn2;~bUc|a(W2L!9gi)B?=O>#zn zXKTosvX-nZ>qzo|Fb#P?kUSuGspM`4%G~XM-0gte?SS0vfZXkX-0gte?SS0pfZXSR z%)!kOu^>l)dCtvbQ7;2+#JBu(~t)Q2TAgP zP`+N02ZSk_QCI1A^oMLGpkgc|ednAV?k%Bo7FZ z2L#Cjg5&`~@_-@_^uTk~|=kUy$Shp-di-I>$H`G|2;kN$GBF$R`!+s}MAh@(2zsqgj zBB#q+*?3Isp0-v$}c6pX_?AVwUu9y3*@VEp?pm)l8faM z`MO*xxhs}AEYlKrQ!bZp$rW;?TqRe_HS%q_R=y+G$@OxB+$cB6cjadJp4=k0%5Cy} z`GNdUZkIdcPWh4CB{>Vr`u2(BEG*^S@-uy|FI4_g<*(!(xmWI!U(5aSfIOJk(cnIg zs!e)?@mqOVekYH}@8wbXgFGfrB))8U5$Dg$lk$}OS^gqV%QNz9VrR=Nj;hUs$x*c# zN+-RHWK5>WTr#)JBlF69GQTV!Q)QZr%XFC`Gi4SQ<*0gBV>^Fl@+;KHuTUeuLXAZe zhg!C^EcY#bvMXEAqTQdIMY}&ai*|o<7VZ9ISGJ%rKO;R2=9P2YSVUoc*;a6$f(m*UE|m zIs0qn{IY;dm1#0A)9rKl44J8#ES1lb1!W;wSQe2*<@vIhMc4GTm?dOMnJr7n(((dX zM$+~&J#8=2_9FLcG_T1SWwW}hA-QjZnp(29tRw5ndXjtEm_{)zXOyknKsJ<(WMkPx zHkHj}bJ;@n(kHq~_Lf)6KC+*d4@ZtH)&6pT94H6L!SZ@dKSYviuYPOJ4S(Cb0+}U_ z*13qx5(k@Cp_gcT^U{`Uk9SM1y#}-CE253^-2Kk8GxREQ?eQvd?eVJHRZp%xUd8>L zyo#JEyoy|Vyy_l3%l(4GY;oPlBo<643ev@7t zE0e9S&M|s%DD%6*Wb3OspI#i3Y<);v@Tm_!q0=hBOFsQglT zaZIxHA=&znY<)AD8Pa!#^qnDnXGk=G7s`udWl5hJ_4KJ>Ras3| zmo;QfSxeTIb!1)1xp-bne;i&a>6N40K+-oynZ7w}ESt!tvYBilTS~I^@rlURhh*zR zvh^W(wUBImNVY!gr0F}$E|Tl+`72y^kKJT<*+X6~uLy#2b?x3Drs*lKl)dCtvbVfi zawdQ&uaVcvzLIM*s!yXA$0UAXngMd493(kIKs{#&kTV3x83G(8hfB^7P;-MEDM!iC za*P}+$I0<>f}AKP$;t9YIYpAKkEKMmJ|tTolC2NP)`w*4L$dWD+4_)deMq)GBwHVn ztq;l8hh*zRvh^X^`jBjWNVYyCTOX3G56RYtWa~q+^%kZgTOwmu|VACj#P$<~Kt z>qD~jA=&znY<)qD~j@psAAS2a#Aj!CvYBwHVntq;l8hh*zRvh^X^ z`jBjWNVYyCTOX3G56RYtWa~q+^%kZgTOwmu|VACj#P$<~Kt>%(0^P;Il_-OXC` ziTqUVmY>Nlf}ldB;7j?H+#~nOee!F$UmlRO*1YP7=5SJ;mS^Nyc}^zGFpy;HE5DRp zB9m-=NVYyCTOX3G54lGa^J#jr^;M0B*(#TlWb3OspI#!HhHQO2TR(BA=5%_A%!aa& zY%H6|rm~rAE?dZ!l2(P+(yAb>3eu_|tqRhrAUXSxwgo%M&a#W_Dld~{^kbUtvWL7P zvA5=2dWp=Q@=DoDUL||Wt0mVQmVc36BAfnN*;n?H*UA2JfE*|X$-(k^IYbVX!{l%| zLf#-p%29H(93#idadNzzAc+EaSCiyqd83>nr^;!Gxc%JdQ;y+lYa5z>#LlhxHOs|FC>SJ}MvM*?X;*h-aS& zYSnyzUZTV07uxm7)NmaS<(Cpis-2;i$jYzC1@cw7P`)M?$;EPsd|fV;T+PEAmT3vR zDVNK)`g{6Kyvx62)Jr~F9n zk{`=YB)?h5v){|3 zlHaeR=9oN@I9l^jdWp=F@|65p{vuDyGxDsqxN|aL(o1B9(n&9Ai>Rk9LfRsvEkfEN zq%A_)BFrcA%K|c0rpdS@YZ_B#$V{1)I8^CwdWl#o=p`}>$-=UTENWW{Jv~P&l%c1` zEH5j_iqc}Ud+F(6yQ8PatSYO?>avEcDY=@uY#(}hY|2Kmv1}rn%4V{;Y$039Rh2F1N}NybAwbwM86Ju1p0N%F>&!S()R76C-j+GS=(XV4=MMTcpx4HJaI!OD5 z*1pYk*1pZP-_=2D-{#uy>Y%l6bKMhij)2;}Q!bZpY5Em%rCcRf z%Qf_XWekYG; zS$!{$${*x0`J+58PspF-NqI{CEPs)wwC1tiOB}>Z-WEn|I%k;FgNK1?S25Hqe{W>PUM2cknz?!m_tS#%v zy0V_UL@ElUU&mh6KsJ<(WMkPxHkHj}bJ;?+v?$%T(&uU|+sL-Eo$O%ow&gCvPO`J? zBD>1VB*(T)(_QwEy)=ibWN&%3>?8YW`Cljd%K>tr93%(J>oxrlIWqBGrTghOI#KCi zdW?RoK7$^k!O@agx(-4lT*8Fe|m$gtj|cF5bZyG zLS}iZ=V~64oZ406^a+`jRiiyDeM0sunYGLIr%%Yr_4Ikm&@;5V$R>J*OfqXDnYEG3 z+DK+?B(pY>SsTf$jbzqFGHWB5wUNx)NcI~fvo?}h8_BGVWY$J9Ya^Mpk<8jiW^E+1 zHj-Hz$*he<6FalV(=%ii3xdpk^bCD=-U51tOfqX@wj{GQWio3cnYFQurY|eY$?~#- ztSBqV3*|+!vaBL6mQ`gnSzXqUHDxVXTh@_vCCim}dx^YM)|U-rL)l0+mQ7?+*-W;O zY(31Ul|E5x*+!CCo6krFTO_kKl35$sdYGQA2iba%tq0k9kgW&VdXTLLFPB#YK~@>- z8KS1Ayi&4lQNy-{Y+J~BhiqGTjl4DpN*A=u+SK$*>^$#zdWOvYsu>^$%0Y6lWNTx3 zwl*9phsoh`guFqHl%wQmIYy3^SsTf$jbzqF zGHWB5wUNx)NM>y$vo?}h8_BGVWY$Km20$`v<6M2NrzBSfQ1i6P^W`)0S^1oNUcMk- zlw{VB+2(TtmR$C9dJyTqEC>#5L5sBiG6Ga)aC` zH_3P9X8E2ZuHm&?`g{6Kyvx62)Jr~F9n3W9=h>lw5eSVoO%Ske87uiJbkxg@{YU(FGD1A4*M`lCWNH&&DWK-Ep zHkU1AOW8`cmThEP*-o~X9b`w@Np_Z9WLJ5a>?XU*9`cIBmeNb?`}p>tug9Kn|3Hr<$H3A+$y)p_vHujL%ChGoNdW z%!Enrkr_%Sy^Lf`rpR0}x6C8+%6u}vEFe>5nvBbInISV}R^o%gcIOW36}?AhAz4@! zkwp``v(MNyM&}>4YmBhCEFnuuT0iPb$_Ob8Ut}$W>TqpZWwS@FV+50D#eCdVsMVUh+x#TGilf!N8 zqA$v7Zjg$>=!;_iLSK|QMvj%^x*J~Yc2a(Uldwv+0Xi- z&|1rW))$4emT0YIKkJLaInr9oe%2R-Psw@mX*pj$BNc;LUlda+2D82>%8J3PFN*TZ z@)fy2zA6{W*CbbS^8OdA%+;KfxtbHXniCa?(HCXqH|28qmZo1JSISj#wOk|LmTToZ za-CdHJX6N{qIidj*_>iFrbBfs$cD0! zY%H6|rm~rAE?dZ!c3fb6QCU@eYuQG&mF;AErtE6j^s}Z|j}&&2on;r?yC5z4T75lD*~CvXAVi@8~+&Uk;E1fWF+8K{DKbk z{DKazx>K*>7j$?Pzo5gb_yrxF-K$sa)2lcq#;f+L{(xS^R>t4@Mz8u-uR3hc(x-JO z^KPzlI-c2^o-Ni}dbZdT(X(Y`e)A#w0eZHq%+koJO3xO18G5$N@>WmJmRZq0uVov? z%Bs;GoSrRvmfx(%et@1WE3@Ws+~-}>Y5W9tY>~VO!=Q@F6--~-48M_`OgB<~Ajg`q zRMS7F^2=t>1;Ze>mBXVL1aT|JZZ@N9FbK}WFld3^@5Ug=FKuo?kS$B1k7mfJswrV* ze~TF_ubRA8j`XhljaGK{S%RRLtcGD=uL^_0swplDpie2Ha+;N09Wz*OM)#}cP7H!{ zm6xkr%F2<=(?{217}T|LY$*mR2SJ9)MbW#-rf+9Pwj6w{JM!gV7@UtjHq7+5n(m0| zZ3%>dK3A+KGEwA0@xdx2zod z+Du7)UlXkCMytLIhCwBn{I2c02!pohVrJn9&+oHxbh&D5y9#aX%HGYlhnwe3y8SYS7>!f?m%D#hY0{N!tj+!xB>%zcU*%vW`wPs9fpwssl(~@z| zTfMVS=feXkzoaQY$1upFnrBVFQso;})_h`jP!6@H@X-TSAL;Xk-K`whcN%IR=lvz- z$|=Rnn4XQbR$0?T4_Y}|u9}DCPx6pVE^XV^!kiXqqt*Lus?oRLXIPCpp?YmQ-oD2$ z(6{E*t_BnA+1L^@+9|u5?jx1An6VdC^RmjxPjn6CFeocEee5!OHl;KML1inuTg=$C zW^_jWVg{>KpMoihE21D$jh1<2Tab58m?@f1inR$ocw5ud$1t!pDhxuaNwN3fW9Llw z7c;W&-A8&=tf7^CRWl{a3_n%PKn#PJ>dVO5W|VwyKUmr8v-_HQwj#!Y2>FLTrZqXG zf|b3NwkvF9_nvB+o6%S^CEaxS-Wykf>$ zXc~QM;T5V$m{9@K*>({oV;J98^~rBxrk+i%T_vsTZQTx&5n8Av8EI|z!}Y2XntqJR zN6eU(SfuqKvTZF4np?daib0Ue%AvN1;C(Cm$)>wd>YcdusxPLR8+U29XD%&^WV_%ux)|nt@dp~J4;Y+5|I_8qw=3}ZUZpKELUh7+Ix0OSEn_k~+ z__C(avWmK?yjebC2B%H8&-CSGFL|FC+m2zf&G*YG2SHQxp?xmWZ&=wK#xQxj;7eIe z%+?1V(|h*G_f}fd*fI%&0`{!`z>Jd9zd)HTJ*$abVn&mtwlB9s^-rtjE!j`nx*Z1I z%CX4w+ESu%R(1(9vb~IFH)igaA4)9^uWdg?+mSzD_0eZ~Rz~-ze6#A~DnD<=Y_0H4 z-)1yM&uVU=);HhXYQi5i%~sPFRQYzf(v0$}Uh@fOSvgiyHM#Zd=PK`2eL2&;qna`p zYQO1S6RU}7J@@(^y_Qn+tftX-`|4sYZ^_NlBx1YsZSj%u4xvlriZj;5+sk5qeE5`6*9fP{b-eH zHp&HNtcc2AN`2lGO&{B&XD6xtw7dnmy;qh;AK4b+qxtf6nOw)LeF%b!tj6s$ee#{0 zM>z~GwVJ3l+M_T?4HgB(f}qlhz%_5#zFYYGkZVQ{3ttY}g`b3b{K4>0c*s8=9%J~$ zIhWUExT3B!L#1Db>h2OZ(JyjMT~oipwRK%xPj{^w%rMeTbT_*@+s$yfK6_@=(C@9KN{YyDt9(ogg^`#b!7 z{$bDkNY`95WOP)sZ}m}q{k455`+*0=A_5mEiVqs9%4n)e$rXn-HxZ`kmm zeq#TT*Yu5g4IDITw4X-xHGamBVPl5+Swn{R9pdkkv*qJ*o_t;|kW1upxn`LC#SP40 zq~9{!=CghH$bQ43_QOXG8ya;RVKtwO96NHD?>)+<{BqQwVg3F7QA7KV@P|i@s#?t- z!|MJN*6`=BW)xwqC@bB-!Zyl&+1 zsEXxm@*{#gO?tM-_RE?z>6RU(H0z38+coK#9X;NmQ~T`bP{;PovtzZpcIuEFySW?F z#I|(n+Ace#DBn5Xe8y2)WyS>xCm-j@sn5NSt&{qcn~7s=+%@@p>`c!1mz?pbqSp|9n_IAKRULKKCa%j|V3o z$ByRI$CKk+!;<5atmHV~Magk2p8TA#ZH#jVVJzo!rZmraUCwgKT`uSGKbGsr{s-rk^{3_)#>caMhw+iD=V5$E&_3lY^5|A#hNt}k=6XdC#jD5b z#v8_)^W69#3iE~e{q=qbkCq3quv}O^tPoZVEAe<)5Q|=m7DcZ{3!}wBSn7@-7Eg^A zjAvvPP0!8NA7=20GB(pn;2Br;58=W>WZCculv{`BQ@vb@txnpHxaTeH752{YUkO}#!Jue(aac917FG{ygf+ujVePO^Sl3>Ka`h^@@5&eVEtGplmE( ztbl*jFZ8eZMSihg;$Qbm{TqJSAJxUuV;Ql`C|8s_Ryi~2_cqJhyM zZQm|FIj9iJA4~OH{Wkx;|GG$I-nD-|mpyC86>pVL+x3BC`G20sMHf**t9!HF>YA1%ah+OLzrso>|}mw$fG z|NXa1OIzK~@VEJy{&tSE?)0<#UH)!=&mS(8GybeU=MzzSlo4e{S!6-W_6_t)I zh^j`_qUuqNXjAm=@3#rIG8fMqbo$rzKFs&;Yk8PI_{lYNja*~bgrmx4uDNUBTDn%Q zwQIxiWjoj2b#NVBC)e3^;b`+R*UfczJ^rAljgO&^#(EPZtP#Pn(DGty_J-CdMxNMDk^ zJbg|2hV(7z+tWWu|1y1l`r-6r>8H}qWkeZyGvXNqGm2$CkWo6LLPnL0npux$Ey!Av zwLNQp)~WOIo>%O=D(5xMnwj}{=KRc;GnZtp$Xu7XC39!y=b8I6F3D(|(K4fbM%Roh zGJ0q9%NU$_Bx6Lz*o?^;H)qVuxI5$ijE6JkWX#WaF=JuI(u@@uYcn=wY|Gf0u{-mp z%yU^O8GAAgX5E*sepYH$!K@Nl<+7?|)y`^|)iUEq#_^0_GJ~v+nJJn1Gcz&^2QG6J zP39;!+Kq8z-8eViO`vCClAG*qbW_|^H_hEdKf^6d;5z3Fb$K4Zdj(d_Gil^K>_q3buo^j8*=jg9^ z!M*5Saxc4A+yeKiTj*YMi`^3Ux?AeraLe4AZn=BQt#B*dDz}tz`Nvf1WSs3;Dvn2t6(5`(nPhFX2o2Y+s6gmkWFu zU)GnS&t{RY;49J(bD_V;SMeA7s=gY1GBrFIjeKoi$Jh1s=%KmP*Y^#4L*K|Z_D$%o zY37^z7QUr#19uUUW%xX;eRI7&VGE2QDLjSmZyp7W}`j1B?Ez zYr)(9#agiK-`0a&tOb2)m>^s|8L9j|FzZpM{D_yw({4Ol5M1- z|KDyKo&Kw~@y~A+c4YD2$2a~>zudp&SNN5Fm0#`G__zI9qS>0XxhekRsDb}y>}|1K zsb3>HYlB<-KH~j-d`|4QIa(Ym6}@Y5>mNmdIme22ENllhcbne&mBWzZQ2I2NsOBe* zd zm$sB6BH}}vJNFGf%iKeHA{GX3h2MqNGj6@MQ+X%LtjuxGy+qRURo^{0?cU)kZ~H_a zBXbC?Puem4GX6$EVtf}CBF=YVVPbt379rkuVNqg!7oJbt@4{ll{w^#YoZxk_U^G1| zy76sugvnfXMf-Vep0uKArPC^phO*_zt}XFVk9eoyk z9(@t*jrK(csLlKP+UR?mn*WY@<>9X-`{|1CIC3ND4GP%191c#iMKlUytjU|hbbAkc z%P;a=mOr1r@A#x#Z^ANZ5wvIAF}#uUWmCf$;iPb;9~R!?Z(!gUp1;wGDfq!Uh7@AQ zQHKdaY09nKG*37-w03!e{P2wx0eazorOx{mA}&0`4kZG`$Z zaxteTgWTa4;TJ(Zj*e>58^i1_H-~mc!o9orC?dG zJa{Wu5qun!DbTsV6$Sbf7+7FLf$;^V6_{Dz-U71=%qj3}fdvJY7FbnaLxF7tb`|)t zz`+7X3!F*~Qgf%qQwyhNr&dUAtQMe#{HC!0J7QPXF8GaS+3HOF4!js{t@Mm|vD@JBz`up`+1CHijVU2K6N|42| zdz+vL>-Qk~>$w7tPn9#je}20E!JPAP-*_j!-Kh+rmaAj!`98=W<%#k#{{m5JaDG%M zDijotibO?&611Q#gY4*rXk<`28WWA-+yK`b{b%|`tcQ_LTi}2Ho}%T^@}O+AHhPEi z7A{ClwynwK=YRA+(&Kc({}i0ZB~Dy7%4bat3iB!L^Hqu}^T}S1UJuGetE1IHdDi1E zf(o`p236RO4+K@C@7V^cG2isxZX4WP-<@s4wzAy8X@+=MmX<$Vt$Bvl>I=12R|}sG zUka+T-VX^bb;I1SpuV=N2DGa01kE`L+ZMFYcG^yz6i166=5n;EVQy#bbRPF6=4Fd^VLtw*3$wWk$AzWb zCs>-ha9nr+8DU&l#(jn5+&(PtzQzh}KUQ=Hu#)=@FLAb)34CD;J*`pbd{OkYH!i%` z(-R(6^~JH8FNrlgJwjnkUjb`*dYQvI{vy28*TTBotKq`@EDuL73ZLCIw$Dy)%2;gb z#$huz9-F%f*uqW3mbPT*Wtohv-Hq7BO~JNqDzC0Z zU+CBuyUK0{*0Lo`k19);UR9PbJ*zBXdRITgOUSw7=w$@+pfh~D@zgY70Z?fd+ z<7COx&&iUfuahP3j@Y>Z*UM+&RsKBe?F-`7z7WgsxR(6)z7#cmY-zh|Y-zi`wzOS8 z?sswSI?p=h`uonbt)u)iINCppWBhYC)<2Kq{0lhVzlan3OE}TLjFYTamfqV(@jm|; z{>?v*_xmUC?|u$G;Ge_?x%R=)v-=c2MBDA?>D-D7J?kF5pzq@%{{b%cAL0_va-&z2 ztFLN=*ZL}mKThn3%VyQHmCxbKLg{*NAvE|wDBG$Ty`0`v>nKdsW z#!Q4&So~)-u+Xm3|{V)CZ`UW@{X4&MLo&9_xeD++=GX zYtd@|E9&Tlrmgi`=g8%>|uWN37-f~5z|%-evYa{ z7YApfrP0zLVS6R^4E>0A`V)IqwAd?X!v5ijpiTH>_+xNQc*1dJ(51La!CKa!9?X&X z(yB7wFu&~?!gQN&m|^n`3)*~%x1FL%I~SCP!4n_a^&A-tF`YL};S7I~oCq`zd$DSq z`>(-}U$JNbqR|FH8uMt(>zXm76YsX?)g9R@7tucMeB$4MTyrp(2)H;g@JOx`7|l?c zD1JOw3rr!FC`UYgJJ$<5O;l2mIA;NWVIf0J&UG#dY7wI>3u+UEtqAG_>liK}7WF?$hh-W^;;H26f&o2Bz)aJBZKeTZ;g3kDH6EenPcJ?#yKhX=!h z!6=Slz6(YZN1X`95KH|WjI~%QnBYnfPfgPIGg&dg3`JJ=Dl&Ljkc#UB~usC=l zm>9gtFf~|7u9j)Rde-FISd(}0-F?FPJX;aWi(z-x)0ecKzQuYvE!Yv>9^Msv#CgTX zgWVj9%?S>NbHjPTcj3$7E3}@A!lksCZxXei4EKcxIIs3icsR@skAy#lWx}5rDse4d z6kf>MT{Nuiio4=rJ6FwosYgnzZ|F8rIt%HjP{z9@hAP*gZ796myPcX9ZrMbF_} zjuG33PubbIaGsr?3+Ho&?(*}!|)xBM9YTjIICPfT+f;1is1%g+bZEk&Y)KhH%05&_bl_@ z5jlr!H>X)&`mvQe*6xsUkMLeMm`BsxP`sP?(1lOC0eHI`jwjspynY(b4AJ;$n-5bD z)D(kk3LYnGO48sl+{r!pSPcsk=rj3+Z5 z#hAUNpTPJg#^V`JVLXQMjf_V#PR=K<##xF5Wey-&B zft<%r=8Rutocz4W@!xVDKb|vwj&X81B**vVJbo-^{47UQ*4wcxydpeEo085W`DHw@_!otJM(`Q|9kL%wqD0ID3p`e@QqM8dHvmJmB&i%#AKRz zzbVi9P5HOe^!!bIkKdGatzpg7#_Xop_4TjGwHe&B*|l7M8|S;Va92%B$;Ny5WBdzg zkpkA^HtacU+=^1bp2!}zpcLhl?6bvwEw@h2i8eAIHvGpw=1NPL3#||0FZ}P%EEn+q zJEp70{{g%%WmT|>r|g|I*1K!Wdu_}vEYOb3YAM@QbIfn&uEW*)EQ zAHDRk29H;GqWPSOPmY*7ckYt8d*`OkjnAz&_tK|I%o{Rq)cg|jv*%ZvUvK`U&y;@V z;%95TQ1pc>U#js^lb6fB+~k$~3o0#W{pw{4b1iJRaLQ|!z1I7+zKhB&KDPM6#RC=( zUs8NYxh2h(v{=&V^|DJ#E-kh6(xuIp_Ie}zjY4nKTQ=m)EpJ}%X3aN;F8^ftx#gEH z@4bBFTlL@Sw4&L{0xQd`?7jM{)v2qit?s;$){K3-=G&LP-TUpU z*FL@W!?mZ@HdxzfZP#~BywmudE7qM|7h6|lUGsHUtlzS}+xo#9c5jF_l;6;J-b(_Y&>)$Q*?$phR&3q>?k8*vKXV=MH`FAz@ zxcMi?KRNwLflp6-dh*l0yBF;KaCg4l#dnw4U3qtv-8FWP{;cEY-+f;7^UhzE`Rd$P zse9htlW$Mz-dg)g?5n=-s;@8KpSpjD!!OTa32j>R&%nvrt4`x0S+_WfoWKpnkad7wR!IQ5Cv(^T8tql&m6MVKcxckH4 z%%{P$J;Ci~2>o30jU)&F_&+DnP5+-wbSM3PPZHsc;PEID&`lr|vrQwpc{n!I*w);+ z3kKVGBl&jlVf>*}#}6=p{VN>1B!3h&{nJAi_OEY7@BhoNh3il}SE+5$|1D8*hUtHD zxG8$}H^ZHjxBfa*OUci*9RKyezMkto3P!gQCETi^QFLocoKo}X3WiNlD*yjE{7Zcmj@hbkMOat*F1qISQP-4I%B48oF7oTVl5^!;62n*j`+?(j{j<3Cw?qCg zdhQQ~ft&#;%Mni5-wgLf|2{m%xsrbj|Nh+OXzH)SP|j@pez=b7&NQUt&RIH(Ih*y* zp#fJ=6p0$-jYSPo@>0&9`~-i-SgwBzx%1@=S-G=v=gOTG9oCSCYrQT>;V>b^r{qgX zi#n&clmgN1oIU!pL$jzEXN_5qs@ym(f)0zZU*jF*8`e-xs3Tkz8m zV_6P9#3LNM_XnQp<2kSc-+NQjA#!w=#b{4ivceu#gCSK}w~`S`aGcZ4_(s;t83CWJ|98M0^#AVxS(SV62{6tC z_{{@ivBtmPLs%G#%pyUyWgr6*i1E+x&uDafP}lF^D;y1(sq64k78p?IDKO@t9dCe7 z8I=jJz6l>4f&_lPhF9SqfVF;~YJ-1|4}^&CUkq#fZ}0@u@b4Hrh84aO@@4;K@bRC! zU^o4Qh$|bv0A5pT$j_!2{!S*F+6}+q+h8UMuELw~aNrX+d?Cc#L+3O5`q)7Y|_<)yzMThye z5e?zr4`99b;pboYcPu)h9D@&vF^ZZa&DsXM8OSCizqeuIyU09zhyHgm__(@$r;q~RcjPODSQ+VS_;KTD z8%gW~q|?@aJbps_%OCxH!X{5$FoF$nmp4XETS1T%sK&82@H z8UQ*>|2G!gGQkjZaIXVkLa>qGa5g**nd_jSBbeZR0eB1$L~zdlEMo)@+&2JkBZ3BE z8gPdVjZHG#VuK-J5+<^cvll+bB&RCSUocJzd$;}1$ddP29~6Wv{1EhK1Pck7Ags@+ z=mr|b`Ud_Ph|K`odjmu^uLHbqka9?3@Yw~BUnk#&{L28yr3{C>$aUaXjRY@yK1E7t z$A`dqy$5vrBG@r)a0Uygp47S2Rn%?N52<^px2Y-ALTVk{`Lm|E(FW7T(`L|?(!Qbn zPAi~~r2jxaM!!hEOMgNyrq|Qea9Z0l1~T4fe8Y%gTxPPEc1$1UaAp|uRpz(M-#Rtgl$VuuieAvJzO?tU9(2dpLU?`)&4*>_6FY?1$`3 zwt}tYa5xSeKh7x563%y=KR9We5{{J9&Sh|gTrcic?nm5x+#k3lTq(C5)Zz^mJ1w#; z7g-irN-W!~SXNF}{%{*-s@1Pnry=h-(JBYBottMMo6Yds%EBsw}R(MmGA}kX&g21-5^|uYSongDwc8l$Y zwtH>Aw>@Ed-&SVZVaK$ywez-{W%srnVfVG&Z+2(wZrVMzE3}i?sqJ0t1MJ7zhubf- z|HJ;g{T=%!_9gZ*`%VY8!)Ax84has~4wVkgjx!yXIc{{^=@{+!jbn`C87H!nm6MB8 zfYVr~aHmC1`<)IuwL3GMh0b2i!<<8$XFKOR*SHLJ8SnCn%MzFMF7LWTxg2yQyH0Z5 z<+|TB)wRgY*X@*>#+~hM@9yh9!u^hWs(X>U#AAiWCXe?$KJ)m_Bg>=GbB^aq&x>9R zuN_{=-q*d8ymNit@e%pD_?8U#W57ATiGH*Fiv14^oIG&ez=(m{27Wy7^MOAO{4?N{ zfF%K&0^T2VZ&2D`=fSTJerGT-_`u*JgHH~=GK4;4=a7^@pTMBNu)sNiuLTwd)(5JG z(uWF$dJg?+=&!?A!+r`12pSs{9<(TEUC`S>kwFK7js%?vx)YQdR5Uzj_~*kPj`(oo z%#q7Rxs383HG0$sqeqV3GWyi$tD_S}XOC_h!x$qR<27d3n2<3$#_SpM?U>)koE>v> z%%d>{V;aUz8asFF_havmO&{AajyZ1VxE13zjXN^#XO5pd{^#+>$Cr~`n_w})dBTSiKA%uCas5QeM0E%w#33XgWNgUvkR>7O zLw1Jj4mlKZEaXzi{g8~1@{p!bYA8R{J#=VjNa(!Kb)h@Ms9|%%j)X}kIZpDMG-}e6 zN%JSIp7iFVzb22FJZYS;sP5o}_A5+gwy*2gm)PkwCQ(LFerU|C`P8&6C`n2WKwodzK+P-N&OgkBF5$+W3 zA08aOApFDdz2V=79}T|{UOwG@`jF|t(`QUyI{l65?@ix5{oCokPfwX%IK6JV>J{oM zb6@%XmHV%x&lojh%8dClR?kSCQ8Ys`qjM%}COA@NhRu9+=Bk-nXYQX_FtcW6^Q;ZC zcFd}s9XZEoj^CWG=luFA_0_NDI?wf=J8JIaxpU{PoV$7M`*WM-Ve@$NT;}bZ7d`Kr zc`@_O%xjuYp3j@_Hh<0h(+lhu3|O#h!Ttq5FF3y7^1@dau3ET#A+hl5g}*IYxM=;N z9g9*HNf)&*W-Ok$c=_Vrme7~HvxHc3bxGnki&Uh&O}m=$MM z++6YKHQH-}*XF!-Y9(i-!^#0GN3NW_a^A{_mD^T+yz;=xn3b}X9jlhC+P3Q6s|68Kn%Fh>*QBp0Thp-Ca_uW?m#mFm`_0;zwP)7e zSev{~u+D4UuyrBpK416axamvQU8{gRYV591dfH!`5BlnG}H<~t) zHd$_R-ZXI2m`z7FUD)(sQ^qFoCgo;qv&Cko&HkImY@W7x#pa!xqc9XTV`%qwq@g%om--}e6uBH%Y`j>w`6Xq+M?RZ+G@AeXX}`))3z?$x_0Z%t%tTI zZq3;$-m2V&Z41~oXWMJrHgEf2+n#NQw*7D0`E9qirEF8ZNqv+5ru&;i-i&(l;G4g_ zdHT(3ZzjB%{btqn;O#TEFWtUj`;P5V+YfG!*`EE@+_%1aE9R}!Z&keQ_4b^%kG|vb z&hmFIzU%Yu>~|mT7``K9$E+Pocf7vitsT2|e6i!l9mjSkc69D!?6leGwliSo=$(^y zzPfY8&W$@y?M!`d;Cugj@7(*;_jkVk>j(B9?EN6?Lz@r7KAiR8k`E7j_|r#>j{-kB z@v-pZh>w5zIN{@rkBdLyf8z3q-zT#^S@Ox+PmX*}tDyV7?R@2cC?@+s+4?x*3OZvQks za%kj+$m@g~F`2k1vJv@+MvA707Kqk~-VsHKz7hQ{Iwz7w*+y-P5=YxauZvEP?%2J3 zch>Hv&xU;#_SyW;)_r#Hvs-%{_I$8s@1CQ3RC`_b4&58J_szX2dyDo;_Nw<+J9jG&-+jAzp_7mf7bqr{qp^~FF*V8r!VuqtogF_0Ple3fe{BL9awT;(}8ae z#2mPKp!O@(gIf|2paGg0E}8)*Q4x=yK5i;K+kv2WKB#rvI!z z7<*9r&HLXJeLL&hliyLkd*!<~ze_yCICSoi`1`Tn@A&@q57s}d{^8`0ul)Gwj}Lxg z|0Mir&rd}^yZ^l4=bsM`JG}kyr-yGGPCT4-xbASv5!w;^Bfdw%j=X>5izCO6+&xlo zr1KZ2Uxxqk+b>1G$bY5(diGc4Z=-)Z_}ls45`Sxm;l#WjbN2VJKV1J9_($j;-~Q3| zzghpgaFl#>!_gbZ+>c%QbHbl-fBF1%^tk)+LC4n|-+FxC@ngr&ACEttdt7>4eS&tv z`h?2~{}ZE5EI9GziTx*zok%@Vdou9kj+0TRNT)tK_1Wp!r<)*BPH|yf8hL)^M9ScbpGaf=>_`>UKd7O z2)nTT!j~6fE?l_q_(IhM?M3g4t1ce7c=BTT#g0oJmxf)MdFhQy?_Sz->FOovB~>gr z)+*LD)<1S+Y*_5<*k!S=$G#Q&aqQmM?_z(8Jrx@ln;cslE068C%)cCPdBo){m-k%$ z^YW$3_u!Lyx%#p)ZbV#Y+^o2zaj(a{8~1HoQe0k~I8G7Qd4+q$<;oC(H&%+rvXZ zUq#w!BpEbkj}Xv|Lom!Wgo0u@SY(LKDl!C9M23*QjttRmkAh#C;TKM1h-OP<2>H9H zNXVaA2we`qqi;hqb{>A21K&dUT@nSOav+<51mB7O=VqOVNOm?VO?HnX`Tr<7gtw%3yMZF zX0(TeT3jiSOq}G9aYilEPaQ6!!X|4EHTsDKIZBh1Imv_%~ zwTQE_vQiTP#gDVHs#;oF+A$i9*4ff~WzX9cfw1b~uxi`$b%iNZUEG6xU*GJxG!~sM zpwk-~+S_xO-~p&Mx0O@8r5p8h@(=Mj&z>MU8Bvzm*ic_qk`G_SHEJ-1*;r$7TAB%Hy_{PDFeCrRKqJie@1&QNiL1)< z(~}ca?e+Qj*_nCelE$Wd7ds0yhx_wL=hw{Kj#cJ0AKdYo}jxJL2s+c&CN}J6dxb|BsaIVt*uQ%0Xe4wD)roH z&*R5`-&SUj{QSNof32a-s;{<}J29pL1ttLnJf0NRHZ(L;Rz7%m_r{GI4<2S@q{Sy@ z^+ddY%cZtA)mGGEn)c>~nu>}FiNwm9Yr?mOjinZZ1&<#(eb&sGGbaU)3mQCTh*wWE zi+D6kI~ypj<}m~;Tlg9eiIY!JtLF-?oDx677@j?uq2VRS{+HzNLYfP%xh%1y1D`ZEP??z_xSjQuWjDE`L!ui zBH+in+ZVd3($mvbt_v5s!%z2xSP&s%=~y&PXJ@BY*PRWD z;0^AAsiHIiy?6pd=s zOHA~7-l*nnzJNDhpe0|{R9#)&BvYxXIR5_roT|7utbicMOcptcasPe=%MVa>KZ-wM z1fYGyi1>I;(cZm#i#V%RVIA-or0zyBmM{0jijN&Tw%m-9v#`$)78sai7Zyyb$%=zU zSPjD!T8*or0#qpVg1|C}Xab`X(Y-auM1T%PEP>gLctz({AoQ`VufKY=s=lP8q`pcn zFUA30y!h-{7>NRG=W4bjTZyEzGc#txEW?-i6`f1nkRu*$9#=yBqPKq0$3ZIU7zi}=hRe|xM_6&J_Fm6oD!7_Gai zBccZfi^cBl_wPsB@lq80CpSI$AH3@rAMfb6YE@9ss#SJ&FmHE7M?{Z_@$^hfi-<7t ziwRb_5!U8zu=a-M+n*Csp9K%7I(@+N!CQ9xXTbxCju|`)0Rt#o(cg%Hleg}{*Y#@` z&tA+*xpC&`4+jq({Qmb|Fg#b)=?0X)5lPH;jT&?!sJ>}Yn5&>LT>}FH?Ww)Me7>^N z4RHO*g)$+R0o(ybxHF!V$>mMWirTXL92&E|wpLtGqfleyUXZ^?-RTB=fDw3%(xPf_ zSE)5P$5LG0)Y_zIsIM!}%kBmKixi%23+TP?%=z-4?gUdmxnT!2_iqQ8`l$^4lrP)Q zd<~Gj4B7NZc1Tbofn|Yep+L+|VAe)?+@EKVRy7sI@u+F0fSuN+Db3FV^S{uuDk3$v=s;#9EpG^6$R8IGCUn9lCoce=x|uh&0a_ zGx=wZF{3s{qZ;HX6@;m(IFSs(R73?y8t;gcW9+Yr^!)z{U}Y{~Wq?fH2%1HMxU5=I zY$wo^rCkHX;$D%2;o>3`3K4oZRv1-{r4jptO?3wSf@jb#N=baCudhTxkOu}hMi0m+ zECMy7u%^|*$M=UnbJ_&6K+9M@$U>bN_vfE~GMR2}4=*QW5?Lf{uDEdl zl#RG5Oc1hi<;uCEt<{y4zP`UwbwW&qL$2LH3N?T=Bab_lA;moJC zR%M*|;fEj2-^?vlnrRH?tL6fPP`%W2qE>pwsp#1SQDk>9Mw@nKRXmHsjUuNEf^CI- zVLZ+Lr?CsDn{QT%(;~Vb^@ohJS8Qid|>F3B}+mF3PGh28rNrQ zXUAwot`%`B!>;@9d-QZL$+27nK7o~ktg}W-ATUoe-LoGnk@@6hGz%c zikQMt;Sr0$R0MM_JDH511*ujh6c1Ygc$!Ff@zqov)6@1`=4V8Al{Tf0#!O&l%@2>WMf7QHWBk zic7-oizrs{tLRvS;RG;YC<)t*degC$UA;|hxrX!H8jJ_y$S4N~`P11q5*t-<#lRhWegREE|huE?mCK$|^p76?BQjuU*3;BFtz~ZWuMoG-`KgQyhz=jnzQ7(AP^! znfEhEj3kg49~jm#vWw)T+jVQssf4{c&C`e-QQE*{Ua|BlD`lWo0 zeN9rUL$Je?nKQ{`_!)^!BB%k?sWz}6JzPU!1h|`m2_8Nqk<{(+t@rLfq?{k!*5X`xZV3vTSBq#;j`6@ze)`JHN-?YZ-B`UVB(dR4?cMF7J}zwxTAx! zGnR+o`5$@q><<8v<>ZSO_eAriPW{_+%H+wDfm)IT*q9iH5KYp(t(yiP&!|b*7y28V z#b)#7Vc#K4bFj5tm^N`t1Iz?%aJ;-+TdJX7-M{Y_3OGRC9G4ay*P4cFTiMRe&RxhD z;q2rLZhJ)sOh+))Epb=Dm0n)2LAVJI*W-rt)mH?$i9(Ndv14l*+gfTX3IR91Mnv6B zNJs=>Gh-|ON-$Ne!IQ!B8a51{q}!zng^y=c7?uY2EFThNA7@T)EhZ1!Yrx#dF6?yY z(bPP|qjj7v9(^8GN5Lx5jVpI~gA_!jv_am8+^@<;dC?u@8!D2MT|h;3A!8-PSC-%v zGHPvEa*P)c9Oa-QTiV#zSdzQELn`}u$SY*8$~qFJ`w)F7N3t*(C~Fi7rqPH5dJho> zuAv&t0l9|49k7}!N6ciA9OeW1tDvhcL>2Eoj&sDFB-aAIqrN>nT>pHZVmxpDeS%IA9uUW5R{ne-lD zU@u@`q7ehRSyj!=ZR!?TRY@V+LMM~e)ks^kRQd~X0IcyMr$Qfi7-Pf(oz*9XBo{QWKE zl)B2w{!)h0S(%hXklH$5M1tNr(#z4Hf3n#eKc;@!2Hd5XaA%Ty*IJ+vBp?4Fw3_)- zUk*|;PC))3Be$6QgNz*2$1~l}+l&%O#wJS;-$vuk54`=F{nYP~XSb9Kc?%?2q59#*MIglB3c5-*Q1W z+$w-#1eT9*+Xh4pz7M`Kk!$%aO2ZB0pmm+0m< z)4{ro!r&4sv-`R(5GcIr2mm zmz6-WK$cSJ;J{)z)TbUcsx|DxsrA;gw{6=t+ZuaBkRl=oQmLXV07kNKB?JbadzXgO zrSTZ?@H8q@P*Mt^!qSogWW-gsLwvBk8e&XO2!c-KSlKwiA4CkXSYzb@5Q?L0$IadJ z_S;^Im&-E6RA@V_y* z)&hZpgNRvN+|G4_DT-z2tZ^7N8(ft0M*1vU79Y>9OG-+rV=r5_VfoE+Yyk9Xkr$?= zB_)ZNwzjI02M{x~ZT+m*=~xNF>#e2%U8jsmiaxJRX6UDZdCWBG|E^zmxhP9Uc8F#` zmmRViFt-{o7g*CE&9`);%PZnxUuGz>av@4yUfa+pYt$v)N-V8wwg7Bu3dou^w5KIl zIzd=#o1Fati$jETMQK4rr9#=#DH7<|F0%XtV8JIfY71viJ3DKxH3yFlR9MJ3e`~TxpeW237P=@RB4W-!o~|G(;o*(=a)rjsk}U!dfhJUwC&DD>icq6L z7E;jz1jaDaIQr2)8)T`mKgq1W%rL)|X?_z=n2&90Yc=!(7p^%^yj(xiX$yPr#X$LirDe7C8BemrG98=EZU_15(vW-S%9SgSlG9k*c2JX z$OX=)(Na>uy_R0p$Z~RWss{QG=I><&Lqxt>)+lc$MGu@980b4@l&z(z;PK2s^{9p&V1jbdu20MM>96`>j zRg0J>|7>h*>?C)#wj1fY0H8)u$${#YHb}E*S5v9k=Py7C)~$Rf@-|?=04JKd^xAn< zTdPW~tP_jHl4j%%aHd&U(G*oxzP=W=K7+VLz@B-{nDrol-jmzX5e-QJq}>CxwWT0J z#p1`0J-48*nf98Jll%7VJ6=MvowcGUzp{;D;ot(fcx*^!ASg_hPA9T0F2<=ey6beX zn_u;}Qe20q$K~__IV4`_t!pFPM&9f4tLEP$*FPXbsV)LvxwE)=9ytc-_wC_sHb> zdgv7?s4lB*RCBppo`bZsprD|nMBIu62NR^=;Am$x%@-&$X|RWG&UAmMn>2z~*gRGrMQj1nZ(zXz^@&X`hL3uQP!h{Ll z$=TcZJzN;L0r z*2g}z3!NTp*qZO);Wpq6Y&<-$jlkZCnxvbli-6Bev-7qZMgg96hG)%mng6cM0oH)L z;M@nC;!{nU=9Af!dzZ0TV4jf#MkBEF&qBbvpE)E1`<7f+TpWUZLmFKiibZ34BT)zz zJ4|3|0$V{~9R|)ucL=;-JPu^HK%UaI>lI8E_JD|lxG|;`VK;Y&VF$2pVX*dgT?n=p zpB)~?z(TMu;R#4~swBuWhR!fZK16TWBl>JgklRuWn+nf?{C>&}|2M5@hcjUXkz|8Z zB~8`C*G<0lZ(g|`=J5XrW*30U1<~o$<@EHbDqLMrk(`WOAjlx22}*(woHy8*DDL#Z zLxz->KdLG$96A(K6Y{|2%L&So>|@6W?6{HBO;WKE`U8jA_N5uUd>d7dGkrgiX~anY zH>zvqQCj@q!GpB2+H2RE)o_UeYeEsdOpPWpv#BXF^UE(YflH`&uOtA+;WT>kfCyEXy)-+#sYkL$WARs@Vpv1&PM8KMj5q$%l+hFS5k)+4^j@|a!|KD0s z?YRiRH5u$tky}mCH{Yb)2UGcO!ks&voy{$G8*kpad|9d7j}_#d_rc-_YT!^mM*&Yn z4+;wM5wdh0qL62V;mBjXSR$6q*p(~&!ax4_J89Z zDTGerEC+|re@!THccRGZv3i2(0Db|3*(t)o5_NEhVmIX7`TIA^`UBsezI{K+bNqN* zZ+zn8#}Au}l4gd*$1hvPc5xF5tKM0-cFpn#a49g8;BtV$4i({QF&L&%Y(?hHt5>kr z!7lE@M)k34>d~&@K;Le>qR+)3R{~tmYLgk-G^Tz?ht=~KCFRUMeHO=_NSxul5%M^U z-Ueh-=yQ)$Dm^0dGqbV^#g!H1}*=2$inWdU`hDB$pC;himD zKvADjzzP5$!q3YhF7fBo-~SsNSd~mBL|u6l zC>Q-S-^)=|apNpXF0a7^lR&+m>7$VePz>lH~x&z?RI+VtE^d$|}jOQ+1H(^ng-%kX`8zxXOv^$s+F^Rp*n@GlZ`jaW%ZSR zEFbpM-QXL#tAFeZ(|w9QoNuod7avdP=;#pVV-|332M@FoF>|i_&xUhq-RebA%mgm? zf4v1~?|TI_fC~1A4n{EBBmWm|R)+bQ{hKceu)G_vJh7d|x3{;qu#hXHH8nMIg+>E{ zNY{VdlVDo=zVGhzlO#i_m>gUdWPI4z3E-I+J~+V3)!$9<@`Mrz%`g98{Un}2Up4Jk za|)rFvXcR0`g5C9Q$*Mp&uyhLFYm&If&%o7)T7=!*Dc#te?nVyJE1KtAG5P;Nj296IY81ifWO9^c+Mx^qJfI9PHap9P0;utnd|U zw!Zh?ds{cIU%7PMvN=d;>IX?2`pkjuEd_rjnOjk>5?;M3mTRL?bd`>mmaQ4Wp< zh$aF=uD_nl%zrLa(S1S{3f16$abC3Kxxn#B*P-fDUjRO}D-7CTssX-c6MSf=&qDB_ zOuEw2nr4+s)v74UeRLa?xU@o~r$chxQP9o#D7y~p3v;0uK!}YUj-h+nRDNo ziXDQt3Y@}z*MAPsHiU=2KZi^X#STL%UI_L(!AH4x&xUv>K!qf>u!n(QG#b!Wajq#k zc+kjQWsRMhC#gkcwMwP(^5x4n(*W2=L*5=~?_T0*>RuGs5{y77;@`{0AQ+8}DUU&U zOim}r(z>STZq@^?w;p=f7J5E)>c*#|Ck(N1^d4Z#Y;A4bxpU|0DSBfg(wG5AYHK5& zCILnn(*q;TgdV{tN*g(Q_3G7QJPGo2M~y+Fng)1y74YEEl?m9D83^i`kkV9D1pU+E;b%UHPgkO*H&xjH^|g)Ghli%V0whSoeCGXIhL7ekRmUZ zVsNks;l(8kh$hiH8s!D))&h#6CL7Yo9_Ne8iy=l_UXQ|W_F9#)jm%OvRhMKVWm>Et zS=lH#-RIndV5U;MSKifE6Gbu zNGg&m+C+SE!|jt`x|~iCH?f?30{or%RGpc3^J(ObYL2((wbdG;d18RV4Bw50wlY&2 zIwOMtr}{I}rl#oJIU`F}RUJEa^yrNn*k4aiNDiRK7kNQ2wHmY&EI6u*bwWadW`WmZ z)iORm6iWV`Ce47u@;mH1kga>s5qau9a&yGQ#GFQMjwlp)iZStt!He_|W&$76Dv(x? z)@nG(B8(KbtvYq;{Q2`&9;Bq?l_aI4=2g_y)rrwAdIrdm_AM%HTRRRhC!w&e5<>LV z(q^(z()O{AWsY;gus!I*dw=cnY2zj=S^Vn4`3n{-n2S^t*c{JYVIA`1_V7&g?Ta4>kgjmUZ1`QbT2M}j@rz78dE?6v-A9%_qnWC1^#(4} zL#?qP=v!Z3fC6Q6FnC$bPZ&Yov}w~U5MMvRyJjBiKNrCyT(6s>|E$-2%KH(R;K!a- z2k^MKq(ZrfAumh<4FUa1LBIB{>3}q`kK8b4r!Gk0{oyDB2Cfk6b!Pofic|5Uc8)lM z#D_F!DawIXHlhURJ1G0vrG!3U@f%~&_dXV63Bk4aEIBU#3bHh7A;C!73?{m`zLkVZ zN;Jv9o@?lDB8u(7gJ156j*N_q-t#3~aXqjviXe#H`wkp@Q7VdFn?gB63*9hVVb{tK z4fJc$|DXt)X}J$OxFwHKEED`SD3S?*%q&(H8L$>D!PK^3D41DL4#qzlhnq4~3p+PA zHzC#rN30rSgFlDeXOI(DD4N?eB8H|-seoIr;KZbLwl>MJo}tf39CL#=4w10!A!H1A z9ex_>GHo}!cLN(G6k?LnFSv8*1wtJD5=4!;WpmZkN}|z}M8=%48NL zpgL96H7yM2^MqhCGzwrWsY2E&veu|t?cgS*fCTF!;dDBeL5BIr6cU8`S~WVHX#v0pcyvtKv%Z91iUusMK$FaNsqzYOHGNrP6Dcrr$R|1GDje=v**I;MbdP(wY5~Y>o|@m8E3dVrNPX*Spt-S zQ=bF_nw>)CZHIs}4uQm*2)Cq4YSilZhnXd{s#c~=dj8At1+yDtIP|P}glb{^BUYJc zZOtLIDii?&1te*1PI2+rp)ZGAW;g6Q^emBtN^JcLe0O>CA}J*ZsgQ5OKo~cA^PYj# zing1BlJs#ye z50j416z1zu@Q? z+>-*&s*3i{XaE_hIyg-H{rEabc9x|zPGd4@3=ZGXrAa}il69K)Hbss2*;H_Tft()P zlM2r&w)QXRVDe>}OYA*Bqt6kIE=fz$;N`#x`@g;OY(hMT4rFwOL#F%F8gBmx+jNf- zl2=*ka4%%p?j9ZRY_z@rtNd=2;roc>f526O9OTU|WEX*OG7J=qjxx**FA-hKBkJ`+-iIzBMuL=a$_2=NLW<7(Uw# z!}0gCDl2Ph#YI^u$?ckkqN2Qq!*{wwh zZzv||I@^_X)h|S}@L4La?+Xe9{v%ZP_Qjja(C5ilvo=QmG-*@6dV>Z|dD(vEaQ*`P z@(bb@6~zjy4-&b)rV81*vQC`_3hTX)O`dWIx0mVWVr|XBG}0=urn93>S&wXHxj<;Rp;W4T6ILh(}<~FR-qVZH?Xod&Fp4A2!<7zZh-niS;#7 zWIDG4@u>BM2@k+}Q^-10Rqc(+PO?^w+S;bDggNEwBmZWy5sSOHweOQ0fatFhmA2qSLK?eG%LS9u>)uLlLc{J8Y+DJ4OM-62@Ixreg zl}>kX5HLHGrTOqeE?q}6WbJflu}&@(*~nFvTze?WXJ?m{RV;0x2(UH)4>_SAoZexy z)#YjtNk`dj1zJ~o3)H^Vfo}t@iGca+NMplW&&imhqpby{qh8rUVpu!kxT+R{7)?Ij84)-1eDtD`b4gc?l?-2AKr&`O^m3qB(qPRH_2yQzzF9zUtYENorsWep7qEtkjR zSy_dKP7)~cj{j)wVAZZD$}ewHs5DKktf~a)Wa%~m4FBNZ&-eY2T!xQ=_yS=C^MP}> zS!r+qBxN^<$U25D8mKK-NFc+B57#C_0zGZ0B1bD1CWl3ZH2xqrys4%gvvTkYR;$bJ zoH=u*(B#`S#QExhWxyLChB=JhfXTqG(?O@bLP*wzex*?%v5LudRYxK-G0M zTJV8rstX^&t&oHwEC=0va2e_yMb1j7*~?8#tiW7=cLHq4ik!qK&*>9JhrBXxc5-q= z#G;uK=FS)a^>ao}Z@%}%7hl|M;)OvVa*9n9L9v~%Xbpi^R9t>o5@ii88XdNLxg%C} z^ytxBwPgFSTssL*xC*w2^0SNtwQj-g@h;WdSVZqw}YROdRjVY$=Cq zopJ@nCJ2G7u<-6xsM7hcUc()=aN)w4;{#b1OcAcrLRPO?L_iJ`0v4&z)y${TcN-Pf z5lJpb{Y0i-NITWjg;I{XeMabq?0v(m?-333G;f;*W1=ipZx9QxGa4ic9Z%rmQc_~$ z$m23dO&T5qX?UG-xZoIv=ot+zED>3fmK|m7<07DgXvmVNIRgNbE?5PS^vcyokc*di z;rxxWCu3qpj{N)2*vq%>aAntHVq$JenZZ!`YGM?D0~NJ!@1;C7uA+jlo;%pv5@;I2 zSC?G_s=|d%BTY8~bZ-E3K`qVAP={I}lUogPb_*Ef<>HZ%keC586;(A-1mLV=$i+E# zz`vLwaI;VXr7L|$>rH>wE=H;Y6u_tw)3`3dHmm6tr zz=s4egam1tRyOm9cGEMJWOBx3LL@$dJ+>n?8np#fAQqEs9?Qqdn$7&IbuFIo>RnJkz0 z3)(D#;#6Pn^a7UgOHLj(tY08&2}(Mbo8BLg_0}BHsSGujQ$bc}O5t-QUvhghc$}NzTtz1Yk0%tim6o=UI8MI4zAhG&wz^85jRlU836T64Darv( zOqEFm`PHUw>6nTxYCbzkaqCQ~Kc2 z;E~>Ji~?IoFVaK~#)Cwy&UpX_MM8ZiXGGVLVkYmThD=u~^f!}tFV|+mcMa;Omv|Jw zcUP9XL8_ULxf*D(33k@7?4N&6z1J=+5n3hon-nZmk=|$sy@)K>fKZ6o?;8^6Q$Zad z&KTWq#IjHcd!zZ%FC^>NjPL<>G(g)_=F?zi4IYM@26g3-Z4_c0LE} zpgfj|Mm%JtS2Q-Zv??VPg?S8ir$kaMu2Xi9@fY$xtxFcYs20tOHpN6EG6Z%kj-@5X z!rI>5OWg_<0F{DiTV;|LZW3CT_<3<@ntq6<7fK70?QJee`q!o~NW9LMm3V4HKl;n| zGl$b;he}bSp;9gt35UKa$1X2Ha{xmgv>Xq`}q5(A~u zk;LTjpqwO~?mukGym|A+*x=2N{{H*#gj}(_>u&f{Xl2qSNLg36vRDO|FN4jJC8I)+ zh1=1RclVZF=d6Jgl#(hemmnNT@aE_ynatZ8IGW!8kjj|00RX7CutOUS5mR0C0BTb? zBrO%>rli;{Tef1w3O{Di@4x?ksZh24cn$1goQM z(1>y4#yQe49#kQMIw1sU)Tn5CjbP~TvE#>&3=0nrU$O-1a%VT_h775!#JmYkV`E+2 zu|GiLzH}q?5kbF`-_YEMojhsC8T=b&J%{8)2awesrn|h8YLT8 z&^T6bY21pWt80K-4E0T&8lJbex3-a84}!X~qrC-m7GXV_^gN1(qBZjs*p|aN5C}(9UrQikcWS$jg>z@8@T0%e8d(9?Ty!Y0{(#KI{g_ zJ0d%|Hv=)-Iyq!MRH)!+EOqr@k<)@$Zed|zzFr|qAxloUG{g%>4lSnF`*@&ipX86P(r3!fue)XB;7 z+sk6wt4~a4XG0ZpHajo3O3}__AiOhyYe$cEcVN*-Q7?mdpJyVzXFx7$Vo2zOQ9)r- zr;Z*weqvZ?$V-#Kt4}fv8REuK*VP3L1BzEy>4~C+j0#<62hV9ZWSvfZ38MHslPJw% z*u&!H=1veIoy}%GJSDQqBgxG$y-~Zp0rb0t=;tI%O~jO9*f9T$jLya#(rFBpl$%u@X7s9#l^K#}s?cHZq(~aH(Ez?}-qF zL?-%}&Tw}1BYpwJyww71EA%C)rIp1+ zg#_diwbbNahg#m(5~U4oM!P8%Ah?3Kr?{9-|2MMM0SVh@u?{zj$;`t3dwGlFV)4I` zxeiF^K8t7UtTQvM|L^23y1%D?w+eD%o=vK%6NBaghYU@UbsKa6ERzuN67s<7tD~?; zBrkWsp0~Ysv@P;iE}Oq};Uu_u0$NCB3v(Q}DlrlfNvWu)0@o{4xgeBWF5PM5xmyT)5J9jzWHPSti1G0x6uF zQo)2pixy3Yh=42o{sjBZokwCVgTc^>AKfw3Pkver9#s*-&OtSJJ0%f=?fgd+@PX z8CPJNo?h0<2HzyRwG496O~3c9_nwEqH&nh9pyd%y>%DhLGy)!oCZNg8*YZzSUS+_h zZU3-os<3?yZga!+@JO;hwxEj+EWjBHds+$pS}Wj5UkQF%Wkj;SsS^Bbcy2cA0bh9k zA!ywVMVVI~K-Oo*lbk0h_Y-pRAp1*Omxr=M6O7#8F6gr=Jee#jdIXV>M@2I79^W88 zE6@R~2aSo?0vY?OS1eyRf93qJKG@H_?`rWqn6fwDo8$NCyE1#idnF;`agMmIxG+1V z0_tG_dM&$yi9s!C5#wp8>1dap@I|bd=E!cTQ8pB3rNcEdpo(R8urR11En;+PM@@uq zX2>uj7x(}^QMDs$6m01|dnBq}-F-%DkWpr{M56lC<|CUh-=btL4qu2YGoc zM5(_oQ^eRQ5PuB2k+soS9f7f63cGUsn0Cx%DQR6O0^P##chWVH;nor(qJ_?#I8q-gv*S3+f*Bi#;0oLxsC&u4M zdXk?nK_t6QMAD_G%y(kEHj30LkLCeCEt@wzWY~ydJW6G1YAWVQFe}1eh4+xp4!35D z$QA=C%vO+Rc(a@(JZoHmFU*L}8NnZCa*NGoy+B0woHZ1S!S?f~Of#E&7r{j?``!Tb zvdxY2t4;GG3Ex%OCH*Us8-~3af_+blH|KTzJMp^pH0}HAuluEO8T_-rIfB+TN1EY{4otbI2Fi(OzsG(s{?}ZuQ3xZkn*6-hc-?&**lMoMA zdCpzFa{2Vt+et|UwY9bIQZcJcfsKuiI6trFZ1!{JtimfRrD1Qp@y4pfv!{$2J4(o? z1wjqvj(C=t6*prW>uG(@ilQBJU~L21+V0)F_*h0`s+*c<&c0Bk&<@fRCc0a+z|@dn zZrPZJO5nkrDiJ9uU&OCS$&rJxtD~^VWS;Zbv19!moe8?7BdF}&b{^3Kl9T7ppFL;w z>+`0g=x5ka2(RtkyZ3sb(rx0zi6>5+DC*>nN7{rG#w(YKD4p6)t%yuwfkshr2QD97 z$Zt0Ob~dlWV5ysStNvAV$TpF7(WvWfVD_+%>k06_jlQ%TinypmAtGzZOoHy|29SE$ zdPds=q>JE{k<*?kIvLFjqoO0C965QX#;EA1;{mL*`C7BHsm7o)s7yM83DW1and=(= zwBG`tB6i6JLJ$<%uGFfe&z5L2y{0gV!dKz>Smuj=Zs;{f&is zvk51Sa#AmgjFGp-a#AOXbL-Hnm-JHX-?Sp_VinLjpy0PZGZS=;m5zdxMtME#(t5ZN zq(WghWh1N*QUb({u*dH8*Ru6bg?5uNeEva?~h&LI7wbIq-@Ef2fh;5ehZl zqx}5cigX?&kB9XG5j{gY&qs=W)yQ{ii0U*p0z?GX2{}Z7MXfQ?g;ur_tqg)$*)Ov) zt+x5uD?{sw^!N36nT2%^-#mX|2HE{KK>jH}KH>aBKxxvwyVovXE6lndd+OI8fBf;6 zL(i4nV(bB+S=ZslGl_KbFD~nNYrXjVRYsTxfGbf#kka4nc&H+ zZ-BHbX>DyL)L6cL1r*_HxB6h+iN?h~D~0`qtPBkJ;s8uP@bGVTHL7vQ1{_9G^m`+c z)LGoMIgpv8$Fun}N$4385=v0CS^tdb1VLL2q>Wza7`U|m@M(<_3k_|MSf+6$hH+)6 z$uO>IUrS8;T4vhUrf9Cd9nuI4js5H0ZFD5Z3{pdeATz|ryN%uk?P&Bg)cCS%epxAP zPK`7Z#jKx2G4p2-Rk|WkmE|NHDrRa)R0H%Ttm6i(<9%TrH(?z&Vg32k?gqUQh6a9v z(#@C-N<0FFxn|J+t7iyUp+0i^^mrn%b$mL!jN1|-%vRPN9k0GR)`15e?>aTl*)wda zjZh$Tw4(EMEK=7Ara#eeIMN^jwvST&_;GoZT~`{3Sr&=N#%<&XH>~S$>pJYB>>A}_ zIM&7TMtJvH{Da#GPfFo!LUna2Rj6*QZVmhJogxOL=CRA-Pe2)rx18#m#E%}_e2@T@ zle-jvE`wm! zA3Vw}tuf>nR%G3|d*zBg)38%q5D&_CJjzTHQ3{`wt3K!1j|qVME<>W>Kx?ca8?w@p zlNT<1C1mvYsnbx3;fQJB!84&OxIWSFPNVf?(6=X9M~NuEp1fBFkvGWkjN*BWdj(su z0@AyV9Xocr4zr)M!U$KW5iT!%>f^2SuGGh8fkiHSSluU79zF6Np8-@P)MtlPh2bp{;40~n(z&8NkvuSHfxN+s?oz(QS?3|p8`#`-b z*ln0^;~0k6Redf;})H<2JNE;HA2x~ILfTh1E}D2@3W{M{iUY|=nPh9RHM$Y9?fc-QOwKH=X{aeEttoGU zME|NniKMCliYT;K6+*5s_|rO-#cAl3V7G52VD|``s=2*G2AAP&wbYb{kR6a)PlW(0 zu!@3*>bDGWBglLqM?`~K`3h-sYt*o1GiO7d`=Xg))256I8|Pyw2eP-g518yl0f9ty z4F`z;K$PN6L+3*om(|ObK|MrTPL5bwDOHPX+jY}sKvIPVIS*1L;2M1XgTKD{=9}v| z1m{5x)B&idEJ%8ol#yeQPCl?QUs#znfZZl5DN4&I%Fj)`hh$R<0q>Y)(?krdHfS{D zS9n=PTLU1io&$%6&0Ms2*|JrT=K*29NQ*nAN~k>0*l^-6$f!7b)xceYVdTLuvJjxS zloX$s6nir%E2E@dDvgVS#En1U^@GjDP}An_eKjO1<1{i0gRBi9yaQp;|Hs~YfX7vx zdE@6!uNw8$ijcu?E24jLv2?T;kLP!WCn{2Yt)d)zkn`JlIY=BJy0RjmS zstqo(*ifxkVIA-U%GYY&Yc@q7;WVOQC$8kb=UCjLz)qW)>(}`bZBB?vC-tA zB_*NP8xaK&Mn-UuzOrvu$%ed~&lZqOkmLtQZ|vhLcyu#W}434otKH0CJiDjf^DThsTHT)kF9y z&)}lBuh=}*+>bawq)^d}^$qD3->;z0*DK0=-ECYT=jUK))QhDCiY2!fOHn>7DS)L=U`Z6xP$!d|o*5aS^g=mN8Q^ER!0~GO-~nqnWB;o++i-=6=B)i@DH9D_{RsxcbK($3sU^Z@7$6 z+u&YW&e%CDW7c?YJnF4Tvv7I8taEZc`VN7x}Al<2%W2whT$| znf>wQ61Qu|JqHdP*p@MY)?2dIUxza4dMRZr-hDmF_>X)i@lmP9Ugj`PV=;lwV7HG> z%{})w8q?m(3=*c`?}jx6_|F0U6;o5o68{vr{O14mMievtlu9vt_((r{Iao@{X1w$YX(wr9)rXr8M+q!7lyB zz1x9#lwclP-3L9tKX8}3f6RaJzh3?2zrIlH{)y~Yume(Ku=GMB=XkOE`<${*J-!JG z4ByPe1dIP(*i4fr;`iTTzq@s1Qb>HH$mxXaL%GQIUE4>QA3Pg^3Pb^m_(mjOf~{RR zF)=hm|MQ*J#q9s*T2MjC;h3F9UM-LPKlmE^f9nW}4+-6zbEsQTUAMsgfA@+|j?+A8 z*Q;B+_W$5(?EmpW3~kM9+qG*~VUTY{GJzq#{eQveW!!lqQv3gcQOda2?KyCxFSP&D z+Z$?2W8I?JZ|ITIpbYtp`pG4TOb?8 zSf~1EOjEPIB!#Bc74msZdHC=-v3PL8My_0$<;Os(TpGilyrL`=TU34hPk*gYc;yfH zdH>MS8uWVp(=SdO{@^dLy`=?7y70VLlD8i@)YlUy`gce&QVWpyiu7<%gD)yfE*5gj znUSV)h0#o&eRjR93#|D?v$XJ8FBK2W)9$rZW}=$~d-}`0dmQ*V2>dLst>W9iG>%^Q zQaD<^blt$`G8}}C9kbcUA@(IMhZeAW_)=yW+|B0q z-GwheU9h$ND?`QI0qbVk3Ow%H;%xN3&h&Tjl^EibxliclxbS|y$Iz;7zuuQ*v zKt0Q1F$<|)-}Kg77vFy$HP-%<<>!udp5=$XByDcy-FIJ2xk`}*szFNo9SAWm1gc$6;1wKrbU z7iTH<$5}4^=38jL^=k}VPDFCA>YCWn#b+3Dt* zssoPG?L#-3w|LIxPP=yLub+YKCaBnS?Pd_WurVxdo**b}e8j{PM#0@7gnJ|82(KQK zv}^ar_4BITBED$uzyGHAs;lcC({Z_t4ZUNt3~8+Eo1E0mPO1CaTX;f?T0JzNnlifr zdVU(Uf6ZBuUPBHtuEp458PjftGn8l*I!vZzMRCH}-kzvfSx`twP;hu$TBhA1kxPVp z4zWy6ERHqt9O9C3E!GyxgLaE}Tb$B742*sa7+reGqUi|_y3qW}>$Nx9Xmj?N#1|7v z1A=dY8ey(loIlofTYd?)`!Ca1ERKUYD3JJ~J!vfPEza{s6lMNQfBo9U`Y$_E1OeY< zrUKtg=|q2#QI!AXW(p6ITbyY**3IKR;aj)&Pgz}S z-W(-f&g3QF{ zdNX_5NWV;*`5e-7SkdRPqU$v#E{rDzV_#QCdwYA|!1U~ldQ5-Qh4ii?6m@bgnI0P@ zZkOFS1>?PLHaaHaCi{AAdM2t6$>S4OZMp|V9HV53ZH~6=`fX`VxqHwy z+w0@n(>JP_nYBA@j3^v}s4uJsK`C8lj#DYZLX0dch0|+OGaSmUbaJ`nazjtO-vKt( zZDyGd-)G5(71>@F*yV*w3X6)CMw5x&BXIZh1^ztr(7n6w-RrSMk>!Z%Mextc4W*@} zo3}V=#;QrH%yb?OvLCiPh6kZqYkq?zuX#0PbSDYvTsflv1(Jjr9W0POZ}hIy+wR?4 zYqu8TZNYf)!<~(FmoJ<=di2QU%NNg{IeX%Z!^f&%j;8jm!>qiZVA&d|a5ro!&We$6 z%DHZb!&%Pog%!a+`u2gl@3^CQ*H^!K;J~8?pZWIpH5DJ_H-7$FUp?aF!o^^6WT>1u^ryhgJ*$kFAI8j?dpr&2%>7p0p7*vj%x$|4 zbENTn@g6(WrJC9!r|LV~`eARY`}E}Hj){q;>U!v{<{G2K)aoW&x(@c^a>7x@d2CU; zN;l@`Mns$2VS9V%hu=N$Fa>$Nx-yhxaJ~UV5@4|JBCyZZJk3Jn!#=S zM%a3XO=2dxXG~UFl_4a290eNtR2o?P=hzYFfnF5EkB7fyK}@L2Y?FwQ^ASn;B(fW0 zwiZ#A(cy`qZfkjjL{zj4I-cCj)L7{p^WVIwU8N=7-O@u9l4Xq3vr4|bv=j5b7xT`j z>lmKwYH4WbQBT;6roWE&RCBoYneqO?33Xq8>*Why9D$iyjW~LTsp^7XDIEr-v>R4y zWM&JALdiF-6ME~4q-eQ*!e-TTMAnJcOCA4|9UoM4(H5OovS-cO6-7&Ozyp3r-sb*v z3;7QEbD6i?>sM0;LrvW}frvueww%I4aSYW=BV0;%Qc=rSYd*v5U@iPnd#J5=TT6+Odg=lPAe%>`PI`X@@K+ zK2(HKxICdeEMVP^$2rSh3KRO$w55fP80tR~do}r*yPT|M$!cYoSPM@yjdQHA&qWb$ zj$FR!tG9#7{KohDGxFk+pFa1UhacQ_M~;{Vf6G%>A{>9snvYNEq-(r6+YAkd-Y+UC z&Dg#jP67AAjOrxZ@~DX1AloF2T{JC)rlTfcNKeKguKNOtk5A6Ej&`3uhQw||SM$1w zJEzJ1pFcDw)Wdl(-E2(oE%L}R^p=E|52_ny>fg5R{m zGUHYRBXJG9egxB_gZ1zoK5@RQv!}Njs+UpJFhttL$#G=y8y|^^2^Y-_RKs1SUNs00 zje#+}{``4Zwb)>RXs83HqYutN@d**s7br?MVCRL$#|81R;C$(v1y7_4UPu=_K8|Y- zOUHs2#7b==gG1x92Ag-iQ$p-h{ahKxQUk1?8KxB^`drDP;KYK=P-qBZHtze*4ipyo z(D(aCB+Pt;{F3}U`v=c|^=qZ6m`rrmsvU5_xPM1(N^;)X`=5UL>0L_-asL)CiHrN* z_sF*Zw6-(zHuwW2i%o+){o`X}hVm$}%x=(Xb(7QB4O1Pb1Dhcco8jySzxmB?KCSH% z6m&OGpCOfAo?W;!Cpk{$-3nLj&)2Lq&$p8MufLL>=&1~Hr+=*|x8li|g0(SDXliN> zcZaCzFkCC#sT!L*`lf(SmjR0Oj#{|OTp`u4O+a&6E?gy@!d;ahDi3qHYA?aBe!%F) zI#YKmBnz#`2LGs)XFU1j<09%i!@te~D#qCPA#!ZC$%p~Giwhz7jT*jq9)_wzG?$=c7pfM9i$ zt>EY96QgmZQl>idgT0rgil5jy98K2Z`Ps8XF8_EA*XE^Py=a6zoq%r^(6)u|XjaD! z7&Q?&N_*87%lUIxdPhx0QCMTgwQyWd9q0!QRba9cLS?e;+lmrI`dO=W->z$cy^e-& z9{fI?G5FG63M9^K0-b^HR4)SMr$d@|Zww_>*)0NP>EuYsjiH1VZXT460$Yy)TT8FS zR^Ke8-qTZ~!=oZ*W~ig){Q2{jTd#%e)^uuqSeIUl(FBy*kjcWLV^g!77D;qm9*%lO z)V0vxl9p`Ve4fC3p1^#TojP@;d&Fcqd#15lWze5J)if|Lq8jXMx_r@W({^^YHg^na zboy&&bgMcq|D2ZP=V!(ROC+~%EsPWBX7UP>Q!_GB)3Xa#uT#hpQxfB1BEw}|a_y{c zQHwQiUIEU3;f|+S36^8dQJ-mh8-&c!3;+77s%s~9jateNy>O!~q1d6hyO^?#f0S>( z7RpMUGp}&z>h&UC!qQbc zAA0Da+m~Gn;ElC4S3&mN1NQ@BTJ2l4qc%|k=#(c$jq+G?uFR5dTpqHpX7UUE>&~+$=Gp;!jxyn0})o?1FN*#N6(x$xtU zlag9m?!KE9bas;T^h1Z{TgNY>SKQeJ0UPQzz(DPhX%5F2HQv;nJ9im@)%~ND-DbEv zn!772hiuT6+J-7gJ*yOprHliKnuy87hQPt0PcK4Z44Sn|o`Sl2%Cz?OeP02>AK1Qq zFH})aee=PDStQM!O?dD@*-UM1?TqX};0HcG0-np^u$ehlGaCY>Q*M|Oc~!yu@U+cX zbT8b+fQ{-oQo1_(5&UU{s>d)nHm({NNQks*Jh`dQ^-Sp%(WIN@7;SDPVO|JfxcuZi zuvOU#qU!)p)iJ0{36D=w*tAsM9h!0RqSNDl_kO)Q<$mNazAsJK_SUP=zHs8>8jjOyinhA<{D$)}2&FY~U`+s+wi7ogjnv{P^- zPTH%FBMF?CFE?TqaF+$#g*hCfsN>r|Hd;Bzg_Ayauo6k|l*o(9up*#GB}@Ublws}d z`2~4-c?J0gQ`P_VU;m{}JxIj(%<1N`A~>Anx?A3O;|(7c_~@Hz?pP$suVC@r0?(o; zi~;P(@Xm)FzgKP4*|R8EXXqaQb34#~sM8Mj4twVz^u*OhMQIlR78M>oHg;X$xZ>$) z@gfigu(lAAxo3K+o!a(gV!v%auog|9k)PAGh|Ga}<68^rS*Rd1Qx3cy@FQ?^R(4f( z*OqZtk0#=bwl6)n{$P4=8TW&7NP#HlIUsq?@qHL`AI8kRVJ6ynP~0%x>>RLi6M{mZVGE6n zN=%*B@}d%!LlRpObA2dp9G*NM?)`v!Kj6;4Nsicgkl&0+nFD;j56ti=u{0=1Di4WH zPO}(=5*UKq(1VZ9TlbssDRU+;|2!<*oqy|j2(;69&yn!f&QYM9)!R<+W!o*BLm<)! zQ6aUgH-9USZudp&U@A$m+4fmLhW~MZR z;|f@f=B#QerukUU_sxh>P$VQR$kKc2xY=YtZPyu?xAYe1df5c7P{Pw| zG?|%lg!waFDD2m5=14M-btOH_>QwS91_$9(1+ExT1-JU9$I!#q1OFKy?8usb?0b3oY3`Jn3UOVc?YM4v17P>-FM!5v2rwjVs2LfH*w88x$k?+(WrWF-@^$afcWIHE3xuN@QC<7-MvoiC6lIcI|g z>mGi%uI|S_)@pzJ<2tgPsJkR!;7_w>{4?TA0{WyK3{Qb#B!wPhhEbp@@kI?;IN+o_bo z1fkC|?vYonK(qWE_N6ki1+MB$Gz=zh;9u8f)$j54kQ(SAFq<#;j(R>W$f;M^`1@bP z7TCus9Y^No)LWGZD|q6n-Lw#aRF9plm!?63oaUQ@$TR}F7Wn`9IrSi0 z9RcYaglLsYrx3`g_qWa>&`#~eO#bySGwywte!X`KvF2arGUkY9MPR+!;#myen$63s zcTopyc{1zWt9!bAOCYn}DqxuMXK~Pt!Xz>~&|Y~7mG0Y`yPBIW)__Ix?4m`&d0vVu zUX=j#UDCSSpZ*~XDBphS;A3C?=2!3Xp?r}zodx+B7tW5xKCS>%p>TO`9ilcSiy3t< zjvt4HTM0ZN*2G#KWH;$2HABN{br-~nu7QeDubY1OH@8IT@niVuzx?DUKY3zv1YGsW zgJ_e?ln14)URSd9fqU-Uwi{B`#^eQZ#DI4dHlcwJlQdo@8L-Ih?Gzi8m8SRJg^>>Z zEeTbU%Piqnh$e~;9&B$vc+gHu%D+y2Q3m5d8S|$zN*sLRm`@vP(HbW#79aV>;A0H0 zeDgM2!Q%O~d@K(+y*V6ix1;h^QeDo?X@6QzzUI9d0(+~4yh?kGA$otWQRtQG-Amou z-1oYFEGh@TjS1j8B~xAf5+Y+HUl+)p&dF(Se;TdIc*l;BuYGO)X&&+RI~(YCF5QZL zmC7^AHZ!5~xvhom0v0H00oy~N)d%cTb9)`}^g65eOG%UtBw;4f)>a{dbFt_bR#nbL!Lw6(Rfg*Wa`MD+dm=w|}j#y|b+i(aPnU z-4D7S#jjAgnY@cF_d2mI*vli{8UeBR-Zy4ox7_GmtGAu|?{8O%Ic~!olP1l4iOXS{ zVh~06zHhSW`2Wbx&dxDC#$)AIGh8~JqjN8Ie~bC$FR7zEO-EUg6v~+%oERJI=t1&_ z2YuKWqS)BKfB%7v0G0LUNZVN8o{ODdlmEi+DVDP?KQyo^Ub}y*$Lftcx0wEqcL&ei zhpnx2ce{)H`|w8iqn4Zn?6YZR}uFw8FB5lgwBggZWS4Gbpba+jE7eV=m#e0ww(+m0VX`}a^2Fz# ze%{_#^~t-hL3(-h?OUJKjdc)5r76DTv~R4cxEUfm zEBzC!rN{5pH{;eT!?Uv{vtB#g(Oo9o zt}rMvW=iWMC$-z6X1{+d{YN5+UB72 z3hC75a|mB<`~ciGFD;|pE}&(@-giTRcY(7$>^tj2bI$rd=q&{aRAh+43fd3YXe>&ak>4JRgc%5(N-hoYB3`3X@10(XTAJw=}>bRtKJV;M8 zb**|izw4&Qr;C-EheNZuLuPJHs6epj1(?VrSx3$$bLvMd8B1^VTu9eH)K;1B;QE9U zZIhEXI|P5h-m? zTCLkHcJ>lv&>_7oG#%F27L>05W?x|XK4-5g(%Il`sYu+`N7Alpf|R*kJ>W9>`e9*i zs;My;Vq^LI*x1q0nwq8#OWn&azg%aDyZf1Ep1C`Y2H*Nv4TgvO!G1+M8!T~v>&PaZ z)M_=C=b$!5nj;{hgIf1GI;hapHVuQ`bX#R5vJ|j6U#-Tqb@}Hv7>FFKu^MR$LOPB)31eG}6W5i%a1vDt&A;#)lgp+=)&QZ*w8v&9slOj1z+ zaTxtecVjM*X5pcJBQXw#dH}kS0d-|%n5?a~a?BbY9&R10B$rqvUnCDh9WGXiLyr_7 zuAoG$oH7Q6vTfkNx8)x^cxGnbT?eIF*l4xVgCq-0jKj@v24khuGP+CkeU}fxKs^8a z4WoCDG@NxkYQFfp32!?IM!L=|@Q-RYml94~qSAmQ%P$Q)0lYm9ye*s1O*k7b^o;35 z@bs0>v<@-0L47*K{_*Y(8ie z%EMEOVuengbOjXDk3Vp4TuuS>-pNuTLR@=Sea<>mnb@%;N~9a?tFLVz9yTZ=B#HS= z=Rbq%`IRYGSW4RR<%PMqiPC^{)m4p26vg}@v<)DPDKMk1mGD|-v= zks=exq&@AK1?}s- z_%MYm1=McKt8zC^SCug_NTKn=kw={mmofU^`|Py~bkXN*p09Z;T?F3?p4WI<@IK&^ zt8D^v?e}IMEP$V|?-bA&0-8l7(S80r{2#9LjgHP*hDXO{9bm{^RaI_xW8?HRnw5nc z%`S_L`sPPQjt}ELP)`^*F)TNRCK5}`P{$$*+Gcpu3dCutamk6W<;VZVekVVm@qr&MNrv2VF%fQ4!w*qAQr`GU#f{ztdWTKb(>oCzLQyRC=S-CYd~{H!Xtuh-YV zUO~P~E2z}<{WNPy!LlAGSSyOjpO|lb3x9ggWd>T)`#wx+K747AbE4NGuKAqTH8_Zy z{tSyVQo}N}TqXDMrAr^L;MaEi^RAV%=#o>KV=p0pAb;Umi#KlAfYelF{<~`m-KiFSx1yaqsA?GBB$Uhegd)p058`q?)yURClh5e1b(Y zkF!iLES0GdEHg4$5i)LznfRyuE5BW9n7Q}A4|2=*vrqG0{&g~_jXfMp_see;M# zzf|`ubMY!q_1VdhlP6uIl0_ajj-%dzzh4kc&HVGO4f(E8WTAs5nlnY7Wl{Q#v$kX{ z{>Hrk>tW#J^`SAIt;I(Ru<`$=cUmu;>;HZ@Q++F=IDNUkeqtiUG-)tYS5rF<2b7rQ z)o)2fZYgHvdrH}^HOrTqOfS?;&djV|Urb&ugVmFh_3*E$qlnG=a z99#+CF*|E>vmti9k&)1=2;8&N%ROiMxaa#GeDI!~yYIR8zWw`896NsE*x_~wZY1dh zSm_C{(reI}Rj*NLHDg9eVzP|GhDf4Aom%kL1fCkR-RB?}E#RJfR`yslU$D=v|BjFO zm2rlsmA(9bvlXR{;$$THW;o1H1MYB~n>B11iSx~YR?zWya;vm@$h~^4O0DW^4^uc* z9p|V9y}jQc3If-not*^-+@scz^Im_4c!zOTL9_Y#T`_xYu^9sD@!*gs#1%R2S<2-M zOgh~W>G5y=b)4sMdDxdq29Lh^>Z`BX`Fs?95b_w0&x6D2LXF{g^H9@;LvVUHt|HN! z;Pg>IXot;bF^4;it^@O>Er07bV_y+b(cYt7Cl>^?9r>ja*+VqosIb!ZI>=5Vr^Q zj5qq*JsI6+)4v9a-Px7}tnn=Db`p~L`x z&G!B&tCZGeNlg}s%cJwKpxiqUad}|heS7ZOR=OrX0sA2=HXhD(4?F;tSKix;OHwsD-lrbd z=*;Dr_R$t~2ND%u>24lJCzE|*8BI<$)DP8zS_fa_vEC2H#y;@v>)SBz4atNga0ti* z;qVYZ3+~%&=0qZ{Ad)kM%{fK$TO*tsAzC70CKTlMb~puNVzETsu0$Cg92*qk@%({o zSoAkENh(dczx{3U6|{K&eOCVd`#X1T+q})L zbH-%9|GxJmWnwH~C@hpv+CXPYQ|i-SOmb%0Zl9i+QK@EPvt#=&9J$bsYntOKM<70F z3CmxXKh<&p$PJx9_2h}r)g>h*t3%C~KmPdR%jVF6{V2)2zks9zx$tEw591LBdGg8I zBZ=l1PW~|siM;(u@6AH(veb{cJjm_hIBk-!5T4lt8}a9CnRM^HcWhmmkvuY?(_3B) z0U;kA8Zp>7ipXFg4-tb~*|JjiJ;;mr6VcEA{lEW59wZYozSBkTkGs)pJn{if(lSz< ziH=ieZHAf2aaDg8w(l4J{hQysT1VFw|PH;DD zAd%?r&Rm&WKXMv51x{RU)VRv!M(u>fz!W}s;6b>~KkQwD?*Y>90n)Xq5xxEB=jYnG z$M`s={8_~CYBUoz#tp-yWw`Ux&t|5zs=>A<(BQUCRMx3)Y;J9BZSO;9LiuugcS|)u ztQj)#A|W^?r@$F-V8AX&z+?(j1p-tla`Si&4xj6Gxe+dC(HZO&Yr~9M^}z$16Rdre z2q3YVm1tzDZGl>xysI|_x%%a_a6tltgC z@jLTlM3eA8@9PVvDU=PZ$KH73jn5B3p^PCBhCMPgVRhOa^7tsO(;uOjh&gXcMoLzO z?>$%8a16tj3v3Y=dMTfXs;h9CUkz=0q9 zU>}ZUxsV$f|HJQo6=mYexJ^xI8%p4lQptv-rJ>4dT3YMnBcFV7w6*uX?Mr<$;cDgy zgLzc{*~h)Ju5cKO!|XF%7fzqH&Wwy8So+8p7YFWdv(L_99gTz|*5+l$vN zDJq2XeUhAo8JZ2%&gK=e{;zWUucm4_P*1k^n{JIp#eQV+fFppg@ktz4OuynIV> zQ>P`eVE5O)MSq_9#>02*C`PE@n!=>WP#X@sK@nC!)A{eo51Rlu%~leGu^5Np)JLzq z_S)gWu-xq2C22u6_yXAlQsj0F*AAN?tQ|UZd5RZJy%}?P6PNtjNlF7!FiXW$dwW;& znJ+9{X;3I{ikx6s?uJc85iCDAC4bq54N8S!@80{q{@img^lR;@1_)a$_mh*Y(=6XP zbP>rre5*{Yu}>uTo^H2wRSst$!tYE?8O*QuJ7 zf?1RSg(pG6s@macBw%1Vl;QH@ik#p*xK6WM#HQF8nUKqD+?W~8fFMCS-FWtFivTfS2!}luZX^(G&Ut}>(=5G>ySe;PHdv3+IpMn>h8>( zyr`W#_t*1NyPnt`Dst(jrl)m;OWh8*jBa{nT4x9fI{*H?n_UBq1DTtBo{|SK+k?Q~ za`g~30W`F$)mq!pFB*p@okDnv@d+nLW*Hx!!qjIc)J754;?pcB_A|u0t_+-%1nJn-}bGg|DG%(NLE-zF8Fi!(C0#| zaT$>*UtsW$apLys0s<|fXbauk79O6P@agj&|8pK1^9cT62#0XV%-nvwBOq}QeXAqR zzzE(q7Vn$pg>3*WZKKA29*HynFW29j%ll?4KG}lxOKC7ECh4u~y>?YIGaL^e!kNMC zi`qH;WPs&P^eD1f>V$%ra)5i`Ldz7vWxQ@(ei*@ptW3$}2nud;Cv!}0VfoGOWj_{K zj9S5xbU%L*cn%E+i#vD$KjVoRid^^{A?Pe_js@SJrO($deBMN#GYg-erq6>GK0iU9 zL;VuKx0mm_OrM7?d|pSNU*)?fw@CBRB+S~J;DepESx{tB8|At*Pp=;3DQB_r6! z&_$va4{jrHP2}?BQ4)6!{6OTULY^xm!@g{HF=M`6SZT9$s{CWjg5`k;KXGIo_}7e z9ntW~MwV3=O%kCZW&NGI?}h|`Jk@N-`puhiM8?*`n0!^w*!1{>*|%!n1?1lav@{qI@D^_Ghh6rS+v?WudEL*lLMnKeqG0~w?p2%XmQ)A;(xN6KiY8X!D zSleiA=0@B&A+Qj|#;Vn$qocN9x=wj%0zS7~ArgZkgPp*+W!w&vwPMYRIEU-U2Y04& zd!fbeb0|W=goTMXGoWV1sFe>qF{QOdW)~FSb=O^+Z`=0BBb$oWqr6ASN|KKO9g?Ee zR8~z7BWjTHuFjAU7-02-r!c=0pVv<^y9Q-ub*!?}}ZqzHc}y zYtzo1TW=##tN_9}Qo6g!L(QhVVoa_iQ8-l%gW`#%K?}?O!yjZa5#P(fe*=(y1CXwm z(hj!N@OXyluJb4HbMDfGSIxCehviyn~mCbOiE>Nh9oIhh=Vegp>T6%s5oe4qKjdPD~@Kmz|Ec>rD><5-3Imf!^j_VTosmCYvTgc+t|hCwo;(PYm{91(@+19x+MRS{8DJON(Dn6bJ; zhib4t$0ldZ4#d>N6&6O~4lvGk!2!E#R+^KOBb_BND9RK=W=Cp~45oGjbcoL|g89rz zGMEzqvwC$(2!kENgruy#e7RLUV+%qEZ;)+9-FjKMHf9YLbJxa=TW~*@=En$TF!Y38 zuZHw)jaiAqy)woLp2->GY5{B7;)+@M$oIeh{YO@kWvtBQGEGd*PEXHTLB7hfK+G&U z{A+5$LELhjD4v3gM02NNs*a4pw#Fv$x=%0@zJ)0e9E-eR&}~})Gk+0K84}R z_L#$!jYBg+_`(Z3Y&hjp^d`!uwA0M%y+$D~>F&oK+m#DGB_dcR;*eFV z;-ofcHgux!n4DG0tfyu5|Q(Qmaj_%=_-U zFVm$y1#8qPH7y>+ln>aianB2XOwGha))rWonZwxx=2!l@(}4wE)%ZJB9PqtMnOnBjp+ytSt#S9 z^rlqmW+g@=$z6jY!k5~nJrcfTu7qC}I|4~^gv2E$C&!V&kAD5@Uw<@6lK0?B+mo!k zM!eq|LA1vq-XGVJh^V^0_Mxztd9<~;v@|{*pFR5MfBgEjH~;LB@LA^3M}4=2k8?wy zma>9O;y=&ny81L0PxSt$@qhy*73DJU;8dZ*!9C6}vY3#}PJ3s4OhjZva;nTHamFlL zwr0;h5^>>zPWL|>H>Rg=+_)3>Kz)5feY+OLYdB3!L9De(X)fD=#BS3Mt{Eg$ersq zEYC_wNr~WF$DsZivoW!yIP;*A9XJ3dRTw8A#D(YQha-Lt2%^62qX{bTsxlP}sV zB9<@J)uKYVZ{IwN^`d%_vQ>Nus6*!3wW;ts;t@Nb=|%V;sD#l*UpyieeX8d)`xJV?D$M<#4@)b!r1qB6Jsp(6R zjY}E~N*Ep_Vk|&_&dLkR1Je&T6Ma)t(-fACV{WUPBfi~J;1}P@!#n~W>FAggNTUjo z?T<=x$|9~~a{TN2^Ed-X{)8Lx_1E8c^Kf-tGmXF0x_BXpNUI&}=8m0(G<}b-n_D$Z!I@tW{_5icz_q@ARs!RIvnKlVuM0+p;X8X^=6lAn^tnC z`$O|?B;O>3Xs5%;jYtyL!icG{Fraq4E?uHg=~T~J7Ab(Ulz`Z9`fMZ)sxFigg^n3V z!6RlVsF-yPmtPiXf;PcKJU26GdgYZ@nn(c*NCjwt^b+L?9^^#I+xLV0>kshuS1Dm= zXj)oET#zg-Ju^8bW*8pQAaR#2ZCaHP6Va!hoVBvdrArkdqoZ|o^=$*gS}Ur`aEOd z_Z<3s+rsA)^!dt#&kc+8^Z(}SUHAXH)^`Q*Mw7;vd63z8W)1j9jhPEG2G^{`t*Rwc zFbkpnc{yk+Lt0y*!Gekf`m0uDOpqN`I(tw`etv#RkRIN#`kxRR8mUTZ(O~iFgML$Lau?}obP;x z+Ch;d4!V%!Y#c15k6OhuM53UWM41HO^DGm+Jur%yH7mvoqs7Nzvix4gL7S`D9THY7CEiDK6LZKxxzN$SBa<%;D(hQNo`g+eI_9J^`|cpuhtz;HxFfPtBwW`bfd zsnjF|r(#(|a$#ZN$`x5zD_6pLoT}jIhDTQxB}&YYr_A8g@Bf>X532{CZ&J6x9lXAEWXd}$Y9%W2ScyFRI#r+J zha;aFQ3S`1^eSXQ&;DN2MC*ubkDX1PN zVQh?5jEzC_g9W-6CSrs-@D230l=0 zR-qZV20)W-oV6+yV%a2h;T|>dz_mw)ida#8{*)F8K6OTAM*d1{>y%I_XGVA1+GH6; zkWP}YVzp*=*|L@E*RNZ_9;Ap9<@sxoTUAiPk!v0W+ z%LgZ|9Pu#VEHD$CEtL3}8S0b5Dm2~;jc9nrTxOC>WeVt0*jC#d;@^Mdz$9 z_55tuxM}qY%5aA|s8>x`$|_iWvvEVPTpRpL%F{U3{!VbXBaBQQq);%hCNe_CO_nZ= z6`CM!Xx+kSza`Fp+snhl)6x)bU@(p&VJ8Q6yx@%de3qM^7cZ`myWQq~sdUShZF`U) zcpF|$UO9P^<@Tt@bw1ALDPZJDU}U*jgAuwqJG-F)(3m;chG7vAVG4zUW2TvkwPr5N zlN>V-XP;*t$8j7d6RebBcw#;W`EcD1cX=xEo(Xt7jzG*~va_RP6bpnRju?0AITY-?*MqU=TezZL&CqifeVlBdm9N&6h%?D)FL!+ZLI*3@& z-7{?$M<>OGhsT5o+(PUFzI6gB8?A-Vs-aMKU_D$AbIUP~7G(w7CPUWTaYs=&j)FwQ zBYAld0y8%9go_`Zr_2vaNBBZcRFK$gS+^!DT%c1Ajf@Pcr=|_q#yoi@ymm8#2t}^d zv(jS5YAa81qfR)q$Qi-r$;oLmwyp)zSX0xOT@D>wG~cdi^38xoq06y8d^vNziCjU3 zjKP8Q48mv2aF~5-NIhjF_@<0I@;P}2o-&+=9z%@q%YI)O`d;LJ|10WAgZDpO`1Z=% z$oh`AOP0Y7;M0ZA>H8JhBl^AzegE6s_ujsH{)4R~N=`U|hJb_@|L8|QDh?lm zt#gcIvr&*sVNyhX0|YX^`f4Z1dh*F9vq@E^Teogr9vj|;Z1nKRpdLSiBNm3IQ1EPyXP>1-KTF)tyMH2n`PH{RIhK-^5W)w- z;g5L-dZoEHGo^gg2yr@1V@*|6pTAQ6f?OUI6`D{0@i{4oH`PB7g+m%b0~CT71_d%y z{cZL2wU;q5R#tcMLh}TPhcsT4nfNnN&c=HY3A6u^Z{LoH)8FIw53oZVCSOF}eYer* zG7UmuV@g`}z*A4CM%20>ayz=T&Q2Sq9Wh%Te(*u+cEkpktVtDXI!+;`>Z7`Dm1@%B zy?LGiw9f$A)fgQ?L=8<1)zz0#U!bn8sb4!ZZ9j3Md29+fljSpN^%$+;p|^wAFbs5` zJ88A()Ksah;0rlIiHPea2r!wR(%Z|ExVB!ZrMNOY!^LG0ACHS>bkrS8L$dNxBmz#w z>bU&PAQ#(9x9r-raouLv7d91S@`eW<*pnM>qv}+R9Hw+Gm&Y4}f@4OKzU|)bra=KI zz;F(eeqzdm(cD6rgbPZa#E~tbs+}E6;@k*~QH|gl2@SOlpMv`0i}Sr&oA2yVf7`rd z2wi@?7;M!XsmmU6`pW4qT>Pkn%hF^EnMfuR%Qqc(9A3)1Zz67qsAD5i4K4x}mePAMQ+(6O#zdNl#B^^bhS{pX9Xj6LQQWCMBvNa zq>D2^g(nXpSP>)`|9N<+4Z_&ZJou zk-K5DTFXx;+VwDw-;+;1@y$o}+*`U9H_Fb!^)m(}%4d1+epYo^#qwu5FI4{i z_r5;Ajn(`%`dp=&q}e)UadC2-<{4EtZv86wO;G2gJ{3(YYE^a6{0l8A3oaTqw*2Ia!%>aHC-b^={1l7mz*D2)bX?*STD2h9L?(?JC=G%kV2P#(!J#%Yt; zZqS%9nptD8n@!V3hGC<3?=B3t;?^AHgct5sD2xL&pMLsj&4AG#H%&RMhcMFuz0D@9 zKHojVL?%Q^!b6=CdM7rD+cZ4}^4Fu9bcchT40h;xK$^QrA0o|g(2>EcdNgv|16$Iu zV?q_+C(1)X3|yqUdy*>-%fV)e6Pdb|1yOQ~{@y!tBgs(1rRqx^gKZNgFdar=YysxD zHZ7V%^Sk#l(Hp+;jc;rqOWE=;XAZ6PyCuukJLv>5uo=uoTY0G5>7bWDxy6e%;C#GZkC0c!5*1$HArE?%#Xz6BJ)f4 zd0VeRYnsO;w(;yIFcf|ai_XkQTTNX9(%w&k^O@=|{D z&WWDJ;~yzw6&bYTP=07QBBD4Fv4FS?gIGf~2jfNW+qZ3bew1Z=g76@dh=lk<9-q14 zZof6$pLL0g2&h+}aj4+xbVPz04?f!E%eq9k4*I@~Y*TV)=?7)xFTVXsXHW0vVs|*_ zMGGl*y~n=DyDi>9zUsecX|5a4KMBUd55*Bz3@zkHyu6N2rN4mO>c+nO1^{?-5 zyL>r%ISBLeXcrhY7xJK#1^-AU`w=NAS)Lk#wjrs@OMGKruV>WVJ@JX!b?VRPd3$E& z_B~YQQ} zxwvwR(itv?eDu-9u1PG}WY@)yl;P{Pe;xe74}b9eCrswiPRKUnp+)!8M4bN-Vl>+$ zSKI}vbk~Yl;3}3pjcyt1gjzK^Dv^K-9q&Bp(rG zKyj<8P8@+%`E+x)&h9#Ysb!#l7B0QB{k3>l3s=5OyPXjVxZI$Gl`A(bPnFpwn_ybL z+-|d2>^pa^NC|@TP*BQ>ol5DdED3cave~3R`r%fTKYxY%&hz_HsBf`YYsZ;g+Ghmfw`s+{m zuSEOrAy<5 zHGSb(S>b&(9?ppB8FpeV)7y`o)eLI~wS(>b9d41<)H|pWST9{_8Jx1&+N+P_pEYm< zNtGsq7Osm8m4?QyD-2CA=>(xk$>~H7Dx)W+^yH*aflis8mS1Ajn&oDBT46@#v>-l0 z&gYG-UzshRRrNxMH9WBRn+ypsitl{)oqQ+gUCJARxH;i%o z@!x*!N=Q?;fUwu|LzGpfU&a&R5Q3`C#MH$HcYw!pM5B=ajjH;W2SBF{te05JqOL ze*mG!TMNQNMc1Pa*Y49K-#*p&Fi$;BC`Ba8lq5%qJcsElG0flL(WJ$*!f(5)}DKrF-48Nb$D2pEI_fhj6df~uqDyYebc_m9G?E>cp8&giG`x8D+p?=NlOpo8AepoVL~%6lu>f?9)Iqqr3e?mujHrCJ)V~UdYC}Au!_-7P7fMpr#Y$1 z93x$`Vz>8{(B3`r+J%YHQOF|j=D2VSJpM7-d)TyU#J~$nPm3`!p**ut84@$lbRNpY z^GyRWSuh}G(T*Lb^Uey^siwRh1X~_ zJD6T*`;wCrbA{57u&{vvn>;NHUUY^Ldr)?^GPO^(1tll0{%n!KU=Ctn9_#V`)d%<> z#r0RcMq#5cZcpPm99yPIgaFMQ0bxm3=3 zey?-ycHXb{kYT7mC|#ppN&{b-HODm{Gndws^O_BJ8n$!x{Aw5i7rwi?SDn3-sT%Bc z@~~xjbg!C%gAsEvGb0gmiPNG~Fu7D$w&-?YFyJg3LQ1)eLxd2Zh+9f$mog(o;5AefaPR3i^?n!`0p=s{WcI z^vQ|CfH=*wU$f)nB2h+0S|Q!RkZDchbnANd_Rti_YDPz&8vX(X*?Oug!B2^3|9TmH zqVDSum3W^~KwY=re&G3#gt(9ps80v)$Pl;;2F;k^-~V#*q(I>6I||)!x(~O$cya$e zHeNV-;o~xFWR~aL`~2SRWz0u=Snocb_vLYoS%WVxtzmh08k!CGa?eM;FMU)WRkI*| z9M!zV$sHw}CAvgT52O#zUJuagx7Z4r{dbP;5ciq4W&3P3CsRu}E3E~bj@x`KY2Wf> z-xp_R57RHe_M$|W)jG?$t;5?ty%z<<8zmVu%OtB9p_RE@FmLR`bnIFG*o(om*mHUo zM1Dv*NC&JEN%iJrFo6Z_zTUbe3e+hxJ=~SM6S$@G zJ_`BsNNutECxV~<4j0kyf3^iG!Ta40!%yn_OnKQ3_rJJ*geT9qA3>T_`uni^SvWE8 zWsAQ3?MHX4%L{Xv!Xp&&kg!Oo>XVB~c0Ky-MPDeR)8w>xt1vn7&O3_>vvZa#Te0zO zknB5)5JE5f)?Roye*LFExeKbn_mH&n-{GA3GxBfbKjF0ed-4&f^!&a@{-dmzyulXz z>}S7y|I-U<<1A|Xc67AizmBFeN8bG9&lY{*T{kLaQcl&6EoSRhB`4%NK-e2B^QvF7 zdR3NQ6ldf^HhS@?#x6QlQo=?r4x)E|`%aj5tqFw>t%x5sy1TqNI?t1TXJ05|o(Ax* z`BxjhgPzQ`^0O^x*h_Ac_oB3 z$7iElD~fCzyOIww8XJvU@bjg`fgwRgU*m~xs?7%!c67+hFTmm zFwp#YN+Aed{ZTG5X(?q5{6F*0GtQ+8=XnWB{9_^Jl7wQcz|8sHA(R0G@V(vnprg{x z(s`PSFTOZEW4rjNhJ0EkIx#$a;^;K54*GgWvc@k`T9) zFljBc6>B}cyT8fw_ZPdrSthNjYUrQFY^M7gs=U3^+l_j&i$}WakRP)iUW#n2Pzu&o zr)jFW;C74-HNxGwaYzrDN{_&`@)U+E;21TRFW0qe-10Q)CYB8U_@hUm4dL;4(1sjU zW-cjx41uQKJovqX(;Ax%O4pF(JD+_HN|eHF2u<8p7y-RW1S~FWD#v9o6Nw@kX`lF0 zy%+xc=RemCT18M&%*O+bouHAU!d^A?_|@y#_|vD4eU5}ujqTHB`vnbv{>meAmre~ge z;&Ehq+m#nGp?Utf{e{#d6IHn5(MKQM0a(+yT!ycZajhtOIi+(Uzzn8})u|F2_sp3q z|6gn00oT@f?tPAqwvdoO0>ltvgN+R~#uM9sx1GcpZJM;VneNw>wrSEm?holWtG8*= zIsUZ-Y$! zLHL(6tJ?(*+~4BN{o%V1B1^t^h988K{^X#>1e=FIf`FFX1$@Kj~v8?$zMoLvjQn4;gqN?p)YK}A zmqo7R?-$&=@4HjeQ;{qAF)d=j+J% zSHJps2f04@W!G;*%Uy?t%k_FspPm@(xYWR>Y{M-d!v@(pVhYjcFJ9^%n{q;HwoMQM zutY`_ms5C&x2}ch=z+)t=V-`(GC5~_t6D?DT+!0RVl!d<{5oqBR{JA-H zZL9^UD-FCEn;0~3Uo&aopr^bfCHI!y<(~H~x8x3GTHdesXAX_2fHzx z?u^q`Dhr}#V=ifQx2(kNu2|_Vk$w2_%8CKz;}4ZeX5ccn)uKOW*+_3aEYS76BW5oa zP_KEU6^@_Q5hsMWv#oB*H*>Mg*KyIY;JDcC$MUcgss6%T5uxBziMU~>B_~J4b;5bz z<$Gy`3gtdOE10DmbAoMKff%g zepcP^af8}^lp577f!!atDh}2l4n-q_(}4rSbRNQ1^623MMZUU$4Z^;Hbp?IAqJhS! zzw>+OyZg|0tDK-wmG(4sPufF>eD)9X_^?#MuQhEqbqG@!m#q`^YB9O{kd&y;V2+FH z>H{-1uO}%UfB5-cpy(>8b6RId_etz|8DpR*I9G)F$ zFD>5=Uss7N*x%LG(%3dSgVcm!RdM%GY%yRAqt!)A%FEUiD5x-*O))7#ybQOahK2DW zF?^3=p)6}{dHLMj(vlUh(fWKmC7hiq-Z%j&0f`<4+CYL8J@y$tqf6oOs3gavFj7(b z`Fk20w{E4b*X1KjjuQ?AVD598jK;V)ZDJf}$r8?zGFY$q*!)Ov%N5f3fZh zg;L?!vzs=YJ-e}j7HLbCv<_OrqQdIxYVdJfIG&N6Dp;_0z$(~mr5K&SK0R$uN={DF zB?dtydjU;-Zi>>NAIdXC0Bw>SiVPEz@Pz%a3MJ0$Z#{SXeA7Vvq4(Z-_sFS+E<<@&)iD+mIlpAi=w!7X{~!V*m}zhy*&stL@KRv2pjF4LfhU^@hE- zeXU3wa?DaN)7foflLRvo=P|Iwx3%*9mdpO}+oX8q%x;gE7OgwGBw-_v^2!h@ytnQe5aSXYSfpb#Ds@Z z6bEgqMHaO$vk zX4*2=DNgXr#v3p)8rpl2YofcOa~KR9tMh%ne3jXZ1UEqNOaO^y=mQa0=g`QAC4gxw z5J>=53OWf!ZD`y+H6P@FK3B*=h`eNJDaKr;RJbs08n@akGYfon^Mwm)H93L{5V4*G zPk;*fX03+y<|J)qq6FhR81N85<8_ftHy6*HLd@lf6Q?_;1qIpf9j-UUOAv)zz6Pim*hL0&B2#)lP`D-RtwS<@#h42Wz?{E*`WTA!i`W6A2N0 z=7-yUcI3j@x8Hud)fE!P#pjl-Em8_%GmG#5Ekk=Ig-+L=t8W?|eeJbNgP{*UwA;x6 z9gjiS9zz!FH%Qf9!_s(CRdFGjCpZnVo=&HNAF4fkZI}W4c!2@rO zjJ)v%^(csK7UnD?9-^iQJR_F51)VJ5813P#Ko-OmoSsof;L(DP?tTb)OE^wl0+7b0 z>TR8^4a6Jfj4RlRP3a9gw|YD|IaCTB=ITH8YCTB^JW>DZv3{3MHaBqS>DQjCsi}GH zwWkja%<14BinR%e!$mBkG!7aHu@vG5kZf^*w^s<`S~LGrdy0FZ5jKMF(8r$CRZ@TF z?s?FPfD&3PuB2WTW{1Kw{qvt2zF!5`N{!$@|5GIT#V>#&)Ng??=nTHcz^Ql>rp(rG z5rBy4>Ep2$T8Fn@j}}Vfa+{kwd;1Xl)7RU1@Swvn;s%h*J%V|K?50H?PmxZ*i<2wU z5n!hm0w62ThAbFoX_4qUY--*`JlkW`OA%AloA~`0{{LC(Sz#!2`w9T9SKMAj{uk41 zP{&-P>5Do+z&_G}d^xA}LM+fD=b*8q#>!NMGJQh}x$F1W)sdqCQTt6zEzJ$Dziu{n zTQShA-LJn6E;9iV|K{WdgSnqwwj>pimDp6GEpbGCkl)C+M}0&k;`lw9=WXgRzHj5N z-%~I0h4*gA%-nEq74;%zhD&Yo6&){NCk53BP)-yxHD-C;g;MTBDRb=hiHYdZ+Svt+ zP*-Q``|nRpLFyUcOHQ6OypNLB2v)A7X^m!LLa#zICa9q@(a7FL0r?u1XJH<*$w{QN zk>ZjflmChQV3Jw}@J%bf9!l-?MHYv>i)gO5r1ypT(wf0xp{LzUdfglE2$Sb-#}ifnmAC% zPqiFA?C}^*ojR$DJALZRh{tpIaBXAV#~(*^v%jK5e?f^h%Vd}j|EByT;#2Yo`MFX( zF`@qdBs<&MQd9q3xk<#P(w7wt#TN(pJ|9=E)~9IQPnpKekiju)}| zdT{DdCt2PO3e5YdEFGUxNxe;fch3&$2o|nes0nJC+6QyVOSDv4Ma@t%n28Ix>pME? zdu(O+8#T;edHG;${*gUdCEk=l(=Hb|R5I~t;lxB;+}V>y`uZ9h$w3gan7Ix|Vxq}p z(J_y9bUdOHT1=+IM290ab$>!{Z(kod&X1ZOSYA+|HT<9ZDyc_A4?g(wpOXXnV+~U1 zrKigX{i2S!ZpV)6bfD&!%4F&3DJju0Ln7F=p(Hs%K|IJV6iA5)*lAT&j&v5Q;h0yf zT)w%2Ag^+Arl;A@+#BWyW{F|~JX!9R%JN|wEzO6bQI##_%nbFEtXq|4wH6g2O!q77 z3@@VqwD9%I7ybQJsc-~Y%AW`NpfF5es1C+LF^D;!Z0yraBFd8#6*|Koh&{nXr zYWMEl8@1x_sG8TFRy@&Aj?1a=8Fq7<@dUYJKN!51t4llMsUxxEs4>{_yOQg9f+T_ah~ zV{0fGxtqzzm2&;=AXnxe9qj2D9G&t6+!jk9At^0Y5eUFwbd|myxJqB=NA&d!zC1Ne zoL0P|bmjIfT0SUOX~*!uK>xY=DW!6Fn7vwCPhQm4w_l^J7o#M_D2Y;`=wINE54(g3TKzCnsv(+qyN__V>RhsMIRi=%`Rg4qOWkkR2Hr zc`BVik&=3R4PE1#&kDH8xgGSS+iZeP(Wo1#b zv}OWqIAr}nraiLuVCkVU1_zza!ND0Gc1FobyqWv%b2_W5@3S5I)1Ury%yu70qvPUo za`Y;)(r}@TQsCpHp&xQh7oohz%JLM-@+4}!8MxLgbv97^MOw8oj?eI=>f&MqAO{&* zn3$faCP+3~F3p4yXZ`YQq{>*LQ74EOR<6s=Kya+v&JT!Tbq#UY6<<-bn_LS%FF#Qx z5cBnlm{zRi@kBxUT*ymw;_>knE5tmErUenQNb&r3b6;~qoI(}P1AIv+{II{D9LJsaB zEbApP%X&|&Cy6Es2196~WK0pnANkOl1C`+icw<8sDb2NuACQ=^976NQNpXSB;t@bB;@)sC*uKC%w=*5IMTL?D4OKT6C&}t ziHlE&wM5jzE;YM3P8G!wi4!^b!!bRN_|{-~j^)iXX3R+#iCr_wtZbG!8P^hXdBnPL zwTFE=f!2)DP{>C&ME*^QCn(X{f>1ZJl<5vFWd(V`R$rd4HOLC=%at9<<@OF%Fq&Nv z7!6cp$J&DUtTqyl80L1g2N=>+>F#pI+G9|Lujp#cCgW7sYofl}@M8$?`RM1IFyIf#r*xK+*0Kv;6+% z!u03---_KqVm;E)%3`&;fPe`-_`&jB?|E0dTD3a5wiYgdULA)h8x)YA6_wQU+|s(- z-;L|&8*hv~Lyc!Zzo*22{F=>uk)N!{rEPVs_P$4fB^9|gkd~s5a=9MjRgC{Gx2_bA zslfxH`bx}uz~>R=9kRu!nNFBa;I&TEbyO{{u$Qz9ZkXa7?LD4g-G!f<1jX< zgM{jHbXb*=#+EP$P6`F1eptA+sK7ii>0_7qIp^6DATn*h%UW$)t6-?D-U+MJ?j5r( z&xsQs9fs<0=y03}6J0FFJ|Q9g=*K5wt+*PsibQwSp6vuVnQ@@&(z!Z{o<(^^%+p?Y z5^8nHlDz8JXG=vz7O|B?J`z@a=iNJsmlUtqxP9-p?w~^ImE~p2vea@eP2H(W-m1ky z;He!ZcIwjm=e(B_$E9E)>Ui52#(2 zC=;Z?s{O(R*S>E}7XJhxZZ)*WqodxANuL*R{j0csGlEh4*a9u^BocM5Rvtjej=8@M z-qX5%vkieIAcx`djHB%r8VznB)3^zRFvb=p^6B~6$+4l4@wf>i7%*S~ciJalquLw@ z5XL-)kA4igL#h?qwqZ+MqK>1iXsW*6G1uHYwrT;sj5phU@)S%AT?o}o17}u2PFiA| zfY@EcLilGvxEEkK-@Wg5Te0tcD)QBDzW2K7QjM76Nf1{hkYNuVMM5C6`yE59NdhboCMBXK;#v4pZ)N|v$KU2J9k#d!b~WR z}2-qAhW0MEzCb8S5qD~w%Z{lw=H^4XCQ zKquh|s;jU?FG{yfdGwNnh0`PvX4kkfQM5lZHM??4<(4gb_kcZbZ&^GqIBXa;^tZMn zM6bmb4{N_p67U>925juvOQi{6y@V&;x*aoV<0^=S*a#yUHbL|$VJFQ~BJ^b5H|Ly% z8N92ud16ARNJLb67JqK6{@Af&7Yqm=vLIznLqkWGCHyciZ0SO@vo8L8*KAk;5~7XE z6yZ5z%lZA8^YbeUxIppx`0}(u#IvXS>;(lbR|`yFM`q-u->(J`6<^*@wk}&nXQqq5J+ZGJ|4ch`N{k%8dl+0dtosM%|Xfdva*wG^;cY?g#&_4Cf2gGp9 zotb(6{WA{>Iz3+Re-w>=zmh_k_vmVXam>v8J&be_d3`VL{T|A(!ZI_56~-|&X)#aE zB*??|$-(n*4%YS>Z8Y36ag@_BV&w<@GoxLw<@Oo@IoIWed#4Lj%+Yg^poK&4Pn0op7yyo`!O@lv-#gV{PhWcr`1k%uzNruHC8B=)CwZKU2wvgOr!nB{)XB5R{wnJu6ypeXMJ$^(rb@D z{`gbROgQyio=6<|?`Df2lp-I95+Y9)zt~J&bB3Y47}rlL zZ^|gzw0l!g#=`7EarJf8Qs1=ihrc8p^cU)nykGus_om&ysm6xruhf&wx3*Vo$5g1n zqxH(5YsO}qnGe2oLRwN)GIRn$RKVyYj8t)|Eo;lyw>aCPV}1aw)Q(o-Jn_Us)c_UZ zD|zUNC;s#ocuW8CrzdK*g5Ujzk?&78*F5pRZyh{%@U8#-H(OMCxk)1}S{eSeF7r#9 z?Ek&dH>^jaU(>vi-Rozaz2-V*J3x;Aq)t)|IQ~ifFXwmnWBGfVItwh#S?Y1_uYUZi zf3u&Caid)#y}C>Om7e>&y+%ytEnHkrxIb*knPD6U^^&rjrLn-bkIjr=bIy)a0;v9NHb5lxDNoeGKEXTWx76Nh;#rR3)Xr zb$OWjFYf){E&cBOB;w%ker@ignFzgbTyj!>&xiwmg{7JT06`iI;cOy6Smqf7S+mGi zKt1!9H&1u=_I93r^Dk6Ycy0I_+;;%#eWwaw*R`=#>zgR!Jt*UnnY8=wxMlCIgu?8l z8@J{ZfPy10O$l0Ykt`ue1?9ywVRc1FB&H|XB}`9oYNAr^Us1f_rrUxczsF%3HZ3qx z_?6-%t=-@e>FpZ;UwE(G!_q|^RpA}sTk{{kWYMH4M&5qmgJWlBih-neMtO3L%Nyx1aeylzikwic-dvhX8YT!k_A4EHxbUH{YHR3WJ2Bqe6G z$@ZX_A7%nRo@#2Sqifuu&@3y-1-^X3LaT|FgiZRpScs%OP|}6szrS?2VaRAi+RB&2 zRpILJ-Q1%_vGHhC`0j8ud72?b4N?G_a$BtVMPTB?$7Sq;g1aV{9=e33C05eQZLXZ$ zxHLB}OH#e@x_iDai`AnNvQ3#>7!u(;MZ0~D5 z`C(57G?T88i69f4wM~whY;zJ7WROUN@QSzuR@`6u>S1$AcHV5q(^cXB2;Yz)p84R} z3obrb_l<}eKXbgi47Tp2dHHL$?^!JqvUJa~D!5YK%zv;f*=U;8e(jz;+bb(8^B9kZ zqpH0B=Hy`5*LM7D>!{HHms@9htR`fH6XcIJ$+jTkg*bjMS7{w=?;M+zXB02bLF|BO zl1d@WoNSUwnWPj3gChJgO-IAP$thC3Ancm7 zU1@2nZPJ#lkWETyikHX zUZ3agJFZ)ut_@5`IEYnU8BVue51E^i!U`hfsgZOx9a$1@^;AEHd zFt=^rux5o{E>6rVmc&DlNSOr)6$kiigC2WtQ*%4gYeBGdxBlx*NS6&(!E<_7{pl}eGP;jc%XQi)>S4LjEt6)uNhS-mz^6Jia07^9ne#^lDhh6#*Irwm1-UWyoj8Iy=YR;~=^slc_j8 z4pWRzrWm7Sne7_8zB+s>d_u>#d(lmMs{uZp2;W+&w`;z6rBA>4%{}F$Ps{gw(`a-E zQZ<@Xfx~FrzI7Yi3){DC-A?ptRb*a$&^Sottj0vcOe?WWy25(CG$pyMZ5U=7Sr*y5 zz~bW^Zfhg6lOeM+)=D+8R{DRf(?(8X!`auqxoJ&#sC><)Z@zZ6p>g)u(PQUYThHOw ztkp!6VH{R@61YhSKS^4WeEZJr6{V#c@QeNT>%r-zR%}5ic0HEJecTr03~kvG-WOgA zk@CD^1yxs=UjRyw!h-z5y1EX_f=;+#>8PuySeDGy$+*eODk^&W;U$BgtiLzbCf`Au z)L`VuFiXQO1Y2UVUVQEmaL;@vT%9;R12f6YxY<135A@WzbM?*r<7EDRhhOu^a~B7w z!@86_l~9rxpOh*M7Nmu}bHs}xR2M-!=;OH(rCL+43^ZITmMzT@1X#(H{=WOS+u*%F zL7hj@Pg0L^t6}J`hSo~oaKm0eWOTqUze*jAd|$=iI-EO+OV8t$H#xt(8y>^I=~N#b zt!+W5;NYbBt&X_lR3He_%(X9619y$DKAs60YhQUx{}4V8Kla~Eb5VQ1w^3t)xLo#T z-&ESNRX0A++CO2#-p4l4-`dpFdJgEYbFB~DxN2G2RNtGgzxCmnGatVB25KtL3kY(S zMr%``Q7a`}J(6-)7O91#&U4;yT0u~nniQWX5vmZN`KoGtremn9V=ysQ=7Gn|BTG%Z z{`$n*tAWkNS9p8kU`N-GBXj*uRP<%)75t2TU!h)xyYz25`A2h2|NYp*r1bj7Ua2(( z8O){;0n<@zSpdj)nO^RIfP+Y}2kk6H`+5>zhe3kXtkzkN%2 z0w*xug6enq#Hy8R#7vNa6^PPkmhZj$*3B!+QTKHf`#ByEcW*IYF^8s9h)UIwBVs`ymW*Xu zDjv?NPuRU=*Vw$SF?Y$N{7;y=q&{I!?U5t3j-rjbc5RH;Nj_=cBHkS-+MOrniQ_3H zA(!MuwHjjmP~l%h5=xRJ`M%@;l6mpx;6!9ug7%##rqVm zJUe1Mc);d=_7&sEfrEY<^`!1;#jk%7(%gTeC;XFNYeF~PANEi`(LH@_JMcj1ucIeh zqwR1_^dw)r`@CGoX;@8hC*7~EG3YoqzZ2UDMLaZ%afPphUgC5tgAzya=(d+))*-U5 zx+9n4PL17>9P1N?myr4Q zN@_nRfyxC!GTwLcWF_?w%K^huCG{M0Fdx)ydV0&2%J6Suqm1+lv1BuRetKgi^%SQO zyg)pc6!Q5&mVv2hX=V5x&QgH*^qikle~jI=_&!c%0uL`>{z2Xc@FTT;ePtN;;eCf7 z?>N!-oj6fRJsi7}cxA}@1W8HMO@FwFyiDo>((7>+_|DWGgamSWA}{+d>Ss~u2RAd7 z65+XIXfB6_!xf>a97Z~;P;?zSG=ba<-EG}<(q$B4W~tc#Paa3PVHM;FAriP;{c+=31B4NZvgXeLC4uxX8_sEgndLemEhZ~PW11sohLu`a>#XST+ z%5k>5Tpj*p_`dKjQ=WX`@2{MlaGJ&txHLU61%!cpxV52=x`6bmMXL(5qH$%V&5 zye#Muc|JEe>0$V+#^&)cikvlDs#oW#;#^pY&E_dLS!QkK@d>IK)HbJ1)wK+}D3Bro zbhyA}=Jb3%4`h^~aBQ?~j+IB#<;%VCTQ{yPD$M1Lb{WUPyxKQv0Ao@Aq&GNCio)x; z)aTEt+OcDqG!zO!VsrI^`Jf~vMI7|A8e?yFV`B_y^FIM3TtGD3`t7kKgbY1x1 z#b^HK<-@g?OcAt*eS8cpU~{;gsKgi59prdl(k{P8-D^sRJ$lLY$Iqem`TskZu$kDV9;?0I48h z!`-rewHp7ElBH3=>Bf_$ZG+}TzV9QV>@msL@3;f-8E~rH^L3R@s9L%U|F>%?w6ee8 z{mC0{xPIsMZ-46>x9$ab*v5R)NfTDUiS(%;8Iw}$OY@7$KKS70+cqI-8|rXw%quBe z8=b+Y(Gq9T5}QFkM1aFo&@ncSFrrJBI)-5U_RaeiY!(aPe^!fW+!0!Eahn<&;6EB1 zx4PgggC}#uG}493Gg`XMo(V~!-)EU}Zlq8qDB=lHEYN#8E>6Jb&<<>UJfJrfCFkViWM{SY-Uu5E}dFq*~;1l~Gm93L1MLlca&H#UFp^q>Fy?9(s4kJbCo zTd%(MKac1U2l%^HehD-d5hL=C5LK?NchqDjm6`Xsrn?)OTAG@B4fQXuEc@K49*czJ zjnsPKMc6EtV?L2P@kvNemHDPUq|1skz|<t#kC^lrV44oiQ2wcl5^#q(3I-0Ok(zzHX`avGQ;;^XDaTPI0cfyPtKQ?_n64(huHEwWJ183-}W z4b4q`!)B5#FMV!u%xa&qP8*Cfj8Y3Lk5*2OIFAF7Vw=PM$pK`G8gX!DeJ;$M1z8?+v(@Fx^VIRO zM0J+TH{+Ny)SZATcWH_#-U~$e>bM{$lVqXVl$RogFP-bNJ1f_fEKNw0QD6#8pAQJa z9DaDZ-#9taeF1*gOKm;FHoj)zK(+^YpxDshgkei)l75;{7j@o;+$CoPwWsVpuNXK`Hf4jt#e*oC!K7dWg?=mVv}T zE4_cfElw}+5SCAvk;ZFN6XJpI%P1~^-4wYcd#r*Jr%5l^sI$6w+BOSAW~L@5Gdn#I z9H)AvNLIcY%zRn}?Y5hzka5e8QtGm&XU?6*Y=yTEcf%X+TwMZ#v)J+SNZ`@#&Q`(|iV@2z?H} zM3tD4rcM-D+v|1NZvV1XTdJ!wINqVrq2W>MoII~&t)YK(cAhCHUQ$q)z*}5Z3EKbH zS}H$}xDQBg#CipxU$M?z?59YF)-jO)i{EvUmZ8#M~W_e2@ep10F{}E zq{2Zv%xrcf-AK&z`49z%%~8Qa@!}c4>RG_{N`iG z#dEBge^sTIb0d|OK{Q@lah&4 z-`l%)uSS!m7Ovry=VoVS+U>iFGPTLzAjgexuP|*^>&rUk6)RYu8Dcq!>2L&Vx)JZs z=rGu@G}sIs4ad+o#~L~YQPshYdLm}2*L3`fin4r7l2ES@CTa4^DnufsrT~pvpizq0 z42{T1Ji;Q)%3Xl8?pmpdhxb2TQw%deG4e+8j~zSJ0zO;4!ap_Ga_Shv*oPvq`9t5yRy9MV5&BcG|>vz||}fWG5BT^^PNshajpFkQvG`ISS;q(T=cfu>{oMw8w_oVi>X8Og+P<8SJ}tT(2!n!PlqLd_|g4 zm64Z+Tu6c>!X2NRWF$nzEp1%Reo=)AE#>Ft?f&jo?5X}hqPVC>CHGY&i$rCFIN{g- zi88?k8~KWU`oQmh_v0V$yNUW8UXV~&*J{(JJ4X79^&gxtxMXv^UE^4+`UeJ9uZB%x z=MFv->t(V}m0zjxkj5!q&~et0;EMeehx&8aM?I$FyrYcPmmrIIAlH*Sxa;Wf z1&aPn+*XjZa3aTJI_{;y<|VPUU=!{nJCtlp%(=Pl?tTlh@e#Yk;>Ts~?d}HKDosu5 zK)5bSKu8t=+4TNF2+M4;!y!>>*leS5WM~M$1k32?xY20E1<}u#0nydX%{>-u3UuP| z!u-5cM&LWCe-PSi(+;tC-nqynvbZCtfoan~Mn@d=v&9_&8Ea$@%i~FTLBd7K@y+_! z$Zi~4-Dn#S+#=!r5Rq$mH}j5?>-f0tE#EdDD}up^$Lr;-#95ZJjOvKB^Ws}|Py|6E zqJuVdnYoXQO(}WnXN^sL^sUd}nrHCV&4VMxG0QCU637~Ez(KgNE{ix*ptmhU;V6r-A^4P%S zKLizg2!8+*%q=d~CdKJ_$k#!BQ^jl?CMa3wqc9@L^_y01x_%d;6L;=DVkIV{@sNk^m3OrwiANT=)VwK%PK7ej@20YSKuB^EU@;7G6W3^@Kjk1)pXnO<88%Tw-zs$%?t4 zCRD^sY7E=8F&6mJg-L=>>YQ8<$4W(3fH;(w=^af(WU>wAW|?(G^|dTxGa46V2zh?w zs4!gmbK#=Toij!BCd&`qNPb~1M9bhglr8ij08jqLav)`J$poz$_k9+ z4jj=n|1v7LTShsTS6h-$Eb{kvzu7{*w_(7*VwOWMIw#(-GBo8&Vd_u0^@DxI=0(fjclQZPGjkOmPY;wK$=SI4za|;FSg} ztQ01C%04()ke@(J_xG_BeQ^lm)#IIqp-jn``9eG?PDctrNX1Z|xK z>v1?d-B%y!-PL#}!QZA#S?mF!7-@gPKAT0Iu7unVz%s5*V^fBv!SyMZBa~8+AeYaR z;9{7K1`R`OibktKQW+7CFH)qWjgKzS$Y!(21zHq#^9D4UWNbySe5W1z-=kX;T06 zB1huo(G@IN=WgZ1U0}(`A_psoJN!}d&e^$JFuG#HD>}Na^n8k1OwE}m)6&3nYGS2< zfCvkOC=hT$Bs{+M zR~rUd#?#vyV?H5v%tVX~5Q<|TH!$F1!Jq|O;hbt28-WNRdyCeyb!%A-Gw(7qea5`a zyLsRp<`?1iU6{9#8B0ZGFPwTFZb~F=dJ6X%e+0Fk%7<`TWRti&n=P)qr7VN68zIZw zymQ_Jd#>NV7>`?wk}gHhC8i6)SU^R|DXA&a#0+gNY!&%zVt8U?(lJkLG^w^>C{YMg zce9BDeST~elIS_l9H8btH_WSMmM4~`YT{|Vm_rp5!7rSrR>tx}E|0xK$)sduWhEnx zpC3KzUl7US_yQ@bgyF|MZkzxtc4i95_))VpL4ngo;!860gE0leFdlxO0N3web#k68 zAtB)Br4^T~fvs(01wX3gE=uYBR61H=MQqewIh%vCX`eBcEy;9>nci4O8#R%BAbl%F z-!b#@k&Emjk?}5>qCKL>zg}JiBkSGb%0|v|(TdTvkqR%a5tq+Lt_Bt;wsw$GfwM8P zqHr!kv5%d9_y41zT zatI0ng?ZFkdU>WS@{)ErXjwP?4O)F2C`D=D+q|;J>Wpg@+HD4wlZqsl8%&!m3DR7aV*)us6PJfSYazco*VZ9)WHR}%Lwhq=pr61hMK^35ok!L_zJQPhLKirl1EL>@t1x`br|Mo=gWm#!I$HDo_jgW2^J z&+9-lh1zUgtj(2Le)4=w14Zcfd0h>)oYmu&8n8h`+kng|jTWrUAiY86ow18!!Smb} z3tWMeG~1o4m!k!fBANLH-Q0i`$r4P+gX&IghDANIh$9+Ml|9P>eOxf$IzUbo{cJs-&tUT;GJm1Cg2Vsq4Sw`9G&% zz0p$CI^;`)dD=DBdG0VGSr4Bxn~xv2Tk1}9cJx45?dj<3YJ~~8wX3Pg00)c#yJ)gS zYU}_r5`WL0tz~)10{t>Ua$eciJ*A0>rKKhLr5PDnd3m{o*vJ*;=H+E&tid!Y#MA(I zFEbD6hS*FEqz!`8$PNelCwxHR1-B*I1%KnGCLtUMgw;6(1vwHePp(ue6*5JNfGbF$ zB*?Rqt?4zG+zZr#+hhn24t84x?X$3ZPg$(k^v@1<#riY0szekn4u{(>Gm9PE0xI7} zalaS$Bf-zO=crd^V%N&i9#*hZmuh;eit6JLVN-h#ul)dPhe^nUVfpw5il^2B;5- zB}ayAl@CS7!cLTm@V?}76QDUKaGmBrkn`@r4~||s^X`jJJn_VfbwYc?yH5J>A+7;8 zbE~RKvI zT|cXi8sDTpt9qk#*ZqcrKiJzyiok`FV#2K#DjOPMacOK&>KXS$Uz3i$aDfH*`apDb zy;Axaq$EMoLAEAg%C*UU`Y;MccJ z&o~IPq0=#AI1Pl;X~PUhgND>_@JMERS-M;=O3+rw(^qY;&?Y<;e}&wt6Y{-yBfDIa zvEU2@;;p%ZbM>1*sS9!>}$5+r8?WAAIn^o7LXV=VO5y%=4WnEHmj6 zOY|b&v|S={TW64f4Kk1l2K8WWg}|krfBvYMO5KSdnVqSW`RMb}dJ{cvaZR*#(he}+ zacOn5*NDWuRZD>>hV0w%iOdBuMIr~2<6O95vcsTiZX1LAiu%pnQ}B^G*`-lD}#4w+Os&c^u@^Igj|Ba!%{3uf9rgb#kva z5aME!zzqewa9R3-dVbL7oTc&=dRmdM=a!=`<*`1x8D;$%`l#^qX_&{q689v~0(5sW zk;h-i_m)>$N*py`g@`g+qf}~++H6N-yojGmMtNmc+=wz~m@H%`cbSW_6VCkF-YZ;` zbYo+TkMhya;G-nUCZ*CL5qAOKifL`VOiNh|gWs0CPtZ~>Qd4p;BtD0jQU|v~RNsx3 zk}wb5j8YbS87s;(5`}y&Gm0L@8`=hv)Ks?M%Q#YsiZGe3=1EEVOa@IXW%MlrI%*&~ zlJ7w|_o1AnUy(`kGZ-{;zmP%mGIM6>moR4vWH+zNUUJ9R=00fW3eEORiWg(dEc|@N zOvM(rJ0;V72~3%duFFiB-$k8%fI7vHA4`G!_zZ-|$Y{juKG0epMTA7aArUM{4MPAQ z`cEN3u<6ZA0|1h%1O9{HKq8P3_7fP7!q0$%B&8}4jPCLX(xTXp2n>WGsE-@<*7;v$^2y93!wPIZ zYyMR>pJJu5xXxy)i)rq+qteFN0pZTOia(E(f!N06Km&t%cg}4w{Q1upE}S?Ko$WlZG>8PuLo|YAMQXLshfC&# z>ME*>>Z14UtAZ!+k($VC<Z+A@bd`>Ki}tb z^nD%=9xYnC8(y=kx%* z-q|nU?2FHLx$>9#d`rne{{C$4zEpM8r4v#2qYBQVvcutu?h)N{-5wO2P!C=q9{h~L zn+2KXvA4$F$~1qv0@2m97&WLy4<;$%o2>lPgRYN`^2clCa*nUR
    -

    ${translate("chatpage.cchange25")}

    - ${unsafeHTML(generateHTML(this.editedMessageObj.message, [ - StarterKit, - Underline, - Highlight - // other extensions … - ]))} +

    + ${translate("chatpage.cchange25")} +

    + ${unsafeHTML(generateHTML(this.editedMessageObj.message, + [StarterKit, + Underline, + Highlight + // other extensions … + ]))}
    this.closeEditMessageContainer()} - > + @click=${() => this.closeEditMessageContainer()}> +
    `} @@ -1030,7 +1057,7 @@ class ChatPage extends LitElement { placeholder=${this.chatEditorPlaceholder} ._sendMessage=${this._sendMessage} .imageFile=${this.imageFile} - .insertImage=${this.insertImage} + .insertFile=${this.insertFile} .editedMessageObj=${this.editedMessageObj} ?isLoading=${this.isLoading} ?isLoadingMessages=${this.isLoadingMessages} @@ -1047,25 +1074,38 @@ class ChatPage extends LitElement { - ${(this.isUploadingImage || this.isDeletingImage) ? html` + ${(this.isUploadingImage || this.isDeletingImage) ? html`
    -

    - ${this.isDeletingImage ? - translate("chatpage.cchange31") : translate("chatpage.cchange30")} -

    -
    -
    -
    - - `: ''} +

    + ${this.isDeletingImage ? + translate("chatpage.cchange31") : translate("chatpage.cchange30")} +

    + + + + + `: ''} + ${(this.isUploadingAttachment) ? html` +
    +
    +
    +
    +

    + ${translate("chatpage.cchange65")} +

    +
    +
    +
    + + `: ''} { this.removeImage(); }} - style=${(this.imageFile && !this.isUploadingImage) ? "visibility:visible;z-index:50" : "visibility: hidden;z-index:-100"}> + style=${(this.imageFile && !this.isUploadingImage) ? "visibility:visible; z-index:50" : "visibility: hidden;z-index:-100"}>
    ${this.imageFile && html` @@ -1078,7 +1118,7 @@ class ChatPage extends LitElement { placeholder=${this.chatEditorPlaceholder} ._sendMessage=${this._sendMessage} .imageFile=${this.imageFile} - .insertImage=${this.insertImage} + .insertFile=${this.insertFile} .editedMessageObj=${this.editedMessageObj} ?isLoading=${this.isLoading} ?isLoadingMessages=${this.isLoadingMessages} @@ -1101,7 +1141,6 @@ class ChatPage extends LitElement { const chatTextEditor = this.shadowRoot.getElementById('chatTextCaption') chatTextEditor.sendMessageFunc({ type: 'image', - imageFile: this.imageFile, }) }} > @@ -1111,6 +1150,58 @@ class ChatPage extends LitElement {
    + { + this.removeAttachment(); + }} + style=${this.attachment && !this.isUploadingAttachment ? "visibility: visible; z-index: 50" : "visibility: hidden; z-index: -100"}> +
    +
    + ${this.attachment && html` +
    + attachment-icon +
    + `} +

    ${this.attachment && this.attachment.name}

    +
    + this.updatePlaceholder(editor, value)} + > + +
    + +
    +
    +

    ${translate("chatpage.cchange41")}


    @@ -1332,14 +1423,15 @@ class ChatPage extends LitElement { - async connectedCallback() { + async connectedCallback() { super.connectedCallback(); this.webWorker = new WebWorker(); - this.webWorkerImage = new WebWorkerImage(); + this.webWorkerFile = new WebWorkerFile(); await this.getUpdateCompleteTextEditor(); - const elementChatId = this.shadowRoot.getElementById('_chatEditorDOM').shadowRoot.getElementById('_chatEditorDOM') - const elementChatImageId = this.shadowRoot.getElementById('chatTextCaption').shadowRoot.getElementById('newChat') + const elementChatId = this.shadowRoot.getElementById('_chatEditorDOM').shadowRoot.getElementById('_chatEditorDOM'); + const elementChatImageId = this.shadowRoot.getElementById('chatTextCaption').shadowRoot.getElementById('newChat'); + const elementChatAttachmentId = this.shadowRoot.getElementById('chatAttachmentId').shadowRoot.getElementById('newAttachmentChat'); this.editor = new Editor({ onUpdate: ()=> { this.shadowRoot.getElementById('_chatEditorDOM').getMessageSize(this.editor.getJSON()) @@ -1396,11 +1488,37 @@ class ChatPage extends LitElement { Extension.create({ addKeyboardShortcuts:()=> { return { - 'Enter':()=> { + 'Enter':() => { const chatTextEditor = this.shadowRoot.getElementById('chatTextCaption') chatTextEditor.sendMessageFunc({ - type: 'image', - imageFile: this.imageFile, + type: 'image' + }) + return true + } + } + }}) + ] + }) + + this.editorAttachment = new Editor({ + onUpdate: () => { + this.shadowRoot.getElementById('chatAttachmentId').getMessageSize(this.editorAttachment.getJSON()) + }, + element: elementChatAttachmentId, + extensions: [ + StarterKit, + Underline, + Highlight, + Placeholder.configure({ + placeholder: 'Write something …', + }), + Extension.create({ + addKeyboardShortcuts:()=> { + return { + 'Enter':()=> { + const chatTextEditor = this.shadowRoot.getElementById('chatAttachmentId') + chatTextEditor.sendMessageFunc({ + type: 'attachment' }) return true } @@ -1411,17 +1529,18 @@ class ChatPage extends LitElement { document.addEventListener('keydown', this.initialChat); } - disconnectedCallback() { - super.disconnectedCallback(); - this.webWorker.terminate(); - this.webWorkerImage.terminate(); - this.editor.destroy() - this.editorImage.destroy() - document.removeEventListener('keydown', this.initialChat); - } + disconnectedCallback() { + super.disconnectedCallback(); + this.webWorker.terminate(); + this.webWorkerFile.terminate(); + this.editor.destroy(); + this.editorImage.destroy(); + this.editorAttachment.destroy(); + document.removeEventListener('keydown', this.initialChat); + } initialChat(e) { - if (this.editor && !this.editor.isFocused && this.currentEditor === '_chatEditorDOM' && !this.openForwardOpen && !this.openTipUser) { + if (this.editor && !this.editor.isFocused && this.currentEditor === '_chatEditorDOM' && !this.openForwardOpen && !this.openTipUser) { // WARNING: Deprecated methods from KeyBoard Event if (e.code === "Space" || e.keyCode === 32 || e.which === 32) { } else if (inputKeyCodes.includes(e.keyCode)) { @@ -1431,8 +1550,6 @@ class ChatPage extends LitElement { this.editor.commands.focus('end') } } - - } async userSearch() { @@ -1498,19 +1615,29 @@ class ChatPage extends LitElement { } - insertImage(file) { + insertFile(file) { if (file.type.includes('image')) { this.imageFile = file; - this.currentEditor = 'newChat' + this.currentEditor = 'newChat'; return; - } - parentEpml.request('showSnackBar', get("chatpage.cchange28")); + } else { + this.attachment = file; + this.currentEditor = "newAttachmentChat"; + return; + } + // parentEpml.request('showSnackBar', get("chatpage.cchange28")); } removeImage() { this.imageFile = null; - this.resetChatEditor() - this.currentEditor = '_chatEditorDOM' + this.resetChatEditor(); + this.currentEditor = '_chatEditorDOM'; + } + + removeAttachment() { + this.attachment = null; + this.resetChatEditor(); + this.currentEditor = '_chatEditorDOM'; } changeMsgInput(id) { @@ -1616,7 +1743,6 @@ class ChatPage extends LitElement { this.groupAdmin = membersAdminsWithName this.groupMembers = membersWithName this.groupInfo = getGroupInfo - console.log({membersAdminsWithName}) } catch (error) { console.error(error) } @@ -1678,7 +1804,6 @@ class ChatPage extends LitElement { this.editor.setEditable(true) } } - } async getName (recipient) { @@ -2002,8 +2127,6 @@ class ChatPage extends LitElement { let decodedMessageObj = {}; if (isReceipientVar === true) { - console.log('encoded', encodedMessageObj.isEncrypted, _publicKeyVar.hasPubKey,encodedMessageObj.data) - // direct chat if (encodedMessageObj.isEncrypted === true && _publicKeyVar.hasPubKey === true && encodedMessageObj.data) { let decodedMessage = window.parent.decryptChatMessage(encodedMessageObj.data, window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey, _publicKeyVar.key, encodedMessageObj.reference); decodedMessageObj = { ...encodedMessageObj, decodedMessage }; @@ -2215,6 +2338,9 @@ class ChatPage extends LitElement { if(this.currentEditor === 'newChat'){ this.editorImage.commands.setContent('') } + if(this.currentEditor === 'newAttachmentChat'){ + this.editorAttachment.commands.setContent('') + } } async _sendMessage(outSideMsg, msg) { @@ -2258,6 +2384,7 @@ class ChatPage extends LitElement { // find specific object property in local let typeMessage = 'regular'; let workerImage; + let workerAttachment; this.isLoading = true; const trimmedMessage = msg @@ -2286,10 +2413,10 @@ class ChatPage extends LitElement { let compressedFile = '' var str = "iVBORw0KGgoAAAANSUhEUgAAAsAAAAGMAQMAAADuk4YmAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAADlJREFUeF7twDEBAAAAwiD7p7bGDlgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAGJrAABgPqdWQAAAABJRU5ErkJggg=="; - if (this.webWorkerImage) { - workerImage = this.webWorkerImage; + if (this.webWorkerFile) { + workerImage = this.webWorkerFile; } else { - this.webWorkerImage = new WebWorkerImage(); + this.webWorkerFile = new WebWorkerFile(); } const b64toBlob = (b64Data, contentType='', sliceSize=512) => { @@ -2367,7 +2494,6 @@ class ChatPage extends LitElement { } const stringifyMessageObject = JSON.stringify(messageObject); this.sendMessage(stringifyMessageObject, typeMessage, chatReference); - } else if (outSideMsg && outSideMsg.type === 'image') { @@ -2379,10 +2505,10 @@ class ChatPage extends LitElement { return; } - if (this.webWorkerImage) { - workerImage = this.webWorkerImage; + if (this.webWorkerFile) { + workerImage = this.webWorkerFile; } else { - this.webWorkerImage = new WebWorkerImage(); + this.webWorkerFile = new WebWorkerFile(); } const image = this.imageFile @@ -2412,41 +2538,101 @@ class ChatPage extends LitElement { this.isUploadingImage = false; return; } - try { - await publishData({ - registeredName: userName, - file : compressedFile, - service: 'QCHAT_IMAGE', - identifier : identifier, - parentEpml, - metaData: undefined, - uploadType: 'file', - selectedAddress: this.selectedAddress, - worker: workerImage - }); + try { + await publishData({ + registeredName: userName, + file : compressedFile, + service: 'QCHAT_IMAGE', + identifier : identifier, + parentEpml, + metaData: undefined, + uploadType: 'file', + selectedAddress: this.selectedAddress, + worker: workerImage + }); + this.isUploadingImage = false; + this.removeImage(); + } catch (error) { + console.error(error); + this.isLoading = false; this.isUploadingImage = false; - this.removeImage() - } catch (error) { - this.isLoading = false; - this.isUploadingImage = false; - return; - } + return; + } - - - const messageObject = { - messageText: trimmedMessage, - images: [{ - service: "QCHAT_IMAGE", - name: userName, - identifier: identifier - }], - isImageDeleted: false, - repliedTo: '', - version: 2 - }; - const stringifyMessageObject = JSON.stringify(messageObject); - this.sendMessage(stringifyMessageObject, typeMessage); + const messageObject = { + messageText: trimmedMessage, + images: [{ + service: "QCHAT_IMAGE", + name: userName, + identifier: identifier + }], + isImageDeleted: false, + repliedTo: '', + version: 2 + }; + const stringifyMessageObject = JSON.stringify(messageObject); + this.sendMessage(stringifyMessageObject, typeMessage); + } + else if (outSideMsg && outSideMsg.type === 'attachment') { + this.isUploadingAttachment = true; + const userName = await getName(this.selectedAddress.address); + if (!userName) { + parentEpml.request('showSnackBar', get("chatpage.cchange27")); + this.isLoading = false; + return; + } + + if (this.webWorkerFile) { + workerAttachment = this.webWorkerFile; + } else { + this.webWorkerFile = new WebWorkerFile(); + } + + const attachment = this.attachment; + const id = this.uid(); + const identifier = `qchat_${id}`; + const fileSize = attachment.size; + if (fileSize > 1000000) { + parentEpml.request('showSnackBar', get("chatpage.cchange67")); + this.isLoading = false; + this.isUploadingAttachment = false; + return; + } + try { + await publishData({ + registeredName: userName, + file : attachment, + service: 'QCHAT_ATTACHMENT', + identifier : identifier, + parentEpml, + metaData: undefined, + uploadType: 'file', + selectedAddress: this.selectedAddress, + worker: workerAttachment + }); + this.isUploadingAttachment = false; + this.removeAttachment(); + } catch (error) { + console.error(error); + this.isLoading = false; + this.isUploadingAttachment = false; + return; + } + const messageObject = { + messageText: trimmedMessage, + attachments: [{ + service: 'QCHAT_ATTACHMENT', + name: userName, + identifier: identifier, + attachmentName: attachment.name, + attachmentSize: attachment.size + }], + isAttachmentDeleted: false, + repliedTo: '', + version: 2 + }; + const stringifyMessageObject = JSON.stringify(messageObject); + this.sendMessage(stringifyMessageObject, typeMessage); } else if (outSideMsg && outSideMsg.type === 'reaction') { typeMessage = 'edit'; let chatReference = outSideMsg.editedMessageObj.reference; diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js b/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js index 2c62a563..4be2d907 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js @@ -612,8 +612,9 @@ export const chatStyles = css` font-family: 'JetBrainsMono', monospace; padding: 0.75rem 1rem; border-radius: 0.5rem; - white-space: pre-wrap; + white-space: pre-wrap; } + .replied-message pre code { color: inherit; padding: 0; @@ -621,12 +622,10 @@ export const chatStyles = css` font-size: 0.8rem; } - .replied-message img { width: 1.7em; height: 1.5em; margin: 0px; - } .replied-message blockquote { @@ -639,4 +638,77 @@ export const chatStyles = css` border-top: 2px solid rgba(#0D0D0D, 0.1); margin: 2rem 0; } + + .attachment-container { + display: flex; + align-items: center; + justify-content: space-evenly; + padding: 5px 0 10px 0; + gap: 20px; + cursor: pointer; + } + + .attachment-container:hover .download-icon::before { + background-color: rgb(161 158 158 / 41%); + } + + + + .attachment-icon-container { + display: flex; + align-items: center; + justify-content: center; + height: 50px; + width: 50px; + border-radius: 50%; + border: none; + background-color: var(--mdc-theme-primary); + } + + .attachment-icon { + width: 70%; + } + + .attachment-info { + display: flex; + flex-direction: column; + gap: 5px; + } + + .attachment-name { + font-family: Work Sans, sans-serif; + font-size: 16px; + color: var(--chat-bubble-msg-color); + margin: 0; + letter-spacing: 0.4px; + padding: 5px 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .attachment-size { + font-family: Roboto, sans-serif; + font-size: 16px; + color: var(--chat-bubble-msg-color); + margin: 0; + letter-spacing: 0.3px; + font-weight: 300; + } + + .download-icon { + position: relative; + color: var(--chat-bubble-msg-color); + width: 19px; + background-color: transparent; + } + + .download-icon::before { + content: ""; + position: absolute; + border-radius: 50%; + padding: 18px; + background-color: transparent; + transition: all 0.3s ease-in-out; + } ` diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index e9db4e5e..3eaecb94 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -6,6 +6,7 @@ import {unsafeHTML} from 'lit/directives/unsafe-html.js'; import { chatStyles } from './ChatScroller-css.js' import { Epml } from "../../../epml"; import { cropAddress } from "../../utils/cropAddress"; +import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js'; import './LevelFounder.js'; import './NameMenu.js'; import './ChatModals.js'; @@ -17,7 +18,9 @@ import '@material/mwc-button'; import '@material/mwc-dialog'; import '@material/mwc-icon'; import { EmojiPicker } from 'emoji-picker-js'; -import { generateHTML } from '@tiptap/core' +import { generateHTML } from '@tiptap/core'; +import { saveAs } from 'file-saver'; +import axios from "axios"; import StarterKit from '@tiptap/starter-kit' import Underline from '@tiptap/extension-underline'; import Highlight from '@tiptap/extension-highlight' @@ -50,7 +53,7 @@ class ChatScroller extends LitElement { openTipUser: { type: Boolean }, openUserInfo: { type: Boolean }, userName: { type: String }, - selectedHead: { type: Object } + selectedHead: { type: Object }, } } @@ -270,7 +273,7 @@ class MessageTemplate extends LitElement { setOpenTipUser: { attribute: false }, setOpenUserInfo: { attribute: false }, setUserName: { attribute: false }, - openTipUser:{ type: Boolean } + openTipUser:{ type: Boolean }, } } @@ -318,6 +321,22 @@ class MessageTemplate extends LitElement { } } + async downloadAttachment(attachment) { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + try{ + const res = await axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}`, { responseType: 'blob' }) + .then(response =>{ + let filename = attachment.attachmentName; + console.log({response: response.data}); + let blob = new Blob([response.data], { type:"application/octet-stream" }); + saveAs(blob , filename); + }) + } catch (error) { + console.error(error); + } + } + render() { const hidemsg = this.hideMessages; let message = ""; @@ -328,8 +347,10 @@ class MessageTemplate extends LitElement { let isImageDeleted = false; let version = 0; let isForwarded = false + let attachment = null; try { const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage); + console.log({parsedMessageObj}); if(parsedMessageObj.version.toString() === '2'){ messageVersion2 = generateHTML(parsedMessageObj.messageText, [ @@ -345,6 +366,10 @@ class MessageTemplate extends LitElement { reactions = parsedMessageObj.reactions || []; version = parsedMessageObj.version isForwarded = parsedMessageObj.type === 'forward' + if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) { + attachment = parsedMessageObj.attachments[0]; + } + console.log({parsedMessageObj}); if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) { image = parsedMessageObj.images[0]; } @@ -403,12 +428,11 @@ class MessageTemplate extends LitElement { const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`; - if(this.viewImage || this.myAddress === this.messageObj.sender){ + if (this.viewImage || this.myAddress === this.messageObj.sender) { imageHTML = createImage(imageUrl); imageHTMLDialog = createImage(imageUrl) imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px"; } - } nameMenu = html` @@ -529,12 +553,12 @@ class MessageTemplate extends LitElement { ${repliedToData.decodedMessage.messageText} ` : ''} ${version.toString() === '2' ? html` - ${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, [ - StarterKit, - Underline, - Highlight - // other extensions … - ]))} + ${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, + [StarterKit, + Underline, + Highlight + // other extensions … + ]))} ` : ''} @@ -548,10 +572,9 @@ class MessageTemplate extends LitElement { }} class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} style=${this.isFirstMessage && "margin-top: 10px;"}> -
    - ${translate("chatpage.cchange40")} -
    - +
    + ${translate("chatpage.cchange40")} +
    ` : html``} ${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html` @@ -561,13 +584,37 @@ class MessageTemplate extends LitElement { ${imageHTML} { this.openDeleteImage = true; - this.chatE }} class="image-delete-icon" icon="vaadin:close" slot="icon"> ` : image && isImageDeleted ? html`

    This image has been deleted

    ` : html``} + ${attachment ? + html` +
    await this.downloadAttachment(attachment)} class="attachment-container"> +
    + attachment-icon +
    +
    +

    + ${attachment && attachment.attachmentName} +

    +

    + ${roundToNearestDecimal(attachment.attachmentSize)} mb +

    +
    + + +
    + ` + : html``}
    * + * { - margin-top: 0.75em; - outline: none; + .ProseMirror > * + * { + margin-top: 0.75em; + outline: none; } - .ProseMirror ul, - ol { - padding: 0 1rem; - } - - .ProseMirror h1, - h2, - h3, - h4, - h5, - h6 { - line-height: 1.1; - } - - .ProseMirror code { - background-color: rgba(#616161, 0.1); - color: #616161; - } - - .ProseMirror pre { - background: #0D0D0D; - color: #FFF; - font-family: 'JetBrainsMono', monospace; - padding: 0.75rem 1rem; - border-radius: 0.5rem; - white-space: pre-wrap; - } - .ProseMirror pre code { - color: inherit; - padding: 0; - background: none; - font-size: 0.8rem; + .ProseMirror ul, + ol { + padding: 0 1rem; } + .ProseMirror h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + } - .ProseMirror img { - width: 1.7em; - height: 1.5em; - margin: 0px; + .ProseMirror code { + background-color: rgba(#616161, 0.1); + color: #616161; + } - } + .ProseMirror pre { + background: #0D0D0D; + color: #FFF; + font-family: 'JetBrainsMono', monospace; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + white-space: pre-wrap; + } + .ProseMirror pre code { + color: inherit; + padding: 0; + background: none; + font-size: 0.8rem; + } - .ProseMirror blockquote { - padding-left: 1rem; - border-left: 2px solid rgba(#0D0D0D, 0.1); - } - .ProseMirror hr { - border: none; - border-top: 2px solid rgba(#0D0D0D, 0.1); - margin: 2rem 0; - } - .chatbar-button-single { - background: var(--white); - outline: none; - border: none; - color: var(--black); - padding: 4px; - border-radius: 5px; - cursor: pointer; - margin-right: 2px; - filter: brightness(100%); - transition: all 0.2s; - display: none; - } - .chatbar-button-single:hover { - filter: brightness(120%); - - } + .ProseMirror img { + width: 1.7em; + height: 1.5em; + margin: 0px; - .chatbar-buttons { - margin-bottom: 5px; - flex-shrink: 0; - } + } - .show-chatbar-buttons { - display: flex; - align-items: center; - justify-content: center; - } - :host(:hover) .chatbar-button-single { - - display: flex; - align-items: center; - justify-content: center; - } - .ProseMirror p.is-editor-empty:first-child::before { - color: #adb5bd; - content: attr(data-placeholder); - float: left; - height: 0; - pointer-events: none; -} -.ProseMirror p { - font-size: 18px; - margin-block-start: 0px; - margin-block-end: 0px; - overflow-wrap: anywhere; -} + .ProseMirror blockquote { + padding-left: 1rem; + border-left: 2px solid rgba(#0D0D0D, 0.1); + } -.ProseMirror { - width: 100%; - box-sizing: border-box; - word-break: break-all; -} + .ProseMirror hr { + border: none; + border-top: 2px solid rgba(#0D0D0D, 0.1); + margin: 2rem 0; + } + .chatbar-button-single { + background: var(--white); + outline: none; + border: none; + color: var(--black); + padding: 4px; + border-radius: 5px; + cursor: pointer; + margin-right: 2px; + filter: brightness(100%); + transition: all 0.2s; + display: none; + } + .chatbar-button-single:hover { + filter: brightness(120%); + + } -.ProseMirror mark { - background-color: #ffe066; - border-radius: 0.25em; - box-decoration-break: clone; - padding: 0.125em 0; -} + .chatbar-buttons { + margin-bottom: 5px; + flex-shrink: 0; + } -.material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; - /* Preferred icon size */ - display: inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; + .show-chatbar-buttons { + display: flex; + align-items: center; + justify-content: center; + } + :host(:hover) .chatbar-button-single { + + display: flex; + align-items: center; + justify-content: center; + } + .ProseMirror p.is-editor-empty:first-child::before { + color: #adb5bd; + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; + } + .ProseMirror p { + font-size: 18px; + margin-block-start: 0px; + margin-block-end: 0px; + overflow-wrap: anywhere; + } -} + .ProseMirror { + width: 100%; + box-sizing: border-box; + word-break: break-all; + } -.material-symbols-outlined { - font-family: 'Material Symbols Outlined'; - font-weight: normal; - font-style: normal; - font-size: 18px; /* Preferred icon size */ - display: inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; -} -.hide-styling { - display: none; -} - - ` + .ProseMirror mark { + background-color: #ffe066; + border-radius: 0.25em; + box-decoration-break: clone; + padding: 0.125em 0; + } + + .material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + /* Preferred icon size */ + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + + } + + .material-symbols-outlined { + font-family: 'Material Symbols Outlined'; + font-weight: normal; + font-style: normal; + font-size: 18px; /* Preferred icon size */ + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + } + + .hide-styling { + display: none; + } + ` } constructor() { @@ -357,163 +360,159 @@ class ChatTextEditor extends LitElement { } render() { - return html`
    - - - - - - - - -
    + @click=${() => this.editor.chain().focus().toggleBold().run()} + ?disabled=${ + this.editor && + !this.editor.can() + .chain() + .focus() + .toggleBold() + .run()} + class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj) && 'show-chatbar-buttons', this.editor && this.editor.isActive('bold') ? 'is-active' : ''].join(" ")}> + + + + + + + + +
    +
    +
    - -
    { - this.preventUserSendingImage(e) - }}> - - -
    - -
    -
    - -
    - - ${this.editedMessageObj ? ( - html` -
    - ${this.isLoading === false ? html` - { - this.sendMessageFunc(); - }} - > - - ` : - html` - - `} -
    - ` - ) : - html` -
    - ${this.isLoading === false ? html` - send-icon { - this.sendMessageFunc(); - }} - /> - ` : - html` - - `} -
    - ` - } -
    - ${this.chatMessageSize >= 750 ? - html` -
    -
    - ${`Your message size is of ${this.chatMessageSize} bytes out of a maximum of 1000`} -
    -
    - ` : - html``} + style=${this.iframeId === "privateMessage" ? "display: none" : "display: block"} + class="file-picker-container" + @click=${(e) => { + this.preventUserSendingImage(e) + }}> + + +
    + +
    + +
    + + ${this.editedMessageObj ? ( + html` +
    + ${this.isLoading === false ? html` + { + this.sendMessageFunc(); + }} + > + + ` : + html` + + `} +
    + ` + ) : + html` +
    + ${this.isLoading === false ? html` + send-icon { + this.sendMessageFunc(); + }} + /> + ` : + html` + + `} +
    + ` + } + + ${this.chatMessageSize >= 750 ? + html` +
    +
    + ${`Your message size is of ${this.chatMessageSize} bytes out of a maximum of 1000`} +
    +
    + ` : + html``} + ` } @@ -524,14 +523,7 @@ class ChatTextEditor extends LitElement { } } - - - - async firstUpdated() { - - - window.addEventListener('storage', () => { const checkTheme = localStorage.getItem('qortalTheme'); const chatbar = this.shadowRoot.querySelector('.element') @@ -548,7 +540,6 @@ class ChatTextEditor extends LitElement { this.emojiPickerHandler = this.shadowRoot.querySelector('.emoji-button'); this.mirrorChatInput = this.shadowRoot.getElementById('messageBox'); - this.chatMessageInput = this.shadowRoot.querySelector('.element') this.emojiPicker = new EmojiPicker({ style: "twemoji", @@ -590,10 +581,6 @@ class ChatTextEditor extends LitElement { if (changedProperties && changedProperties.has('placeholder') && this.updatePlaceholder && this.editor) { this.updatePlaceholder(this.editor, this.placeholder ) } - - if (changedProperties && changedProperties.has("imageFile")) { - this.chatMessageInput = "newChat"; - } } shouldUpdate(changedProperties) { @@ -603,7 +590,7 @@ class ChatTextEditor extends LitElement { } sendMessageFunc(props) { - if(this.editor.isEmpty) return + if(this.editor.isEmpty && (!this.imageFile || !this.attachment)) return this.getMessageSize(this.editor.getJSON()) if (this.chatMessageSize > 1000 ) { parentEpml.request('showSnackBar', get("chatpage.cchange29")); @@ -615,8 +602,7 @@ class ChatTextEditor extends LitElement { getMessageSize(message){ try { - - const trimmedMessage = message + const trimmedMessage = message; let messageObject = {}; if (this.repliedToMessageObj) { @@ -636,20 +622,33 @@ class ChatTextEditor extends LitElement { const parsedMessageObj = JSON.parse(this.editedMessageObj.decodedMessage); message = parsedMessageObj; } catch (error) { - message = this.messageObj.decodedMessage + message = this.messageObj.decodedMessage; } messageObject = { ...message, messageText: trimmedMessage, } - } else if(this.imageFile && this.iframeId === 'newChat') { + } else if (this.imageFile && this.iframeId === 'newChat') { messageObject = { messageText: trimmedMessage, images: [{ service: "QCHAT_IMAGE", name: '123456789123456789123456789', identifier: '123456' - }], + }], + repliedTo: '', + version: 2 + }; + } else if (this.attachment && this.iframeId === 'newAttachmentChat') { + messageObject = { + messageText: trimmedMessage, + attachments: [{ + service: "QCHAT_ATTACHMENT", + name: '123456789123456789123456789', + identifier: '123456', + attachmentName: "123456789123456789123456789", + attachmentSize: "123456" + }], repliedTo: '', version: 2 }; @@ -661,7 +660,6 @@ class ChatTextEditor extends LitElement { version: 2 }; } - const stringified = JSON.stringify(messageObject); const size = new Blob([stringified]).size; this.chatMessageSize = size; @@ -670,7 +668,6 @@ class ChatTextEditor extends LitElement { } } - } diff --git a/qortal-ui-plugins/plugins/core/components/computePowWorkerImage.js b/qortal-ui-plugins/plugins/core/components/computePowWorkerFile.js similarity index 100% rename from qortal-ui-plugins/plugins/core/components/computePowWorkerImage.js rename to qortal-ui-plugins/plugins/core/components/computePowWorkerFile.js diff --git a/qortal-ui-plugins/plugins/utils/roundToNearestDecimal.js b/qortal-ui-plugins/plugins/utils/roundToNearestDecimal.js new file mode 100644 index 00000000..5e3fe0aa --- /dev/null +++ b/qortal-ui-plugins/plugins/utils/roundToNearestDecimal.js @@ -0,0 +1,4 @@ +export function roundToNearestDecimal(num) { + const mb = num / 1000000; + return Math.round(mb * 10) / 10; +} \ No newline at end of file From dd52365abb4d565b23f31253c1ec3146576fac87 Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Fri, 20 Jan 2023 21:43:49 -0500 Subject: [PATCH 02/32] Added Edited Message Tag to Chat Bubbles --- qortal-ui-core/language/us.json | 3 ++- .../core/components/ChatScroller-css.js | 22 ++++++++++++------ .../plugins/core/components/ChatScroller.js | 23 +++++++++++++++---- .../core/messaging/q-chat/q-chat.src.js | 2 -- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 819b2454..9d77a5d7 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -573,7 +573,8 @@ "cchange64": "Enter Disabled", "cchange65": "Uploading attachment. This may take up to one minute.", "cchange66": "Deleting attachment. This may take up to one minute.", - "cchange67": "Attachment size exceeds 1 MB" + "cchange67": "Attachment size exceeds 1 MB", + "cchange68": "edited" }, "welcomepage": { "wcchange1": "Welcome to Q-Chat", diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js b/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js index 4be2d907..da79a691 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js @@ -85,9 +85,10 @@ export const chatStyles = css` color: #888888; font-size: 13px; user-select: none; - float: right; - padding-left: 15px; - text-align: right; + display: flex; + justify-content: space-between; + width: 100%; + padding-top: 2px; } .message-data-time-hidden { @@ -96,10 +97,10 @@ export const chatStyles = css` color: #888888; font-size: 13px; user-select: none; - float: right; - padding-left: 15px; - padding-bottom: 3px; - text-align: right; + display: flex; + justify-content: space-between; + width: 100%; + padding-top: 2px; } .message-user-info { @@ -711,4 +712,11 @@ export const chatStyles = css` background-color: transparent; transition: all 0.3s ease-in-out; } + + .edited-message-style { + font-family: "Work Sans", sans-serif; + font-style: italic; + font-size: 13px; + visibility: visible; + } ` diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index 3eaecb94..17100219 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -71,12 +71,12 @@ class ChatScroller extends LitElement { } render() { + console.log(4, "here"); let formattedMessages = this.messages.reduce((messageArray, message, index) => { const lastGroupedMessage = messageArray[messageArray.length - 1]; let timestamp; let sender; let repliedToData; - let firstMessageInChat; if (index === 0) { @@ -328,7 +328,6 @@ class MessageTemplate extends LitElement { const res = await axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}`, { responseType: 'blob' }) .then(response =>{ let filename = attachment.attachmentName; - console.log({response: response.data}); let blob = new Blob([response.data], { type:"application/octet-stream" }); saveAs(blob , filename); }) @@ -338,6 +337,7 @@ class MessageTemplate extends LitElement { } render() { + console.log(this.messageObj, "message object here"); const hidemsg = this.hideMessages; let message = ""; let messageVersion2 = "" @@ -347,10 +347,10 @@ class MessageTemplate extends LitElement { let isImageDeleted = false; let version = 0; let isForwarded = false + let isEdited = false let attachment = null; try { const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage); - console.log({parsedMessageObj}); if(parsedMessageObj.version.toString() === '2'){ messageVersion2 = generateHTML(parsedMessageObj.messageText, [ @@ -366,10 +366,10 @@ class MessageTemplate extends LitElement { reactions = parsedMessageObj.reactions || []; version = parsedMessageObj.version isForwarded = parsedMessageObj.type === 'forward' + isEdited = this.messageObj.editedTimestamp && true if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) { attachment = parsedMessageObj.attachments[0]; } - console.log({parsedMessageObj}); if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) { image = parsedMessageObj.images[0]; } @@ -384,6 +384,7 @@ class MessageTemplate extends LitElement { let levelFounder = ''; let hideit = hidemsg.includes(this.messageObj.sender); let forwarded = '' + let edited = '' levelFounder = html``; if (this.messageObj.senderName) { @@ -446,6 +447,12 @@ class MessageTemplate extends LitElement { `; + edited = html` + + ${translate("chatpage.cchange68")} + + `; + if (repliedToData) { try { const parsedMsg = JSON.parse(repliedToData.decodedMessage); @@ -634,6 +641,14 @@ class MessageTemplate extends LitElement { ? 'message-data-time' : 'message-data-time-hidden' }"> + ${isEdited ? + html` + + ${edited} + + ` + : null + } diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index c4580e35..6610a848 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -163,8 +163,6 @@ class Chat extends LitElement { } render() { - console.log(12, "q-chat here"); - console.log(window.location.href); return html`
    From 66f32f799a7a2e277ee15a7f95c724856ab6b853 Mon Sep 17 00:00:00 2001 From: Justin Ferrari <‘justinwesleyferrari@gmail.com’> Date: Mon, 23 Jan 2023 16:40:44 -0500 Subject: [PATCH 03/32] Added delete attachment UI --- qortal-ui-core/language/us.json | 6 +- qortal-ui-core/package.json | 22 +-- qortal-ui-plugins/package.json | 22 +-- .../plugins/core/components/ChatPage.js | 181 +++++++++++++----- .../core/components/ChatScroller-css.js | 10 +- .../plugins/core/components/ChatScroller.js | 71 ++++++- 6 files changed, 229 insertions(+), 83 deletions(-) diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 9d77a5d7..f6a2ed92 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -574,7 +574,11 @@ "cchange65": "Uploading attachment. This may take up to one minute.", "cchange66": "Deleting attachment. This may take up to one minute.", "cchange67": "Attachment size exceeds 1 MB", - "cchange68": "edited" + "cchange68": "edited", + "cchange69": "Are you sure you want to delete this image?", + "cchange70": "Are you sure you want to delete this attachment?", + "cchange71": "This image has been deleted", + "cchange72": "This attachment has been deleted" }, "welcomepage": { "wcchange1": "Welcome to Q-Chat", diff --git a/qortal-ui-core/package.json b/qortal-ui-core/package.json index ba56c34c..8fee1ccf 100644 --- a/qortal-ui-core/package.json +++ b/qortal-ui-core/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-core", - "version": "2.2.5", + "version": "3.0.0", "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet", "keywords": [ "QORT", @@ -17,7 +17,7 @@ "author": "QORTAL ", "license": "GPL-3.0", "dependencies": { - "@hapi/hapi": "21.1.0", + "@hapi/hapi": "21.2.0", "@hapi/inert": "7.0.0", "sass": "1.57.1" }, @@ -53,28 +53,28 @@ "@polymer/paper-spinner": "3.0.2", "@polymer/paper-toast": "3.0.1", "@polymer/paper-tooltip": "3.0.1", - "@rollup/plugin-alias": "4.0.2", + "@rollup/plugin-alias": "4.0.3", "@rollup/plugin-babel": "6.0.3", - "@rollup/plugin-commonjs": "24.0.0", + "@rollup/plugin-commonjs": "24.0.1", "@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-replace": "5.0.2", "@rollup/plugin-terser": "0.3.0", - "@vaadin/button": "23.3.3", - "@vaadin/grid": "23.3.3", - "@vaadin/icons": "23.3.3", - "@vaadin/password-field": "23.3.3", - "@vaadin/tooltip": "23.3.3", + "@vaadin/button": "23.3.5", + "@vaadin/grid": "23.3.5", + "@vaadin/icons": "23.3.5", + "@vaadin/password-field": "23.3.5", + "@vaadin/tooltip": "23.3.5", "asmcrypto.js": "2.3.2", "bcryptjs": "2.4.3", "epml": "0.3.3", "file-saver": "2.0.5", - "lit": "2.6.0", + "lit": "2.6.1", "lit-translate": "2.0.1", "pwa-helpers": "0.9.1", "random-sentence-generator": "0.0.8", "redux": "4.2.0", "redux-thunk": "2.4.2", - "rollup": "3.9.1", + "rollup": "3.10.1", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", "rollup-plugin-scss": "3.0.0", diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index da44ec3b..d44522c5 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-plugins", - "version": "2.2.5", + "version": "3.0.0", "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet", "keywords": [ "QORT", @@ -64,24 +64,24 @@ "@polymer/paper-slider": "3.0.1", "@polymer/paper-spinner": "3.0.2", "@polymer/paper-tooltip": "3.0.1", - "@rollup/plugin-alias": "4.0.2", + "@rollup/plugin-alias": "4.0.3", "@rollup/plugin-babel": "6.0.3", - "@rollup/plugin-commonjs": "24.0.0", + "@rollup/plugin-commonjs": "24.0.1", "@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-replace": "5.0.2", "@rollup/plugin-terser": "0.3.0", - "@vaadin/avatar": "23.3.3", - "@vaadin/button": "23.3.3", - "@vaadin/grid": "23.3.3", - "@vaadin/icons": "23.3.3", - "@vaadin/tooltip": "23.3.3", + "@vaadin/avatar": "23.3.5", + "@vaadin/button": "23.3.5", + "@vaadin/grid": "23.3.5", + "@vaadin/icons": "23.3.5", + "@vaadin/tooltip": "23.3.5", "epml": "0.3.3", "file-saver": "2.0.5", - "highcharts": "10.3.2", + "highcharts": "10.3.3", "html-escaper": "3.0.3", - "lit": "2.6.0", + "lit": "2.6.1", "lit-translate": "2.0.1", - "rollup": "3.9.1", + "rollup": "3.10.1", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", "rollup-plugin-web-worker-loader": "1.6.1" diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js index 05d76dd3..14f55fe0 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatPage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js @@ -79,7 +79,9 @@ class ChatPage extends LitElement { imageFile: { type: Object }, attachment: { type: Object }, isUploadingImage: { type: Boolean }, + isDeletingImage: { type: Boolean }, isUploadingAttachment: { type: Boolean }, + isDeletingAttachment: { type: Boolean }, userLanguage: { type: String }, lastMessageRefVisible: { type: Boolean }, isLoadingOldMessages: { type: Boolean }, @@ -1088,13 +1090,14 @@ class ChatPage extends LitElement {
    `: ''} - ${(this.isUploadingAttachment) ? html` + ${(this.isUploadingAttachment || this.isDeletingAttachment) ? html`

    - ${translate("chatpage.cchange65")} + ${this.isDeletingAttachment ? + translate("chatpage.cchange66") : translate("chatpage.cchange65")}

    @@ -2419,56 +2422,146 @@ class ChatPage extends LitElement { this.webWorkerFile = new WebWorkerFile(); } - const b64toBlob = (b64Data, contentType='', sliceSize=512) => { - const byteCharacters = atob(b64Data); - const byteArrays = []; - - for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { - const slice = byteCharacters.slice(offset, offset + sliceSize); - - const byteNumbers = new Array(slice.length); - for (let i = 0; i < slice.length; i++) { - byteNumbers[i] = slice.charCodeAt(i); + const b64toBlob = (b64Data, contentType='', sliceSize=512) => { + const byteCharacters = atob(b64Data); + const byteArrays = []; + + for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { + const slice = byteCharacters.slice(offset, offset + sliceSize); + + const byteNumbers = new Array(slice.length); + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + const byteArray = new Uint8Array(byteNumbers); + byteArrays.push(byteArray); + } + + const blob = new Blob(byteArrays, {type: contentType}); + return blob; + } + const blob = b64toBlob(str, 'image/png'); + await new Promise(resolve => { + new Compressor(blob, { + quality: 0.6, + maxWidth: 500, + success(result) { + const file = new File([result], "name", { + type: 'image/png' + }); + + compressedFile = file; + resolve(); + }, + error(err) { + console.log(err.message); + }, + }) + }) + try { + await publishData({ + registeredName: userName, + file : compressedFile, + service: 'QCHAT_IMAGE', + identifier: identifier, + parentEpml, + metaData: undefined, + uploadType: 'file', + selectedAddress: this.selectedAddress, + worker: workerImage + }) + this.isDeletingImage = false + } catch (error) { + this.isLoading = false; + return + } + typeMessage = 'edit'; + let chatReference = outSideMsg.editedMessageObj.reference; + + if(outSideMsg.editedMessageObj.chatReference){ + chatReference = outSideMsg.editedMessageObj.chatReference; } - const byteArray = new Uint8Array(byteNumbers); - byteArrays.push(byteArray); + let message = ""; + try { + const parsedMessageObj = JSON.parse(outSideMsg.editedMessageObj.decodedMessage); + message = parsedMessageObj; + + } catch (error) { + message = outSideMsg.editedMessageObj.decodedMessage; } - - const blob = new Blob(byteArrays, {type: contentType}); - return blob; - } - const blob = b64toBlob(str, 'image/png'); - await new Promise(resolve => { - new Compressor(blob, { - quality: 0.6, - maxWidth: 500, - success(result) { - const file = new File([result], "name", { - type: 'image/png' - }); - - compressedFile = file; - resolve(); - }, - error(err) { - console.log(err.message); - }, + const messageObject = { + ...message, + isImageDeleted: true + } + const stringifyMessageObject = JSON.stringify(messageObject); + this.sendMessage(stringifyMessageObject, typeMessage, chatReference); + } else if (outSideMsg && outSideMsg.type === 'deleteAttachment') { + this.isDeletingAttachment = true; + let compressedFile = '' + var str = "iVBORw0KGgoAAAANSUhEUgAAAsAAAAGMAQMAAADuk4YmAAAAA1BMVEX///+nxBvIAAAAAXRSTlMAQObYZgAAADlJREFUeF7twDEBAAAAwiD7p7bGDlgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAGJrAABgPqdWQAAAABJRU5ErkJggg=="; + const userName = outSideMsg.name; + const identifier = outSideMsg.identifier; + + if (this.webWorkerFile) { + workerAttachment = this.webWorkerFile; + } else { + this.webWorkerFile = new WebWorkerFile(); + } + + const b64toBlob = (b64Data, contentType='', sliceSize=512) => { + const byteCharacters = atob(b64Data); + const byteArrays = []; + + for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { + const slice = byteCharacters.slice(offset, offset + sliceSize); + + const byteNumbers = new Array(slice.length); + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + const byteArray = new Uint8Array(byteNumbers); + byteArrays.push(byteArray); + } + + const blob = new Blob(byteArrays, {type: contentType}); + return blob; + } + + const blob = b64toBlob(str, 'image/png'); + await new Promise(resolve => { + new Compressor(blob, { + quality: 0.6, + maxWidth: 500, + success(result) { + const file = new File([result], "name", { + type: 'image/png' + }); + + compressedFile = file; + resolve(); + }, + error(err) { + console.log(err.message); + }, + }) }) - }) + try { await publishData({ registeredName: userName, - file : compressedFile, - service: 'QCHAT_IMAGE', + file: compressedFile, + service: 'QCHAT_ATTACHMENT', identifier: identifier, parentEpml, metaData: undefined, uploadType: 'file', selectedAddress: this.selectedAddress, - worker: workerImage - }) - this.isDeletingImage = false + worker: workerAttachment + }) + this.isDeletingAttachment = false } catch (error) { this.isLoading = false; return @@ -2479,7 +2572,7 @@ class ChatPage extends LitElement { if(outSideMsg.editedMessageObj.chatReference){ chatReference = outSideMsg.editedMessageObj.chatReference; } - + let message = ""; try { const parsedMessageObj = JSON.parse(outSideMsg.editedMessageObj.decodedMessage); @@ -2490,13 +2583,11 @@ class ChatPage extends LitElement { } const messageObject = { ...message, - isImageDeleted: true + isAttachmentDeleted: true } const stringifyMessageObject = JSON.stringify(messageObject); this.sendMessage(stringifyMessageObject, typeMessage, chatReference); - - } - else if (outSideMsg && outSideMsg.type === 'image') { + } else if (outSideMsg && outSideMsg.type === 'image') { this.isUploadingImage = true; const userName = await getName(this.selectedAddress.address); if (!userName) { diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js b/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js index da79a691..ea085627 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller-css.js @@ -649,11 +649,6 @@ export const chatStyles = css` cursor: pointer; } - .attachment-container:hover .download-icon::before { - background-color: rgb(161 158 158 / 41%); - } - - .attachment-icon-container { display: flex; @@ -704,6 +699,11 @@ export const chatStyles = css` background-color: transparent; } + .download-icon:hover::before { + background-color: rgb(161 158 158 / 41%); + } + + .download-icon::before { content: ""; position: absolute; diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index 17100219..28c4326d 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -71,7 +71,6 @@ class ChatScroller extends LitElement { } render() { - console.log(4, "here"); let formattedMessages = this.messages.reduce((messageArray, message, index) => { const lastGroupedMessage = messageArray[messageArray.length - 1]; let timestamp; @@ -262,6 +261,7 @@ class MessageTemplate extends LitElement { sendMessageForward: { attribute: false }, openDialogImage: { attribute: false }, openDeleteImage: { type: Boolean }, + openDeleteAttachment: { type: Boolean }, isImageLoaded: { type: Boolean }, isFirstMessage: { type: Boolean }, isSingleMessageInGroup: { type: Boolean }, @@ -337,7 +337,6 @@ class MessageTemplate extends LitElement { } render() { - console.log(this.messageObj, "message object here"); const hidemsg = this.hideMessages; let message = ""; let messageVersion2 = "" @@ -345,6 +344,7 @@ class MessageTemplate extends LitElement { let repliedToData = null; let image = null; let isImageDeleted = false; + let isAttachmentDeleted = false; let version = 0; let isForwarded = false let isEdited = false @@ -363,6 +363,7 @@ class MessageTemplate extends LitElement { message = parsedMessageObj.messageText; repliedToData = this.messageObj.repliedToData; isImageDeleted = parsedMessageObj.isImageDeleted; + isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted; reactions = parsedMessageObj.reactions || []; version = parsedMessageObj.version isForwarded = parsedMessageObj.type === 'forward' @@ -588,16 +589,18 @@ class MessageTemplate extends LitElement {
    - ${imageHTML} { - this.openDeleteImage = true; - }} - class="image-delete-icon" icon="vaadin:close" slot="icon"> + ${imageHTML} + { + this.openDeleteImage = true; + }} + class="image-delete-icon" icon="vaadin:close" slot="icon"> +
    ` : image && isImageDeleted ? html` -

    This image has been deleted

    +

    ${translate("chatpage.cchange71")}

    ` : html``} - ${attachment ? + ${attachment && !isAttachmentDeleted ? html`
    await this.downloadAttachment(attachment)} class="attachment-container">
    @@ -619,8 +622,28 @@ class MessageTemplate extends LitElement { slot="icon" class="download-icon"> + ${this.myAddress === this.messageObj.sender + ? html` + { + e.stopPropagation(); + this.openDeleteAttachment = true; + }} + class="image-delete-icon" icon="vaadin:close" slot="icon"> + + ` : html``}
    ` + : attachment && isAttachmentDeleted ? + html` +
    +
    +

    + ${translate("chatpage.cchange72")} +

    +
    +
    + ` : html``}
    -

    Are you sure you want to delete this image?

    +

    ${translate("chatpage.cchange69")}

    + { + this.openDeleteAttachment = false; + }}> +
    +

    ${translate("chatpage.cchange70")}

    +
    + +
    ` } } From cc05764863fb554b5e2bc7bd86fba2a557b2a045 Mon Sep 17 00:00:00 2001 From: Phillip Date: Tue, 31 Jan 2023 12:53:25 +0200 Subject: [PATCH 04/32] missing import highlight --- qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index a8b268e5..f4922363 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -28,6 +28,8 @@ import '@vaadin/grid' import StarterKit from '@tiptap/starter-kit' import Underline from '@tiptap/extension-underline'; import Placeholder from '@tiptap/extension-placeholder' +import Highlight from '@tiptap/extension-highlight' + import { Editor, Extension } from '@tiptap/core' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) From 648db552e7943f7bb57a6129945bfdb6df46f6e1 Mon Sep 17 00:00:00 2001 From: Phillip Date: Thu, 2 Feb 2023 10:19:52 +0200 Subject: [PATCH 05/32] fix bug that when a user clicks on a notification and goes back to the app it correctly show the chat page. before it would have an infinite spinner --- .../notification-actions/new-message.js | 5 +-- .../core/messaging/q-chat/q-chat.src.js | 39 ++++++++++++++++--- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/qortal-ui-core/src/notifications/notification-actions/new-message.js b/qortal-ui-core/src/notifications/notification-actions/new-message.js index faaaad32..f430fdd2 100644 --- a/qortal-ui-core/src/notifications/notification-actions/new-message.js +++ b/qortal-ui-core/src/notifications/notification-actions/new-message.js @@ -2,7 +2,6 @@ import { store } from '../../store.js' import { doPageUrl } from '../../redux/app/app-actions.js' export const newMessage = (data) => { - const alert = playSound(data.sound) // Should I show notification ? @@ -18,7 +17,7 @@ export const newMessage = (data) => { } notify.onclick = (e) => { - const pageUrl = `/app/q-chat/${data.req.url}` + const pageUrl = `/app/q-chat/?chat=${data.req.url}` store.dispatch(doPageUrl(pageUrl)) } } else { @@ -26,7 +25,7 @@ export const newMessage = (data) => { const notify = new Notification(data.title, data.options) notify.onclick = (e) => { - const pageUrl = `/app/q-chat/${data.req.url}` + const pageUrl = `/app/q-chat/?chat=${data.req.url}` store.dispatch(doPageUrl(pageUrl)) } } diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index a8b268e5..fbe12aa5 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -29,7 +29,7 @@ import StarterKit from '@tiptap/starter-kit' import Underline from '@tiptap/extension-underline'; import Placeholder from '@tiptap/extension-placeholder' import { Editor, Extension } from '@tiptap/core' - +import Highlight from '@tiptap/extension-highlight' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) class Chat extends LitElement { @@ -55,7 +55,7 @@ class Chat extends LitElement { userFoundModalOpen: { type: Boolean }, userSelected: { type: Object }, editor: {type: Object}, - groupInvites: { type: Array } + groupInvites: { type: Array }, } } @@ -118,6 +118,7 @@ class Chat extends LitElement { } async connectedCallback() { + super.connectedCallback(); await this.getUpdateCompleteTextEditor(); @@ -147,15 +148,36 @@ class Chat extends LitElement { }}) ] }) + + this.unsubscribeStore = window.parent.reduxStore.subscribe(() => { + try { + + if(window.parent.location && window.parent.location.search){ + const queryString = window.parent.location.search; + const params = new URLSearchParams(queryString); + const chat = params.get("chat") + if(chat && chat !== this.activeChatHeadUrl){ + let url = window.parent.location.href; + let newUrl = url.split("?")[0]; + window.parent.history.pushState({}, "", newUrl); + this.setActiveChatHeadUrl(chat) + } + } + } catch (error) { + console.error(error) + } + + }); } disconnectedCallback() { super.disconnectedCallback(); - this.editor.destroy() - + this.editor.destroy(); + this.unsubscribeStore(); } + updatePlaceholder(editor, text){ editor.extensionManager.extensions.forEach((extension) => { if (extension.name === "placeholder") { @@ -215,7 +237,7 @@ class Chat extends LitElement {
    - ${window.parent.location.pathname !== "/app/q-chat" || this.activeChatHeadUrl ? html`${this.renderChatPage(this.chatId)}` : html`${this.renderChatWelcomePage()}`} + ${this.activeChatHeadUrl ? html`${this.renderChatPage()}` : html`${this.renderChatWelcomePage()}`}
    @@ -367,6 +389,8 @@ class Chat extends LitElement { ` } + + async firstUpdated() { this.changeLanguage(); this.changeTheme(); @@ -482,8 +506,11 @@ class Chat extends LitElement { }) }) parentEpml.imReady() + } + + setOpenPrivateMessage(props) { this.openPrivateMessage = props.open; this.shadowRoot.getElementById("sendTo").value = props.name @@ -839,7 +866,7 @@ class Chat extends LitElement { }) } - renderChatPage(chatId) { + renderChatPage() { // Check for the chat ID from and render chat messages // Else render Welcome to Q-CHat From e07884c1e1a718485de3760a4c04581a33167d05 Mon Sep 17 00:00:00 2001 From: Phillip Date: Fri, 3 Feb 2023 16:07:14 +0200 Subject: [PATCH 06/32] last message timestamp with red dot --- qortal-ui-core/language/us.json | 3 +- qortal-ui-core/package.json | 5 +- qortal-ui-core/src/components/app-view.js | 18 ++++ .../src/components/login-view/login-view.js | 6 +- qortal-ui-core/src/plugins/streams.js | 5 ++ .../src/redux/app/actions/app-core.js | 15 +++- .../src/redux/app/app-action-types.js | 4 +- qortal-ui-core/src/redux/app/app-reducer.js | 42 ++++++++- .../plugins/core/components/ChatHead.js | 88 ++++++++++++++++--- .../plugins/core/components/ChatPage.js | 18 ++-- .../plugins/core/components/TimeAgo.js | 3 +- 11 files changed, 178 insertions(+), 29 deletions(-) diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 55ad9dd7..6f625f02 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -579,7 +579,8 @@ "cchange71": "and", "cchange72": "other", "cchange73": "s", - "cchange74": "reacted with" + "cchange74": "reacted with", + "cchange90": "No messages" }, "welcomepage": { "wcchange1": "Welcome to Q-Chat", diff --git a/qortal-ui-core/package.json b/qortal-ui-core/package.json index 5515942d..8e4f2ad3 100644 --- a/qortal-ui-core/package.json +++ b/qortal-ui-core/package.json @@ -77,9 +77,10 @@ "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", "rollup-plugin-scss": "3.0.0", - "rollup-plugin-web-worker-loader": "1.6.1" + "rollup-plugin-web-worker-loader": "1.6.1", + "localforage": "1.10.0" }, "engines": { "node": ">=16.17.1" } -} \ No newline at end of file +} diff --git a/qortal-ui-core/src/components/app-view.js b/qortal-ui-core/src/components/app-view.js index cffc315f..60d02813 100644 --- a/qortal-ui-core/src/components/app-view.js +++ b/qortal-ui-core/src/components/app-view.js @@ -4,6 +4,11 @@ import { store } from '../store.js' import { Epml } from '../epml.js' import { addTradeBotRoutes } from '../tradebot/addTradeBotRoutes.js' import { get, translate, translateUnsafeHTML } from 'lit-translate' +import localForage from "localforage"; + +const chatLastSeen = localForage.createInstance({ + name: "chat-last-seen", +}); import '@polymer/paper-icon-button/paper-icon-button.js' import '@polymer/paper-progress/paper-progress.js' @@ -27,6 +32,7 @@ import './user-info-view/user-info-view.js' import '../functional-components/side-menu.js' import '../functional-components/side-menu-item.js' import './start-minting.js' +import { setChatLastSeen } from '../redux/app/app-actions.js' const parentEpml = new Epml({type: 'WINDOW', source: window.parent}) @@ -1386,6 +1392,17 @@ class AppView extends connect(store)(LitElement) { } } + const getChatLastSeen=async() => { + let items = []; + + await chatLastSeen.iterate(function(value, key, iterationNumber) { + + items.push({key, timestamp: value}); + }) + store.dispatch(setChatLastSeen(items)) + return items; + } + await getOpenTradesBTC() await appDelay(1000) await getOpenTradesLTC() @@ -1397,6 +1414,7 @@ class AppView extends connect(store)(LitElement) { await getOpenTradesRVN() await appDelay(1000) await getOpenTradesARRR() + await getChatLastSeen() } async getNodeType() { diff --git a/qortal-ui-core/src/components/login-view/login-view.js b/qortal-ui-core/src/components/login-view/login-view.js index aadc1973..ebdf31f2 100644 --- a/qortal-ui-core/src/components/login-view/login-view.js +++ b/qortal-ui-core/src/components/login-view/login-view.js @@ -14,12 +14,14 @@ import './login-section.js' import '../qort-theme-toggle.js' import settings from '../../functional-components/settings-page.js' -import { addAutoLoadImageChat, removeAutoLoadImageChat } from '../../redux/app/app-actions.js' +import { addAutoLoadImageChat, removeAutoLoadImageChat, addChatLastSeen } from '../../redux/app/app-actions.js' window.reduxStore = store window.reduxAction = { addAutoLoadImageChat: addAutoLoadImageChat, - removeAutoLoadImageChat: removeAutoLoadImageChat + removeAutoLoadImageChat: removeAutoLoadImageChat, + addChatLastSeen: addChatLastSeen + } const animationDuration = 0.7 // Seconds diff --git a/qortal-ui-core/src/plugins/streams.js b/qortal-ui-core/src/plugins/streams.js index ad6696d1..9cb6c3f7 100644 --- a/qortal-ui-core/src/plugins/streams.js +++ b/qortal-ui-core/src/plugins/streams.js @@ -9,6 +9,7 @@ const CHAT_HEADS_STREAM_NAME = 'chat_heads' const NODE_CONFIG_STREAM_NAME = 'node_config' const COPY_MENU_SWITCH = 'copy_menu_switch' const FRAME_PASTE_MENU_SWITCH = 'frame_paste_menu_switch' +const CHAT_LAST_SEEN = 'chat_last_seen' export const loggedInStream = new EpmlStream(LOGIN_STREAM_NAME, () => store.getState().app.loggedIn) export const configStream = new EpmlStream(CONFIG_STREAM_NAME, () => store.getState().config) @@ -18,6 +19,7 @@ export const chatHeadsStateStream = new EpmlStream(CHAT_HEADS_STREAM_NAME, () => export const nodeConfigStream = new EpmlStream(NODE_CONFIG_STREAM_NAME, () => store.getState().app.nodeConfig) export const copyMenuSwitchStream = new EpmlStream(COPY_MENU_SWITCH, () => store.getState().app.copyMenuSwitch) export const framePasteMenuSwitchStream = new EpmlStream(FRAME_PASTE_MENU_SWITCH, () => store.getState().app.framePasteMenuSwitch) +export const chatLastSeenStream = new EpmlStream(CHAT_LAST_SEEN, () => store.getState().app.chatLastSeen) let oldState = { @@ -46,6 +48,9 @@ store.subscribe(() => { if (oldState.app.framePasteMenuSwitch !== state.app.framePasteMenuSwitch) { framePasteMenuSwitchStream.emit(state.app.framePasteMenuSwitch) } + if (oldState.app.chatLastSeen !== state.app.chatLastSeen) { + chatLastSeenStream.emit(state.app.chatLastSeen) + } if (oldState.app.selectedAddress !== state.app.selectedAddress) { selectedAddressStream.emit({ diff --git a/qortal-ui-core/src/redux/app/actions/app-core.js b/qortal-ui-core/src/redux/app/actions/app-core.js index d91c2fa2..894f2c2e 100644 --- a/qortal-ui-core/src/redux/app/actions/app-core.js +++ b/qortal-ui-core/src/redux/app/actions/app-core.js @@ -1,5 +1,5 @@ // Core App Actions here... -import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT } from '../app-action-types.js' +import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN } from '../app-action-types.js' export const doUpdateBlockInfo = (blockObj) => { return (dispatch, getState) => { @@ -120,3 +120,16 @@ export const removeAutoLoadImageChat = (payload) => { } } +export const setChatLastSeen = (payload) => { + return { + type: SET_CHAT_LAST_SEEN, + payload + } +} +export const addChatLastSeen = (payload) => { + return { + type: ADD_CHAT_LAST_SEEN, + payload + } +} + diff --git a/qortal-ui-core/src/redux/app/app-action-types.js b/qortal-ui-core/src/redux/app/app-action-types.js index b0bb7813..518d4cd7 100644 --- a/qortal-ui-core/src/redux/app/app-action-types.js +++ b/qortal-ui-core/src/redux/app/app-action-types.js @@ -21,4 +21,6 @@ export const COPY_MENU_SWITCH = 'COPY_MENU_SWITCH' export const PASTE_MENU_SWITCH = 'PASTE_MENU_SWITCH' export const FRAME_PASTE_MENU_SWITCH = 'FRAME_PASTE_MENU_SWITCH' export const ADD_AUTO_LOAD_IMAGES_CHAT = 'ADD_AUTO_LOAD_IMAGES_CHAT' -export const REMOVE_AUTO_LOAD_IMAGES_CHAT = 'REMOVE_AUTO_LOAD_IMAGES_CHAT' \ No newline at end of file +export const REMOVE_AUTO_LOAD_IMAGES_CHAT = 'REMOVE_AUTO_LOAD_IMAGES_CHAT' +export const SET_CHAT_LAST_SEEN = 'SET_CHAT_LAST_SEEN' +export const ADD_CHAT_LAST_SEEN = 'ADD_CHAT_LAST_SEEN' diff --git a/qortal-ui-core/src/redux/app/app-reducer.js b/qortal-ui-core/src/redux/app/app-reducer.js index 6f3d8e1d..68a48675 100644 --- a/qortal-ui-core/src/redux/app/app-reducer.js +++ b/qortal-ui-core/src/redux/app/app-reducer.js @@ -1,9 +1,14 @@ // Loading state, login state, isNavDrawOpen state etc. None of this needs to be saved to localstorage. import { loadStateFromLocalStorage, saveStateToLocalStorage } from '../../localStorageHelpers.js' -import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT } from './app-action-types.js' +import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN } from './app-action-types.js' import { initWorkersReducer } from './reducers/init-workers.js' import { loginReducer } from './reducers/login-reducer.js' import { setNode, addNode } from './reducers/manage-node.js' +import localForage from "localforage"; +const chatLastSeen = localForage.createInstance({ + name: "chat-last-seen", +}); + const INITIAL_STATE = { loggedIn: false, @@ -44,7 +49,8 @@ const INITIAL_STATE = { isOpen: false, elementId: '' }, - autoLoadImageChats: loadStateFromLocalStorage('autoLoadImageChats') || [] + autoLoadImageChats: loadStateFromLocalStorage('autoLoadImageChats') || [], + chatLastSeen: [] } export default (state = INITIAL_STATE, action) => { @@ -169,6 +175,38 @@ export default (state = INITIAL_STATE, action) => { autoLoadImageChats: updatedState } } + case SET_CHAT_LAST_SEEN: { + return { + ...state, + chatLastSeen: action.payload + } + } + case ADD_CHAT_LAST_SEEN: { + const chatId = action.payload.key + const timestamp = action.payload.timestamp + if(!chatId || !timestamp) return state + let newChatLastSeen = [...state.chatLastSeen] + const findChatIndex = state.chatLastSeen.findIndex((chat)=> chat.key === chatId) + if(findChatIndex !== -1){ + + newChatLastSeen[findChatIndex] = { + key: chatId, + timestamp, + } + } + if(findChatIndex === -1){ + + newChatLastSeen = [...newChatLastSeen, { + key: chatId, + timestamp, + }] + } + chatLastSeen.setItem(chatId, timestamp) + return { + ...state, + chatLastSeen: newChatLastSeen + } + } default: return state diff --git a/qortal-ui-plugins/plugins/core/components/ChatHead.js b/qortal-ui-plugins/plugins/core/components/ChatHead.js index c28a583f..7b706e0c 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatHead.js +++ b/qortal-ui-plugins/plugins/core/components/ChatHead.js @@ -1,11 +1,15 @@ import { LitElement, html, css } from 'lit' import { render } from 'lit/html.js' import { Epml } from '../../../epml.js' +import localForage from "localforage"; +import { translate} from 'lit-translate'; import '@material/mwc-icon' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) - +const chatLastSeen = localForage.createInstance({ + name: "chat-last-seen", +}); class ChatHead extends LitElement { static get properties() { return { @@ -15,7 +19,8 @@ class ChatHead extends LitElement { iconName: { type: String }, activeChatHeadUrl: { type: String }, isImageLoaded: { type: Boolean }, - setActiveChatHeadUrl: {attribute: false} + setActiveChatHeadUrl: {attribute: false}, + lastReadMessageTimestamp: {type: Number} } } @@ -24,9 +29,13 @@ class ChatHead extends LitElement { li { width: 100%; - padding: 7px 5px 7px 5px; + padding: 10px 5px 10px 5px; cursor: pointer; width: 100%; + box-sizing: border-box; + display: flex; + align-items: flex-start; + } li:hover { @@ -44,12 +53,21 @@ class ChatHead extends LitElement { color: var(--chat-group); } - .about { - margin-top: 8px; - } + .about { - padding-left: 8px; + + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin: 0px; + } + .inner-container { + display: flex; + width: calc(100% - 45px); + flex-direction: column; + justify-content: center; } .status { @@ -64,6 +82,13 @@ class ChatHead extends LitElement { clear: both; height: 0; } + + .name { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + } ` } @@ -82,6 +107,7 @@ class ChatHead extends LitElement { this.activeChatHeadUrl = '' this.isImageLoaded = false this.imageFetches = 0 + this.lastReadMessageTimestamp = 0 } createImage(imageUrl) { @@ -108,33 +134,59 @@ class ChatHead extends LitElement { }; return imageHTMLRes; } + updated(){ + } render() { let avatarImg = ''; let backupAvatarImg = '' + let isUnread = false + if(this.chatInfo.name){ const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; avatarImg= this.createImage(avatarUrl) - } + if(this.lastReadMessageTimestamp && this.chatInfo.timestamp){ + if(this.lastReadMessageTimestamp < this.chatInfo.timestamp){ + isUnread = true + } + } + + if(this.activeChatHeadUrl === this.chatInfo.url){ + isUnread = false + } + return html`
  1. this.getUrl(this.chatInfo.url)} class="clearfix ${this.activeChatHeadUrl === this.chatInfo.url ? 'active' : ''}"> ${this.isImageLoaded ? html`${avatarImg}` : html`` } ${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`account_circle` : html`` } - ${!this.isImageLoaded && this.chatInfo.name ? html`
    ${this.chatInfo.name.charAt(0)}
    `: ''} - ${!this.isImageLoaded && this.chatInfo.groupName ? html`
    ${this.chatInfo.groupName.charAt(0)}
    `: ''} + ${!this.isImageLoaded && this.chatInfo.name ? html`
    ${this.chatInfo.name.charAt(0)}
    `: ''} + ${!this.isImageLoaded && this.chatInfo.groupName ? html`
    ${this.chatInfo.groupName.charAt(0)}
    `: ''} +
    -
    ${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? this.chatInfo.name : this.chatInfo.address.substr(0, 15)} ${this.chatInfo.groupId !== undefined ? 'lock_open' : 'lock'}
    +
    ${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? this.chatInfo.name : this.chatInfo.address.substr(0, 15)} ${this.chatInfo.groupId !== undefined ? 'lock_open' : 'lock'}
    +
    +
    +
    +
    + + ${translate('chatpage.cchange90')} +
    +
    +
    +
    +
  2. ` } - firstUpdated() { + async firstUpdated() { let configLoaded = false + this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatInfo.url) || 0 parentEpml.ready().then(() => { parentEpml.subscribe('selected_address', async selectedAddress => { this.selectedAddress = {} @@ -142,6 +194,15 @@ class ChatHead extends LitElement { if (!selectedAddress || Object.entries(selectedAddress).length === 0) return this.selectedAddress = selectedAddress }) + parentEpml.subscribe('chat_last_seen', async chatList => { + const parsedChatList = JSON.parse(chatList) + const findChatSeen = parsedChatList.find(chat=> chat.key === this.chatInfo.url) + + if(findChatSeen && this.lastReadMessageTimestamp !== findChatSeen.timestamp){ + this.lastReadMessageTimestamp = findChatSeen.timestamp + this.requestUpdate() + } + }) parentEpml.subscribe('config', c => { if (!configLoaded) { configLoaded = true @@ -156,6 +217,9 @@ class ChatHead extends LitElement { if(changedProperties.has('activeChatHeadUrl')){ return true } + if(changedProperties.has('lastReadMessageTimestamp')){ + return true + } if(changedProperties.has('chatInfo')){ return true } diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js index 91691e05..8aacdc1b 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatPage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js @@ -10,7 +10,7 @@ import Highlight from '@tiptap/extension-highlight' import {unsafeHTML} from 'lit/directives/unsafe-html.js'; import { Editor, Extension } from '@tiptap/core' -// import localForage from "localforage"; +import localForage from "localforage"; registerTranslateConfig({ loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) }); @@ -42,9 +42,9 @@ import WebWorker from 'web-worker:./computePowWorker.js'; import WebWorkerImage from 'web-worker:./computePowWorkerImage.js'; import '@polymer/paper-dialog/paper-dialog.js' -// const messagesCache = localForage.createInstance({ -// name: "messages-cache", -// }); +const chatLastSeen = localForage.createInstance({ + name: "chat-last-seen", +}); const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) @@ -1444,6 +1444,14 @@ class ChatPage extends LitElement { document.removeEventListener('keydown', this.initialChat); document.removeEventListener('paste', this.pasteImage); + if(this.messagesRendered.length !== 0){ + window.parent.reduxStore.dispatch( window.parent.reduxAction.addChatLastSeen({ + key: this.chatId, + timestamp: Date.now() + })) + + } + } initialChat(e) { @@ -1465,9 +1473,7 @@ class ChatPage extends LitElement { const handleTransferIntoURL = (dataTransfer) => { try { const [firstItem] = dataTransfer.items; - console.log({firstItem}); const blob = firstItem.getAsFile(); - console.log({blob}); return blob; } catch (error) { console.log(error); diff --git a/qortal-ui-plugins/plugins/core/components/TimeAgo.js b/qortal-ui-plugins/plugins/core/components/TimeAgo.js index ca3633b7..66164179 100644 --- a/qortal-ui-plugins/plugins/core/components/TimeAgo.js +++ b/qortal-ui-plugins/plugins/core/components/TimeAgo.js @@ -19,7 +19,7 @@ class TimeAgo extends LitElement { updated(changedProps) { changedProps.forEach((OldProp, name) => { - if (name === 'timeIso') { + if (name === 'timeIso' || name === 'timestamp') { this.renderTime(this.timestamp) } }); @@ -35,7 +35,6 @@ class TimeAgo extends LitElement { } render() { - return html` ` From 5c98615d540e2ff8e90a3c59060f8aa8d5be4c65 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Fri, 3 Feb 2023 21:19:53 +0100 Subject: [PATCH 07/32] add new search field and sort member column --- .../group-management/group-management.src.js | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js index 0148f33b..3338464c 100644 --- a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js +++ b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js @@ -22,6 +22,8 @@ import '@vaadin/icon' import '@vaadin/icons' import '@vaadin/grid' import '@vaadin/grid/vaadin-grid-filter-column.js' +import '@vaadin/grid/vaadin-grid-sort-column.js' +import '@vaadin/text-field' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) @@ -33,6 +35,7 @@ class GroupManagement extends LitElement { privateGroups: { type: Array }, joinedGroups: { type: Array }, groupInvites: { type: Array }, + filteredItems: { type: Array }, privateGroupSearch: { type: Array }, newMembersList: { type: Array }, newAdminsList: { type: Array }, @@ -111,6 +114,9 @@ class GroupManagement extends LitElement { --_lumo-grid-secondary-border-color: var(--border2); } +[part="input-field"] { + background-color: #fff; +} #group-management-page { background: var(--white); padding: 12px 24px; @@ -437,6 +443,7 @@ class GroupManagement extends LitElement { this.privateGroups = [] this.joinedGroups = [] this.groupInvites = [] + this.filteredItems = [] this.privateGroupSearch = [] this.newMembersList = [] this.newAdminsList = [] @@ -1314,8 +1321,8 @@ class GroupManagement extends LitElement {

    ${translate("grouppage.gchange3")}

    - - + + { render(html`${this.renderRole(data.item)}`, root) }}> @@ -1359,11 +1366,25 @@ class GroupManagement extends LitElement {

    ${translate("grouppage.gchange9")}

    - - - - - + + +
    + + + + + { render(html` this.joinGroup(data.item)}>queue ${translate("grouppage.gchange51")}`, root) }}> @@ -1773,7 +1794,8 @@ class GroupManagement extends LitElement { this.publicGroups = results this.privateGroups = _privateGroups this.joinedGroups = _joinedGroups - setTimeout(getOpen_JoinedGroups, 60000) + this.filteredItems = this.publicGroups + setTimeout(getOpen_JoinedGroups, 600000) } window.addEventListener("contextmenu", (event) => { From 24e1028e37aacb99b8777fa002cfd7abe1b25d8f Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Sat, 4 Feb 2023 14:07:17 +0100 Subject: [PATCH 08/32] add hide ticker button --- qortal-ui-core/language/de.json | 3 ++- qortal-ui-core/language/es.json | 3 ++- qortal-ui-core/language/fr.json | 3 ++- qortal-ui-core/language/hindi.json | 3 ++- qortal-ui-core/language/hr.json | 3 ++- qortal-ui-core/language/hu.json | 3 ++- qortal-ui-core/language/it.json | 3 ++- qortal-ui-core/language/ko.json | 3 ++- qortal-ui-core/language/no.json | 3 ++- qortal-ui-core/language/pl.json | 5 +++-- qortal-ui-core/language/pt.json | 3 ++- qortal-ui-core/language/ro.json | 3 ++- qortal-ui-core/language/rs.json | 3 ++- qortal-ui-core/language/ru.json | 3 ++- qortal-ui-core/language/us.json | 3 ++- qortal-ui-core/language/zhc.json | 3 ++- qortal-ui-core/language/zht.json | 3 ++- qortal-ui-core/src/components/app-view.js | 25 +++++++++++++++++++++-- 18 files changed, 58 insertions(+), 20 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index 00f413af..f55a563e 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -657,7 +657,8 @@ "gchange55": "Private Gruppe suchen", "gchange56": "Zu suchender Gruppenname", "gchange57": "Privater Gruppenname nicht gefunden", - "gchange58": "Beachten Sie, dass der Gruppenname genau übereinstimmen muss." + "gchange58": "Beachten Sie, dass der Gruppenname genau übereinstimmen muss.", + "gchange59": "Ticker ein-/ausblenden" }, "puzzlepage": { "pchange1": "Rätsel", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index 1c378f28..b7c9c8ba 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -657,7 +657,8 @@ "gchange55": "Buscar grupo privado", "gchange56": "Nombre del grupo a buscar", "gchange57": "Nombre de grupo privado no encontrado", - "gchange58": "Tenga en cuenta que el nombre del grupo debe coincidir exactamente." + "gchange58": "Tenga en cuenta que el nombre del grupo debe coincidir exactamente.", + "gchange59": "Mostrar/ocultar teletipo" }, "puzzlepage": { "pchange1": "Rompecabezas", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 3aef9139..ab1adae2 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -657,7 +657,8 @@ "gchange55": "Rechercher un groupe privé", "gchange56": "Nom du groupe à rechercher", "gchange57": "Nom de groupe privé introuvable", - "gchange58": "Notez que le nom du groupe doit correspondre exactement." + "gchange58": "Notez que le nom du groupe doit correspondre exactement.", + "gchange59": "Afficher / Masquer le téléscripteur" }, "puzzlepage": { "pchange1": "Puzzles", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index 1601ffdf..ac392733 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -658,7 +658,8 @@ "gchange55": "निजी समूह खोजें", "gchange56": "खोजने के लिए समूह का नाम", "gchange57": "निजी समूह का नाम नहीं मिला", - "gchange58": "ध्यान दें कि समूह का नाम सटीक मेल खाना चाहिए।" + "gchange58": "ध्यान दें कि समूह का नाम सटीक मेल खाना चाहिए।", + "gchange59": "टिकर दिखाएं / छुपाएं" }, "puzzlepage": { "pchange1": "पहेलि", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 0a18cf7c..5759c849 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -657,7 +657,8 @@ "gchange55": "Traži privatnu grupu", "gchange56": "Naziv grupe za pretraživanje", "gchange57": "Ime privatne grupe nije pronađeno", - "gchange58": "Imajte na umu da se naziv grupe mora točno podudarati." + "gchange58": "Imajte na umu da se naziv grupe mora točno podudarati.", + "gchange59": "Prikaži / sakrij ticker" }, "puzzlepage": { "pchange1": "Zagonetke", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index e0480559..6e7e8ee9 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -657,7 +657,8 @@ "gchange55": "Keresés privát csoportban", "gchange56": "A keresendő csoport neve", "gchange57": "A privát csoport neve nem található", - "gchange58": "Ne feledje, hogy a csoport nevének pontosan meg kell egyeznie." + "gchange58": "Ne feledje, hogy a csoport nevének pontosan meg kell egyeznie.", + "gchange59": "Ticker megjelenítése / elrejtése" }, "puzzlepage": { "pchange1": "Rejtvények", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index 0f3c6058..e4a5c8c2 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -657,7 +657,8 @@ "gchange55": "Cerca gruppo privato", "gchange56": "Nome gruppo da cercare", "gchange57": "Nome gruppo privato non trovato", - "gchange58": "Nota che il nome del gruppo deve corrispondere esattamente." + "gchange58": "Nota che il nome del gruppo deve corrispondere esattamente.", + "gchange59": "Mostra / Nascondi ticker" }, "puzzlepage": { "pchange1": "Puzzle", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index baf03675..e5db9895 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -657,7 +657,8 @@ "gchange55": "비공개 그룹 검색", "gchange56": "검색할 그룹 이름", "gchange57": "비공개 그룹 이름을 찾을 수 없음", - "gchange58": "그룹 이름이 정확히 일치해야 합니다." + "gchange58": "그룹 이름이 정확히 일치해야 합니다.", + "gchange59": "티커 표시/숨기기" }, "puzzlepage": { "pchange1": "퍼즐", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index d6833c6e..791e89b5 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -657,7 +657,8 @@ "gchange55": "Søk i privat gruppe", "gchange56": "Gruppenavn å søke", "gchange57": "Privat gruppenavn ikke funnet", - "gchange58": "Merk at gruppenavnet må samsvare nøyaktig." + "gchange58": "Merk at gruppenavnet må samsvare nøyaktig.", + "gchange59": "Vis / Skjul Ticker" }, "puzzlepage": { "pchange1": "Puzzles", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index 32935439..3100be86 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -657,7 +657,8 @@ "gchange55": "Wyszukaj grupę prywatną", "gchange56": "Nazwa grupy do wyszukania", "gchange57": "Nie znaleziono nazwy grupy prywatnej", - "gchange58": "Zauważ, że nazwa grupy musi dokładnie pasować." + "gchange58": "Zauważ, że nazwa grupy musi dokładnie pasować.", + "gchange59": "Pokaż / Ukryj Znacznik" }, "puzzlepage": { "pchange1": "Zagadki", @@ -868,7 +869,7 @@ "inf9": "„Automatyczne kupowanie” to funkcja umożliwiająca składanie „zleceń kupna” w portalu handlowym. Te „zlecenia kupna” są widoczne tylko dla osoby, która je składa. Nie są to „publiczne” zlecenia kupna, jak np. „sprzedaże z otwartego rynku” są i NIE są przechowywane w łańcuchu blokowym Qortal. Auto Buy to funkcja interfejsu użytkownika i jako taka wymaga, aby interfejs użytkownika URUCHAMIAŁ.", "inf10": "Aby złożyć zamówienie automatycznego zakupu, kliknij przycisk „Dodaj zamówienie automatycznego zakupu” i wypełnij pole, które się pojawi. Wprowadź KWOTĘ KWARTOŚCI, którą chcesz KUPIĆ, oraz CENĘ, do której chcesz KUPIĆ. Raz zamówienie jest aktywne, Auto Buy kupi dla ciebie DO tej kwoty QORT, po MAKSYMALNIE ustalonej cenie (zaczynając od najniższego zamówienia i przesuwając się w górę).", "inf11": "Po prostu ZOSTAW DZIAŁAJĄCY UI, a Auto Buy zrobi resztę automatycznie!", - "inf12": "MOŻESZ przeglądać inne wtyczki w interfejsie użytkownika (Q-Chat, portfele itp.), ale NIE MOŻESZ ZAMKNĄĆ interfejsu użytkownika, jeśli chcesz, aby zakup automatyczny został zakończony. Pozostawienie interfejsu użytkownika „zminimalizowanego” na „pasku zadań” lub „panel” jest w porządku, o ile interfejs użytkownika pozostaje OTWARTY, Auto Buy będzie działać." + "inf12": "MOŻESZ przeglądać inne wtyczki w interfejsie użytkownika (Q-Chat, portfele itp.), ale NIE MOŻESZ ZAMKNĄĆ interfejsu użytkownika, jeśli chcesz, aby zakup automatyczny został zakończony. Pozostawienie interfejsu użytkownika „zminimalizowanego” na „pasku zadań” lub „panel” jest w porządku, o ile interfejs użytkownika pozostaje OTWARTY, Auto Buy będzie działać.", "inf13": "Kup automatycznie", "inf14": "z", "inf15": "Aktywne automatyczne zamówienia zakupu", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index 42cda962..6e5e33b6 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -657,7 +657,8 @@ "gchange55": "Pesquisar Grupo Privado", "gchange56": "Nome do grupo para pesquisar", "gchange57": "Nome do grupo privado não encontrado", - "gchange58": "Observe que o nome do grupo deve corresponder exatamente." + "gchange58": "Observe que o nome do grupo deve corresponder exatamente.", + "gchange59": "Mostrar / Ocultar Ticker" }, "puzzlepage": { "pchange1": "Enigmas", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 3037c208..756c8fce 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -657,7 +657,8 @@ "gchange55": "Căutați grup privat", "gchange56": "Numele grupului de căutat", "gchange57": "Numele grupului privat nu a fost găsit", - "gchange58": "Rețineți că numele grupului trebuie să se potrivească exact." + "gchange58": "Rețineți că numele grupului trebuie să se potrivească exact.", + "gchange59": "Afișează / Ascunde Ticker" }, "puzzlepage": { "pchange1": "Puzzle-uri", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index 791753b6..61a6f04b 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -657,7 +657,8 @@ "gchange55": "Pretraži privatnu grupu", "gchange56": "Ime grupe za pretragu", "gchange57": "Ime privatne grupe nije pronađeno", - "gchange58": "Imajte na umu da ime grupe mora potpuno da se podudara." + "gchange58": "Imajte na umu da ime grupe mora potpuno da se podudara.", + "gchange59": "Prikaži / Sakrij Oznaku" }, "puzzlepage": { "pchange1": "Slagalice", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index cd7e54f4..dfcaa64d 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -657,7 +657,8 @@ "gchange55": "Поиск в закрытой группе", "gchange56": "Имя группы для поиска", "gchange57": "Имя частной группы не найдено", - "gchange58": "Обратите внимание, что название группы должно точно совпадать." + "gchange58": "Обратите внимание, что название группы должно точно совпадать.", + "gchange59": "Показать/скрыть бегущую строку" }, "puzzlepage": { "pchange1": "Головоломки", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 55ad9dd7..87760fd7 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -670,7 +670,8 @@ "gchange55": "Search Private Group", "gchange56": "Group Name To Search", "gchange57": "Private Group Name Not Found", - "gchange58": "Note that group name must exact match." + "gchange58": "Note that group name must exact match.", + "gchange59": "Show / Hide Ticker" }, "puzzlepage": { "pchange1": "Puzzles", diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index 4b4ccde5..57fe5ec0 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -670,7 +670,8 @@ "gchange55": "搜索私人群组", "gchange56": "要搜索的群组名称", "gchange57": "未找到私人群组名称", - "gchange58": "注意群组名称必须完全匹配。" + "gchange58": "注意群组名称必须完全匹配。", + "gchange59": "显示/隐藏代码" }, "puzzlepage": { "pchange1": "益智游戏", diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index f797fd5e..457d0bc4 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -670,7 +670,8 @@ "gchange55": "搜索私人群組", "gchange56": "要搜索的群組名稱", "gchange57": "未找到私人群組名稱", - "gchange58": "注意群組名稱必須完全匹配。" + "gchange58": "注意群組名稱必須完全匹配。", + "gchange59": "顯示/隱藏代碼" }, "puzzlepage": { "pchange1": "益智游戲", diff --git a/qortal-ui-core/src/components/app-view.js b/qortal-ui-core/src/components/app-view.js index cffc315f..5e9c58de 100644 --- a/qortal-ui-core/src/components/app-view.js +++ b/qortal-ui-core/src/components/app-view.js @@ -207,7 +207,7 @@ class AppView extends connect(store)(LitElement) { #balanceheader { flex: 0 0 24px; - padding: 12px 12px 20px 12px; + padding: 12px 12px 45px 12px; border-bottom: 1px solid var(--border); background: var(--sidetopbar); } @@ -329,7 +329,16 @@ class AppView extends connect(store)(LitElement) { .sideBarMenu::-webkit-scrollbar-thumb:hover { background-color: rgb(148, 146, 146); cursor: pointer; - } + } + + .balanceButton { + background-color: #03a9f4; + color: #ffffff; + margin-left: 12px; + margin-right: 12px; + padding-top: 5px; + padding-bottom: 5px; + } ` ] } @@ -426,6 +435,8 @@ class AppView extends connect(store)(LitElement) {
    + + @@ -1399,6 +1411,15 @@ class AppView extends connect(store)(LitElement) { await getOpenTradesARRR() } + shBalanceTicker() { + const targetDiv = this.shadowRoot.getElementById("theTicker") + if (targetDiv.style.display !== "none") { + targetDiv.style.display = "none"; + } else { + targetDiv.style.display = "inline"; + } + } + async getNodeType() { const myAppNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] const nodeAppUrl = myAppNode.protocol + '://' + myAppNode.domain + ':' + myAppNode.port From b674fd2e6980cae96d9d4106dd89a9dbc9c66018 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Sat, 4 Feb 2023 14:26:54 +0100 Subject: [PATCH 09/32] change update error string --- locales/bg.json | 2 +- locales/de.json | 2 +- locales/en.json | 2 +- locales/es.json | 2 +- locales/fr.json | 2 +- locales/it.json | 2 +- locales/ko.json | 2 +- locales/nl.json | 2 +- locales/pt.json | 2 +- locales/pt_BR.json | 2 +- locales/ru_RU.json | 2 +- locales/tr.json | 2 +- locales/uk.json | 2 +- locales/zh_CN.json | 2 +- locales/zh_TW.json | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/locales/bg.json b/locales/bg.json index 2b9de1c2..17038a8a 100644 --- a/locales/bg.json +++ b/locales/bg.json @@ -10,7 +10,7 @@ "electron_translate_7": "Актуализацията е готова за инсталиране", "electron_translate_8": "Беше изтеглена нова версия на Qortal UI.", "electron_translate_9": "Щракнете върху ИНСТАЛИРАНЕ СЕГА, за да приложите актуализация, МОЖЕ БИ ПО-КЪСНО, за да не актуализирате потребителския интерфейс.", - "electron_translate_10": "Грешка при актуализиране...", + "electron_translate_10": "Временна неуспешна актуализация, ще опитаме отново по-късно", "electron_translate_11": "ИЗТЕГЛЕТЕ АКТУАЛИЗАЦИЯТА", "electron_translate_12": "Ще бъде изтеглено ⌛️ във фонов режим!" } \ No newline at end of file diff --git a/locales/de.json b/locales/de.json index 590401f2..6761d651 100644 --- a/locales/de.json +++ b/locales/de.json @@ -10,7 +10,7 @@ "electron_translate_7": "Update zur Installation bereit", "electron_translate_8": "Eine neue Qortal-UI-Version wurde heruntergeladen.", "electron_translate_9": "Klicken Sie auf JETZT INSTALLIEREN, um das Update anzuwenden, VIELLEICHT SPÄTER, um die Benutzeroberfläche nicht zu aktualisieren.", - "electron_translate_10": "Fehler beim Aktualisieren...", + "electron_translate_10": "Vorübergehender Aktualisierungsfehler, wir versuchen es später erneut", "electron_translate_11": "UPDATE HERUNTERLADEN", "electron_translate_12": "Das Update wird im Hintergrund heruntergeladen ⌛️!" } \ No newline at end of file diff --git a/locales/en.json b/locales/en.json index a10a2de0..177e5ca7 100644 --- a/locales/en.json +++ b/locales/en.json @@ -10,7 +10,7 @@ "electron_translate_7": "Update ready to install", "electron_translate_8": "A new Qortal UI version has been downloaded.", "electron_translate_9": "Click INSTALL NOW to apply update, MAYBE LATER to not update the UI.", - "electron_translate_10": "Error while Updating...", + "electron_translate_10": "Temporary update failure, will try again later", "electron_translate_11": "DOWNLOAD UPDATE", "electron_translate_12": "It will be downloaded ⌛️ in the background!" } \ No newline at end of file diff --git a/locales/es.json b/locales/es.json index 552305c9..9aec6683 100644 --- a/locales/es.json +++ b/locales/es.json @@ -10,7 +10,7 @@ "electron_translate_7": "Actualización lista para instalar", "electron_translate_8": "Se ha descargado una nueva versión de la interfaz de usuario de Qortal.", "electron_translate_9": "Haz clic en INSTALAR AHORA para aplicar la actualización, QUIZÁS MÁS TARDE para no actualizar la interfaz de usuario"., - "electron_translate_10": "Error al actualizar...", + "electron_translate_10": "Error de actualización temporal, lo intentaré más tarde", "electron_translate_11": "DESCARGAR ACTUALIZACIÓN", "electron_translate_12": "¡Se descargará ⌛️ en segundo plano!" } \ No newline at end of file diff --git a/locales/fr.json b/locales/fr.json index 624cbdee..4b8a821e 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -10,7 +10,7 @@ "electron_translate_7": "Mise à jour prête à installer", "electron_translate_8": "Une nouvelle version de l'interface utilisateur de Qortal a été téléchargée.", "electron_translate_9": "Cliquez sur INSTALLER MAINTENANT pour appliquer la mise à jour, PEUT-ÊTRE PLUS TARD pour ne pas mettre à jour l'interface utilisateur.", - "electron_translate_10": "Erreur lors de la mise à jour...", + "electron_translate_10": "Échec de la mise à jour temporaire, réessayera plus tard", "electron_translate_11": "TÉLÉCHARGER LA MISE À JOUR", "electron_translate_12": "Il sera téléchargé ⌛️ en arrière-plan !" } \ No newline at end of file diff --git a/locales/it.json b/locales/it.json index bd851cd6..de35772c 100644 --- a/locales/it.json +++ b/locales/it.json @@ -10,7 +10,7 @@ "electron_translate_7": "Aggiornamento pronto per l'installazione", "electron_translate_8": "È stata scaricata una nuova versione dell'interfaccia utente di Qortal.", "electron_translate_9": "Fai clic su INSTALLA ORA per applicare l'aggiornamento, FORSE DOPO per non aggiornare l'interfaccia utente.", - "electron_translate_10": "Errore durante l'aggiornamento...", + "electron_translate_10": "Errore di aggiornamento temporaneo, riproverà più tardi", "electron_translate_11": "SCARICA AGGIORNAMENTO", "electron_translate_12": "Verrà scaricato ⌛️ in background!" } \ No newline at end of file diff --git a/locales/ko.json b/locales/ko.json index 65f679d4..3b89345d 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -10,7 +10,7 @@ "electron_translate_7": "업데이트 설치 준비 완료", "electron_translate_8": "새로운 Qortal UI 버전이 다운로드되었습니다.", "electron_translate_9": "업데이트를 적용하려면 지금 설치를 클릭하고 UI를 업데이트하지 않으려면 나중에 할 수도 있습니다.", - "electron_translate_10": "업데이트 중 오류...", + "electron_translate_10": "임시 업데이트 실패, 나중에 다시 시도합니다.", "electron_translate_11": "업데이트 다운로드", "electron_translate_12": "백그라운드에서 ⌛️ 다운로드됩니다!" } \ No newline at end of file diff --git a/locales/nl.json b/locales/nl.json index 73756911..82bd482f 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -10,7 +10,7 @@ "electron_translate_7": "Update klaar om te installeren", "electron_translate_8": "Er is een nieuwe Qortal UI-versie gedownload.", "electron_translate_9": "Klik op NU INSTALLEREN om de update toe te passen, MISSCHIEN LATER om de gebruikersinterface niet bij te werken.", - "electron_translate_10": "Fout tijdens updaten...", + "electron_translate_10": "Tijdelijke update mislukt, zal het later opnieuw proberen", "electron_translate_11": "UPDATE DOWNLOADEN", "electron_translate_12": "Het wordt ⌛️ op de achtergrond gedownload!" } \ No newline at end of file diff --git a/locales/pt.json b/locales/pt.json index 516e26cd..7a02c1b5 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -10,7 +10,7 @@ "electron_translate_7": "Atualização pronta para instalar", "electron_translate_8": "Uma nova versão Qortal UI foi baixada.", "electron_translate_9": "Clique em INSTALAR AGORA para aplicar a atualização, TALVEZ MAIS TARDE para não atualizar a UI.", - "electron_translate_10": "Erro ao atualizar...", + "electron_translate_10": "Falha de atualização temporária, tentará novamente mais tarde", "electron_translate_11": "BAIXAR ATUALIZAÇÃO", "electron_translate_12": "Será baixado ⌛️ em segundo plano!" } \ No newline at end of file diff --git a/locales/pt_BR.json b/locales/pt_BR.json index 6e272b8d..9293187f 100644 --- a/locales/pt_BR.json +++ b/locales/pt_BR.json @@ -10,7 +10,7 @@ "electron_translate_7": "Atualização pronta para instalar", "electron_translate_8": "Uma nova versão Qortal UI foi baixada.", "electron_translate_9": "Clique em INSTALAR AGORA para aplicar a atualização, TALVEZ MAIS TARDE para não atualizar a UI.", - "electron_translate_10": "Erro ao atualizar...", + "electron_translate_10": "Falha de atualização temporária, tentará novamente mais tarde", "electron_translate_11": "BAIXAR ATUALIZAÇÃO", "electron_translate_12": "Será baixado ⌛️ em segundo plano!" } \ No newline at end of file diff --git a/locales/ru_RU.json b/locales/ru_RU.json index c0f6ff7c..09a1e79d 100644 --- a/locales/ru_RU.json +++ b/locales/ru_RU.json @@ -10,7 +10,7 @@ "electron_translate_7": "Обновление готово к установке", "electron_translate_8": "Загружена новая версия пользовательского интерфейса Qortal.", "electron_translate_9": "Нажмите УСТАНОВИТЬ СЕЙЧАС, чтобы применить обновление, МОЖЕТ БЫТЬ ПОЗЖЕ, чтобы не обновлять пользовательский интерфейс.", - "electron_translate_10": "Ошибка при обновлении...", + "electron_translate_10": "Временная ошибка обновления, повторите попытку позже", "electron_translate_11": "СКАЧАТЬ ОБНОВЛЕНИЕ", "electron_translate_12": "Он будет загружен ⌛️ в фоновом режиме!" } \ No newline at end of file diff --git a/locales/tr.json b/locales/tr.json index 11491409..b3b089f2 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -10,7 +10,7 @@ "electron_translate_7": "Güncelleme yüklenmeye hazır", "electron_translate_8": "Yeni bir Qortal UI sürümü indirildi.", "electron_translate_9": "Güncellemeyi uygulamak için ŞİMDİ YÜKLE'ye tıklayın, kullanıcı arayüzünü güncellememek için MAYBE SONRA tıklayın.", - "electron_translate_10": "Güncelleme Sırasında Hata...", + "electron_translate_10": "Geçici güncelleme hatası, daha sonra tekrar denenecek", "electron_translate_11": "GÜNCELLEMEYİ İNDİRİN", "electron_translate_12": "Arka planda ⌛️ indirilecek!" } \ No newline at end of file diff --git a/locales/uk.json b/locales/uk.json index 1e52aca0..a3ef9499 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -10,7 +10,7 @@ "electron_translate_7": "Update ready to install", "electron_translate_8": "A new Qortal UI version has been downloaded.", "electron_translate_9": "Click INSTALL NOW to apply update, MAYBE LATER to not update the UI.", - "electron_translate_10": "Error while Updating...", + "electron_translate_10": "Temporary update failure, will try again later", "electron_translate_11": "DOWNLOAD UPDATE", "electron_translate_12": "It will be downloaded ⌛️ in the background!" } \ No newline at end of file diff --git a/locales/zh_CN.json b/locales/zh_CN.json index 7fe1625d..b0f3e0e4 100644 --- a/locales/zh_CN.json +++ b/locales/zh_CN.json @@ -10,7 +10,7 @@ "electron_translate_7": "更新准备安装", "electron_translate_8": "已下载新的 Qortal UI 版本。", "electron_translate_9": "点击现在安装应用更新,可能稍后不更新用户界面。", - "electron_translate_10": "更新时出错...", + "electron_translate_10": "暂时更新失败,稍后再试", "electron_translate_11": "下载更新", "electron_translate_12": "它将在后台下载 ⌛️!" } \ No newline at end of file diff --git a/locales/zh_TW.json b/locales/zh_TW.json index 07182899..ae8699a5 100644 --- a/locales/zh_TW.json +++ b/locales/zh_TW.json @@ -10,7 +10,7 @@ "electron_translate_7": "更新準備安裝", "electron_translate_8": "已下載新的 Qortal UI 版本。", "electron_translate_9": "點擊現在安裝應用更新,可能稍後不更新用戶界面。", - "electron_translate_10": "更新時出錯...", + "electron_translate_10": "暫時更新失敗,稍後再試", "electron_translate_11": "下載更新", "electron_translate_12": "它將在後台下載 ⌛️!" } \ No newline at end of file From fe9af1dc828f07e0bfc8b2975e94b64d801b39f1 Mon Sep 17 00:00:00 2001 From: Phillip Date: Sat, 4 Feb 2023 17:34:43 +0200 Subject: [PATCH 10/32] fix avatar issue --- .../plugins/core/components/ChatHead.js | 22 ++++++++++++++----- .../core/messaging/q-chat/q-chat.src.js | 8 +++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/components/ChatHead.js b/qortal-ui-plugins/plugins/core/components/ChatHead.js index 7b706e0c..61a30fb2 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatHead.js +++ b/qortal-ui-plugins/plugins/core/components/ChatHead.js @@ -108,9 +108,10 @@ class ChatHead extends LitElement { this.isImageLoaded = false this.imageFetches = 0 this.lastReadMessageTimestamp = 0 + this.loggedInAddress = window.parent.reduxStore.getState().app.selectedAddress.address } - createImage(imageUrl) { + createImage(imageUrl) { const imageHTMLRes = new Image(); imageHTMLRes.src = imageUrl; imageHTMLRes.style= "width:40px; height:40px; float: left; border-radius:50%"; @@ -125,7 +126,7 @@ class ChatHead extends LitElement { setTimeout(() => { this.imageFetches = this.imageFetches + 1; imageHTMLRes.src = imageUrl; - }, 500); + }, 750); } else { @@ -134,9 +135,9 @@ class ChatHead extends LitElement { }; return imageHTMLRes; } - updated(){ - } + + render() { let avatarImg = ''; let backupAvatarImg = '' @@ -158,7 +159,10 @@ class ChatHead extends LitElement { if(this.activeChatHeadUrl === this.chatInfo.url){ isUnread = false } - + + if(this.chatInfo.sender === this.loggedInAddress){ + isUnread = false + } return html`
  3. this.getUrl(this.chatInfo.url)} class="clearfix ${this.activeChatHeadUrl === this.chatInfo.url ? 'active' : ''}"> ${this.isImageLoaded ? html`${avatarImg}` : html`` } @@ -221,6 +225,14 @@ class ChatHead extends LitElement { return true } if(changedProperties.has('chatInfo')){ + + const prevChatInfo = changedProperties.get('chatInfo') + + if(prevChatInfo.address !== this.chatInfo.address){ + + this.isImageLoaded = false + this.requestUpdate() + } return true } diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index fbe12aa5..872802d6 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -8,6 +8,7 @@ import { Epml } from '../../../../epml.js'; import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'; import { qchatStyles } from './q-chat-css.src.js' import WebWorker from 'web-worker:./computePowWorker.src.js'; +import {repeat} from 'lit/directives/repeat.js'; registerTranslateConfig({ loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) @@ -856,14 +857,11 @@ class Chat extends LitElement { } renderChatHead(chatHeadArr) { - - let tempUrl = document.location.href - let splitedUrl = decodeURI(tempUrl).split('?') - // let activeChatHeadUrl = splitedUrl[1] === undefined ? '' : splitedUrl[1] - + return chatHeadArr.map(eachChatHead => { return html` this.setActiveChatHeadUrl(val)} chatInfo=${JSON.stringify(eachChatHead)}>` }) + } renderChatPage() { From 96e235ef92846e72b41489428fc19d8eb35c0962 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Sat, 4 Feb 2023 23:11:15 -0500 Subject: [PATCH 11/32] Fix error due to duplicate line SyntaxError: Identifier 'Highlight' has already been declared --- qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index b62fea3f..e618a1b0 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -32,7 +32,6 @@ import Placeholder from '@tiptap/extension-placeholder' import Highlight from '@tiptap/extension-highlight' import { Editor, Extension } from '@tiptap/core' -import Highlight from '@tiptap/extension-highlight' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) class Chat extends LitElement { @@ -982,4 +981,4 @@ class Chat extends LitElement { } } -window.customElements.define('q-chat', Chat) \ No newline at end of file +window.customElements.define('q-chat', Chat) From 330f556f15020c40d064a0c573672666e115c61c Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Sun, 5 Feb 2023 13:36:14 +0100 Subject: [PATCH 12/32] Update Translations --- qortal-ui-core/language/de.json | 17 +++++++++++++++-- qortal-ui-core/language/es.json | 17 +++++++++++++++-- qortal-ui-core/language/fr.json | 17 +++++++++++++++-- qortal-ui-core/language/hindi.json | 17 +++++++++++++++-- qortal-ui-core/language/hr.json | 17 +++++++++++++++-- qortal-ui-core/language/hu.json | 17 +++++++++++++++-- qortal-ui-core/language/it.json | 17 +++++++++++++++-- qortal-ui-core/language/ko.json | 17 +++++++++++++++-- qortal-ui-core/language/no.json | 17 +++++++++++++++-- qortal-ui-core/language/pl.json | 17 +++++++++++++++-- qortal-ui-core/language/pt.json | 17 +++++++++++++++-- qortal-ui-core/language/ro.json | 17 +++++++++++++++-- qortal-ui-core/language/rs.json | 17 +++++++++++++++-- qortal-ui-core/language/ru.json | 17 +++++++++++++++-- qortal-ui-core/language/us.json | 3 +-- 15 files changed, 211 insertions(+), 30 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index f55a563e..326683b0 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -574,7 +574,12 @@ "cchange65": "Bitte geben Sie einen Empfänger ein", "cchange66": "Beantwortete Nachricht kann nicht abgerufen werden. Nachricht ist zu alt.", "cchange68": "bearbeitet", - "cchange69": "Zeige Bilder automatisch" + "cchange69": "Zeige Bilder automatisch", + "cchange70": "Dieser Bildtyp wird nicht unterstützt", + "cchange71": "und", + "cchange72": "andere", + "cchange73": "s", + "cchange74": "reagiert mit" }, "welcomepage": { "wcchange1": "Willkommen zu Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENÜ", "bcchange8": "Adresse Kopieren", "bcchange9": "Private Nachricht", - "bcchange10": "Mehr" + "bcchange10": "Mehr", + "bcchange11": "Antworten", + "bcchange12": "Bearbeiten", + "bcchange13": "Reaktion", + "bcchange14": "Weiterleiten", + "bcchange15": "Nachricht weitergeleitet", + "bcchange16": "Empfänger auswählen oder darunter suchen", + "bcchange17": "WEITERGELEITET", + "bcchange18": "Tipp Benutzer" }, "grouppage": { "gchange1": "Qortal-Gruppen", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index b7c9c8ba..82e6dce4 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -574,7 +574,12 @@ "cchange65": "Ingrese un destinatario", "cchange66": "No se puede recuperar el mensaje respondido. El mensaje es demasiado antiguo.", "cchange68": "editado", - "cchange69": "Mostrar imágenes automáticamente" + "cchange69": "Mostrar imágenes automáticamente", + "cchange70": "Este tipo de imagen no es compatible", + "cchange71": "y", + "cchange72": "otro", + "cchange73": "s", + "cchange74": "reaccionó con" }, "welcomepage": { "wcchange1": "Bienvenido al Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENÚ", "bcchange8": "Copiar Dirección", "bcchange9": "Mensaje Privado", - "bcchange10": "Más" + "bcchange10": "Más", + "bcchange11": "Responder", + "bcchange12": "Editar", + "bcchange13": "Reacción", + "bcchange14": "Adelante", + "bcchange15": "Mensaje reenviado", + "bcchange16": "Elegir destinatario o buscar uno a continuación", + "bcchange17": "REENVIADO", + "bcchange18": "Sugerencia para el usuario" }, "grouppage": { "gchange1": "Grupos Qortal", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index ab1adae2..62cd43e1 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -574,7 +574,12 @@ "cchange65": "Veuillez saisir un destinataire", "cchange66": "Impossible de récupérer le message auquel vous avez répondu. Le message est trop ancien.", "cchange68": "modifié", - "cchange69": "Afficher automatiquement les images" + "cchange69": "Afficher automatiquement les images", + "cchange70": "Ce type d'image n'est pas pris en charge", + "cchange71": "et", + "cchange72": "autre", + "cchange73": "s", + "cchange74": "a réagi avec" }, "welcomepage": { "wcchange1": "Bienvenue dans Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENU", "bcchange8": "Copier l'Adresse", "bcchange9": "Message Privé", - "bcchange10": "Suite" + "bcchange10": "Suite", + "bcchange11": "Répondre", + "bcchange12": "Modifier", + "bcchange13": "Réaction", + "bcchange14": "En avant", + "bcchange15": "Message transféré", + "bcchange16": "Choisissez le destinataire ou recherchez-en un ci-dessous", + "bcchange17": "TRANSMIS", + "bcchange18": "Astuce à l'utilisateur" }, "grouppage": { "gchange1": "Groupes Qortal", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index ac392733..2ef18b37 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -575,7 +575,12 @@ "cchange65": "कृपया एक प्राप्तकर्ता दर्ज करें", "cchange66": "इसका उत्तर दिया गया संदेश प्राप्त नहीं किया जा सकता। संदेश बहुत पुराना है।", "cchange68": "संपादित", - "cchange69": "ऑटो-शो छवियां" + "cchange69": "ऑटो-शो छवियां", + "cchange70": "यह छवि प्रकार समर्थित नहीं है", + "cchange71": "और", + "cchange72": "अन्य", + "cchange73": "s", + "cchange74": "के साथ प्रतिक्रिया" }, "welcomepage": { "wcchange1": "क्यू-चैट में आपका स्वागत है", @@ -598,7 +603,15 @@ "bcchange7": "मेन्यू", "bcchange8": "कॉपी पता", "bcchange9": "निजी संदेश", - "bcchange10": "अधिक" + "bcchange10": "अधिक", + "bcchange11": "उत्तर", + "bcchange12": "संपादित करें", + "bcchange13": "प्रतिक्रिया", + "bcchange14": "आगे", + "bcchange15": "संदेश अग्रेषित किया गया", + "bcchange16": "प्राप्तकर्ता चुनें या नीचे किसी को खोजें", + "bcchange17": "अग्रेषित", + "bcchange18": "टिप उपयोगकर्ता" }, "grouppage": { "gchange1": "क्वॉर्टल समूह", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 5759c849..26fc0d4a 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -574,7 +574,12 @@ "cchange65": "Molimo unesite primatelja", "cchange66": "Ne mogu dohvatiti poruku s odgovorom. Poruka je prestara.", "cchange68": "uređeno", - "cchange69": "Automatsko prikazivanje slika" + "cchange69": "Automatsko prikazivanje slika", + "cchange70": "Ova vrsta slike nije podržana", + "cchange71": "i", + "cchange72": "ostalo", + "cchange73": "s", + "cchange74": "reagirao sa" }, "welcomepage": { "wcchange1": "Dobrodošli u Q-Čavrljanje", @@ -597,7 +602,15 @@ "bcchange7": "MENI", "bcchange8": "Kopiraj Adresu", "bcchange9": "Privatna Poruka", - "bcchange10": "Više" + "bcchange10": "Više", + "bcchange11": "Odgovori", + "bcchange12": "Uredi", + "bcchange13": "Reakcija", + "bcchange14": "Naprijed", + "bcchange15": "Poruka proslijeđena", + "bcchange16": "Odaberite primatelja ili potražite jednog ispod", + "bcchange17": "PROSLJEĐENO", + "bcchange18": "Savjet korisniku" }, "grouppage": { "gchange1": "Qortal Grupe", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 6e7e8ee9..d92390cf 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -574,7 +574,12 @@ "cchange65": "Kérjük, adjon meg egy címzettet", "cchange66": "Nem sikerült lekérni a válaszolt üzenetet. Az üzenet túl régi.", "cchange68": "szerkesztve", - "cchange69": "Képek automatikus megjelenítése" + "cchange69": "Képek automatikus megjelenítése", + "cchange70": "Ez a képtípus nem támogatott", + "cchange71": "és", + "cchange72": "egyéb", + "cchange73": "s", + "cchange74": "reagált:" }, "welcomepage": { "wcchange1": "Üdvözöljük a Q-Chathoz", @@ -597,7 +602,15 @@ "bcchange7": "MENÜ", "bcchange8": "Cím Másolása", "bcchange9": "Privát Üzenet", - "bcchange10": "Több" + "bcchange10": "Több", + "bcchange11": "Válasz", + "bcchange12": "Szerkesztés", + "bcchange13": "Reakció", + "bcchange14": "Tovább", + "bcchange15": "Üzenet továbbítva", + "bcchange16": "Válasszon címzettet vagy keressen egyet lent", + "bcchange17": "TOVÁLTVA", + "bcchange18": "Tipp felhasználó" }, "grouppage": { "gchange1": "Qortal Csoportok", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index e4a5c8c2..1523e212 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -574,7 +574,12 @@ "cchange65": "Inserisci un destinatario", "cchange66": "Impossibile recuperare il messaggio di risposta. Il messaggio è troppo vecchio.", "cchange68": "modificato", - "cchange69": "Mostra automaticamente le immagini" + "cchange69": "Mostra automaticamente le immagini", + "cchange70": "Questo tipo di immagine non è supportato", + "cchange71": "e", + "cchange72": "altro", + "cchange73": "s", + "cchange74": "ha reagito con" }, "welcomepage": { "wcchange1": "Benvenuto in Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENÙ", "bcchange8": "Copia Indirizzo", "bcchange9": "Messaggio Privato", - "bcchange10": "Di più" + "bcchange10": "Di più", + "bcchange11": "Rispondi", + "bcchange12": "Modifica", + "bcchange13": "Reazione", + "bcchange14": "Avanti", + "bcchange15": "Messaggio inoltrato", + "bcchange16": "Scegli il destinatario o cercane uno qui sotto", + "bcchange17": "INOLTRATO", + "bcchange18": "Suggerimento utente" }, "grouppage": { "gchange1": "Gruppi Qortal", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index e5db9895..5e533527 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -574,7 +574,12 @@ "cchange65": "받는 사람을 입력하세요", "cchange66": "답장한 메시지를 가져올 수 없습니다. 메시지가 너무 오래되었습니다.", "cchange68": "편집됨", - "cchange69": "이미지 자동 표시" + "cchange69": "이미지 자동 표시", + "cchange70": "이 이미지 유형은 지원되지 않습니다", + "cchange71": "그리고", + "cchange72": "기타", + "cchange73": "들", + "cchange74": "반응" }, "welcomepage": { "wcchange1": "Q-Chat에 오신 것을 환영합니다.", @@ -597,7 +602,15 @@ "bcchange7": "메뉴", "bcchange8": "주소 복사", "bcchange9": "개인 메시지", - "bcchange10": "더" + "bcchange10": "더", + "bcchange11": "답장", + "bcchange12": "편집", + "bcchange13": "반응", + "bcchange14": "앞으로", + "bcchange15": "전달된 메시지", + "bcchange16": "받는 사람을 선택하거나 아래에서 검색하십시오", + "bcchange17": "전달됨", + "bcchange18": "팁 사용자" }, "grouppage": { "gchange1": "Qortal 그룹", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index 791e89b5..1ee638af 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -574,7 +574,12 @@ "cchange65": "Vennligst skriv inn en mottaker", "cchange66": "Kan ikke hente besvart melding. Meldingen er for gammel.", "cchange68": "redigert", - "cchange69": "Vis bilder automatisk" + "cchange69": "Vis bilder automatisk", + "cchange70": "Denne bildetypen støttes ikke", + "cchange71": "og", + "cchange72": "annet", + "cchange73": "s", + "cchange74": "reagerte med" }, "welcomepage": { "wcchange1": "Velkommen til Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENY", "bcchange8": "Kopier adresse", "bcchange9": "Privat melding", - "bcchange10": "Mer" + "bcchange10": "Mer", + "bcchange11": "Svar", + "bcchange12": "Rediger", + "bcchange13": "Reaksjon", + "bcchange14": "Videresend", + "bcchange15": "Melding videresendt", + "bcchange16": "Velg mottaker eller søk etter en nedenfor", + "bcchange17": "VIDERESENDE", + "bcchange18": "Tipsbruker" }, "grouppage": { "gchange1": "Qortal-grupper", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index 3100be86..5c75270f 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -574,7 +574,12 @@ "cchange65": "Wprowadź odbiorcę", "cchange66": "Nie można pobrać wiadomości, na którą udzielono odpowiedzi. Wiadomość jest za stara.", "cchange68": "edytowano", - "cchange69": "Automatyczne wyświetlanie obrazów" + "cchange69": "Automatyczne wyświetlanie obrazów", + "cchange70": "Ten typ obrazu nie jest obsługiwany", + "cchange71": "i", + "cchange72": "inne", + "cchange73": "s", + "cchange74": "zareagował z" }, "welcomepage": { "wcchange1": "Witamy w Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENU", "bcchange8": "Kopiuj Adres", "bcchange9": "Prywatna Wiadomość", - "bcchange10": "Więcej" + "bcchange10": "Więcej", + "bcchange11": "Odpowiedz", + "bcchange12": "Edytuj", + "bcchange13": "Reakcja", + "bcchange14": "Przekaż", + "bcchange15": "Wiadomość przekazana", + "bcchange16": "Wybierz odbiorcę lub wyszukaj poniżej", + "bcchange17": "PRZEKAZANA", + "bcchange18": "Użytkownik napiwku" }, "grouppage": { "gchange1": "Grupy Qortal", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index 6e5e33b6..870b3dd3 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -574,7 +574,12 @@ "cchange65": "Insira um destinatário", "cchange66": "Não foi possível buscar a mensagem respondida. A mensagem é muito antiga.", "cchange68": "editado", - "cchange69": "Mostrar imagens automaticamente" + "cchange69": "Mostrar imagens automaticamente", + "cchange70": "Este tipo de imagem não é suportado", + "cchange71": "e", + "cchange72": "outro", + "cchange73": "s", + "cchange74": "reagiu com" }, "welcomepage": { "wcchange1": "Bem-vindo ao Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENU", "bcchange8": "Copiar Endereço", "bcchange9": "Mensagem Privada", - "bcchange10": "Mais" + "bcchange10": "Mais", + "bcchange11": "Responder", + "bcchange12": "Editar", + "bcchange13": "Reação", + "bcchange14": "Avançar", + "bcchange15": "Mensagem encaminhada", + "bcchange16": "Escolha o destinatário ou procure um abaixo", + "bcchange17": "ENCAMINHADO", + "bcchange18": "Dica de usuário" }, "grouppage": { "gchange1": "Grupos Qortal", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 756c8fce..4e17e959 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -574,7 +574,12 @@ "cchange65": "Vă rugăm să introduceți un destinatar", "cchange66": "Nu se poate prelua mesajul la care sa răspuns. Mesajul este prea vechi.", "cchange68": "editat", - "cchange69": "Afișează automat imaginile" + "cchange69": "Afișează automat imaginile", + "cchange70": "Acest tip de imagine nu este acceptat", + "cchange71": "și", + "cchange72": "altul", + "cchange73": "s", + "cchange74": "a reacționat cu" }, "welcomepage": { "wcchange1": "Bine ai venit la Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENIU", "bcchange8": "Copiati adresa", "bcchange9": "Mesaj privat", - "bcchange10": "Mai mult" + "bcchange10": "Mai mult", + "bcchange11": "Răspuns", + "bcchange12": "Editați", + "bcchange13": "Reacție", + "bcchange14": "Înainte", + "bcchange15": "Mesajul redirecționat", + "bcchange16": "Alegeți destinatarul sau căutați unul mai jos", + "bcchange17": "REMISE", + "bcchange18": "Sfat utilizator" }, "grouppage": { "gchange1": "Grupuri Qortal Groups", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index 61a6f04b..e1722ca2 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -574,7 +574,12 @@ "cchange65": "Unesite primaoca", "cchange66": "Ne mogu preuzeti odgovor na poruku. Poruka je prestara.", "cchange68": "uređeno", - "cchange69": "Automatski prikaži slike" + "cchange69": "Automatski prikaži slike", + "cchange70": "Ovaj tip slike nije podržan", + "cchange71": "i", + "cchange72": "drugo", + "cchange73": "s", + "cchange74": "reagovao sa" }, "welcomepage": { "wcchange1": "Dobrodošli na Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "MENI", "bcchange8": "Kopiraj Adresu", "bcchange9": "Privatna Poruka", - "bcchange10": "Više" + "bcchange10": "Više", + "bcchange11": "Odgovori", + "bcchange12": "Izmeni", + "bcchange13": "Reakcija", + "bcchange14": "Napred", + "bcchange15": "Poruka je prosleđena", + "bcchange16": "Izaberite primaoca ili potražite jednog ispod", + "bcchange17": "PROSLEĐEN", + "bcchange18": "Korisnik saveta" }, "grouppage": { "gchange1": "Qortal Grupe", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index dfcaa64d..ea20a79d 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -574,7 +574,12 @@ "cchange65": "Пожалуйста, введите получателя", "cchange66": "Невозможно получить ответное сообщение. Сообщение слишком старое.", "cchange68": "отредактировано", - "cchange69": "Автоматическое отображение изображений" + "cchange69": "Автоматическое отображение изображений", + "cchange70": "Этот тип изображения не поддерживается", + "cchange71": "и", + "cchange72": "другое", + "cchange73": "с", + "cchange74": "отреагировал" }, "welcomepage": { "wcchange1": "Добро пожаловать в Q-Chat", @@ -597,7 +602,15 @@ "bcchange7": "МЕНЮ", "bcchange8": "Копировать адрес", "bcchange9": "Приватное сообщение", - "bcchange10": "Более" + "bcchange10": "Более", + "bcchange11": "Ответить", + "bcchange12": "Редактировать", + "bcchange13": "Реакция", + "bcchange14": "Вперед", + "bcchange15": "Сообщение переадресовано", + "bcchange16": "Выберите получателя или найдите его ниже", + "bcchange17": "ПЕРЕДАНО", + "bcchange18": "Подсказка пользователю" }, "grouppage": { "gchange1": "Qortal группы", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 02bb9586..87760fd7 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -579,8 +579,7 @@ "cchange71": "and", "cchange72": "other", "cchange73": "s", - "cchange74": "reacted with", - "cchange90": "No messages" + "cchange74": "reacted with" }, "welcomepage": { "wcchange1": "Welcome to Q-Chat", From fd96605148ae7c9a42f7e6d9d6433feebd137e43 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Sun, 5 Feb 2023 13:42:19 +0100 Subject: [PATCH 13/32] Add sell name --- .../name-registration.src.js | 809 +++++++++++++++++- 1 file changed, 784 insertions(+), 25 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js index 037d9d85..1e3e0289 100644 --- a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js +++ b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js @@ -7,11 +7,17 @@ registerTranslateConfig({ loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) }) -import '@material/mwc-icon' import '@material/mwc-button' -import '@material/mwc-textfield' import '@material/mwc-dialog' +import '@material/mwc-formfield' +import '@material/mwc-icon' +import '@material/mwc-icon-button' +import '@material/mwc-textfield' import '@polymer/paper-spinner/paper-spinner-lite.js' +import '@polymer/paper-progress/paper-progress.js' +import '@vaadin/button' +import '@vaadin/icon' +import '@vaadin/icons' import '@vaadin/grid' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) @@ -19,17 +25,32 @@ const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) class NameRegistration extends LitElement { static get properties() { return { + theme: { type: String, reflect: true }, + qortWalletBalance: { type: Number }, loading: { type: Boolean }, names: { type: Array }, + marketSellNames: { type: Array }, recipientPublicKey: { type: String }, selectedAddress: { type: Object }, btnDisable: { type: Boolean }, + isLoading: { type: Boolean }, registerNameLoading: { type: Boolean }, error: { type: Boolean }, message: { type: String }, removeError: { type: Boolean }, removeMessage: { type: String }, - theme: { type: String, reflect: true } + fee: { type: Number }, + sellFee: { type: Number }, + cancelSellFee: { type: Number }, + buyFee: { type: Number }, + toSellName: { type: String }, + toSellPrice: { type: String }, + toCancelSellName: { type: String }, + toBuyName: { type: String }, + toBuyPrice: { type: String }, + toBuySeller: { type: String }, + errorMessage: { type: String }, + successMessage: { type: String } } } @@ -37,20 +58,32 @@ class NameRegistration extends LitElement { return css` * { --mdc-theme-primary: rgb(3, 169, 244); - --mdc-theme-secondary: var(--mdc-theme-primary); --paper-input-container-focus-color: var(--mdc-theme-primary); --mdc-theme-surface: var(--white); + --mdc-text-field-outlined-idle-border-color: var(--txtfieldborder); + --mdc-text-field-outlined-hover-border-color: var(--txtfieldhoverborder); + --mdc-text-field-label-ink-color: var(--black); + --mdc-text-field-ink-color: var(--black); --mdc-dialog-content-ink-color: var(--black); + --mdc-dialog-min-width: 400px; + --mdc-dialog-max-width: 1024px; --lumo-primary-text-color: rgb(0, 167, 245); --lumo-primary-color-50pct: rgba(0, 167, 245, 0.5); --lumo-primary-color-10pct: rgba(0, 167, 245, 0.1); --lumo-primary-color: hsl(199, 100%, 48%); --lumo-base-color: var(--white); --lumo-body-text-color: var(--black); + --lumo-secondary-text-color: var(--sectxt); + --lumo-contrast-60pct: var(--vdicon); --_lumo-grid-border-color: var(--border); --_lumo-grid-secondary-border-color: var(--border2); } + [hidden] { + display: hidden !important; + visibility: none !important; + } + #name-registration-page { background: var(--white); padding: 12px 24px; @@ -78,21 +111,100 @@ class NameRegistration extends LitElement { max-height: 42px; } + paper-progress { + --paper-progress-active-color: var(--mdc-theme-primary); + } + .red { --mdc-theme-primary: #F44336; } + + .green { + --mdc-theme-primary: #198754; + } + + .buttons { + text-align: right; + } + + .card-container { + background-color: var(--white); + border-radius: 5px; + color: var(--black); + padding-top: 30px; + position: relative; + width: 350px; + max-width: 100%; + text-align: center; + } + + .successBox { + height: 34px; + min-width: 300px; + width: 100%; + border: 1px solid green; + border-radius: 5px; + background-color: transparent; + margin-top: 15px; + } + + .errorBox { + height: 34px; + min-width: 300px; + width: 100%; + border: 1px solid red; + border-radius: 5px; + background-color: transparent; + margin-top: 15px; + } + + .manage-group-dialog { + min-height: 300px; + min-width: 350px; + box-sizing: border-box; + position: relative; + } + + .btn-clear-success { + --mdc-icon-button-size: 32px; + color: red; + } + + .btn-clear-error { + --mdc-icon-button-size: 32px; + color: green; + } + + .error-icon { + font-size: 48px; + color: red; + } ` } constructor() { super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.qortWalletBalance = 0 this.selectedAddress = {} this.names = [] + this.marketSellNames = [] this.recipientPublicKey = '' this.btnDisable = false + this.isLoading = false this.registerNameLoading = false this.fee = 0.001 - this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.sellFee = 0.001 + this.cancelSellFee = 0.001 + this.buyFee = 0.001 + this.toSellName = '' + this.toSellPrice = '' + this.toCancelSellName = '' + this.toBuyName = '' + this.toBuyPrice = '' + this.toBuySeller = '' + this.errorMessage = '' + this.successMessage = '' } render() { @@ -114,13 +226,38 @@ class NameRegistration extends LitElement { { render(html`${this.renderAvatarButton(data.item)}`, root) }}> + { + if (this.marketSellNames.some(e => e.owner === this.selectedAddress.address)) { + render(html`${this.renderCancelSellNameButton(data.item)}`, root) + } else { + render(html`${this.renderSellNameButton(data.item)}`, root) + } + }}> ${this.isEmptyArray(this.names) ? html` ${translate("registernamepage.nchange8")} `: ''} +

    +
    +

    ${translate("registernamepage.nchange22")}

    + + + + + { + if (data.item.owner === this.selectedAddress.address) { + render(html`${this.renderCancelSellNameButton(data.item)}`, root) + } else { + render(html`${this.renderBuyNameButton(data.item)}`, root) + } + }}> + + ${this.isEmptyArray(this.marketSellNames) ? html` + ${translate("registernamepage.nchange24")} + `: ''} +
    -
    ${translate("registernamepage.nchange9")}

    @@ -161,6 +298,204 @@ class NameRegistration extends LitElement { ${translate("general.close")}
    + + +
    +
    +

    ${translate("registernamepage.nchange19")}

    +
    +
    +
    +

    + + +

    +

    + + +

    +
    +

    ${translate("walletpage.wchange21")} ${this.sellFee} QORT

    +
    +
    + ${this.renderClearSuccess()} + ${this.renderClearError()} + ${this.isLoading ? html`` : ''} +
    +
    + this.createSellName()}> + + ${translate("registernamepage.nchange19")} + +
    +
    +
    + + ${translate("general.close")} + +
    + + +
    +
    +

    ${translate("registernamepage.nchange20")} ${translate("registernamepage.nchange5")}

    +
    +
    +
    +

    + + +

    +
    +

    ${translate("walletpage.wchange21")} ${this.cancelSellFee} QORT

    +
    +
    + ${this.renderClearSuccess()} + ${this.renderClearError()} + ${this.isLoading ? html`` : ''} +
    +
    + this.createCancelSellName()}> + + ${translate("registernamepage.nchange20")} ${translate("registernamepage.nchange5")} + +
    +
    +
    + + ${translate("general.close")} + +
    + + +
    +
    +

    ${translate("registernamepage.nchange21")}

    +
    +
    +
    +

    + + +

    +

    + + +

    +

    + + +

    +
    +

    ${translate("walletpage.wchange21")} ${this.buyFee} QORT

    +
    +
    + ${this.renderClearSuccess()} + ${this.renderClearError()} + ${this.isLoading ? html`` : ''} +
    +
    + this.openCheckFunds()}> + + ${translate("registernamepage.nchange21")} + +
    +
    +
    + + ${translate("general.close")} + +
    + + +
    + warning +

    ${translate("registernamepage.nchange35")}

    +

    ${translate("registernamepage.nchange36")}

    +
    + this.closeBuyErrorNameDialog()} + class="red" + > + ${translate("general.close")} + +
    + + +
    + warning +

    ${translate("registernamepage.nchange37")}

    +

    ${translate("registernamepage.nchange38")}

    +
    + this.closeBuyErrorPriceDialog()} + class="red" + > + ${translate("general.close")} + +
    ` } @@ -170,6 +505,9 @@ class NameRegistration extends LitElement { this.changeTheme() this.changeLanguage() this.unitFee() + this.unitSellFee() + this.unitCancelSellFee() + this.unitBuyFee() const fetchNames = () => { parentEpml.request('apiCall', { @@ -180,6 +518,15 @@ class NameRegistration extends LitElement { setTimeout(fetchNames, this.config.user.nodeSettings.pingInterval) } + const fetchMarketSellNames = async () => { + await parentEpml.request('apiCall', { + url: `/names/forsale?limit=0&reverse=true` + }).then(res => { + this.marketSellNames = res + }) + setTimeout(fetchMarketSellNames, 60000) + } + window.addEventListener("contextmenu", (event) => { event.preventDefault() this._textMenu(event) @@ -221,6 +568,7 @@ class NameRegistration extends LitElement { parentEpml.subscribe('config', c => { if (!configLoaded) { setTimeout(fetchNames, 1) + setTimeout(fetchMarketSellNames, 1) configLoaded = true } this.config = JSON.parse(c) @@ -237,11 +585,11 @@ class NameRegistration extends LitElement { changeTheme() { const checkTheme = localStorage.getItem('qortalTheme') if (checkTheme === 'dark') { - this.theme = 'dark'; + this.theme = 'dark' } else { - this.theme = 'light'; + this.theme = 'light' } - document.querySelector('html').setAttribute('theme', this.theme); + document.querySelector('html').setAttribute('theme', this.theme) } changeLanguage() { @@ -255,6 +603,50 @@ class NameRegistration extends LitElement { } } + async updateQortWalletBalance() { + let qortAddress = window.parent.reduxStore.getState().app.selectedAddress.address + + await parentEpml.request('apiCall', { + url: `/addresses/balance/${qortAddress}`, + }).then((res) => { + this.qortWalletBalance = res + }) + } + + renderClearSuccess() { + let strSuccessValue = this.successMessage + if (strSuccessValue === "") { + return html`` + } else { + return html` +
    + ${this.successMessage} + +
    +
    +

    ${translate("walletpage.wchange43")}

    +
    + ` + } + } + + renderClearError() { + let strErrorValue = this.errorMessage + if (strErrorValue === "") { + return html`` + } else { + return html` +
    + ${this.errorMessage} + +
    +
    +

    ${translate("walletpage.wchange44")}

    +
    + ` + } + } + renderCoreText() { return html`${translate("registernamepage.nchange16")}` } @@ -263,6 +655,18 @@ class NameRegistration extends LitElement { return html`${translate("registernamepage.nchange18")}` } + renderSellSuccessText() { + return html`${translate("registernamepage.nchange32")}` + } + + renderCancelSuccessText() { + return html`${translate("registernamepage.nchange33")}` + } + + renderBuySuccessText() { + return html`${translate("registernamepage.nchange34")}` + } + renderFailText() { return html`${translate("registernamepage.nchange17")}` } @@ -271,7 +675,7 @@ class NameRegistration extends LitElement { let name = nameObj.name const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port - const url = `${nodeUrl}/arbitrary/THUMBNAIL/${name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`; + const url = `${nodeUrl}/arbitrary/THUMBNAIL/${name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}` return html`` } @@ -280,28 +684,168 @@ class NameRegistration extends LitElement { } async uploadAvatar(nameObj) { - let name = encodeURIComponent(nameObj.name) + let name = nameObj.name window.location.href = `../qdn/publish/index.html?service=THUMBNAIL&identifier=qortal_avatar&name=${name}&uploadType=file&category=Avatar&showName=false&showService=false&showIdentifier=false` } + renderSellNameButton(nameObj) { + return html` this.openSellNameDialog(nameObj)}>sell ${translate("registernamepage.nchange19")}` + } + + openSellNameDialog(nameObj) { + this.toSellName = '' + this.toSellPrice = '' + this.shadowRoot.getElementById("toSellName").value = '' + this.shadowRoot.getElementById("toSellPrice").value = '' + this.toSellName = nameObj.name + this.toSellPrice = '5' + this.shadowRoot.querySelector('#sellNameDialog').show() + } + + closeSellNameDialog() { + this.shadowRoot.querySelector('#sellNameDialog').close() + this.toSellName = '' + this.toSellPrice = '' + this.shadowRoot.getElementById("toSellName").value = '' + this.shadowRoot.getElementById("toSellPrice").value = '' + } + + renderCancelSellNameButton(nameObj) { + return html` this.openCancelSellNameDialog(nameObj)}>cancel ${translate("registernamepage.nchange20")}` + } + + openCancelSellNameDialog(nameObj) { + this.toCancelSellName = '' + this.shadowRoot.getElementById("toCancelSellName").value = '' + this.toCancelSellName = nameObj.name + this.shadowRoot.querySelector('#cancelSellNameDialog').show() + } + + closeCancelSellNameDialog() { + this.shadowRoot.querySelector('#cancelSellNameDialog').close() + this.toCancelSellName = '' + this.shadowRoot.getElementById("toCancelSellName").value = '' + } + + renderBuyNameButton(nameObj) { + return html` this.openCheck(nameObj)}>shop ${translate("registernamepage.nchange21")}` + } + + openCheck(nameObj) { + const _name = nameObj.name + const _seller = nameObj.owner + const _price = nameObj.salePrice + if (this.isEmptyArray(this.names) === true) { + this.openBuyNameDialog(_name, _seller, _price) + } else { + this.shadowRoot.querySelector('#buyErrorNameDialog').show() + } + } + + openBuyNameDialog(_name, _seller, _price) { + this.toBuyName = '' + this.toBuyPrice = '' + this.toBuySeller = '' + this.shadowRoot.getElementById("toBuyName").value = '' + this.shadowRoot.getElementById("toBuyPrice").value = '' + this.shadowRoot.getElementById("toBuySeller").value = '' + this.toBuyName = _name + this.toBuyPrice = _price + this.toBuySeller = _seller + this.shadowRoot.querySelector('#buyNameDialog').show() + } + + closeBuyNameDialog() { + this.toBuyName = '' + this.toBuyPrice = '' + this.toBuySeller = '' + this.shadowRoot.getElementById("toBuyName").value = '' + this.shadowRoot.getElementById("toBuyPrice").value = '' + this.shadowRoot.getElementById("toBuySeller").value = '' + this.shadowRoot.querySelector('#buyNameDialog').close() + } + + async openCheckFunds() { + await this.updateQortWalletBalance() + + const myFunds = this.round(parseFloat(this.qortWalletBalance)) + const myBuyPrice = this.round(parseFloat(this.toBuyPrice)) + + if (Number(myFunds) < Number(myBuyPrice)) { + this.shadowRoot.querySelector('#buyErrorPriceDialog').show() + } else { + this.createBuyName() + } + } + + closeBuyErrorNameDialog() { + this.shadowRoot.querySelector('#buyErrorNameDialog').close() + } + + closeBuyErrorPriceDialog() { + this.shadowRoot.querySelector('#buyErrorPriceDialog').close() + } + async unitFee() { - const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; - const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; - const url = `${nodeUrl}/transactions/unitfee?txType=REGISTER_NAME`; + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const url = `${nodeUrl}/transactions/unitfee?txType=REGISTER_NAME` await fetch(url).then((response) => { if (response.ok) { - return response.json(); + return response.json() } - return Promise.reject(response); + return Promise.reject(response) }).then((json) => { - this.fee = (Number(json) / 1e8).toFixed(2); + this.fee = (Number(json) / 1e8).toFixed(2) + }) + } + + async unitSellFee() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const url = `${nodeUrl}/transactions/unitfee?txType=SELL_NAME` + await fetch(url).then((response) => { + if (response.ok) { + return response.json() + } + return Promise.reject(response) + }).then((json) => { + this.sellFee = (Number(json) / 1e8).toFixed(8) + }) + } + + async unitCancelSellFee() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const url = `${nodeUrl}/transactions/unitfee?txType=CANCEL_SELL_NAME` + await fetch(url).then((response) => { + if (response.ok) { + return response.json() + } + return Promise.reject(response) + }).then((json) => { + this.cancelSellFee = (Number(json) / 1e8).toFixed(8) + }) + } + + async unitBuyFee() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const url = `${nodeUrl}/transactions/unitfee?txType=BUY_NAME` + await fetch(url).then((response) => { + if (response.ok) { + return response.json() + } + return Promise.reject(response) + }).then((json) => { + this.buyFee = (Number(json) / 1e8).toFixed(8) }) } getApiKey() { - const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; - let apiKey = myNode.apiKey; - return apiKey; + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + let apiKey = myNode.apiKey + return apiKey } clearSelection() { @@ -391,16 +935,227 @@ class NameRegistration extends LitElement { this.registerNameLoading = false } + async createSellName() { + const name = this.shadowRoot.getElementById("toSellName").value + const price = this.shadowRoot.getElementById("toSellPrice").value + const sellFeeInput = this.sellFee + this.isLoading = true + this.btnDisable = true + + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + } + + const validateReceiver = async () => { + let lastRef = await getLastRef() + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + } + + const makeTransactionRequest = async (lastRef) => { + const myName = name + const myPrice = price + const myLastRef = lastRef + const myFee = sellFeeInput + const mySellNameDialog1 = get("registernamepage.nchange26") + const mySellNameDialog2 = get("registernamepage.nchange27") + const mySellNameDialog3 = get("registernamepage.nchange28") + + let myTxnrequest = await parentEpml.request('transaction', { + type: 5, + nonce: this.selectedAddress.nonce, + params: { + fee: myFee, + name: myName, + sellPrice: myPrice, + lastReference: myLastRef, + sellNameDialog1: mySellNameDialog1, + sellNameDialog2: mySellNameDialog2, + sellNameDialog3: mySellNameDialog3 + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + if (txnResponse.success === false && txnResponse.message) { + this.errorMessage = txnResponse.message + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.shadowRoot.getElementById("toSellName").value = '' + this.shadowRoot.getElementById("toSellPrice").value = '' + this.toSellName = '' + this.toSellPrice = '' + this.errorMessage = '' + this.successMessage = this.renderSellSuccessText() + this.isLoading = false + this.btnDisable = false + } else { + this.errorMessage = txnResponse.data.message + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } + } + validateReceiver() + } + + async createCancelSellName() { + const name = this.shadowRoot.getElementById("toCancelSellName").value + const cancelSellFeeInput = this.cancelSellFee + this.isLoading = true + this.btnDisable = true + + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + } + + const validateReceiver = async () => { + let lastRef = await getLastRef() + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + } + + const makeTransactionRequest = async (lastRef) => { + const myName = name + const myLastRef = lastRef + const myFee = cancelSellFeeInput + const myCancelSellNameDialog1 = get("registernamepage.nchange30") + const myCancelSellNameDialog2 = get("registernamepage.nchange31") + + let myTxnrequest = await parentEpml.request('transaction', { + type: 6, + nonce: this.selectedAddress.nonce, + params: { + fee: myFee, + name: myName, + lastReference: myLastRef, + cancelSellNameDialog1: myCancelSellNameDialog1, + cancelSellNameDialog2: myCancelSellNameDialog2 + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + if (txnResponse.success === false && txnResponse.message) { + this.errorMessage = txnResponse.message + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.shadowRoot.getElementById("toCancelSellName").value = '' + this.toCancelSellName = '' + this.errorMessage = '' + this.successMessage = this.renderCancelSuccessText() + this.isLoading = false + this.btnDisable = false + } else { + this.errorMessage = txnResponse.data.message + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } + } + validateReceiver() + } + + createBuyName() { + const name = this.shadowRoot.getElementById("toBuyName").value + const price = this.shadowRoot.getElementById("toBuyPrice").value + const seller = this.shadowRoot.getElementById("toBuySeller").value + const buyFeeInput = this.buyFee + this.isLoading = true + this.btnDisable = true + + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + } + + const validateReceiver = async () => { + let lastRef = await getLastRef() + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + } + + const makeTransactionRequest = async (lastRef) => { + const myName = name + const myPrice = price + const mySeller = seller + const myLastRef = lastRef + const myFee = buyFeeInput + const myBuyNameDialog1 = get("registernamepage.nchange39") + const myBuyNameDialog2 = get("registernamepage.nchange27") + const myBuyNameDialog3 = get("registernamepage.nchange40") + + let myTxnrequest = await parentEpml.request('transaction', { + type: 7, + nonce: this.selectedAddress.nonce, + params: { + fee: myFee, + name: myName, + sellPrice: myPrice, + recipient: mySeller, + lastReference: myLastRef, + buyNameDialog1: myBuyNameDialog1, + buyNameDialog2: myBuyNameDialog2, + buyNameDialog3: myBuyNameDialog3 + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + if (txnResponse.success === false && txnResponse.message) { + this.errorMessage = txnResponse.message + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.shadowRoot.getElementById("toBuyName").value = '' + this.shadowRoot.getElementById("toBuyPrice").value = '' + this.shadowRoot.getElementById("toBuySeller").value = '' + this.toBuyName = '' + this.toBuyPrice = '' + this.toBuySeller = '' + this.errorMessage = '' + this.successMessage = this.renderBuySuccessText() + this.isLoading = false + this.btnDisable = false + } else { + this.errorMessage = txnResponse.data.message + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } + } + validateReceiver() + } + _textMenu(event) { const getSelectedText = () => { - var text = ""; + var text = "" if (typeof window.getSelection != "undefined") { - text = window.getSelection().toString(); + text = window.getSelection().toString() } else if (typeof this.shadowRoot.selection != "undefined" && this.shadowRoot.selection.type == "Text") { - text = this.shadowRoot.selection.createRange().text; + text = this.shadowRoot.selection.createRange().text } - return text; + return text } const checkSelectedTextAndShowMenu = () => { @@ -414,10 +1169,14 @@ class NameRegistration extends LitElement { parentEpml.request('openCopyTextMenu', textMenuObject) } } - checkSelectedTextAndShowMenu() } + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } + isEmptyArray(arr) { if (!arr) { return true } return arr.length === 0 From 3a115c35e351f8c4585d4acddc81f0306ed1e391 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Mon, 6 Feb 2023 11:16:00 +0100 Subject: [PATCH 14/32] Add missing translation --- qortal-ui-core/language/de.json | 3 ++- qortal-ui-core/language/es.json | 3 ++- qortal-ui-core/language/fr.json | 3 ++- qortal-ui-core/language/hindi.json | 3 ++- qortal-ui-core/language/hr.json | 3 ++- qortal-ui-core/language/hu.json | 3 ++- qortal-ui-core/language/it.json | 3 ++- qortal-ui-core/language/ko.json | 3 ++- qortal-ui-core/language/no.json | 3 ++- qortal-ui-core/language/pl.json | 3 ++- qortal-ui-core/language/pt.json | 3 ++- qortal-ui-core/language/ro.json | 3 ++- qortal-ui-core/language/rs.json | 3 ++- qortal-ui-core/language/ru.json | 3 ++- qortal-ui-core/language/us.json | 3 ++- qortal-ui-core/language/zhc.json | 3 ++- qortal-ui-core/language/zht.json | 3 ++- 17 files changed, 34 insertions(+), 17 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index 326683b0..89e22f0a 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -579,7 +579,8 @@ "cchange71": "und", "cchange72": "andere", "cchange73": "s", - "cchange74": "reagiert mit" + "cchange74": "reagiert mit", + "cchange90": "Keine Nachrichten" }, "welcomepage": { "wcchange1": "Willkommen zu Q-Chat", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index 82e6dce4..f214975c 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -579,7 +579,8 @@ "cchange71": "y", "cchange72": "otro", "cchange73": "s", - "cchange74": "reaccionó con" + "cchange74": "reaccionó con", + "cchange90": "Sin mensajes" }, "welcomepage": { "wcchange1": "Bienvenido al Q-Chat", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 62cd43e1..1c675ae8 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -579,7 +579,8 @@ "cchange71": "et", "cchange72": "autre", "cchange73": "s", - "cchange74": "a réagi avec" + "cchange74": "a réagi avec", + "cchange90": "Aucun message" }, "welcomepage": { "wcchange1": "Bienvenue dans Q-Chat", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index 2ef18b37..fcc46ed9 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -580,7 +580,8 @@ "cchange71": "और", "cchange72": "अन्य", "cchange73": "s", - "cchange74": "के साथ प्रतिक्रिया" + "cchange74": "के साथ प्रतिक्रिया", + "cchange90": "कोई संदेश नहीं" }, "welcomepage": { "wcchange1": "क्यू-चैट में आपका स्वागत है", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 26fc0d4a..287904ce 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -579,7 +579,8 @@ "cchange71": "i", "cchange72": "ostalo", "cchange73": "s", - "cchange74": "reagirao sa" + "cchange74": "reagirao sa", + "cchange90": "Nema poruka" }, "welcomepage": { "wcchange1": "Dobrodošli u Q-Čavrljanje", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index d92390cf..0ddb80d5 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -579,7 +579,8 @@ "cchange71": "és", "cchange72": "egyéb", "cchange73": "s", - "cchange74": "reagált:" + "cchange74": "reagált:", + "cchange90": "Nincs üzenet" }, "welcomepage": { "wcchange1": "Üdvözöljük a Q-Chathoz", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index 1523e212..bae18193 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -579,7 +579,8 @@ "cchange71": "e", "cchange72": "altro", "cchange73": "s", - "cchange74": "ha reagito con" + "cchange74": "ha reagito con", + "cchange90": "Nessun messaggio" }, "welcomepage": { "wcchange1": "Benvenuto in Q-Chat", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index 5e533527..e07e921e 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -579,7 +579,8 @@ "cchange71": "그리고", "cchange72": "기타", "cchange73": "들", - "cchange74": "반응" + "cchange74": "반응", + "cchange90": "메시지 없음" }, "welcomepage": { "wcchange1": "Q-Chat에 오신 것을 환영합니다.", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index 1ee638af..f32175ed 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -579,7 +579,8 @@ "cchange71": "og", "cchange72": "annet", "cchange73": "s", - "cchange74": "reagerte med" + "cchange74": "reagerte med", + "cchange90": "Ingen meldinger" }, "welcomepage": { "wcchange1": "Velkommen til Q-Chat", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index 5c75270f..ee1d4b14 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -579,7 +579,8 @@ "cchange71": "i", "cchange72": "inne", "cchange73": "s", - "cchange74": "zareagował z" + "cchange74": "zareagował z", + "cchange90": "Brak wiadomości" }, "welcomepage": { "wcchange1": "Witamy w Q-Chat", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index 870b3dd3..6558c1f4 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -579,7 +579,8 @@ "cchange71": "e", "cchange72": "outro", "cchange73": "s", - "cchange74": "reagiu com" + "cchange74": "reagiu com", + "cchange90": "Sem mensagens" }, "welcomepage": { "wcchange1": "Bem-vindo ao Q-Chat", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 4e17e959..e5908fb5 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -579,7 +579,8 @@ "cchange71": "și", "cchange72": "altul", "cchange73": "s", - "cchange74": "a reacționat cu" + "cchange74": "a reacționat cu", + "cchange90": "Fără mesaje" }, "welcomepage": { "wcchange1": "Bine ai venit la Q-Chat", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index e1722ca2..1873c837 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -579,7 +579,8 @@ "cchange71": "i", "cchange72": "drugo", "cchange73": "s", - "cchange74": "reagovao sa" + "cchange74": "reagovao sa", + "cchange90": "Nema poruka" }, "welcomepage": { "wcchange1": "Dobrodošli na Q-Chat", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index ea20a79d..a27dce40 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -579,7 +579,8 @@ "cchange71": "и", "cchange72": "другое", "cchange73": "с", - "cchange74": "отреагировал" + "cchange74": "отреагировал", + "cchange90": "Нет сообщений" }, "welcomepage": { "wcchange1": "Добро пожаловать в Q-Chat", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 87760fd7..02bb9586 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -579,7 +579,8 @@ "cchange71": "and", "cchange72": "other", "cchange73": "s", - "cchange74": "reacted with" + "cchange74": "reacted with", + "cchange90": "No messages" }, "welcomepage": { "wcchange1": "Welcome to Q-Chat", diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index 57fe5ec0..ee0c77b5 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -579,7 +579,8 @@ "cchange71": "和", "cchange72": "其他", "cchange73": "的", - "cchange74": "心情回应" + "cchange74": "心情回应", + "cchange90": "没有消息" }, "welcomepage": { "wcchange1": "欢迎来到Q-Chat", diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index 457d0bc4..1842d4d9 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -579,7 +579,8 @@ "cchange71": "和", "cchange72": "其他", "cchange73": "的", - "cchange74": "心情回應" + "cchange74": "心情回應", + "cchange90": "沒有消息" }, "welcomepage": { "wcchange1": "歡迎來到 Q-Chat", From 8bf10588b95c33ce9dec637650a074613c898c5b Mon Sep 17 00:00:00 2001 From: Phillip Date: Mon, 6 Feb 2023 12:19:08 +0200 Subject: [PATCH 15/32] fix unread error in general chat --- .../plugins/core/components/ChatPage.js | 16 ++++++++++++++-- .../plugins/core/messaging/q-chat/q-chat.src.js | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js index 8aacdc1b..bb6023b1 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatPage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js @@ -1421,6 +1421,12 @@ class ChatPage extends LitElement { }) document.addEventListener('keydown', this.initialChat); document.addEventListener('paste', this.pasteImage); + if(this.chatId){ + window.parent.reduxStore.dispatch( window.parent.reduxAction.addChatLastSeen({ + key: this.chatId, + timestamp: Date.now() + })) + } } disconnectedCallback() { @@ -1444,7 +1450,7 @@ class ChatPage extends LitElement { document.removeEventListener('keydown', this.initialChat); document.removeEventListener('paste', this.pasteImage); - if(this.messagesRendered.length !== 0){ + if(this.chatId){ window.parent.reduxStore.dispatch( window.parent.reduxAction.addChatLastSeen({ key: this.chatId, timestamp: Date.now() @@ -2181,7 +2187,13 @@ class ChatPage extends LitElement { await this.renderNewMessage(msg) }) await Promise.all(renderEachMessage) - + if(this.chatId){ + window.parent.reduxStore.dispatch( window.parent.reduxAction.addChatLastSeen({ + key: this.chatId, + timestamp: Date.now() + })) + + } // this.newMessages = this.newMessages.concat(_newMessages) this.messagesRendered = [...this.messagesRendered].sort(function (a, b) { return a.timestamp diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index e618a1b0..3ed87067 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -887,7 +887,7 @@ class Chat extends LitElement { setChatHeads(chatObj) { const chatObjGroups = Array.isArray(chatObj.groups) ? chatObj.groups : []; const chatObjDirect = Array.isArray(chatObj.direct) ? chatObj.direct : []; - let groupList = chatObjGroups.map(group => group.groupId === 0 ? { groupId: group.groupId, url: `group/${group.groupId}`, groupName: "Qortal General Chat", timestamp: group.timestamp === undefined ? 2 : group.timestamp } : { ...group, timestamp: group.timestamp === undefined ? 1 : group.timestamp, url: `group/${group.groupId}` }) + let groupList = chatObjGroups.map(group => group.groupId === 0 ? { groupId: group.groupId, url: `group/${group.groupId}`, groupName: "Qortal General Chat", timestamp: group.timestamp === undefined ? 2 : group.timestamp, sender: group.sender } : { ...group, timestamp: group.timestamp === undefined ? 1 : group.timestamp, url: `group/${group.groupId}` }) let directList = chatObjDirect.map(dc => { return { ...dc, url: `direct/${dc.address}` } }) From ce6a7873665e32581ca89a227fc942bdd3fbe317 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Mon, 6 Feb 2023 11:27:04 +0100 Subject: [PATCH 16/32] Update dependencies --- package.json | 2 +- qortal-ui-core/package.json | 24 ++++++++++++------------ qortal-ui-plugins/package.json | 29 +++++++++++++++-------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index e868253e..256ce0c4 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "os-locale": "3.0.0" }, "devDependencies": { - "electron": "22.0.3", + "electron": "22.2.0", "electron-builder": "23.6.0", "electron-packager": "17.1.1", "shelljs": "0.8.5" diff --git a/qortal-ui-core/package.json b/qortal-ui-core/package.json index 8e4f2ad3..d8361eda 100644 --- a/qortal-ui-core/package.json +++ b/qortal-ui-core/package.json @@ -17,9 +17,9 @@ "author": "QORTAL ", "license": "GPL-3.0", "dependencies": { - "@hapi/hapi": "21.2.0", + "@hapi/hapi": "21.2.1", "@hapi/inert": "7.0.0", - "sass": "1.57.1" + "sass": "1.58.0" }, "devDependencies": { "@babel/core": "7.20.12", @@ -58,27 +58,27 @@ "@rollup/plugin-commonjs": "24.0.1", "@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-replace": "5.0.2", - "@rollup/plugin-terser": "0.3.0", - "@vaadin/button": "23.3.5", - "@vaadin/grid": "23.3.5", - "@vaadin/icons": "23.3.5", - "@vaadin/password-field": "23.3.5", - "@vaadin/tooltip": "23.3.5", + "@rollup/plugin-terser": "0.4.0", + "@vaadin/button": "23.3.6", + "@vaadin/grid": "23.3.6", + "@vaadin/icons": "23.3.6", + "@vaadin/password-field": "23.3.6", + "@vaadin/tooltip": "23.3.6", "asmcrypto.js": "2.3.2", "bcryptjs": "2.4.3", "epml": "0.3.3", "file-saver": "2.0.5", "lit": "2.6.1", "lit-translate": "2.0.1", + "localforage": "1.10.0", "pwa-helpers": "0.9.1", - "redux": "4.2.0", + "redux": "4.2.1", "redux-thunk": "2.4.2", - "rollup": "3.10.1", + "rollup": "3.14.0", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", "rollup-plugin-scss": "3.0.0", - "rollup-plugin-web-worker-loader": "1.6.1", - "localforage": "1.10.0" + "rollup-plugin-web-worker-loader": "1.6.1" }, "engines": { "node": ">=16.17.1" diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index 7ab1011d..261ca4d0 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -20,13 +20,14 @@ "@lit-labs/motion": "1.0.3", "@material/mwc-list": "0.27.0", "@material/mwc-select": "0.27.0", - "@tiptap/core": "2.0.0-beta.209", - "@tiptap/extension-highlight": "2.0.0-beta.209", - "@tiptap/extension-image": "2.0.0-beta.209", - "@tiptap/extension-placeholder": "2.0.0-beta.209", - "@tiptap/extension-underline": "2.0.0-beta.209", - "@tiptap/html": "2.0.0-beta.209", - "@tiptap/starter-kit": "2.0.0-beta.209", + "@tiptap/pm": "2.0.0-beta.212", + "@tiptap/core": "2.0.0-beta.212", + "@tiptap/extension-highlight": "2.0.0-beta.212", + "@tiptap/extension-image": "2.0.0-beta.212", + "@tiptap/extension-placeholder": "2.0.0-beta.212", + "@tiptap/extension-underline": "2.0.0-beta.212", + "@tiptap/html": "2.0.0-beta.212", + "@tiptap/starter-kit": "2.0.0-beta.212", "asmcrypto.js": "2.3.2", "compressorjs": "1.1.1", "emoji-picker-js": "https://github.com/Qortal/emoji-picker-js", @@ -69,19 +70,19 @@ "@rollup/plugin-commonjs": "24.0.1", "@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-replace": "5.0.2", - "@rollup/plugin-terser": "0.3.0", - "@vaadin/avatar": "23.3.5", - "@vaadin/button": "23.3.5", - "@vaadin/grid": "23.3.5", - "@vaadin/icons": "23.3.5", - "@vaadin/tooltip": "23.3.5", + "@rollup/plugin-terser": "0.4.0", + "@vaadin/avatar": "23.3.6", + "@vaadin/button": "23.3.6", + "@vaadin/grid": "23.3.6", + "@vaadin/icons": "23.3.6", + "@vaadin/tooltip": "23.3.6", "epml": "0.3.3", "file-saver": "2.0.5", "highcharts": "10.3.3", "html-escaper": "3.0.3", "lit": "2.6.1", "lit-translate": "2.0.1", - "rollup": "3.10.1", + "rollup": "3.14.0", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", "rollup-plugin-web-worker-loader": "1.6.1", From 544727c0493b3aa26cf3bbc794f55f59ab24b72e Mon Sep 17 00:00:00 2001 From: Phillip Date: Thu, 9 Feb 2023 17:38:43 +0200 Subject: [PATCH 17/32] translation fixes --- qortal-ui-plugins/plugins/core/components/ChatPage.js | 6 +++--- qortal-ui-plugins/plugins/core/components/ChatScroller.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js index a18f6c13..1203b541 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatPage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js @@ -1110,7 +1110,7 @@ class ChatPage extends LitElement {

    ${this.isDeletingAttachment ? - translate("chatpage.cchange66") : translate("chatpage.cchange65")} + translate("chatpage.cchange76") : translate("chatpage.cchange75")}

    @@ -1631,7 +1631,7 @@ class ChatPage extends LitElement { this.insertImage(file); } catch (error) { console.error(error); - let errorMsg = get("chatpage.cchange70") + let errorMsg = get("chatpage.cchange81") parentEpml.request('showSnackBar', `${errorMsg}`) } } else { @@ -2993,7 +2993,7 @@ class ChatPage extends LitElement { const identifier = `qchat_${id}`; const fileSize = attachment.size; if (fileSize > 1000000) { - parentEpml.request('showSnackBar', get("chatpage.cchange67")); + parentEpml.request('showSnackBar', get("chatpage.cchange77")); this.isLoading = false; this.isUploadingAttachment = false; return; diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index 6b50f5e0..ca5f4963 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -642,7 +642,7 @@ class MessageTemplate extends LitElement { ` : image && isImageDeleted ? html` -

    ${translate("chatpage.cchange71")}

    +

    ${translate("chatpage.cchange80")}

    ` : html``} ${attachment && !isAttachmentDeleted ? html` @@ -683,7 +683,7 @@ class MessageTemplate extends LitElement {

    - ${translate("chatpage.cchange72")} + ${translate("chatpage.cchange82")}

    @@ -860,7 +860,7 @@ class MessageTemplate extends LitElement { this.openDeleteImage = false; }}>
    -

    ${translate("chatpage.cchange69")}

    +

    ${translate("chatpage.cchange78")}

    -

    Unused DGB Address

    +

    DGB


    ${this.wallets.get(this._selectedWallet).unusedWalletAddress} + + +
    + ${translate("walletpage.wchange38")}

    -

    Unused RVN Address

    +

    RVN


    ${this.wallets.get(this._selectedWallet).unusedWalletAddress} + + +
    + ${translate("walletpage.wchange38")}

    this.getUnusedAddress(this._selectedWallet)}>Get Unused Address` + return html` this.getUnusedAddress(this._selectedWallet)}> ${translate("walletpage.wchange58")}` case "btc": case "ltc": case "doge": case "dgb": case "rvn": - return html` this.getUnusedAddress(this._selectedWallet)}>Get Unused Address` + return html` this.getUnusedAddress(this._selectedWallet)}> ${translate("walletpage.wchange58")}` default: return html`` } From 96979b30b3790f08104ea4c65066748628380121 Mon Sep 17 00:00:00 2001 From: Pigpig105 <81789882+Pigpig105@users.noreply.github.com> Date: Sun, 12 Feb 2023 21:50:15 -0800 Subject: [PATCH 24/32] Update account-view.js --- qortal-ui-core/src/components/settings-view/account-view.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qortal-ui-core/src/components/settings-view/account-view.js b/qortal-ui-core/src/components/settings-view/account-view.js index 6861e47a..43aa9e1d 100644 --- a/qortal-ui-core/src/components/settings-view/account-view.js +++ b/qortal-ui-core/src/components/settings-view/account-view.js @@ -95,12 +95,12 @@ class AccountView extends connect(store)(LitElement) { getAvatar() { let numberBlocks = (this.accountInfo.addressInfo.blocksMinted + this.accountInfo.addressInfo.blocksMintedAdjustment); if (Number.isNaN(numberBlocks) || numberBlocks == "" || numberBlocks === null) { - return html`` + return html`` } else { const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port const url = `${avatarUrl}/arbitrary/THUMBNAIL/${this.accountInfo.names[0].name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}` - return html`` + return html`` } } From 84f08d433254d4639ccc708354432ca978e87f59 Mon Sep 17 00:00:00 2001 From: Phillip Date: Mon, 13 Feb 2023 15:49:02 +0200 Subject: [PATCH 25/32] fix ui --- qortal-ui-core/package.json | 12 +- qortal-ui-plugins/package.json | 15 +- .../plugins/core/components/ChatScroller.js | 4 +- .../plugins/core/components/ChatTextEditor.js | 347 ++++++++---------- 4 files changed, 167 insertions(+), 211 deletions(-) diff --git a/qortal-ui-core/package.json b/qortal-ui-core/package.json index e2852edb..16b78477 100644 --- a/qortal-ui-core/package.json +++ b/qortal-ui-core/package.json @@ -59,11 +59,11 @@ "@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-replace": "5.0.2", "@rollup/plugin-terser": "0.4.0", - "@vaadin/button": "23.3.6", - "@vaadin/grid": "23.3.6", - "@vaadin/icons": "23.3.6", - "@vaadin/password-field": "23.3.6", - "@vaadin/tooltip": "23.3.6", + "@vaadin/button": "23.3.7", + "@vaadin/grid": "23.3.7", + "@vaadin/icons": "23.3.7", + "@vaadin/password-field": "23.3.7", + "@vaadin/tooltip": "23.3.7", "asmcrypto.js": "2.3.2", "bcryptjs": "2.4.3", "epml": "0.3.3", @@ -83,4 +83,4 @@ "engines": { "node": ">=16.17.1" } -} \ No newline at end of file +} diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index f2b3b44c..d9d0e0d9 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -71,11 +71,11 @@ "@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-replace": "5.0.2", "@rollup/plugin-terser": "0.4.0", - "@vaadin/avatar": "23.3.6", - "@vaadin/button": "23.3.6", - "@vaadin/grid": "23.3.6", - "@vaadin/icons": "23.3.6", - "@vaadin/tooltip": "23.3.6", + "@vaadin/avatar": "23.3.7", + "@vaadin/button": "23.3.7", + "@vaadin/grid": "23.3.7", + "@vaadin/icons": "23.3.7", + "@vaadin/tooltip": "23.3.7", "epml": "0.3.3", "file-saver": "2.0.5", "highcharts": "10.3.3", @@ -86,9 +86,10 @@ "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", "rollup-plugin-web-worker-loader": "1.6.1", - "passive-events-support": "1.0.33" + "passive-events-support": "1.0.33", + "axios": "1.3.2" }, "engines": { "node": ">=16.17.1" } -} \ No newline at end of file +} diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index ca5f4963..fe429bfd 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -348,10 +348,12 @@ class MessageTemplate extends LitElement { } async downloadAttachment(attachment) { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; try{ - const res = await axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}`, { responseType: 'blob' }) + axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`, { responseType: 'blob'}) .then(response =>{ let filename = attachment.attachmentName; let blob = new Blob([response.data], { type:"application/octet-stream" }); diff --git a/qortal-ui-plugins/plugins/core/components/ChatTextEditor.js b/qortal-ui-plugins/plugins/core/components/ChatTextEditor.js index 54fcbcc6..0ca49c54 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatTextEditor.js +++ b/qortal-ui-plugins/plugins/core/components/ChatTextEditor.js @@ -15,9 +15,10 @@ class ChatTextEditor extends LitElement { isLoadingMessages: { type: Boolean }, _sendMessage: { attribute: false }, placeholder: { type: String }, - imageFile: { type: Object }, attachment: { type: Object }, insertFile: { attribute: false }, + imageFile: { type: Object }, + insertImage: { attribute: false }, iframeHeight: { type: Number }, editedMessageObj: { type: Object }, repliedToMessageObj: {type: Object}, @@ -166,32 +167,30 @@ class ChatTextEditor extends LitElement { color: var(--black); padding: 0px 10px; height: 100%; - display: flex; - align-items: center; + display: flex; + align-items: center; } - .element::-webkit-scrollbar-track { - background-color: whitesmoke; - border-radius: 7px; - } - - .element::-webkit-scrollbar { - width: 6px; - border-radius: 7px; - background-color: whitesmoke; - } - - .element::-webkit-scrollbar-thumb { - background-color: rgb(180, 176, 176); - border-radius: 7px; - transition: all 0.3s ease-in-out; - } - - .element::-webkit-scrollbar-thumb:hover { - background-color: rgb(148, 146, 146); - cursor: pointer; - } - + background-color: whitesmoke; + border-radius: 7px; + } + + .element::-webkit-scrollbar { + width: 6px; + border-radius: 7px; + background-color: whitesmoke; + } + + .element::-webkit-scrollbar-thumb { + background-color: rgb(180, 176, 176); + border-radius: 7px; + transition: all 0.3s ease-in-out; + } + + .element::-webkit-scrollbar-thumb:hover { + background-color: rgb(148, 146, 146); + cursor: pointer; + } .ProseMirror:focus { outline: none; } @@ -200,45 +199,57 @@ class ChatTextEditor extends LitElement { background-color: var(--white) } - .ProseMirror > * + * { - margin-top: 0.75em; - outline: none; + .ProseMirror > * + * { + margin-top: 0.75em; + outline: none; } - .ProseMirror ul, - ol { - padding: 0 1rem; + .ProseMirror ul, + ol { + padding: 0 1rem; + } + + .ProseMirror h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.1; + } + + .ProseMirror code { + background-color: rgba(#616161, 0.1); + color: #616161; + } + + .ProseMirror pre { + background: #0D0D0D; + color: #FFF; + font-family: 'JetBrainsMono', monospace; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + white-space: pre-wrap; + } + .ProseMirror pre code { + color: inherit; + padding: 0; + background: none; + font-size: 0.8rem; } - .ProseMirror h1, - h2, - h3, - h4, - h5, - h6 { - line-height: 1.1; - } - .ProseMirror code { - background-color: rgba(#616161, 0.1); - color: #616161; - } + .ProseMirror img { + width: 1.7em; + height: 1.5em; + margin: 0px; - .ProseMirror pre { - background: #0D0D0D; - color: #FFF; - font-family: 'JetBrainsMono', monospace; - padding: 0.75rem 1rem; - border-radius: 0.5rem; - white-space: pre-wrap; - } - .ProseMirror pre code { - color: inherit; - padding: 0; - background: none; - font-size: 0.8rem; - } + } + .ProseMirror blockquote { + padding-left: 1rem; + border-left: 2px solid rgba(#0D0D0D, 0.1); + } .ProseMirror hr { border: none; @@ -270,12 +281,35 @@ class ChatTextEditor extends LitElement { } - + .chatbar-buttons { + margin-bottom: 5px; + flex-shrink: 0; + } - .ProseMirror blockquote { - padding-left: 1rem; - border-left: 2px solid rgba(#0D0D0D, 0.1); - } + .show-chatbar-buttons { + display: flex; + align-items: center; + justify-content: center; + } + :host(:hover) .chatbar-button-single { + + display: flex; + align-items: center; + justify-content: center; + } + .ProseMirror p.is-editor-empty:first-child::before { + color: #adb5bd; + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} +.ProseMirror p { + font-size: 18px; + margin-block-start: 0px; + margin-block-end: 0px; + overflow-wrap: anywhere; +} .ProseMirror { width: 100%; @@ -283,64 +317,28 @@ class ChatTextEditor extends LitElement { word-break: break-word; } - .chatbar-buttons { - margin-bottom: 5px; - flex-shrink: 0; - } +.ProseMirror mark { + background-color: #ffe066; + border-radius: 0.25em; + box-decoration-break: clone; + padding: 0.125em 0; +} - .show-chatbar-buttons { - display: flex; - align-items: center; - justify-content: center; - } - :host(:hover) .chatbar-button-single { - - display: flex; - align-items: center; - justify-content: center; - } - .ProseMirror p.is-editor-empty:first-child::before { - color: #adb5bd; - content: attr(data-placeholder); - float: left; - height: 0; - pointer-events: none; - } - .ProseMirror p { - font-size: 18px; - margin-block-start: 0px; - margin-block-end: 0px; - overflow-wrap: anywhere; - } +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + /* Preferred icon size */ + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; - .ProseMirror { - width: 100%; - box-sizing: border-box; - word-break: break-all; - } - - .ProseMirror mark { - background-color: #ffe066; - border-radius: 0.25em; - box-decoration-break: clone; - padding: 0.125em 0; - } - - .material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; - /* Preferred icon size */ - display: inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; - - } +} .material-symbols-outlined { font-family: 'Material Symbols Outlined'; @@ -530,7 +528,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b html`
    ${this.isLoading === false ? html` @@ -560,66 +558,6 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b ` : html``}
    - -
    - - ${this.editedMessageObj ? ( - html` -
    - ${this.isLoading === false ? html` - { - this.sendMessageFunc(); - }} - > - - ` : - html` - - `} -
    - ` - ) : - html` -
    - ${this.isLoading === false ? html` - send-icon { - this.sendMessageFunc(); - }} - /> - ` : - html` - - `} -
    - ` - } - - ${this.chatMessageSize >= 750 ? - html` -
    -
    - ${`Your message size is of ${this.chatMessageSize} bytes out of a maximum of 1000`} -
    -
    - ` : - html``} - ` } @@ -630,7 +568,14 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b } } + + + + async firstUpdated() { + + + window.addEventListener('storage', () => { const checkTheme = localStorage.getItem('qortalTheme'); const chatbar = this.shadowRoot.querySelector('.element') @@ -647,6 +592,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b this.emojiPickerHandler = this.shadowRoot.querySelector('.emoji-button'); this.mirrorChatInput = this.shadowRoot.getElementById('messageBox'); + this.chatMessageInput = this.shadowRoot.querySelector('.element') this.emojiPicker = new EmojiPicker({ style: "twemoji", @@ -688,6 +634,10 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b if (changedProperties && changedProperties.has('placeholder') && this.updatePlaceholder && this.editor) { this.updatePlaceholder(this.editor, this.placeholder ) } + + if (changedProperties && changedProperties.has("imageFile")) { + this.chatMessageInput = "newChat"; + } } shouldUpdate(changedProperties) { @@ -709,7 +659,8 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b getMessageSize(message){ try { - const trimmedMessage = message; + + const trimmedMessage = message let messageObject = {}; if (this.repliedToMessageObj) { @@ -729,37 +680,37 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b const parsedMessageObj = JSON.parse(this.editedMessageObj.decodedMessage); message = parsedMessageObj; } catch (error) { - message = this.messageObj.decodedMessage; + message = this.messageObj.decodedMessage } messageObject = { ...message, messageText: trimmedMessage, } - } else if (this.imageFile && this.iframeId === 'newChat') { + } else if(this.imageFile && this.iframeId === 'newChat') { messageObject = { messageText: trimmedMessage, images: [{ service: "QCHAT_IMAGE", name: '123456789123456789123456789', identifier: '123456' - }], + }], repliedTo: '', version: 2 }; } else if (this.attachment && this.iframeId === 'newAttachmentChat') { - messageObject = { - messageText: trimmedMessage, - attachments: [{ - service: "QCHAT_ATTACHMENT", - name: '123456789123456789123456789', - identifier: '123456', - attachmentName: "123456789123456789123456789", - attachmentSize: "123456" - }], - repliedTo: '', - version: 2 - }; - } else { + messageObject = { + messageText: trimmedMessage, + attachments: [{ + service: "QCHAT_ATTACHMENT", + name: '123456789123456789123456789', + identifier: '123456', + attachmentName: "123456789123456789123456789", + attachmentSize: "123456" + }], + repliedTo: '', + version: 2 + }; + } else { messageObject = { messageText: trimmedMessage, images: [''], @@ -767,6 +718,7 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b version: 2 }; } + const stringified = JSON.stringify(messageObject); const size = new Blob([stringified]).size; this.chatMessageSize = size; @@ -775,7 +727,8 @@ mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::b } } + } -window.customElements.define("chat-text-editor", ChatTextEditor) +window.customElements.define("chat-text-editor", ChatTextEditor) \ No newline at end of file From 326b21a269672474544d9ef9bce77366d4004d5a Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Tue, 14 Feb 2023 12:31:48 +0100 Subject: [PATCH 26/32] Update appinfo --- qortal-ui-core/src/components/app-info.js | 167 +++++++++--------- .../plugins/core/wallet/wallet-app.src.js | 4 +- 2 files changed, 88 insertions(+), 83 deletions(-) diff --git a/qortal-ui-core/src/components/app-info.js b/qortal-ui-core/src/components/app-info.js index 6d0aef89..5ce24ae7 100644 --- a/qortal-ui-core/src/components/app-info.js +++ b/qortal-ui-core/src/components/app-info.js @@ -18,6 +18,7 @@ class AppInfo extends connect(store)(LitElement) { coreInfo: { type: Array }, nodeConfig: { type: Object }, pageUrl: { type: String }, + publicizeAddress: { type: String }, theme: { type: String, reflect: true } } } @@ -95,6 +96,7 @@ class AppInfo extends connect(store)(LitElement) { this.coreInfo = [] this.nodeStatus = {} this.pageUrl = '' + this.publicizeAddress = '' this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.publicKeyisOnChainConfirmation = false this.interval @@ -112,24 +114,46 @@ class AppInfo extends connect(store)(LitElement) { ` } + firstUpdated() { + this.publicizeAddress = store.getState().app.selectedAddress.address + '_publicize' + this.setStorage() + this.getNodeInfo() + this.getCoreInfo() + try { + this.confirmPublicKeyOnChain(store.getState().app.selectedAddress.address) + } catch (error) { + console.error(error) + } + + setInterval(() => { + this.getNodeInfo() + this.getCoreInfo() + }, 30000) + } + + setStorage() { + if (localStorage.getItem(this.publicizeAddress) === null) { + localStorage.setItem(this.publicizeAddress, 'false') + } + } + async confirmPublicKeyOnChain(address) { const _computePow2 = async (chatBytes) => { - const difficulty = 14; + const difficulty = 14 const path = window.parent.location.origin + '/memory-pow/memory-pow.wasm.full' const worker = new WebWorker(); let nonce = null let chatBytesArray = null - await new Promise((res, rej) => { - worker.postMessage({chatBytes, path, difficulty}); - + await new Promise((res, rej) => { + worker.postMessage({chatBytes, path, difficulty}) + worker.onmessage = e => { - worker.terminate() - chatBytesArray = e.data.chatBytesArray + worker.terminate() + chatBytesArray = e.data.chatBytesArray nonce = e.data.nonce res() - } - }) + }) let _response = await routes.sign_chat({ data: { @@ -137,93 +161,74 @@ class AppInfo extends connect(store)(LitElement) { chatBytesArray: chatBytesArray, chatNonce: nonce }, - - }); - return _response - }; + }) + return _response + } - - let stop = false - const checkPublicKey = async () => { - if (!stop) { - stop = true; - try { - if(this.publicKeyisOnChainConfirmation){ - clearInterval(this.interval) - return - } - const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]; - const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; - const url = `${nodeUrl}/addresses/publickey/${address}`; - const res = await fetch(url) - let data = '' + let stop = false + const checkPublicKey = async () => { + if (!stop) { + stop = true try { - data = await res.text(); - } catch (error) { - data = { - error: 'error' + if(localStorage.getItem(this.publicizeAddress) === 'true') { + clearInterval(this.interval) + return } - } - if(data === 'false' && this.nodeInfo.isSynchronizing !== true){ - let _reference = new Uint8Array(64); - window.crypto.getRandomValues(_reference); - let reference = window.parent.Base58.encode(_reference); - const chatRes = await routes.chat({ - data: { - type: 19, - nonce: store.getState().app.selectedAddress.nonce, - params: { - lastReference: reference, - proofOfWorkNonce: 0, - fee: 0, - timestamp: Date.now(), - + const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const url = `${nodeUrl}/addresses/publickey/${address}` + const res = await fetch(url) + let data = '' + try { + data = await res.text() + } catch (error) { + data = { + error: 'error' + } + } + if(data === 'false' && this.nodeInfo.isSynchronizing !== true) { + let _reference = new Uint8Array(64) + window.crypto.getRandomValues(_reference) + let reference = window.parent.Base58.encode(_reference) + const chatRes = await routes.chat({ + data: { + type: 19, + nonce: store.getState().app.selectedAddress.nonce, + params: { + lastReference: reference, + proofOfWorkNonce: 0, + fee: 0, + timestamp: Date.now(), + }, + disableModal: true }, - disableModal: true - }, - disableModal: true, - }); + disableModal: true, + }); try { - const powRes = await _computePow2(chatRes) - if(powRes === true){ + const powRes = await _computePow2(chatRes) + if(powRes === true) { clearInterval(this.interval) - - this.publicKeyisOnChainConfirmation = true + localStorage.removeItem(this.publicizeAddress) + localStorage.setItem(this.publicizeAddress, 'true') } } catch (error) { console.error(error) } } - if (!data.error && data !== 'false' && data) { - clearInterval(this.interval) - - this.publicKeyisOnChainConfirmation = true - } + if (!data.error && data !== 'false' && data) { + clearInterval(this.interval) + localStorage.removeItem(this.publicizeAddress) + localStorage.setItem(this.publicizeAddress, 'true') + } - } catch (error) { - } - stop = false - } - }; - this.interval = setInterval(checkPublicKey, 5000); - } - - firstUpdated() { - this.getNodeInfo() - this.getCoreInfo() - try { - this.confirmPublicKeyOnChain(store.getState().app.selectedAddress.address) - } catch (error) { - console.error(error) - } - - - setInterval(() => { - this.getNodeInfo() - this.getCoreInfo() - }, 30000) + } catch (error) { + } + stop = false + } + } + this.interval = setInterval(checkPublicKey, 5000); } async getNodeInfo() { diff --git a/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js b/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js index 01cf544f..c24b550f 100644 --- a/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js +++ b/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js @@ -4686,10 +4686,10 @@ class MultiWallet extends LitElement { } }) const txsQort = await parentEpml.request('apiCall', { - url: `/transactions/search?address=${this.wallets.get('qort').wallet.address}&confirmationStatus=CONFIRMED&reverse=true&txType=PAYMENT&txType=REGISTER_NAME&txType=UPDATE_NAME&txType=SELL_NAME&txType=CANCEL_SELL_NAME&txType=BUY_NAME&txType=CREATE_POLL&txType=VOTE_ON_POLL&txType=ISSUE_ASSET&txType=TRANSFER_ASSET&txType=CREATE_ASSET_ORDER&txType=CANCEL_ASSET_ORDER&txType=MULTI_PAYMENT&txType=DEPLOY_AT&txType=MESSAGE&txType=PUBLICIZE&txType=AIRDROP&txType=AT&txType=CREATE_GROUP&txType=UPDATE_GROUP&txType=ADD_GROUP_ADMIN&txType=REMOVE_GROUP_ADMIN&txType=GROUP_BAN&txType=CANCEL_GROUP_BAN&txType=GROUP_KICK&txType=GROUP_INVITE&txType=CANCEL_GROUP_INVITE&txType=JOIN_GROUP&txType=LEAVE_GROUP&txType=GROUP_APPROVAL&txType=SET_GROUP&txType=UPDATE_ASSET&txType=ACCOUNT_FLAGS&txType=ENABLE_FORGING&txType=REWARD_SHARE&txType=ACCOUNT_LEVEL&txType=TRANSFER_PRIVS&txType=PRESENCE`, + url: `/transactions/search?address=${this.wallets.get('qort').wallet.address}&confirmationStatus=CONFIRMED&reverse=true&txType=PAYMENT&txType=REGISTER_NAME&txType=UPDATE_NAME&txType=SELL_NAME&txType=CANCEL_SELL_NAME&txType=BUY_NAME&txType=CREATE_POLL&txType=VOTE_ON_POLL&txType=ISSUE_ASSET&txType=TRANSFER_ASSET&txType=CREATE_ASSET_ORDER&txType=CANCEL_ASSET_ORDER&txType=MULTI_PAYMENT&txType=DEPLOY_AT&txType=MESSAGE&txType=AIRDROP&txType=AT&txType=CREATE_GROUP&txType=UPDATE_GROUP&txType=ADD_GROUP_ADMIN&txType=REMOVE_GROUP_ADMIN&txType=GROUP_BAN&txType=CANCEL_GROUP_BAN&txType=GROUP_KICK&txType=GROUP_INVITE&txType=CANCEL_GROUP_INVITE&txType=JOIN_GROUP&txType=LEAVE_GROUP&txType=GROUP_APPROVAL&txType=SET_GROUP&txType=UPDATE_ASSET&txType=ACCOUNT_FLAGS&txType=ENABLE_FORGING&txType=REWARD_SHARE&txType=ACCOUNT_LEVEL&txType=TRANSFER_PRIVS&txType=PRESENCE`, }) const pendingTxsQort = await parentEpml.request('apiCall', { - url: `/transactions/unconfirmed?creator=${this.wallets.get('qort').wallet.base58PublicKey}&reverse=true&txType=PAYMENT&txType=REGISTER_NAME&txType=UPDATE_NAME&txType=SELL_NAME&txType=CANCEL_SELL_NAME&txType=BUY_NAME&txType=CREATE_POLL&txType=VOTE_ON_POLL&txType=ISSUE_ASSET&txType=TRANSFER_ASSET&txType=CREATE_ASSET_ORDER&txType=CANCEL_ASSET_ORDER&txType=MULTI_PAYMENT&txType=DEPLOY_AT&txType=MESSAGE&txType=PUBLICIZE&txType=AIRDROP&txType=AT&txType=CREATE_GROUP&txType=UPDATE_GROUP&txType=ADD_GROUP_ADMIN&txType=REMOVE_GROUP_ADMIN&txType=GROUP_BAN&txType=CANCEL_GROUP_BAN&txType=GROUP_KICK&txType=GROUP_INVITE&txType=CANCEL_GROUP_INVITE&txType=JOIN_GROUP&txType=LEAVE_GROUP&txType=GROUP_APPROVAL&txType=SET_GROUP&txType=UPDATE_ASSET&txType=ACCOUNT_FLAGS&txType=ENABLE_FORGING&txType=REWARD_SHARE&txType=ACCOUNT_LEVEL&txType=TRANSFER_PRIVS&txType=PRESENCE`, + url: `/transactions/unconfirmed?creator=${this.wallets.get('qort').wallet.base58PublicKey}&reverse=true&txType=PAYMENT&txType=REGISTER_NAME&txType=UPDATE_NAME&txType=SELL_NAME&txType=CANCEL_SELL_NAME&txType=BUY_NAME&txType=CREATE_POLL&txType=VOTE_ON_POLL&txType=ISSUE_ASSET&txType=TRANSFER_ASSET&txType=CREATE_ASSET_ORDER&txType=CANCEL_ASSET_ORDER&txType=MULTI_PAYMENT&txType=DEPLOY_AT&txType=MESSAGE&txType=AIRDROP&txType=AT&txType=CREATE_GROUP&txType=UPDATE_GROUP&txType=ADD_GROUP_ADMIN&txType=REMOVE_GROUP_ADMIN&txType=GROUP_BAN&txType=CANCEL_GROUP_BAN&txType=GROUP_KICK&txType=GROUP_INVITE&txType=CANCEL_GROUP_INVITE&txType=JOIN_GROUP&txType=LEAVE_GROUP&txType=GROUP_APPROVAL&txType=SET_GROUP&txType=UPDATE_ASSET&txType=ACCOUNT_FLAGS&txType=ENABLE_FORGING&txType=REWARD_SHARE&txType=ACCOUNT_LEVEL&txType=TRANSFER_PRIVS&txType=PRESENCE`, }) if (this._selectedWallet == coin) { this.wallets.get(coin).transactions = pendingTxsQort.concat(txsQort) From 11e4a7f3e2d467660d5a55a57ad271299834c396 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Tue, 14 Feb 2023 16:29:30 +0100 Subject: [PATCH 27/32] Add sorting and search field --- .../name-registration.src.js | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js index 1e3e0289..d8906e93 100644 --- a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js +++ b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js @@ -19,6 +19,8 @@ import '@vaadin/button' import '@vaadin/icon' import '@vaadin/icons' import '@vaadin/grid' +import '@vaadin/grid/vaadin-grid-filter-column.js' +import '@vaadin/grid/vaadin-grid-sort-column.js' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) @@ -30,6 +32,7 @@ class NameRegistration extends LitElement { loading: { type: Boolean }, names: { type: Array }, marketSellNames: { type: Array }, + filteredItems: { type: Array }, recipientPublicKey: { type: String }, selectedAddress: { type: Object }, btnDisable: { type: Boolean }, @@ -189,6 +192,7 @@ class NameRegistration extends LitElement { this.selectedAddress = {} this.names = [] this.marketSellNames = [] + this.filteredItems = [] this.recipientPublicKey = '' this.btnDisable = false this.isLoading = false @@ -241,10 +245,24 @@ class NameRegistration extends LitElement {

    ${translate("registernamepage.nchange22")}

    - + + +
    + - + { if (data.item.owner === this.selectedAddress.address) { render(html`${this.renderCancelSellNameButton(data.item)}`, root) @@ -523,8 +541,9 @@ class NameRegistration extends LitElement { url: `/names/forsale?limit=0&reverse=true` }).then(res => { this.marketSellNames = res + this.filteredItems = this.marketSellNames }) - setTimeout(fetchMarketSellNames, 60000) + setTimeout(fetchMarketSellNames, 120000) } window.addEventListener("contextmenu", (event) => { From c3b00f2b7d48803e26a8447e00a33a9f00169238 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Wed, 15 Feb 2023 16:08:33 +0100 Subject: [PATCH 28/32] Add pagination --- .../name-registration.src.js | 161 ++++++++++++++++-- 1 file changed, 151 insertions(+), 10 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js index d8906e93..1157a73a 100644 --- a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js +++ b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js @@ -19,8 +19,7 @@ import '@vaadin/button' import '@vaadin/icon' import '@vaadin/icons' import '@vaadin/grid' -import '@vaadin/grid/vaadin-grid-filter-column.js' -import '@vaadin/grid/vaadin-grid-sort-column.js' +import '@vaadin/text-field' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) @@ -33,6 +32,7 @@ class NameRegistration extends LitElement { names: { type: Array }, marketSellNames: { type: Array }, filteredItems: { type: Array }, + searchSellNames: { type: Array }, recipientPublicKey: { type: String }, selectedAddress: { type: Object }, btnDisable: { type: Boolean }, @@ -130,6 +130,43 @@ class NameRegistration extends LitElement { text-align: right; } + #pages { + display: flex; + flex-wrap: wrap; + padding: 10px 5px 5px 5px; + margin: 0px 20px 20px 20px; + } + + #pages > button { + user-select: none; + padding: 5px; + margin: 0 5px; + border-radius: 10%; + border: 0; + background: transparent; + font: inherit; + outline: none; + cursor: pointer; + color: var(--black); + } + + #pages > button:not([disabled]):hover, + #pages > button:focus { + color: #ccc; + background-color: #eee; + } + + #pages > button[selected] { + font-weight: bold; + color: var(--white); + background-color: #ccc; + } + + #pages > button[disabled] { + opacity: 0.5; + cursor: default; + } + .card-container { background-color: var(--white); border-radius: 5px; @@ -193,6 +230,7 @@ class NameRegistration extends LitElement { this.names = [] this.marketSellNames = [] this.filteredItems = [] + this.searchSellNames = [] this.recipientPublicKey = '' this.btnDisable = false this.isLoading = false @@ -250,19 +288,24 @@ class NameRegistration extends LitElement { style="width: 25%; margin-bottom: 20px;" clear-button-visible @value-changed="${(e) => { - this.filteredItems = [] + this.searchSellNames = [] const searchTerm = (e.target.value || '').trim() - const keys = ['name', 'owner'] + const keys = ['name', 'owner', 'salePrice'] const filtered = this.marketSellNames.filter((search) => keys.some((key) => search[key].toLowerCase().includes(searchTerm.toLowerCase()))) - this.filteredItems = filtered + if (!e.target.value) { + this.updatePageSize() + } else { + this.searchSellNames = filtered + this.updatePageSizeSearch() + } }}" >
    - + - + { if (data.item.owner === this.selectedAddress.address) { render(html`${this.renderCancelSellNameButton(data.item)}`, root) @@ -271,6 +314,7 @@ class NameRegistration extends LitElement { } }}> +
    ${this.isEmptyArray(this.marketSellNames) ? html` ${translate("registernamepage.nchange24")} `: ''} @@ -541,9 +585,9 @@ class NameRegistration extends LitElement { url: `/names/forsale?limit=0&reverse=true` }).then(res => { this.marketSellNames = res - this.filteredItems = this.marketSellNames }) - setTimeout(fetchMarketSellNames, 120000) + this.updatePageSize() + setTimeout(fetchMarketSellNames, 180000) } window.addEventListener("contextmenu", (event) => { @@ -622,6 +666,103 @@ class NameRegistration extends LitElement { } } + async updatePageSize() { + this.filteredItems = [] + this.marketSellNames.sort((a, b) => parseFloat(a.salePrice) - parseFloat(b.salePrice)) + this.filteredItems = this.marketSellNames + await this.setPages() + await this.updateItemsFromPage(1, true) + } + + async setPages() { + this.namesGrid = this.shadowRoot.querySelector(`#marketSellNames`) + this.pagesControl = this.shadowRoot.querySelector('#pages') + this.pages = undefined + } + + async updatePageSizeSearch() { + this.filteredItems = [] + this.searchSellNames.sort((a, b) => parseFloat(a.salePrice) - parseFloat(b.salePrice)) + this.filteredItems = this.searchSellNames + await this.setPagesSearch() + await this.updateItemsFromPage(1, true) + } + + async setPagesSearch() { + this.namesGrid = this.shadowRoot.querySelector(`#marketSellNames`) + this.pagesControl = this.shadowRoot.querySelector('#pages') + this.pages = undefined + } + + async updateItemsFromPage(page, changeNames = false) { + if (page === undefined) { + return + } + + changeNames === true ? (this.pagesControl.innerHTML = '') : null + + if (!this.pages) { + this.pages = Array.apply(null, { length: Math.ceil(this.filteredItems.length / this.namesGrid.pageSize) }).map((item, index) => { + return index + 1 + }) + + const prevBtn = document.createElement('button') + prevBtn.textContent = '<' + prevBtn.addEventListener('click', () => { + const selectedPage = parseInt(this.pagesControl.querySelector('[selected]').textContent) + this.updateItemsFromPage(selectedPage - 1) + }) + this.pagesControl.appendChild(prevBtn) + + this.pages.forEach((pageNumber) => { + const pageBtn = document.createElement('button') + pageBtn.textContent = pageNumber + pageBtn.addEventListener('click', (e) => { + this.updateItemsFromPage(parseInt(e.target.textContent)) + }) + if (pageNumber === page) { + pageBtn.setAttribute('selected', true) + } + this.pagesControl.appendChild(pageBtn) + }) + + const nextBtn = window.document.createElement('button') + nextBtn.textContent = '>' + nextBtn.addEventListener('click', () => { + const selectedPage = parseInt(this.pagesControl.querySelector('[selected]').textContent) + this.updateItemsFromPage(selectedPage + 1) + }) + this.pagesControl.appendChild(nextBtn) + } + + const buttons = Array.from(this.pagesControl.children) + buttons.forEach((btn, index) => { + if (parseInt(btn.textContent) === page) { + btn.setAttribute('selected', true) + } else { + btn.removeAttribute('selected') + } + if (index === 0) { + if (page === 1) { + btn.setAttribute('disabled', '') + } else { + btn.removeAttribute('disabled') + } + } + if (index === buttons.length - 1) { + if (page === this.pages.length) { + btn.setAttribute('disabled', '') + } else { + btn.removeAttribute('disabled') + } + } + }) + let start = (page - 1) * this.namesGrid.pageSize + let end = page * this.namesGrid.pageSize + + this.namesGrid.items = this.filteredItems.slice(start, end) + } + async updateQortWalletBalance() { let qortAddress = window.parent.reduxStore.getState().app.selectedAddress.address @@ -1202,4 +1343,4 @@ class NameRegistration extends LitElement { } } -window.customElements.define('name-registration', NameRegistration) +window.customElements.define('name-registration', NameRegistration) \ No newline at end of file From b8d5f5784d46933c70d9098112aefe3e590d96ac Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Wed, 15 Feb 2023 17:17:16 +0100 Subject: [PATCH 29/32] Update to nedoe 18 , electron 23 --- README.md | 4 ++-- package.json | 6 +++--- qortal-ui-core/package.json | 12 ++++++------ qortal-ui-crypto/package.json | 6 +++--- qortal-ui-plugins/package.json | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9217fc03..3fcec3b0 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,9 @@ Easiest way to install the lastest required packages on Linux is via nvm. ``` source ~/.profile ``` (For Debian based distro)
    ``` source ~/.bashrc ``` (For Fedora / CentOS)
    ``` nvm ls-remote ``` (Fetch list of available versions)
    -``` nvm install v16.17.1 ``` (LTS: Gallium supported by Electron)
    +``` nvm install v18.12.1 ``` (LTS: Hydrogen supported by Electron)
    ``` npm --location=global install yarn@1.22.19 ```
    -``` npm --location=global install npm@9.3.1 ```
    +``` npm --location=global install npm@9.4.2 ```
    On BSD do a ``` pkg_add node followed by npm install -g yarn ``` diff --git a/package.json b/package.json index f39b57db..3160aea9 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ "os-locale": "3.0.0" }, "devDependencies": { - "electron": "22.2.1", + "electron": "23.0.0", "electron-builder": "23.6.0", "electron-packager": "17.1.1", "shelljs": "0.8.5" }, "engines": { - "node": ">=16.17.1" + "node": ">=18.12.1" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/package.json b/qortal-ui-core/package.json index bccf855a..3ebc2750 100644 --- a/qortal-ui-core/package.json +++ b/qortal-ui-core/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-core", - "version": "3.0.0", + "version": "3.1.0", "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet", "keywords": [ "QORT", @@ -17,9 +17,9 @@ "author": "QORTAL ", "license": "GPL-3.0", "dependencies": { - "@hapi/hapi": "21.2.1", - "@hapi/inert": "7.0.0", - "sass": "1.58.0" + "@hapi/hapi": "21.3.0", + "@hapi/inert": "7.0.1", + "sass": "1.58.1" }, "devDependencies": { "@babel/core": "7.20.12", @@ -81,6 +81,6 @@ "rollup-plugin-web-worker-loader": "1.6.1" }, "engines": { - "node": ">=16.17.1" + "node": ">=18.12.1" } -} +} \ No newline at end of file diff --git a/qortal-ui-crypto/package.json b/qortal-ui-crypto/package.json index 8495143d..cae726c6 100644 --- a/qortal-ui-crypto/package.json +++ b/qortal-ui-crypto/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-crypto", - "version": "3.0.0", + "version": "3.1.0", "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet", "keywords": [ "QORT", @@ -23,6 +23,6 @@ "lodash": "4.17.21" }, "engines": { - "node": ">=16.17.1" + "node": ">=18.12.1" } -} +} \ No newline at end of file diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index 421b5d69..759a24e6 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-plugins", - "version": "3.0.0", + "version": "3.1.0", "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet", "keywords": [ "QORT", @@ -36,7 +36,7 @@ "prosemirror-dropcursor": "1.7.0", "prosemirror-gapcursor": "1.3.1", "prosemirror-history": "1.3.0", - "prosemirror-keymap": "1.2.0", + "prosemirror-keymap": "1.2.1", "prosemirror-model": "1.19.0", "prosemirror-schema-list": "1.2.2", "prosemirror-state": "1.4.2", @@ -76,6 +76,7 @@ "@vaadin/grid": "23.3.7", "@vaadin/icons": "23.3.7", "@vaadin/tooltip": "23.3.7", + "axios": "1.3.3", "epml": "0.3.3", "file-saver": "2.0.5", "highcharts": "10.3.3", @@ -86,11 +87,10 @@ "rollup": "3.15.0", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", - "rollup-plugin-web-worker-loader": "1.6.1", - "axios": "1.3.2" + "rollup-plugin-web-worker-loader": "1.6.1" }, "engines": { - "node": ">=16.17.1" + "node": ">=18.12.1" } -} +} \ No newline at end of file From 5a34bf8512fe81654143c39076ecc2fbaefea5b0 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:04:03 +0100 Subject: [PATCH 30/32] Add update name transaction --- .../names/UpdateNameTransaction.js | 73 +++++++++++++++++++ .../api/transactions/transactions.js | 2 + 2 files changed, 75 insertions(+) create mode 100644 qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js diff --git a/qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js b/qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js new file mode 100644 index 00000000..878d4f35 --- /dev/null +++ b/qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js @@ -0,0 +1,73 @@ +'use strict' +import TransactionBase from '../TransactionBase.js' +import { QORT_DECIMALS } from '../../constants.js' + +export default class UpdateNameTransaction extends TransactionBase { + constructor() { + super() + this.type = 4 + } + + render(html) { + return html` + ${this._dialogUpdateName1} +
    + ${this.nameText} +
    + ${this._dialogUpdateName2} +
    + ${this.newNameText} +
    + ${this._dialogUpdateName3} + ` + } + + set dialogUpdateName1(dialogUpdateName1) { + this._dialogUpdateName1 = dialogUpdateName1 + } + + set dialogUpdateName1(dialogUpdateName1) { + this._dialogUpdateName1 = dialogUpdateName1 + } + + set dialogUpdateName2(dialogUpdateName2) { + this._dialogUpdateName2 = dialogUpdateName2 + } + + set fee(fee) { + this._fee = fee * QORT_DECIMALS + this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) + } + + set name(name) { + this.nameText = name + this._nameBytes = this.constructor.utils.stringtoUTF8Array(name) + this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) + } + + set newName(mewName) { + this.newNameText = newName + this._newNameBytes = this.constructor.utils.stringtoUTF8Array(mewName) + this._newNameLength = this.constructor.utils.int32ToBytes(this._newNameBytes.length) + } + + set value(value) { + this.valueText = value.length === 0 ? "Registered Name on the Qortal Chain" : value + this._valueBytes = this.constructor.utils.stringtoUTF8Array(this.valueText) + this._valueLength = this.constructor.utils.int32ToBytes(this._valueBytes.length) + } + + get params() { + const params = super.params + params.push( + this._nameLength, + this._nameBytes, + this._newNameLength, + this._newNameBytes, + this._valueLength, + this._valueBytes, + this._feeBytes + ) + return params + } +} diff --git a/qortal-ui-crypto/api/transactions/transactions.js b/qortal-ui-crypto/api/transactions/transactions.js index 16b7b09b..9fa87e99 100644 --- a/qortal-ui-crypto/api/transactions/transactions.js +++ b/qortal-ui-crypto/api/transactions/transactions.js @@ -1,5 +1,6 @@ import PaymentTransaction from './PaymentTransaction.js' import RegisterNameTransaction from './names/RegisterNameTransaction.js' +import UpdateNameTransaction from './names/UpdateNameTransaction.js' import SellNameTransacion from './names/SellNameTransacion.js' import CancelSellNameTransacion from './names/CancelSellNameTransacion.js' import BuyNameTransacion from './names/BuyNameTransacion.js' @@ -25,6 +26,7 @@ import TransferPrivsTransaction from './TransferPrivsTransaction.js' export const transactionTypes = { 2: PaymentTransaction, 3: RegisterNameTransaction, + 4: UpdateNameTransaction, 5: SellNameTransacion, 6: CancelSellNameTransacion, 7: BuyNameTransacion, From 99e812a1e2e598c70dc563ae644bc938da94fa81 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Thu, 16 Feb 2023 20:11:51 +0100 Subject: [PATCH 31/32] Fix transaction --- .../names/UpdateNameTransaction.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js b/qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js index 878d4f35..bfc31563 100644 --- a/qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js +++ b/qortal-ui-crypto/api/transactions/names/UpdateNameTransaction.js @@ -26,14 +26,14 @@ export default class UpdateNameTransaction extends TransactionBase { this._dialogUpdateName1 = dialogUpdateName1 } - set dialogUpdateName1(dialogUpdateName1) { - this._dialogUpdateName1 = dialogUpdateName1 - } - set dialogUpdateName2(dialogUpdateName2) { this._dialogUpdateName2 = dialogUpdateName2 } + set dialogUpdateName3(dialogUpdateName3) { + this._dialogUpdateName3 = dialogUpdateName3 + } + set fee(fee) { this._fee = fee * QORT_DECIMALS this._feeBytes = this.constructor.utils.int64ToBytes(this._fee) @@ -45,16 +45,16 @@ export default class UpdateNameTransaction extends TransactionBase { this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length) } - set newName(mewName) { + set newName(newName) { this.newNameText = newName - this._newNameBytes = this.constructor.utils.stringtoUTF8Array(mewName) + this._newNameBytes = this.constructor.utils.stringtoUTF8Array(newName) this._newNameLength = this.constructor.utils.int32ToBytes(this._newNameBytes.length) } - set value(value) { - this.valueText = value.length === 0 ? "Registered Name on the Qortal Chain" : value - this._valueBytes = this.constructor.utils.stringtoUTF8Array(this.valueText) - this._valueLength = this.constructor.utils.int32ToBytes(this._valueBytes.length) + set newData(newData) { + this.newDataText = newData.length === 0 ? "Registered Name on the Qortal Chain" : newData + this._newDataBytes = this.constructor.utils.stringtoUTF8Array(this.newDataText) + this._newDataLength = this.constructor.utils.int32ToBytes(this._newDataBytes.length) } get params() { @@ -64,10 +64,10 @@ export default class UpdateNameTransaction extends TransactionBase { this._nameBytes, this._newNameLength, this._newNameBytes, - this._valueLength, - this._valueBytes, + this._newDataLength, + this._newDataBytes, this._feeBytes ) return params } -} +} \ No newline at end of file From 1b59cd3dbc9fdbe174b7be57c18c7c321c2a24d2 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Thu, 16 Feb 2023 20:41:03 +0100 Subject: [PATCH 32/32] Update Translations --- qortal-ui-core/language/de.json | 16 +++++++++++++--- qortal-ui-core/language/es.json | 16 +++++++++++++--- qortal-ui-core/language/fr.json | 16 +++++++++++++--- qortal-ui-core/language/hindi.json | 16 +++++++++++++--- qortal-ui-core/language/hr.json | 16 +++++++++++++--- qortal-ui-core/language/hu.json | 16 +++++++++++++--- qortal-ui-core/language/it.json | 16 +++++++++++++--- qortal-ui-core/language/ko.json | 16 +++++++++++++--- qortal-ui-core/language/no.json | 16 +++++++++++++--- qortal-ui-core/language/pl.json | 16 +++++++++++++--- qortal-ui-core/language/pt.json | 16 +++++++++++++--- qortal-ui-core/language/ro.json | 16 +++++++++++++--- qortal-ui-core/language/rs.json | 16 +++++++++++++--- qortal-ui-core/language/ru.json | 16 +++++++++++++--- qortal-ui-core/language/us.json | 16 +++++++++++++--- qortal-ui-core/language/zhc.json | 16 +++++++++++++--- qortal-ui-core/language/zht.json | 16 +++++++++++++--- 17 files changed, 221 insertions(+), 51 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index c0421968..21e9f57f 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -36,7 +36,10 @@ "puzzles": "RÄTSEL", "nodemanagement": "KNOTENVERWALTUNG", "trading": "HANDELN", - "groups": "GRUPPEN" + "groups": "GRUPPEN", + "sm1": "NAMEN", + "sm2": "NAMENSVERWALTUNG", + "sm3": "NAMENSMARKT" }, "login": { "login": "Einloggen", @@ -383,7 +386,7 @@ "nchange19": "Name Verkaufen", "nchange20": "Verkauf abbrechen", "nchange21": "Name kaufen", - "nchange22": "Offenstehende Namen zum Verkauf", + "nchange22": "Namensmarkt", "nchange23": "Verkaufspreis", "nchange24": "Keine Namen zu verkaufen", "nchange25": "Name zu verkaufen", @@ -401,7 +404,14 @@ "nchange37": "ACHTUNG!", "nchange38": "Du hast nicht genug Qort, um diesen Namen zu kaufen.", "nchange39": "Sind Sie sicher, diesen Namen zu kaufen?", - "nchange40": "Wenn Sie auf Bestätigen drücken, wird die Anfrage zum Kauf des Namens gesendet!" + "nchange40": "Wenn Sie auf Bestätigen drücken, wird die Anfrage zum Kauf des Namens gesendet!", + "nchange41": "Alter Name", + "nchange42": "Neuer Name", + "nchange43": "Möchten Sie diesen Namen wirklich ändern?", + "nchange44": "Zum neuen Namen", + "nchange45": "Beim Bestätigen wird die Anfrage zur Namensaktualisierung gesendet!", + "nchange46": "Namensverkaufshistorie", + "nchange47": "Namensaktualisierung erfolgreich!" }, "websitespage": { "schange1": "Webseiten durchsuchen", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index 8cc4177f..86e98739 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -36,7 +36,10 @@ "puzzles": "ROMPECABEZAS", "nodemanagement": "GESTIÓN DE NODO", "trading": "COMERCIO", - "groups": "GRUPOS" + "groups": "GRUPOS", + "sm1": "NOMBRES", + "sm2": "GESTIÓN DE NOMBRES", + "sm3": "MERCADO DE NOMBRES" }, "login": { "login": "Iniciar sesión", @@ -383,7 +386,7 @@ "nchange19": "Nombre de venta", "nchange20": "Cancelar venta", "nchange21": "Nombre de compra", - "nchange22": "Nombres de mercado abierto para vender", + "nchange22": "Mercado de nombres", "nchange23": "Precio de venta", "nchange24": "No hay nombres para vender", "nchange25": "Nombre para vender", @@ -401,7 +404,14 @@ "nchange37": "¡ATENCIÓN!", "nchange38": "No tienes suficiente qort para comprar este nombre.", "nchange39": "¿Estás seguro de comprar este nombre?", - "nchange40": "¡Al presionar confirmar, se enviará la solicitud de compra de nombre!" + "nchange40": "¡Al presionar confirmar, se enviará la solicitud de compra de nombre!", + "nchange41": "Nombre anterior", + "nchange42": "Nuevo Nombre", + "nchange43": "¿Estás seguro de cambiar este nombre?", + "nchange44": "Al nuevo nombre", + "nchange45": "¡Al presionar confirmar, se enviará la solicitud de actualización de nombre!", + "nchange46": "Historial de venta de nombres", + "nchange47": "¡Actualización de nombre exitosa!" }, "websitespage": { "schange1": "Navegar sitios web", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 6a774c6f..bfd5bbe1 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -36,7 +36,10 @@ "puzzles": "PUZZLES", "nodemanagement": "GESTION DE NOEUDS", "trading": "COMMERCE", - "groups": "GROUPES" + "groups": "GROUPES", + "sm1": "NOMS", + "sm2": "GESTION DES NOM", + "sm3": "MARCHÉ DES NOMS" }, "login": { "login": "Connexion", @@ -383,7 +386,7 @@ "nchange19": "Nom de vente", "nchange20": "Annuler la vente", "nchange21": "Acheter le nom", - "nchange22": "Noms du marché ouvert à vendre", + "nchange22": "Marché des noms", "nchange23": "Prix de vente", "nchange24": "Aucun nom à vendre", "nchange25": "Nom à vendre", @@ -401,7 +404,14 @@ "nchange37": "ATTENTION !", "nchange38": "Vous n'avez pas assez de qort pour acheter ce nom.", "nchange39": "Êtes-vous sûr d'acheter ce nom ?", - "nchange40": "En appuyant sur confirmer, la demande d'achat de nom sera envoyée !" + "nchange40": "En appuyant sur confirmer, la demande d'achat de nom sera envoyée !", + "nchange41": "Ancien nom", + "nchange42": "Nouveau nom", + "nchange43": "Êtes-vous sûr de vouloir changer ce nom ?", + "nchange44": "Au nouveau nom", + "nchange45": "En appuyant sur confirmer, la demande de mise à jour du nom sera envoyée !", + "nchange46": "Historique des ventes de noms", + "nchange47": "Mise à jour du nom réussie !" }, "websitespage": { "schange1": "Parcourir les sites Web", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index ee85ecb9..b5248901 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -37,7 +37,10 @@ "puzzles": "पहेलि", "nodemanagement": "नोड प्रबंधन", "trading": "व्यापार", - "groups": "समूह" + "groups": "समूह", + "sm1": "नाम", + "sm2": "नाम प्रबंधन", + "sm3": "नाम बाजार" }, "login": { "login": "लॉग इन करें", @@ -384,7 +387,7 @@ "nchange19": "बेचने का नाम", "nchange20": "बेचना रद्द करें", "nchange21": "खरीदें नाम", - "nchange22": "बेचने के लिए खुले बाज़ार के नाम", + "nchange22": "नाम बाजार", "nchange23": "मूल्य बेचें", "nchange24": "बिक्री के लिए कोई नाम नहीं", "nchange25": "बेचने के लिए नाम", @@ -402,7 +405,14 @@ "nchange37": "ध्यान!", "nchange38": "आपके पास इस नाम को खरीदने के लिए पर्याप्त संसाधन नहीं हैं।", "nchange39": "क्या आप वाकई इस नाम को खरीदना चाहते हैं?", - "nchange40": "कन्फर्म दबाने पर, बाय नेम रिक्वेस्ट भेजी जाएगी!" + "nchange40": "कन्फर्म दबाने पर, बाय नेम रिक्वेस्ट भेजी जाएगी!", + "nchange41": "पुराना नाम", + "nchange42": "नया नाम", + "nchange43": "क्या आप वाकई इस नाम को बदलना चाहते हैं?", + "nchange44": "नए नाम से", + "nchange45": "पुष्टि करें दबाने पर, नाम अद्यतन अनुरोध भेजा जाएगा!", + "nchange46": "नाम बिक्री इतिहास", + "nchange47": "नाम अद्यतन सफल!" }, "websitespage": { "schange1": "वेबसाइट ब्राउज़ करें", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index be605ccd..dfaa535f 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -36,7 +36,10 @@ "puzzles": "ZAGONETKE", "nodemanagement": "UPRAVLJANJE ČVOROVIMA", "trading": "TRGOVANJE", - "groups": "GRUPE" + "groups": "GRUPE", + "sm1": "IMENA", + "sm2": "UPRAVLJANJE IMENIMA", + "sm3": "TRŽIŠTE IMENA" }, "login": { "login": "Prijavi se", @@ -383,7 +386,7 @@ "nchange19": "Ime prodaje", "nchange20": "Otkaži prodaju", "nchange21": "Ime kupovine", - "nchange22": "Imena otvorenog tržišta za prodaju", + "nchange22": "Tržište imena", "nchange23": "Prodajna cijena", "nchange24": "Nema imena za prodaju", "nchange25": "Ime za prodaju", @@ -401,7 +404,14 @@ "nchange37": "PAŽNJA!", "nchange38": "Nemate dovoljno qort da kupite ovo ime.", "nchange39": "Jeste li sigurni da želite kupiti ovo ime?", - "nchange40": "Pritiskom na potvrdu, zahtjev za kupnju imena bit će poslan!" + "nchange40": "Pritiskom na potvrdu, zahtjev za kupnju imena bit će poslan!", + "nchange41": "Staro ime", + "nchange42": "Novo ime", + "nchange43": "Jeste li sigurni da želite promijeniti ovo ime?", + "nchange44": "Na novo ime", + "nchange45": "Kada pritisnete potvrdu, bit će poslan zahtjev za ažuriranje imena!", + "nchange46": "Povijest prodaje imena", + "nchage47": "Ažuriranje imena uspješno!" }, "websitespage": { "schange1": "Pregledavanje web stranica", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 84898ba0..92c39979 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -36,7 +36,10 @@ "puzzles": "REJTVÉNYEK", "nodemanagement": "CSOMÓPONTKEZELÉS", "trading": "KERESKEDÉS", - "groups": "CSOPORTOK" + "groups": "CSOPORTOK", + "sm1": "NAMES", + "sm2": "NÉVKEZELÉS", + "sm3": "NAMES PIAC" }, "login": { "login": "Bejelentkezés", @@ -383,7 +386,7 @@ "nchange19": "Eladási név", "nchange20": "Eladás törlése", "nchange21": "Vásárlási név", - "nchange22": "Eladó nyílt piaci nevek", + "nchange22": "Nevek Piac", "nchange23": "Eladási ár", "nchange24": "Nincsenek eladható nevek", "nchange25": "Eladó név", @@ -401,7 +404,14 @@ "nchange37": "FIGYELEM!", "nchange38": "Nincs elég qortja ennek a névnek a megvásárlásához.", "nchange39": "Biztosan megveszi ezt a nevet?", - "nchange40": "A megerősítés megnyomására a vásárlási névkérelem elküldésre kerül!" + "nchange40": "A megerősítés megnyomására a vásárlási névkérelem elküldésre kerül!", + "nchange41": "Régi név", + "nchange42": "Új név", + "nchange43": "Biztosan megváltoztatja ezt a nevet?", + "nchange44": "Az új névre", + "nchange45": "A megerősítés megnyomására a névfrissítési kérés elküldésre kerül!", + "nchange46": "Név eladási előzmények", + "nchange47": "A névfrissítés sikeres!" }, "websitespage": { "schange1": "Webhelyek Böngészése", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index e3fc1a17..cbe9baf7 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -36,7 +36,10 @@ "puzzles": "PUZZLES", "nodemanagement": "GESTIONE DEI NODI", "trading": "COMMERCIO", - "groups": "GRUPPI" + "groups": "GRUPPI", + "sm1": "NOMI", + "sm2": "GESTIONE NOMI", + "sm3": "MERCATO DEI NOMI" }, "login": { "login": "Login", @@ -383,7 +386,7 @@ "nchange19": "Vendi nome", "nchange20": "Annulla vendita", "nchange21": "Acquista nome", - "nchange22": "Nomi di mercato aperto da vendere", + "nchange22": "Mercato dei nomi", "nchange23": "Prezzo di vendita", "nchange24": "Nessun nome da vendere", "nchange25": "Nome da vendere", @@ -401,7 +404,14 @@ "nchange37": "ATTENZIONE!", "nchange38": "Non hai abbastanza qort per acquistare questo nome.", "nchange39": "Sei sicuro di acquistare questo nome?", - "nchange40": "Premendo conferma, verrà inviata la richiesta di acquisto del nome!" + "nchange40": "Premendo conferma, verrà inviata la richiesta di acquisto del nome!", + "nchange41": "Vecchio nome", + "nchange42": "Nuovo nome", + "nchange43": "Sei sicuro di voler cambiare questo nome?", + "nchange44": "Al nuovo nome", + "nchange45": "Premendo conferma, verrà inviata la richiesta di aggiornamento del nome!", + "nchange46": "Nome Cronologia vendite", + "nchange47": "Aggiornamento nome riuscito!" }, "websitespage": { "schange1": "Sfoglia siti Web", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index e4b607c1..be7c9b4c 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -36,7 +36,10 @@ "puzzles": "퍼즐", "nodemanagement": "노드 관리", "trading": "무역", - "groups": "여러 떼" + "groups": "여러 떼", + "sm1": "이름", + "sm2": "이름 관리", + "sm3": "이름 시장" }, "login": { "login": "로그인", @@ -383,7 +386,7 @@ "nchange19": "판매 이름", "nchange20": "매도 취소", "nchange21": "구매 이름", - "nchange22": "판매할 공개 시장 이름", + "nchange22": "이름 시장", "nchange23": "판매 가격", "nchange24": "판매할 이름 없음", "nchange25": "판매할 이름", @@ -401,7 +404,14 @@ "nchange37": "주의!", "nchange38": "이 이름을 살 만큼 충분한 qort가 없습니다.", "nchange39": "이 이름을 구입하시겠습니까?", - "nchange40": "확인을 누르면 구매 이름 요청이 전송됩니다!" + "nchange40": "확인을 누르면 구매 이름 요청이 전송됩니다!", + "nchange41": "이전 이름", + "nchange42": "새 이름", + "nchange43": "이 이름을 변경하시겠습니까?", + "nchange44": "새 이름으로", + "nchange45": "확인을 누르면 이름 업데이트 요청이 전송됩니다!", + "nchange46": "네임 세일 내역", + "nchange47": "이름 업데이트 성공!" }, "websitespage": { "schange1": "웹 사이트 찾아보기", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index 66d07aab..dd21aec0 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -36,7 +36,10 @@ "puzzles": "PUZZLES", "nodemanagement": "NODEADMINISTRASJON", "trading": "HANDEL", - "groups": "GRUPPER" + "groups": "GRUPPER", + "sm1": "NAVN", + "sm2": "NAVNEBEHANDLING", + "sm3": "NAMES MARKET" }, "login": { "login": "Logg på", @@ -383,7 +386,7 @@ "nchange19": "Selgsnavn", "nchange20": "Avbryt salg", "nchange21": "Kjøp navn", - "nchange22": "Åpne markedsnavn å selge", + "nchange22": "Navnemarked", "nchange23": "Selgspris", "nchange24": "Ingen navn å selge", "nchange25": "Navn å selge", @@ -401,7 +404,14 @@ "nchange37": "OBS!", "nchange38": "Du har ikke nok qort til å kjøpe dette navnet.", "nchange39": "Er du sikker på å kjøpe dette navnet?", - "nchange40": "Når du trykker bekreft, vil forespørselen om kjøpsnavn bli sendt!" + "nchange40": "Når du trykker bekreft, vil forespørselen om kjøpsnavn bli sendt!", + "nchange41": "Gammelt navn", + "nchange42": "Nytt navn", + "nchange43": "Er du sikker på at du vil endre dette navnet?", + "nchange44": "Til det nye navnet", + "nchange45": "Når du trykker bekreft, vil forespørselen om navneoppdatering bli sendt!", + "nchange46": "Navnsalgshistorikk", + "nchange47": "Navneoppdateringen er vellykket!" }, "websitespage": { "schange1": "Bla gjennom nettsteder", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index 19fb9bf4..105fc61e 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -36,7 +36,10 @@ "puzzles": "PUZZLE", "nodemanagement": "ZARZĄDZANIE WĘZŁAMI", "trading": "HANDLOWY", - "groups": "GRUPY" + "groups": "GRUPY", + "sm1": "NAZWY", + "sm2": "ZARZĄDZANIE NAZWAMI", + "sm3": "RYNEK NAZW" }, "login": { "login": "Zaloguj się", @@ -383,7 +386,7 @@ "nchange19": "Nazwa sprzedaży", "nchange20": "Anuluj sprzedaż", "nchange21": "Kup nazwę", - "nchange22": "Otwórz nazwy rynków do sprzedaży", + "nchange22": "Rynek imion", "nchange23": "Cena sprzedaży", "nchange24": "Brak nazw do sprzedania", "nchange25": "Nazwa do sprzedania", @@ -401,7 +404,14 @@ "nchange37": "UWAGA!", "nchange38": "Nie masz wystarczającej ilości qort, aby kupić tę nazwę.", "nchange39": "Czy na pewno kupisz tę nazwę?", - "nchange40": "Po naciśnięciu potwierdzenia, prośba o nazwę kupna zostanie wysłana!" + "nchange40": "Po naciśnięciu potwierdzenia, prośba o nazwę kupna zostanie wysłana!", + "nchange41": "Stara nazwa", + "nchange42": "Nowa nazwa", + "nchange43": "Czy na pewno chcesz zmienić tę nazwę?", + "nchange44": "Do nowej nazwy", + "nchange45": "Po naciśnięciu potwierdzenia, prośba o aktualizację nazwy zostanie wysłana!", + "nchange46": "Historia sprzedaży nazw", + "nchange47": "Aktualizacja nazwy powiodła się!" }, "websitespage": { "schange1": "Przeglądaj strony internetowe", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index f4dd06c2..cd04871d 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -36,7 +36,10 @@ "puzzles": "ENIGMAS", "nodemanagement": "GERENCIAMENTO DO NÓDULO", "trading": "NEGOCIAÇÃO", - "groups": "GRUPOS" + "groups": "GRUPOS", + "sm1": "NOMES", + "sm2": "GESTÃO DE NOME", + "sm3": "MERCADO DE NOMES" }, "login": { "login": "Login", @@ -383,7 +386,7 @@ "nchange19": "Nome de venda", "nchange20": "Cancelar venda", "nchange21": "Nome da compra", - "nchange22": "Nomes de mercado aberto para vender", + "nchange22": "Mercado de nomes", "nchange23": "Preço de venda", "nchange24": "Sem nomes para vender", "nchange25": "Nome para vender", @@ -401,7 +404,14 @@ "nchange37": "ATENÇÃO!", "nchange38": "Você não tem qort suficiente para comprar este nome.", "nchange39": "Tem certeza que deseja comprar este nome ?", - "nchange40": "Ao pressionar confirmar, a solicitação do nome de compra será enviada!" + "nchange40": "Ao pressionar confirmar, a solicitação do nome de compra será enviada!", + "nchange41": "Nome antigo", + "nchange42": "Novo nome", + "nchange43": "Tem certeza que deseja alterar este nome?", + "nchange44": "Para o novo nome", + "nchange45": "Ao pressionar confirmar, a solicitação de atualização de nome será enviada!", + "nchange46": "Histórico de vendas de nomes", + "nchange47": "Atualização de nome com sucesso!" }, "websitespage": { "schange1": "Navegar Sites", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index b1060649..976f6b2b 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -36,7 +36,10 @@ "puzzles": "PUZZLE-URI", "nodemanagement": "GESTIONARE NOD", "trading": "COMERCIAL", - "groups": "GRUPURI" + "groups": "GRUPURI", + "sm1": "NUME", + "sm2": "GESTIONAREA NUMELE", + "sm3": "PIAȚA NUMELOR" }, "login": { "login": "Login", @@ -383,7 +386,7 @@ "nchange19": "Numele vinde", "nchange20": "Anulați vânzarea", "nchange21": "Cumpărați numele", - "nchange22": "Nume piețe deschise de vândut", + "nchange22": "Piața de nume", "nchange23": "Preț de vânzare", "nchange24": "Fără nume de vândut", "nchange25": "Nume de vândut", @@ -401,7 +404,14 @@ "nchange37": "ATENȚIE!", "nchange38": "Nu aveți suficient qort pentru a cumpăra acest nume.", "nchange39": "Sunteți sigur că veți cumpăra acest nume?", - "nchange40": "La apăsarea confirmării, cererea de nume de cumpărare va fi trimisă!" + "nchange40": "La apăsarea confirmării, cererea de nume de cumpărare va fi trimisă!", + "nchange41": "Nume vechi", + "nchange42": "Nume nou", + "nchange43": "Sigur vei schimba acest nume?", + "nchange44": "La noul nume", + "nchange45": "La apăsarea confirmării, cererea de actualizare a numelui va fi trimisă!", + "nchange46": "Istoricul vânzărilor de nume", + "nchange47": "Actualizarea numelui a reușit!" }, "websitespage": { "schange1": "Navigati pe Site-uri Web", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index 96f7a473..1cdd35f0 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -36,7 +36,10 @@ "puzzles": "SLAGALICE", "nodemanagement": "MENADŽMENT ČVORIŠTA", "trading": "TRADING", - "groups": "GRUPE" + "groups": "GRUPE", + "sm1": "NAMES", + "sm2": "UPRAVLJANJE IMENOM", + "sm3": "TRŽIŠTE IMENA" }, "login": { "login": "Prijava", @@ -383,7 +386,7 @@ "nchange19": "Ime prodaje", "nchange20": "Otkaži prodaju", "nchange21": "Kupite ime", - "nchange22": "Imena na otvorenom tržištu za prodaju", + "nchange22": "Tržište imena", "nchange23": "Cena prodaje", "nchange24": "Nema imena za prodaju", "nchange25": "Ime za prodaju", @@ -401,7 +404,14 @@ "nchange37": "PAŽNJA!", "nchange38": "Nemate dovoljno kort da kupite ovo ime.", "nchange39": "Da li ste sigurni da kupujete ovo ime?", - "nchange40": "Pritiskom na potvrdu, zahtev za kupovinu imena će biti poslat!" + "nchange40": "Pritiskom na potvrdu, zahtev za kupovinu imena će biti poslat!", + "nchange41": "Staro ime", + "nchange42": "Novo ime", + "nchange43": "Da li ste sigurni da promenite ovo ime?", + "nchange44": "Na novo ime", + "nchange45": "Pritiskom na potvrdu, zahtev za ažuriranje imena će biti poslat!", + "nchange46": "Istorija prodaje imena", + "nchange47": "Ažuriranje imena je uspešno!" }, "websitespage": { "schange1": "Pretraživanje web sajtova", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index d8acd09f..a26526ad 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -36,7 +36,10 @@ "puzzles": "ГОЛОВОЛОМКИ", "nodemanagement": "УПРАВЛЕНИЕ УЗЛАМИ", "trading": "ТОРГОВЛЯ", - "groups": "ГРУППЫ" + "groups": "ГРУППЫ", + "sm1": "ИМЕНА", + "sm2": "УПРАВЛЕНИЕ ИМЕНАМИ", + "sm3": "РЫНОК ИМЕН" }, "login": { "login": "Войти", @@ -383,7 +386,7 @@ "nchange19": "Продать имя", "nchange20": "Отменить продажу", "nchange21": "Купить имя", - "nchange22": "Имена на открытом рынке для продажи", + "nchange22": "Рынок имен", "nchange23": "Цена продажи", "nchange24": "Нет имен для продажи", "nchange25": "Имя для продажи", @@ -401,7 +404,14 @@ "nchange37": "ВНИМАНИЕ!", "nchange38": "У вас недостаточно qort, чтобы купить это имя.", "nchange39": "Вы уверены, что купите это имя?", - "nchange40": "При нажатии кнопки подтверждения будет отправлен запрос имени на покупку!" + "nchange40": "При нажатии кнопки подтверждения будет отправлен запрос имени на покупку!", + "nchange41": "Старое имя", + "nchange42": "Новое имя", + "nchange43": "Вы уверены, что хотите изменить это имя?", + "nchange44": "На новое имя", + "nchange45": "При нажатии подтверждения будет отправлен запрос на обновление имени!", + "nchange46": "История продажи имени", + "nchange47": "Имя обновлено успешно!" }, "websitespage": { "schange1": "Просматривать веб-сайты", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index fc783bdc..008bee19 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -36,7 +36,10 @@ "puzzles": "PUZZLES", "nodemanagement": "NODE MANAGEMENT", "trading": "TRADING", - "groups": "GROUPS" + "groups": "GROUPS", + "sm1": "NAMES", + "sm2": "NAME MANAGEMENT", + "sm3": "NAMES MARKET" }, "login": { "login": "Login", @@ -383,7 +386,7 @@ "nchange19": "Sell Name", "nchange20": "Cancel Sell", "nchange21": "Buy Name", - "nchange22": "Open Market Names To Sell", + "nchange22": "Names Market", "nchange23": "Sell Price", "nchange24": "No Names To Sell", "nchange25": "Name To Sell", @@ -401,7 +404,14 @@ "nchange37": "ATTENTION!", "nchange38": "You not have enough qort to buy this name.", "nchange39": "Are you sure to buy this name ?", - "nchange40": "On pressing confirm, the buy name request will be sent!" + "nchange40": "On pressing confirm, the buy name request will be sent!", + "nchange41": "Old Name", + "nchange42": "New Name", + "nchange43": "Are you sure to change this name ?", + "nchange44": "To the new name", + "nchange45": "On pressing confirm, the name update request will be sent!", + "nchange46": "Name Sale History", + "nchange47": "Name Update Successful!" }, "websitespage": { "schange1": "Browse Websites", diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index 6165e0f1..b8d3aec6 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -36,7 +36,10 @@ "puzzles": "益智游戏", "nodemanagement": "节点管理", "trading": "贸易", - "groups": "群组" + "groups": "群组", + "sm1": "名字", + "sm2": "名称管理", + "sm3": "名称市场" }, "login": { "login": "登入", @@ -383,7 +386,7 @@ "nchange19": "销售名称", "nchange20": "取消销售", "nchange21": "购买名称", - "nchange22": "公开市场名称出售", + "nchange22": "地名市场", "nchange23": "卖出价", "nchange24": "没有名字可以卖", "nchange25": "出售名称", @@ -401,7 +404,14 @@ "nchange37": "注意!", "nchange38": "你没有足够的qort 来购买这个名字。", "nchange39": "你确定要买这个名字吗?", - "nchange40": "按下确认后,将发送购买名称请求!" + "nchange40": "按下确认后,将发送购买名称请求!", + "nchange41": "旧名称", + "nchange42": "新名称", + "nchange43": "您确定要更改此名称吗?", + "nchange44": "改成新名字", + "nchange45": "按下确认后,将发送名称更新请求!", + "nchange46": "命名销售历史", + "nchange47": "名称更新成功!" }, "websitespage": { "schange1": "浏览网站", diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index a7b440ba..a3888c7d 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -36,7 +36,10 @@ "puzzles": "益智遊戲", "nodemanagement": "節點管理", "trading": "貿易", - "groups": "群組" + "groups": "群組", + "sm1": "名字", + "sm2": "名稱管理", + "sm3": "名稱市場" }, "login": { "login": "登入", @@ -383,7 +386,7 @@ "nchange19": "銷售名稱", "nchange20": "取消銷售", "nchange21": "購買名稱", - "nchange22": "公開市場名稱出售", + "nchange22": "地名市場", "nchange23": "賣出價", "nchange24": "沒有名字可以賣", "nchange25": "出售名稱", @@ -401,7 +404,14 @@ "nchange37": "注意!", "nchange38": "你沒有足夠的 qort 來購買這個名字。", "nchange39": "你確定要買這個名字嗎?", - "nchange40": "按下確認後,將發送購買名稱請求!" + "nchange40": "按下確認後,將發送購買名稱請求!", + "nchange41": "舊名稱", + "nchange42": "新名稱", + "nchange43": "您確定要更改此名稱嗎?", + "nchange44": "改成新名字", + "nchange45": "按下確認後,將發送名稱更新請求!", + "nchange46": "命名銷售歷史", + "nchange47": "名稱更新成功!" }, "websitespage": { "schange1": "瀏覽網站",