From 1460aaa7d01d8e3389b6ba5d5dcd8a12abbfdfee Mon Sep 17 00:00:00 2001 From: hoho Date: Tue, 12 Jan 2021 18:41:01 +0800 Subject: [PATCH] fix opacity --- .vscode/settings.json | 3 + src.zip | Bin 0 -> 117075 bytes src/css/index.ts | 17 +- .../__tests__/font-family.ts | 24 - .../property-descriptors/background-clip.ts | 5 +- src/css/property-descriptors/filter.ts | 53 + src/css/property-descriptors/font-family.ts | 27 +- .../property-descriptors/text-fill-color.ts | 9 + .../property-descriptors/text-stroke-color.ts | 9 + .../property-descriptors/text-stroke-width.ts | 8 + src/css/syntax/parser.ts | 1 + src/css/types/color.ts | 153 +++ src/css/types/functions/stack-blur.ts | 1057 +++++++++++++++++ src/dom/document-cloner.ts | 20 +- src/dom/node-parser.ts | 2 +- src/global.d.ts | 1 + src/index.ts | 2 +- src/render/canvas/canvas-renderer.ts | 84 +- src/render/effects.ts | 21 +- src/render/font-color-gradient.ts | 13 + src/render/image-filter.ts | 99 ++ src/render/stacking-context.ts | 15 +- 22 files changed, 1534 insertions(+), 89 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src.zip delete mode 100644 src/css/property-descriptors/__tests__/font-family.ts create mode 100644 src/css/property-descriptors/filter.ts create mode 100644 src/css/property-descriptors/text-fill-color.ts create mode 100644 src/css/property-descriptors/text-stroke-color.ts create mode 100644 src/css/property-descriptors/text-stroke-width.ts create mode 100644 src/css/types/functions/stack-blur.ts create mode 100644 src/render/font-color-gradient.ts create mode 100644 src/render/image-filter.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..eec067e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 0 +} \ No newline at end of file diff --git a/src.zip b/src.zip new file mode 100644 index 0000000000000000000000000000000000000000..5f6557b81434ac90d66eadac869d41fb2e4f3b6d GIT binary patch literal 117075 zcmWIWW@Zs#0D;y{ogfem!z>I848=vs`nXkaLRBQ^7p20LaWDw+_yzi{f1l#bz`zj3 zz`!7ktSl+Bq&%}YRj;Jj^pqi2g8`4*!@I$M^pXx9+$r?_l2@CP?-JjmkGVysv-8{4 z>FKsPFZ~wf5#%@{*@!-psQ3eVn!bl%<*sQLz{Fk8Dz54Ewsi-fiQPC4pWI7tKVL8R2g>1m1v4Yw4(d2(9JP~87&{e@l6nw|bGIWj5vY{nCjAC3uI z?q436{n<**}8uwk3TvJ0ped zw&$d?7kEf^pPXYPRNd&he)pLRQd44rf1gM_)xY&}Tjr_Y|En*4oN-FEDZu&px%{bI zyNf#tJDR;Yr}v%B+L-q`a_g16DZ;bY6?R$1EKA{= zf(ecLUmu%6ujGKGC*4>{~ zD!noDz%|Y3Hjg-Wr5xNj;Z*0({Heck^_C=YgeY&yQ#eoH%$$@J)&l1iJmI^L zaG_ws+4p8Qn@+gT@%$A0>-dzTe|EIrd-Y%Rx%q)Nf>NIho&_%B^3K?FXPV^w!*KyW z_J&F~+(>S5^{Z$J<$LXXOT1c{?Y)lDzk4Q|OHXlH?T+i;-Bgjl75T1pZ|3Kw+bv7N z1Y4GdaJ%lx*gE_8bnf!rO@CTrrS9I^_TR@#*|fI$?COW}*t{zh+HZ-T?RMI+q*!KR z+RJSnC9DSRC9WaUn2u$+1vO9N{Pf~!N>t0%$fKTz`uPnHn3*ZeugiP)^yYfK@&4Vf>Wc4Qx7&U((R*tX%#uILwLr_R*Z-oq_H)hYAuSh9u`%3U zzy4X(!FjdWLQirtcdvF8?0Y=X>PA$fR_(&5=dtSF6Ld}qbu5{q)51DW#_`M5%YNQ* zNef?xSKr(oadplr$AFIp4>vms)Fc$YkH#aK|X_Zm@>q{&RE0 z8BJDHe$^3GUiK%Tga1M+_tzP`x~tcnn;`8H_=LIAqW15rtEW#s6nFiY`sL5T%MU}| z&R?r_WzlKv&=kL=K9iTU|YYInM@`;Jl<+s;{9rcb7) zhG`of?+oC(^Ze)Lo%s*k6*t(L-cC1A5_FMnVOO{&dUm38#xxoEWUZ<1uBBFT?NT~p zs=daKaoZ=>M4z;qhyMRk==03660+LQ>gzP|Y|rn%r)5jN9y^=B-n(n&Za2=W&p+S! zR2#j^%HdP5i_oz(HrJ(2Zj8F|x}z{-Z_SFg6O5;3I6TdsvPa`kn6_cyVzvP3gQZ=0 z(~BBe<~^RWNPGI>Y>!pSVg~FRly%NH$R~GRRnQ4ICZv>L{M6}&!@>i82Oi2K9=n-# z)HqRfiu$zn6V5s(&c6Ptbep4h*{{$*fnTjW>+_DE+j!fB`OjPHR~a$;qE^3-4CI-` zb)za<#QpueeqRZW6$_+0>Sk$7zHTA9B>9+C2WLT}*zscDw4u7BQSrMGbR3!@u_lLBrO%dhS1<^R80sdr-2=kxjBxo@v^S)jeoC?>y# z*J|yD--a@u_8xoRbZy__S$0dFX0Eew4b{CZSx}c1*P5Nw;P7KAU*?jUA5XSoYr;k%L)Yp3Y>FXpeDUbgqg^3xsPo1)L$5zo7;v$Q!;ZU3A} zFRmS~U)|!cy-mD`gPncRoNsLJ*T?Of!M9&#U%jcz_X+0BoadZvpRD}7;(C7LpPfFw zg`u2(dEUJg{-oU8Z`rH0`o*l!ZC5tDigaC}=P~m_>yuYcmHyvav06;CYIWwxM|RgD zqXN#|3)p6%Q=&y@sA>k0N>t&()8nqUzrxFg8)@Rhgk`C9)id0{In*TlSk-uIQ45wi>!d%wb21HST=(m;kB$1VZ#QkWU7fIMipIYD50%PaKRw9L zx_jc^tMh67Kf3v5dKX`Il426rI)C^5Rhg}yRgBJY9TJw#yBX^%;`r*ukDpR=GIsK* z?%1%(r|hz0Y~yB}qI{CMen=%uB9tv()Rzx?p* zYrfiwjrNz7V~; z;cV%OI8Q$|{jI#JEQ_0i{MIf%X0)&9;TiEZ`@>UoL%&{r{i}M-+uYXATTU)_wcH`r zGO7Qkp{IPK=04Y!!{4U-KUJ~f)gNiphQadP)d&8uGB7ZSF))ZAHw@BJ6H7{qQj7IU zinm5o_g``l`2XMT=&TjGg52{A4d-O|9ocQnCH|o1$c)WfyjM=s&D(CX=fmB(H@D?% zf68E)%Q5BMnKw3dpKn+xcC!eY2-(iA{8B1mY_8;+v}>WTzlJdHC$+t+Cp_~mDt39` zwS(>LzX!ke)E1W6G4!b)_xJC6vdTW_&9uWSbytYQdG4Ib(%0WuF1$6;a^jVlE}q$y zAs(`|&$cqjM^s%A>`W-tTzresb>~yvORSeA7MZA+7TA|Qy}8yyhdHZv<@?^iYu|sH zTiy=8eDr(lnsxHN%+;KGju~*wo!@qB!4&DXcb7IT4?cD0+712~V|A`CmuCpIyO=QW zmpDxq%Q(&VFsfzykIQ@J{kne&(Kr&HRL z551TZSj?EWKzV-P^$RTDGE7?SOBj^4asB)A@b6B6%W{83 znWf(xF!j5mBqZ}_>FM|NHn-<&%UYcD@0T!tMA5N1PsC(rpM8_z{YZtS?VO6(fr)Dr zwnQlkmoOeVt|ydMxR<>olHqh?fS?oa+79a(MeB6mwDnhATQu`~h9T!F{ReFac)MpE zlZd&t;_9={J5B4eTJNluz5cpX#WZf;f%((#U;ZLkvvQkTukQ5QpN}wah@X1p;o<`` zB9#xBFE|+bzV_KN(KQ_DK0donPZ2T`z4GTpPw*+dzpT6lHJ7}Ko*IALBYI=9W~smB z_UTbPOKnvCMEzI$7@*ECuf{)5g}-mUZM40W-X)vZ%Nai^rHedFuF3vuU@Fj_q*rP2 zQTdnB)n`jvuEYik|J-k=I`7Ks4Tl)!#2cG%N+4{L zL|TO8Psa<#C-p6zQ|-Jt&NW;qrAN4zi(7NuA@;9R?rY@VXZa!^@u^bY_<@h)>fM<%OnQjk+&-?e_thSn)whs+*d&dyhVA!YdDO7y40 zU(;J1U5b_@AGuIgSGDP-SMUbCV>Slw?`JHG58XFiHD^X*z{yV^f>ux5Znu1Kf#W<| zX4_1bKTJ-XwY|dgSufl^!QGO_`)k@;SNErKYp$9`UfGnf`t?<|h&Q}}TUG>3yLn^U z5u=I!jb9z8%)P!?b^FQ#w$p5LwqO3%@*`E|KhL8X5LjZ%$&eiBks@TIhpO??OP=a6YkDZp1V);XlN&6N|n0g z)7+cxO~)pjI&?R2jo5OgV`~z1G%jqm;An{Xb##T$Pwu5V^w|wJZ<=iQalyo$LFuk> zv0r6xOJC5EZ`f9CthQV3&`ySBu6}p-C&V(n{$voAachdP^s6k9V|Nw``W?2r79Vj$ zQcGOx?T#g!_g_Ty$|yv?2-Vm)N2@hxY8vN+WfkGgfg2|+5uS95;dZTC?kpJ|p0o)U zJ@=kcJ$Fu1@%3Lt=cfVkC+B)h5nbitVaab9pf-DH+50)0{pV~vtLS$&qDRht{k{DQ z&oC}tUnRb_fJt5WP4vrysc)6|o=?;GZS0|_u)8jG>bKcfmreSzt=4MCtE~@TOx?xk zrSLL7Hm6Q$yoe{HUdNu6v?P5^MeApy1u#tC2J>awV;nDMs z+fu#WW{ZhEut%*g)GSW>8!<63NOLkU2qD)OIr-`7sYQAv#aBbTvu`^H?0X&Vqj$db3{yYI~7!U{mr^n91H*7yLt81Q})IqmM+V`o~z+MpZxKu#uBz; zrl)K?1?##NIsE*+V&(&`DE=48kt|-`$GX@4?X|f%-D7&SqLH-6SEdMSJ4LRs|rroKsky-k{qs0i=L@$;FZK4<5yIcax1 zWEt1ky%O8)=ZZc&cYD6^I?sezE5$-@II4Li`#uRUp4Tb0?}*_Vt$B%l z8**5`f7cVT+OxAzpj+{Y_dYD{NTFL(Sk&8U{uXLEV^T3hiI|2HuIdi`!e z8UKS->D9)yvI#3cG%cUk_x#X)rZ2}X@4317g~24_tj=ANYRuFG&ah?7nwfb#sB_Aj z?z))u{_DA09{bsS69}H5KFg{o!lwQgzRI^ z%`u3wQ+vFt?>*;{>ni8cix256FWz8l8r*d&?BVVyUo_s-acZnR;WCl?&}E<3=?l^y z^xf;tGk;xKe(9KC#p_sea!`ECZKlaPY$>w@d$`JWLrg;{r9yOqJfz>v$pz#zZ?8E;7jji!{AWaj9V6svYxbF~;Su>Q7nwVK_U zvvGG*(#NJnjUD@$J+AyrI(K`k&W%3OaX8I?loTNO1MUD67LN z)~>r3mTK=@{r~&i`eOzkkIP9)SjYu=y_Ebb!8G~MkAKT7Pcz;wiWi5uih%)r6bNPT zCO$qlKRLTNJ|1nf=T%CqASWXO!(&DU24!S#AT*$lz2pY&&1*IgxL4UFov83;Nv~Gv zseqM1$2C^P6f1?zG2X;4)$-|`t%1GsQ@ZS$bk-w7>D5EtBF5 zUAEs-?$vDB|2o-UveRbHXQ!F}5}Wp?WJxNAO|=w}<~98^F~vVB<>`L@+g3V>(Vb{T zPUM>C+jR^K3=bF>7~~j`5(FY7(-4i-+CE>dLk0q^-z|^I91zyfyHqBnJcHA?(|&cN z$M&!+7RNoiS;L)fZ~uGmaqZpL_xM@Y{e2s_z;(9K;sav$*p7MhO|;Ow{UAX5)WV6T zQy$)2`)`NLT(ca1yB(T$gcg@KuCl$BxiUhy`&Vz#xO&|^U>)}DUnF5huJVAd>qlikd2wKx0J8xk)++`zhJ`wK6v`Mk27iff5Z<|V1c zC78*)t)O`M6E+40Cl!oj4%2|1%-=@6&cAFR@PFU_L-GQ?ODB|0{&6W#py|_u2H&^G zoG+W)Ig-olDU*~QJ@eQ8;<}@iTV6LeIS6pxx>dgK`1^M?8^w=lp0(J;%i-B>!(Dst z|Fp*^T$5$oicQXC^hC`tob>V9#d!mj`U-($ytc$ZHUoE^g<@1)^`~C9@=C~dCoViE; zxz5V@jaE-|zwWnN%&BfJb3tutMN^E6n!r~XF7)$J*yFKu|;jX*RA2P}gtGIm? z4wU~qfoZ?Q=JO`XkM)AG#f6Kv|Bwo>@Ao{ET@!V*&gICG;+YR-NL;<8ddbP=;ftUX zLVOvrrq7I--ubH}e@;B0Q!!Ut=BcmY+2sPNl2Vt}^qx&mO1U~cKWp`poIO?vXIJ_! zE>%%^A~l!yo<&g5ZR0i4r{@G6V|9r>ab?Zy>cAyO#Xhmz7K*Iy6?Dzud}D5_Ykzv? z8%KkeONxBvCA8VKcsi`Von<@q)I6@j=Vm;+RvhJgtSdf|rC*7OIa$Wgh@CCl)3xuR zSsyOHrK;Ml!!6gBhxl%^w>>W$>iAD=*I8fJ zK&MFYy>0%H5$E(3Y|rWUefe|NsJ3tNC*EYV5fZ%{^c+Bua)$`^&Uro?pyE!h@0J6&(rIVU}?=x`m) zQ@2)a)yhnKJv(maOp{G+SC72mv|P4H;Gfg>ghkf{LwT(FKCe$nn^PI6a=WJRI`gVW zHceJKL+x_FewU={4(<0-kNe|fu%k4IekcFg4U;bo5wY%mMA?|x|5ySBSw;hyJHHay%s z<-*1{a;>u$9QeLK^|D^pLiMFH*L_?0FL!NLQlt;#vZ-&?70#SLB$(53_1H~QQ?H@cTvUcOIXkQ63Q%T^b?PYFB8r?vLoiltasBT@0)8UdpbM82NL8wXO_{ zk)uFO;w-7ct`^A!`?S(UmAe8$OjcZf^LQ@n+5H}JJNvtQRw%g zlOOginS5>AnfA8b3G4R;S&G*0d?AqWUd{G>OvQ%M-5Ig1({+j$Opjc-%W(sz;_OQ{ zTmeoy9fVnTb}%`x-LjnEe#7tK9kHhQhw_7e@P+-Z75>4s{dP%u_~fWb(h(w3LYxXl zE1zE0X`Uo@R;+2GvV+-o?TjD6+%mI7p9p0yVm`T*?Ppd?%ZsnmqUNS7-Kz7*H)P@S z=cy5gH~LslTmI;1A4~bd=#{^=bDL%SzV(%#&2{;kzfUXG@At~BV4fSrv~EQ=n^C4a zQ}d?C`{x%htKK}f`O-G^Zx?ST%v-TfZN$Y`!Xk1${SL0u) zX~N>7Xy=~b)a(DR{JtH;zh~vb+m>Pbg{3U&BBZCgc`+I92$o?ryZrUDZKTR;DzM4!vU*fM76XW!2{zS5N8_}cQ^E?h;gXl9`QylHY}Rt#SkGZe?j zoU*&OWunbLcbSs9+=+kLl{c^38Fb}N zK*Q3bc}e*!Jb{Jjn_kS@lGz_&AgIwN3E^9;1oQ2lich`(n{yqm^^diPbLD{2JDtvf2Js$C{d*$=*jMNzY5F zduZx^maqEam6p8Pt*;a_!|pzE@O4nuy|HRSE;FCV#;UVgQ&+BC81!>vT%JyJ&h3~L z&m12ne0=t?{`bK)Wu9HCyYtU9 zD{1(!lz(`nnjLpNW7d=>XDyHI+nssq=As4c2OTmiEBEgYx!(Jk<@nUwc^Bs|__B^o z?Z}xWjL|-7T+R=_vRS@PxBS2}ZF<2R2OBodExLDiS)Osc##`g3^CCGf4;n>KSr?8mBbbGGa)DxTKWYF4S;N3QO>+^)m<*FUK9 zjbWFo_aE&j;q-+?){7TRIOg!)WlO}3@Qj70c{83KTOjrPy&Yf9)|Af7q_0ful=<7l+20uA50!pxokQrdDLLy^xk(L_a1Djkl%TA z-_-ny){JnY3tLSle)SAKy4Y*Kj#H7it$66`qHk;TUKyM&l3>}l-Sq9PO+9s*J})P1 z^7+4VW6-P*%TKLY9GDxM`uP9SS~~_%PNy=7XtBFEb8t|^|LL8mHy zzW<^6@#niYDy$PO%-a3DvZid#=33v$-Jx+I7tgzIzF9bNy-Q%Mo!HYw9$}$zJW6@i zeceVU&pn&2{n6S=_fz+aV;T8}3eEH4Gq>0u@77Ou-FI$U|D_bxHKD#zdb+FcFn=ge zyjVBW=4`iaiO3yolbs#sUFsLQ9Ng>V?pic0h+g1?>n4&Wrq`~t%F++aG z*xTWRH!C-oEccL(|R%EWoBQ2vlSN%!b?zIXkJivqgXs=tI6 z?h3FnA-e&*cf)K`9j)d(U+Q{N08%avxHo)Z@tACqdW$j;PKySYd} zyq3kJW`;?HUa6AT#qeblBElcNwp#CU)6q=fyTr`;_gmL_$;5{}4boM9nIJVGC06jh zY3{^hsgftxtQ3CoY2yU8&ws9_OU2B%(eLN7RnKVZ<991MXU!Kn;<5Zn^>)t4^eKNm z{x7uKo6V=itfG(`cE&*7fU&V^`&~6g4IQU!jzrD_EG!}aI4&|?oN&`o-fg;^{=~_1 zgLjJetv(@cb-grY)wwA^Lw>MQi##TH|_iSX1*B- z;aj$^7FXoFx;u=qfnn}-Bc~jPmS6WB*?l(rS7hbm`V#;9*6;H#&1P43JaC!9TK(Xa zQ@8t~LPf^O_ibO?ddxWcip28Y6K$@Y=&ksoed=0fl=G!Kv#<0puRJJrs-0b5`Q?%0 z+lsEqHEWYUIYav3Khs!IzVx<38;^ll=PoO7%%qublhU_Z(|w%GPqt|CXD| zWqq%Reab?cwF>rGTwY#{&l#pJurf%CuyV+%X__!CbHk^*f0wO!njE3N?1qzwcg2qz zC;tA4c$qzQam43`cX;Bwns$XYurs){N(sLgSZUw-&g<%}P1{3*g0obs%uWP|d@?-P z?XfN6$-@TW-~}O1R2!u)`ZS(6yuwy&&Bad#G^TI=>NKyZY+AvIiDaZVRMY|_XyLa%J` zzBOKZ6K__TGA2G#x^(9Jk(Y(1y@J`rmc?mx_a}yl%-*T;V}*jV@)@Xvm{!d zQ8{?f_26x#>@d+~+;Vz8cVi^^X2poVi4mO>GkLy^)IM`X?bIs%FVn)~lhbmS#1v249#Y&n^O#%b4h{uYi&ld#7w>OdvfktQsZX7o zE-hQYmT)uSkWj++&IO`73=cHFBoVjuletmo}<>E==|N_jPr( z_+jt-ZNBdg`4;W>pY!JIfg?x%>^2TrzS-);sU-(ouX^O3enN}!t4ma~b5m{Ckxjd+4ZPRi+shsA)fBZ^ z`?d15Cpk+~?=O%&`;7hb$;Auayk25ezx;h*IkSa~>9_E?nFd|gL;G%DY*#U@+_m+q zqPJ__z4h|4aeXo9F70n#uAMV!Zi&Z?$KMSnkFYxMr^ z$BEAC8Gp44B)^ET(*MtT;=%v*kGqnZ7~S~HF5NiYey3MV<=}}Y2Oi&R*}KOU=g>AuC1ziuV>V|rJ`yxUa5HT}8yi|ZOEpLhu@|L~2mW#x9B zB=_UN?i`Pks|(lYmo=!CYZXPVeS(@=b^Hs*DiW-amKVBvL zD%z7@nUe6@^YR|cFB;SHXQve{dO6Qj*=L%Bx|Z?7JK-OFrSO!2Mdsap@#n?QA0Lu8?6=L2n%{O$u0PYdQFFz%tiq>$PFK$H zer{%)@UdmB;@ril?A_ZJ_AgnRdqY0+Pq6i`a`CrsHBQ)XP2_U%Q~o1qI$^C`kWlL9 z-_5ehs`6*tO78zPbk%VCwz%tB!Pmq8>=$j#KYpb%q;;+R7T3SG?;3_bcine!Wm$|! z(b0b;3fzyywYSPopSdz_&9%s#aV!6}N4RnC{BK|!vtH!l*7-TF{{K^1{J-}1DS23< z9@4vF)Ha?Q#K^#4%gTTlA1*G|_Y5e?FGwvasdPy#PAui-~W(-!14Rv z!@uzQ^6bfXN=o7HX7OC1a!exlVVYFr?k(;{O@H2Rlikc*_~zi-mG=9}zu){`WpjF| z)27|?&x_~mZM5~Add6eD{e|epH4;yJG6VK%tk8UE#{PC{#&%}CumFizhF0OG8!0Kv zQuw&GCtvVBT0A8y?Ni~$#NY|B4rjLSm~C^^JkQee*_Ig9#}!`pv%sa*5= z0hcRhrmS&TaQadA-AjUvOpa^5RQ}ku;_s|-c8lW7Me73UnpW&sf3m>k1#7_U_bs32 zE1a&YIu^33C(&co^HRn&#-IOfTPrm&?3nM4V|pLnw9Hv}S?>MQt)dEB6J@tv>rdl7 z)Eae4>&K0G3%&ney^^^8gWP<<7@5sM*M6Mv_S_sD_<Q$(&G+QdX;!aPXV&!DdW6gPAlwQ0|&$eHF<{?A2Tx6J% z+=K^wyQb0BG;Erq@*!rj?y=}SyQ@{Ul(_Z%RSGTGqZqVP^sYr%2SfYS zm0RaWT;u898-71U;lx>s#*3E63_fO0`z~SRcI0qH>fe(FPj>{TPs{wbs^LLY>fQGb ztIUqSFFY4j>UMt4W8a^@7QEc0R^guL{N|9th862G-|ODI@p1i1oyWPiSd-IM^GIww zVX^wPf9Z3w$fwU0Vl{9mI) zvGw^p{Zh@==eLsl@87u1@!@Lu#Wzjw{>Q`82&8qGcyCjS#AafG0bQ$Nv$Z+ zD=A(Z7Mp+BK&1BldINu7msg5%(V|;69$?N`d#Tq>!DMOYZa43@C(5q-Uig3RYj{pO ze`Z5y?%MZXcRrVkkKcW>yvO@X?ng`gd-YLk@~@WHeFc5iOF{pZ_r|M~Ol z=C1p=W*l6eG-pn(rzZa#jrW3;SKdwN5O^`m#*4XUU#|d9LV)otJ4McEnnIJzzvPL{ z_IaXd#LXBKsqJ>~XF%fw&mCscJCp1Q65l8ZJd2CsFo;$`{yN%ZV}iA`z~mi<^M*tPba=F@Da zodPd)O5QJQ+wkIa_~RQwoj1y~I}X(>ef~?M;&eq^nDyFaOOxC;yLR$rTxrgk;psM2 zDvxvTWa}?~cefo{aL~^4?i=YZr`|WX7jJD2H$VM!(T;t(=hoXj*)HBad-dt5@ekVl z`O4WhdTUs+d1jrH+@-up4LeJDRpCu5@%-Y+CBf8Yc?pW{+SfYzO;R@tZ$Z3RC~s) zrEeGZso6BR^zCRe*6_TSb#n4YzjwEs;RcDRyhfTNhmdmTK7dlDGRwr*kS zvX9j)G8={No&UY(jqUB-2^Y;o85h`pTL1=?lFQGjLxta|GT;Bie+9|nk$-)bhBQpLUc_k;}S+d#D z@lUz9ttC(K%u46iB9Rx4O<=jL|Lnq2SyMKzM>C$?F|Lh1c{YRl`Mm$d0aK>#Yz#i! zsTUC$z35+pae$%cvjzw5b@zU;U(sLx@mtlF@MjD#w_djy^?!5Sh?UpafkL|O&E7`em{n9TE7u*hJY2IjSJ2$J~tc9lC z%x!#}jEjQ~?wOQ$*nIY$^;-5>FKhU^lyaIQPsOnPx_9Qb$`p;JYcJm!2?;BTO)nM` z{~q&zwKF$j!PX;^Q+gBUZ$JC0N_oG)g%!)=UVc^CL8$p;pv0@0n_6#}#%;YC ztiE>f@k>_0&b!x?y#K#CaL?ZPSsF5{_jfPP-?46%#MR6CEvMpFPVtudWi9LxbqsC6 z!y#P}{~Zhr433Np3)1{Qgh&MaR?0*F=g79V68~Xt(P)cb>a{ zyOg)JfYr=1?e_nLbETcv`%T*1&lP?~zWm|x{cCrmcqaPra{rLXIG60I8N*gFYx|ef zOIy!bmqu(W*t|*WMY4n9`fc(o3lHrL(%8^*#r@WeOCQfq`m1?+PXFncOrph@>bfdLqZO-Me)HE$sRaxY8KW;wXLDr^cH63c_=6;<}zGT6Y zJqGduu7W)^-j=eizH2SkTzj1S^Fir+y;XBGYuqlJTI+V=@5N(}9F1qaEsWWvFz=k$ z%j{o&Orj5*<=wK!e@k|!-i-g2MJs0Oq%U2#>b;73__RZUHD3xgUf}!G+;w8#-oppn z-Xxdb{%P=SA6hh?`DP{E$;iNPmzjaVkc7mXUzC}inFlJgf@1TV3`A<5hu<+zdsJ{+ zIZsXN*aS&~Q`1zB&EUFf>g9cFS0>N@+NBFtSZsb{ckt!T=ktC$K58lCtS&zh*|hTx zZ^r~aF-7&xls$axdyceqa63&usCQ6P$D;e-mNjkBs+&yw3vpoK{^8d5NY5z{V-7WWqH{jw&cfWj- z*FWSxF!7i6^4{)yT=kc!=FGu$?^$j2H4dmN+lEbCCH`VT!PX}^&dT#At1j#c*0h`% z7d1OR<>rUe8uRtPMz3)x-0nglZaX7 zt5(!{KmE5qwCnZmI91bg|NkwCkgL<2aqY_XX&$qW!7FM=jeD%0`G_xQynva3!I*@2 zEyypN?^(A8+U-)JsVxSNxcWAViI=eo{%-g#;sGkf07Njxc@ zcN8LyB{xf3&i$9VyLP#2t6FQ+x3JDD1)B2|t~0mWaXb5;mXZuB-kYdo9?#i4MRC&N z=k+P!D;Hh%f5ukJbI()1dsftncpmFb!FMk*J(;=exJ-(wo$$(?veRw&nhHBrW7Fz8 z<`fp5|JwHQ;O|!;|v zb!jQ4*+JjrBzEnVIH-2-om3R#=^vUZlDa0@&K!#l8h<*`!sof2<#)5Z-@m(C=kBsS z;KQ1QOKM*qBTckO0NbB}miqxn76`a`AFy?<6LTYt{9sEz^F#(>05 z>7pg;&w=L3SxL)vMX3d;iJ*?y+{w4|ZX58lz5gq+rkBgjbY)2KVSdWoV2j=U~ALzmp&huT5rV$uha`vYZB|~ z7MN8bK3h;x^R`Yo>pMkJ8+Q9Hzvb&L$XE0JOWOFuag&hqf}&k+%clJijO1uddpLcP zYFCk&p!7}UQ?kJ+)mbW{+}qohZqc7J`AMO2#mQ^MIukU0@I0T~oge>v`~7^q@XxDn zUQMa|Z}4rF_lZ+?LJv%4|8;}Ew?cf@``NccrK{w>sjQxF_35EP(Q;+^y|vj>3e-gZ zNKFwxJn>(hl-(1b&pDwzPb8Y}FZ62({Bdo*(JGCTtJZ~WYK~DfSbAF`Xp*Oi;^nPB zo+i3ozkGBP+lv*``a?`rC7q4;{ccyNTlPIY^yF8;6`(G5F5T|XULfQAtuh;x>5B;=zpU{_Ea|Jpd7~j)zzmv8ramRxdu^&G+ zq!_A4GZzVe$Xfnve({lv=Mr1_KBz6;y>Oy)WU|*OpS@hSXTPdPO&qeFS9>=wGBEsP zCao1&oLQBsS5jO%Io7|~K&0*cPtoUn6MdeUUREuxYuS<0y+B;Adu7B1!Kh1On_qsr zZ>#%8c&hfNZ*MPupEGaMB9WlOQK+Z+ z1h2q`*T45}$SwJOPRzb*k@}?YE!;k>`BPp;)CAmX6^i__?NMi16MyP5zK*P;rjg&) zJe_d#%8XAw`_HfN(TFlyGEa+LqSrE1e|y{JqURP2Zc$0LX7hWmo?Fm+W?v3ZyWU%~ zQ>&TQwJrX=yZz ziKDy>p9k9qMEHhsrQ^bmAj??VB!B#is z^Ksd>=xV>&vTNmKNukpxq_(uwe80(;+`Lt`WS_*6?{$|Z=jjSLZ`a^ht7JEYeRbHT zp8@=*LYL?&_dZT!q{sNbCueWAt%7=Z?1C?owKiOTyyAk}=4rkT6-yH)9gFll zwLyYqTFBb(mRB|vW@&7kKXd;1SyR^^{gYd|yJjZ;JgJpa->4sXsPxkPvP$6ZiYxPX zdT%lO6))I7M^m&Xtm2*Eo5}a~etw$3^-cJNb;_}CMc>qZ`5&0R>fiBOz2)p5)KPO& zyx_v}ct!>W6BY&rT_OXtC^03o6g0@WcA{f`lY>C(`#)W_CX7M{=WVRo;43W5=jNgB zD;&svN94xYH#0JN{_j=pQ0+R_9OH3sPo?~xiLGHCN%k`rU253Ey>ubJf%lYIBHvsV z|Ifb6(yOz%V);Xbppe!LwfpUNn%!bv-C5tiXuZn)E#ha^ElIh3eGXghY7@mNl53YY zcqe$X*%q!mB0YC=%-gcFw`3l@H~G7#sPb1%vQm!rql*g1xWXGYr>L5{@$7D7J{`5{ z(u9aDXXbM$?_|1H$g`h0kz=Q0jAi zxqj1$ph-TTlBecujX%0NmfyrlYkQ-|S?VZB6^#%`qHg9(+g^Vi|-@;7&IJDERRct9xVT#zlu$ zGx&J!WOH1K<$L_{PT}*@3V%MuT1YKOGMc-?pk?~JeXPOvi?4jWs==$A7UB_MJWqlD ziQ-}YW6YBloGNdz$`zQj+H9BEow*O*%2+o&KvZ zb&^Y@>IyC2gGQ5Pl&*R(W71yU%qg5}<}bO`URxhK?fH($1v@Lg+uwC_SG~Hv@7Olw z*f*7L2w(j+@aV1$ zZ}@s9?|vTEy5>UO-^)|A-O7DVas?-@3wV~Yc`Eq4}o&kPtjrx6~^9q$)dMzH9c@# zwKaUPUrFYs-DmnjCjR;UE-y=T_0k7%Vrlnket+xL*s9d_H^IeA`D5NJQRz2}BPU!~ zvx2ex*haHcCpdP_dSencVX~H@&!lS)wi~=*VgGlz;>}53fo1w$Q`|Hx1LZaR1=7?l zZ*SZ3bg^`tMyABGu4x|clTN-CcTL{t^1>MMP)Qp@nOVa@tF0zHPs}|Fg$3{=?g!N3G*5-sr1% zo~!x3>gp{W9e0lhTeKwoy>2%5^sOb^CpwqR3$v9v9#>}5vPY3~-^?p(&h;(vaR~@e(w#wLXLn};)m~+->>~xYZjXt zqw(pFMg^Nwu6sb-;{)rQ&=pMVxV`HT8$yqD3^X5dC52BgB>dFJsGrmc){{5Tr zMcM26374k{d_I4dWMwK#nl<@ni)o*_S-o)r-|9O?qPe@I9)0>-_v7E&^B*3*e*RIM zeQm(%H_Hx)WmWbjB^`LhYEf$Fzw^V1+I?5KN-r@6TV4r~GyL7@7jdznSMTKEC(QSI zrd^!#Up?_>^@0S`xr*k&3X?S2vbt={IrL7ctZ1+)oac1pm({XRR<})^A5APLLYR`nJVy}VMc>LtPxRJ>Gg2rx}txb#PSf{{Y&;boi49`rpp z=XIrM64U$H7bhC7QrbJtGY!rSq# zk*mMzzYh(~SDk*^&u!L5nICF42dw&D=2lgyc9wH!>OP5C61?K1WJuyB;|Z5a(;ccm z-qp#JK75AyKZkxu0I>awW7OV zM!sI!ygd_t-eo}xS;i`yVv_0WnT#HTlKPk zkLk3lmH88nHGJ&bgO*;;cr}TyW?*2@VPs%XC6r!aZJgSETW;{Qy}JMzQTt%~fWzHV)PU=0;dCb#k>kprzFiDC|E_3vcy;=gC9WMUEYEXv zZ?dDdL+s}9_!=-WFf3tWU{ELQ+q{z0JWxk(>ZEwknDEj2zct@Yo~v;6`L17!?51=I zM}1RDX4zSqm@72N;+E(B- z)xh`F%6BTi8ja?#+s=EZBCb)od7;<(09h5$S*JO_d`{e^928jg=h@tU4C~)ckNoCx z>au=fOW|furZu{&d-ZiJFP|_`x$ypuJ^zVDa zi;d2wRxDfchVP}zF-fbVYMuY}6>cqwe3zlPaX~2$tET9$iHyC6=jz5B4KJ5;-t=K% zPTQjPMM5_N_d88eF_4Z1QYB zm*$nE7U^c@B^RaULV|y8uy6is2Z6nxwVyrVzo~jo)TyeV#=+N9(6(-(@e)!tPSC7NV~u1`?s@tr@LY_EPkI?(hpd1S$Xm+#;0)`oSr6e=I&i@ zd)&$BpV&OEH41{2Y{9qpeLQ|(W%kxDuit(vo-=I+i(0eM?=Gb?`<3IF&i|aw9K1vK z(O=cMHie?{`COL{c`Mxw^X}icDlW<1^vqq6dA^VieQ9;*!>?~@?-o7rhbk3^~f!NE>wmW<{#&waeF0g)9S)E6( zch&4?aZmX_-j6+fXR_bp*~cZV3`|4bTOGf=^d@7lj^FDG*%C*4(v}L$*sz${Ysw1k zD(7?e*>mlG-M;W8cjesNnr-~J#$(OJIX-RI>%+pIe~4Ru>#Q#`d){5a zMER|;Q$873mtVj3$3!{y+kq^*l$CD9Z=T=T9Ng~~vhFL|IU;M%a8EeT$iQGuOkV<) zoQhJ5Q$aboHu!YjZ3BTlueG;Lk7$Zj-Rl)t#M4TV%2m1)Xw^M?zEKnfh(_Acb;4I|HRqL$$S5|U$Xtw_5Y?}@5&R~ zlM73qSlP#L&+2izcDVU!YRqDt4({*v^|ooJzuy096q{D?slo2{DzmQ~Q>LX@Y+K;c zYQ>ay{8-%O?3arbW^PN(^_bujYJUE2!lY+i`($@d{iAT`_Qq$jo!g#O>^shp=J#mz zR@3MiLUX$^r%hKga9$X2A++U4ldRH(O>W&0*DpTz=8_M~eD;e|b=P{|J6cV7y0f1% zOtOA{CQ8F9C_<_Jsi$V$(kNj*8;R6JkyVhYwb$Z zedPbnQGkDvtLj1hiDv$`=7xXoeA7Co+4%YA%!@Dgt=Rhdrh!qZ=aY&H!eVD0MxU2a zl8R5ezmmP6^r|n{eV<()R6XxV9p9&yyl7^biKfqep8E?JYqnqAuy?mWAj9({y|)2Z zvYyZ1C7zI#esR*Qgj;v#tS^Yz5Ujdyt=Y!!e5eJ@PR;i>8krdwrtuO{x~F6o7vv;X zf*M|7xAPtu2-H5;{=??cB39L&r>c=@eoI7P$?mmb+EW@b*S4t?&0)Rr^#1j};=*-H zY$jS3_oe5Yuefk*qaow`QzEzdj-;$tn%=Wu_mLBdW_gVl{>x_OynM&@+fz2K3YdT3#?xarKUXa5 zaegdmZnt7`{KA(uX1ZyUJH>A4?}(M=x$5!R=*!f2r^}+|w&$hRu^`O&k5oZmVbKSj)T|5p6bOIK+7+Uki1jM}g7^?A_AePyp#!XwvLaq0>m z=auAVwMRuLw1g>2C|)=GYFoOXHOb-5deztIM?cDGtoU@>;X$9)hfkZn@V;rRa#-^@ zVnQp&CEu_UJTB8Zbu=WG?ft`HBg1n&<2rNp-I9g*pP1iFEuUz1dy7Lu)XM7S4c{Jo zbGTt%_doO7Ba`F@`C(pH|TDSbmp4i{MNz$!2Mrqgzi3aE0NPXSi=7)sCava&VA+U z%LLvAl`F3JocsRi6M@zz_b=BqiO-9&oEX2$M7Vld%bFs!_viQ>Wh9A;f&@X~ffX z|3f#XZ;qJky*cK*#lDyiYD;&jy%iTyDhNK;w=4c#d6sy)Q z=LxaCZRgo4O%)`+y#MI*fxRiluF*QE@%*yZMPG0BUAQ>)TS$zfrsXV=V_#H##bP(A z1uybn7_BGbXY+G?YskZmg6WHyrgr(N*m{bt2+er^=*-mhK?~<|ACBDT)~crGQ~0nW zp!=?nvCDj3{{Ltr5Ic5tJa=SbV3@|nz@SE`jh2>~QvyBe4$= zFa6!pnn8S1gWk$5l94fDS$#yI`i0u5Pxr4&d=^~l@{u+D+1byt*MD%1^l1A0$X86& z%Sw0WqWLR?PI_xC;h$T(VXxuJ9XIyR_gbSk^(f;Vg^H*c=DR0ezNlXlwm&j%A%Bc+ znZ^tLRg#Lbg1*Lo{GnLHeFrqd&|tE8KW^@w+p{ zDeLtwgk8u9Fb}x)^T&@+Uw!q%M|cmfT6w%C`|#$@bsony-}zfxp|(u+!b@YHS#_&# zZhawk%P%h=OzZQxw@&=ZkE4IST|Un@`0Ba8*VejxEA-uZcuJ^c>-S?kPkJ|WzkToh z;j7W@CGM|zR)~N6cXa(+tD=8sBfvpHx-7Pg3=H3yi0Brj<>V)V8W+7mpl*Ud{rlxR zw)+*_y}#Uw_24d6FVX8iwH6{qzf^OVN^4)O-u?UU&eO3GaU3fj`b-NF(fHb#Gx4?Q zSw0KrsUBe&itPoCw{K1II{HJ;PBg+v%aw24bK433UCN(4*qGJyXTo8d7@uSd>jb4J z7sj54g%PQV2K~O@LejNA{Y$+3+)6djV^P9qN&oO|zdkLOaxo6BF7eXzsBx-Gims|dN5Eiuk-64z1J!xSXih34i9*Tyk?(#viG}n0F(mC?}{&e>= zN0Iz%mp^D{)bM14l%GA|-s?x>0nPxO&Z zU3V&q&Do`SiSim>C!zMazwS@(&pPc=e7We;9QBjc9!F;e6nKa=73zf8DhKs$yu)=! zRJhjS`t4gxoSrYjW?m5kzDfVi``DIGV7R&d=unP2_SvpH~!!1s(K5p(HEuAWtT`zL) zI7TSGb6IxvUyHq)7|+^gPv(~R=*{?nOH#rOGg4?nVX%1}#Pg22G;k1~w_yZ^#WkFaRx@6PPD;Mn`|?(ciMFZXUfa$&ykCce(yTZ(6_ z{MG9Ig*Ef|w6G7c|F*bHyuM88F7uQW=?Q^bEdNVC_2bA{KIxFu`GBay;eU@wOtVVl z-`il|W_oZ||CL+pI`O`n%w}y}<*v2nY1p|<$~vXe&MUL}Hy=^7iTkJE_5S3`iL!dz zFCS@IU5;id2JIOcy5O|_kbZ4o@ zn%&bIkGQG3zG~=g+H!e{XnFS(wb(~|0!6Ud-%?CFP$j!GH5lE ziQEe@mAn}miEL_D_c$zVUvl0m_vDs`pR6rK0w(&bp7X=;gM&Wz--+zMmvNpjQrGXU z{It`{?oo$~ZBp{Siu$?7QhaJwmYi#`&qlT(g}%CWROd9_*Yu8uHw&hd#l+rR&rRhXaUaean&-<(c1!Fgw+GEV%k zQ;`&Ai{f0X|FHBnlT~-l|Fb4>?y|?#-mbO&%5Ei`B`(x_D~$g{MNt3s)|-Vs^)WXW zoZRU#lQG@@#?zRL13{-J`?Abky_nnQ{XgF+WvSCA+*@mWLd+YA4udA2tv;T3+9ETc&ySo1)1}V|gT* z%p#rGCeG_#q1$;`^#)7*cdviW%uDxr-r0NqvF*!qkx3O(+&A%cwl{zK)A!Ho*2bkW z*Z!V=x8g+h=SY4&_ly%|6Sy|&)^lH0ihO&2)vG19q@OLQ{n^--{k!!U=lABstNF1X zj&UT}H-=~1I4ASY7s*VGI3({GZyW4Uy6;UQdq2w|iK`1+OfKnvm-PSqa#3&E+FKHu zT0N#aVl1{y`afN)ebcJ+t;#ti`U?Zs9#0i*Ihc5O;2!_@1jLxz9t%YEH~I@%aP1s^gLt|K*=PZdtL8@-B&daGbODJ`p}$U^Iq!j zh20lL+9f6BLS^Oj3rdH1V3&2Q3=!Z}mp7e0M(u~12s*Kv(X?V=lR zI}fgX)PJck-Jo?d`_qXwu5A0x-jj{FA#dsE;n<{Zmd_jXa*Fy^x2HX8>RAu|dinP8 z)=lYB$%QEbpN}N`4YBe*((tB%N3UAKXa(1yk}&a-*d&Q9VL?yZoS$d*xt-d|qRu9& zu}6w){n6Fp_u7rw9WUX!4yOqHliruNUHX2~na?d% zJHOZObgh22Zs(+~3%}9owcT3r%XC0zbulq8=n*PAb5ct{Lt(`QiOHFH>7Y5A36B2Y zIh((t#fv5-JdDxl^y2Rg>d=ij>$NN`dH3#_dTc6x-{EZN9=|pu{^WZek=LoMo5>Qp_?JN6*G8Uc&%S=AGxlSfNi#zq z-wdHO4)QV9bUm##A|h_l)Gd+M{{IZT_v~M#>^2r< z>HogPz5I8x+XT+s+B)mUn$ikWxh92%%jtVEsaVC82|KNG zDryB^$#Tx{@y~ktN6#QmU+$3j?26W`^tbK*`F<^H?lWh zax_2Wls5gwvWNS3*KlS|K4$qQ(Qe+%@39l7vu0ZSj@I&0sr|&S@wG2qG%r9^#VhuL zOxvp&w~KW(cJ%Jx`!;$Ybv_W!#s#zj=*Okc33h~v+N+v}Cj*S2kDRXcS3#Zle_ z&TTpx&u7}^U(PRlVt>>(DpRO)+uB=F4xWlr+k8A?gg@o8nXj*x317#v{lB(_aGzidj!XSz z|Ky#!dr@v_{@x9BXvtfFqkeNWBLhPcGZD=;a4=`UOBX~PX~#HsL5B6VS1Q{Sd3y~_ z?AEO?*zS@&S;JG^?nr(7q-mGdcF3*LsQY_kz3ikyH4z#2;DZ)iTjy`iUvgUG>_^t8 zZUXCOyf|c8wR5(clhCX)-(~y-xTiVZ{`_n4vJJ8p&y~(^o~c^ic;cdXP-^$pOHb#m z%DDb~R_|Ar)y7L_MWs4D`QScpfz%pR@8Z_by@r=ua+hnEt@u32$}@Aro8@5_9#3^* zE=+Hfc3avsC7{N2QqEL<^W&_FGO`!sdyT#<%oVDRo_+J(Erx|I6E#dL-5h3~ShS_5 zu+i}5PXYIpM;@Ird2#<`R1|l3~fK-P#IzTcQ&D@AW%6O0-SYzP@DIe21yrT;K08-wA!n zByGo-wSSv~X zXAv&Nc}5LA4$D4?mgq7vFcdKnv8FgDv$#YT+|1F1ttqaZcsA>>fk4aqx-MH)UX^cm z*D){scyP-S#@#!`1fM8qdAm*OzI)|;T;^Zl$roNJ-S}B%vO6hNb!wxxp8d)bx4X&$ zG$IdO`p$fp%X!uTi_Y5Mtv!1L{pWAi(LFhHUE|H{d#P(W`8HVPTWCZErruJ1XnHrW zHfW7e(Aq?ibz7MOXLWw|o%Ka-&d=L6FKXoXZ_1l-cQNlKbGGT?54z~B!!yqN=FKLE}Q>;sdw|a{LkCoo7#^u zRlPgCOTpqv?84h$HL|i)3wST8l*}t%{8*}n=O%kZq|`~hPv}AKVViCUYE}0z5wTtr zDd>^bi%y+*uG;uAQZ(9`|c6o8|F~ z9<*dJ`v2s2y3wLbrvzdmlcu;ii%IQdy7+s+niRgC_3}-2bil>6&lUwu_g_vbTHc zRUFCnh-mP8qiXa(X#2rLzZ=Jv)qTI&dHKU}gELB7Pn4f~U$e1xpYhytLh~Z)1KQP% z4{c_Z5}tPVVTIo;71=_o(?b7FX>Falrn%DV=929*CaS-lryl(J$Jf{98Jiz}yejx} zsIkRxfin*EO_X(^-|gX zFOCYg)mNG4EHEo*eGsy@pjbNW&@Zp`7g`r|?g{(B%eL>rJkhA@|GAnCKHNSaeCV^& zjrS$fTEtJ=Ml-f-clwbR$#JKTx#dIc*DBRDn)wb4>jITs;)G3?>!dahQ zOI7JUP}0lz>cXCwoeX>q^61ShXdC~r&Ev$bRg!a68vR3G8e#>H871ymn zB3piM_tOiKv-)7w5I>z&gF!bjy2~%zRgtSkJzV2>z5{#32JXY>>jKVvIeBvyTjf;0 z%35w4ev{&Ju9`0!qf;%NxwOQ?~nuO7Hp`7SHv{>j8tx?DSQg z>k`**s9&eTn344+J|t@G-}3X)1z#s$Eq3qz@#xpxItJ(c%uAoWdE4gp@6fU4@y_c`-vz&kS%uMh0kKYRYEEM~H8&5y?>8+2zO^jA zwauFIS)_>m8^z-}B9aZ}m)7TsDK*s#sQE0BoH#_k1uDa2=}E=JgF4n&GfbK z8GBkq)6RoWLM4~gcfJWWn{x0kXADLUx8xGO7PoEj=rLBxxXeJd^7haE8HP4yKV(*gT-h3Q6B# z&^WEFtNxJ1*!ob8QGc&`eE8D0+deh?KYW7s#`U(JUYD=++`7Xmt3I0RR!o=jp6!px$UQy{@Opr>(VN`cz1jXayqa0vBt=p;pukCsGt^J zzluuEMN2Ozp7ePZ9XmfGQl_7OUVi1-O~-4zrT-h=zjP|@v#!>P=0%rYASRyz<}v%E#IstzIwXPw{NycQEb&^Z=p%c+q+e7 z9=`kKUuvak{XDm`8X|_X-^J|zo${p5?SHgwn3Est9ll%2V(*P+9ABHw|8bVf3aOr& zw>i}_iYN2)N3sQ<{>_9MXpFB}{=Z>hV7Si6KzNl)KB#wD0v%y~&B_`F|KBUUy3Et#*^hk{`60QyhI6)A8;N!9Uc5%C z#x;DxrW}pdnHI^`o_;UiJEw1KpX9?^I8Ar^_Cpr^&;CsRYp1e&!5#PDdPUhY{IBZk zu4hhrd+Aq!%$yr+>4{sC4Z_5_tQo&o*}W9l)o40L>BI8WUZzJUC6+UWL~WdO^?~e( zt1O%2mE`!(_MLw}KY#HZp=ql`15TZK$WpnqKjMRd*MU7|f(GB3zNWnr&6=`dN5$b6 z-jfzs$}5^LQJip7V#%oWvNAJIr-(f z5U+dv(}K;fn3Yt&3GwOr{^4C_nlcHXV|X41{289e{@YRfBTCCxc3 z_iS$Mv$>M<#3z}|X)-;@L#*nn=obb89DQp*H3)ES6(Qg zd+w6B;_ta3lfG>WjkYzO=#)93-Xl|O^^Ayzhwetk>G7Q5-T74^qEGLJZDx>U$n$3I zb(W{4CR9J&sNnPSVC$4ruG>@0vAJ1&5{mI>`36lf5E$ zYrgGP^rgmMLbdjSHZRU)W?;|&?ZB-b&?neZ40xOC0-r@yk*9`U3}nljPZpiDKS@Pl`N%GF0+3;rzs!+SaAi%e##py$ke zeT)9=-5+VRx^AoQGl`SCuAyyiQ=hPhi;0neVLCBu=?W54QXpekQz!U>%4X2cm|}r# zt!7nyApvUwUMObhRBXyN@)V!+#>m>q`$T>DB#lSz(VM2X-mIKAx7_Ecj-kow8Ec#$ zITsb|xcJoi-eUig7TZjk*B^f$wdUmPT`zV;pEwz|;LwMnuiFD-*&Va8&p#-c#`!7X z<;fHd?im$3l|T3d&2uZrVvD$QU3TWy*4s7pCR-C1pYEHcR=Tup zlk%Cfc1*r4oJej6-_L1&Jy$W-2lcTdr8?`c)PZIh5 z*Q%byZ;kczx7YL3y4D6i`tAQcqf545EXZ9p_+DgMbZ^rmg>t)~KS>qal!;yq8y!fkTK#OS5FFKo~?dwz@K zr(AyVox@S9dH%$`pYvDrS1rGJsNfV=w$G7AlFViwI>+3lA$RnR99zM~wV#$3I2|xj zd2&f{@$L+>$RnynPbaW{lj+mkTbAj<)~`S1ens{Al~Xk`b)KB!(4NBc;Kl#E*~{9V zJo+`s<}kybV+YEYwk+vc{`B)C(`nHYuf(h?)HP#VQ<-(aaax3B?-ou%HcFFjP zCBAi5*=v0ype&)yYp{3`QE8Taa;BLU-FL0GwS5GTJ$q? z?YxhjQDS0XsAMIg%~4pIUjn|m3b9C0t(`4E^QVQA2Y)Yr*kzTecjE+Bbr|1R{U~;m z&eYiJ|M&jR%(<)&5cuT8t5e$*F9L{sCRAq5>fLT=PWW=9t-ZMkgwLL z^nOvZE3bfCG~N5d;~zOTEWcJhzf?cVXW#VFcEJf{N;?*9p6~Jf$J<$5MWxqHJlS#n zibZI4hv=)0Ki3NQ!=?OJIK)jgZdrf--E!p{6$?CU(O(&^c6RHTon*DFa zy?VL32VZJR+|k#JU|wsSfBDOsTQkL9I^^lHv&}E-N*3niduC#@t0mcT|Gk5%bM_qF z+*QbtHKmw)(%e(~wJp5XHN7|9QL>W%%j=WVLXMyGOtkTCk1+oHJ7K1T-apq3eMfz- z>oxVR5|sRACKI!G|FTri7Q4H0>rczH-r`VI+^{;?T(?O*=ZMloH(mLO?q!MclB+i^ zS$I3xc-uNob+zN?rpL9vx8}WfPvFL-wyE`+N0s7)q+Xnw$h*6(uAzH_N59wJxRWlv z!FIEzZ2af(rrpdi^x7Y^HiyWidvT_W3=F%88IcB^{G*$glbN0es{bbWgLZC!Tese< zv;SEh)AI1mb#PIcdM~9@`0k{fL+@I&CcVFRH%DcmU?E@QP4l07Z%+TP=7j1&%b1gU z96Xm7v^SaQ=oYqVP1~~|hVS8`ZRy%-qT;__ge0(bo-r=4eXse3f0r_Q|5nZ}qh^gC zecLX(TEri^$r#)HK)K1#hX3_D+X%OVQQ!5q#kYU>^@EqA%7E|y@$CP97@)-e(MTCUwz9=THbgFjw_3q!>j6DU* z^tasg5s!B~d~s3T>xG;5DXl!tZZL1g+gU;T=R9&w&kN|zJ?f*lW72#6C%<|xO>@y& z`eMo3tDb%96q4pjdYygYZNF4$-kf)jZtGt+-=O)=`Hewhs@L~|v;Qv!X&<*+J@GTz zI`BQ&|L*KzU|{fKWMD8Q)bs!ceoAU`eo-Q5Q#O33Uv0lF_aO(Kw(q}1Z{}Ux6)xi< z%hJxLWs$jJrOcM;%Hj)ye%*h|{z}2*@oe??&M~49BC~4MWIVo<*05S7|6HPbG$tls znq{?7Qdl(rQSXdbtOh^q5&@et|1S~i<|CWJB z?fLZ|=3Vlb@b0x{k5{x~w#k%`35z^KTh|n5UK6t3G?y>r>Hhb(I5kzLtbAMRSyS_F z=ke>;b@W@VJTcreZ4txD?ZV5xaD-?@E^K`>QTAU;Z175n{r=lnLsC>-{bKIh^Y3;k zfAHpse)?w1vtBO*mmN|qnA>50;QOVk)3SG2>IX>sEMk_PJ(Fvl31i$d70Lg8Vv9ef z+Wei@Qn+PC%f%TsRxHY0AIm-LzP)*+Ce^!qNA?1LQ<3x0Pl|hITvggR>zKLydV#+| zi|qyewVqz@cQxulMZly3Y_}(Id728>9I{Y&J!A3h-OitPO_rJaROYDmGA#12 zzx?c8h(nopRrPaL_u0jXG3JVn(m+S z`LRFjk56vilD8y;vCw-TOXh5`l8E!X@_Xa=&s}%0Q|ZyeSkp&8|9eaP+!yB^cyImv zh_wH_bF_{go$a3_9}>R1C{9`EX#3g~dln~8FZgJ;B2>(w^7|@&A0VF<{wY?QNo&=ljY&=+;LrTXFT9?RxbUCruR= zznJik|7jq{8*$I4Ij0ZY`0OiPTzD*yCpyXEpoYSnP~Kw!=U(jNd$VuW!=mj|Ze?9N zbtXr`Q!O=mJ%{e;(g&Q`caI;q@^Dr`!O`3Cu0M^v41zw3*6Xmd>ac|pMuMF>K6_$<#+e8*>o%K7A zwL$rIiKF(v-+nUf7A)Tmi=Q^mzh`sS=5p?#Rwe!5gTex@KCq@}WGvb-gUu;u+dO%p zjLC`fOZ(0S^iu)){Kt9m{a?vDG#PqHnCFl0N^HJEKCpjH>Wg1bc*uOJlvU{5;&75&2as40P4@c&ArrWl0Z96Tx$(U7%%gN&O z#OYE87rXA5d1+qX2aQP!>UO%d%EnA-u75mNL+N;oSdmfZBfqzIva9#%tJn&h*GM`j z78B%uI)CZ8tGBHjB|}X*E|)H2I3}4gvxHBg@^bXKj2GvPOOzBARZn+u)H&E9(80rd z^grWdy_2 z?4}am_Xe>&ZEM5Ue_Oe1Sz^}lEsOWb{#8D6NB_fA+v10ZCBt4{R`g|a>)wktl5^xo z@U)%KQ;rB831>-uS_wOGklV>S77~v=zp7z6l{2Bx~^JdhSmkpiYxxFVVXH=o_=~9 z9A-tL%gp6gsPrXz@>;(T;d$=UYM7$DhfjF#Id_|A->s!#Yc$=fW@IKlZZhxj`(pE< zr!_Z8U|peK%7&w-=eurqFREJh`}AKEW7fjd6ak;SwZGQ>VnD5+g2UaGf$D1~Mh3zM z^M$adL`6Q$pnr{;sX< zXLn4Lh&41Yt4T8Q-?NJSx#Ah!!)#XkZI?G45ol@to3-Tc^utG!woDCMlkzY_Ji0bT zPU`T_=gpsWZ{2_V_T#4c65cabsj)1RQdcz%@si8pz9RYjU%<2lX)_mo__g&r8){In zmof%{_WW`%6S0B>926x*iFw6o`9-;)y$+M$XVTuet#a_^>s*eLOvaH-$&HQ2#4nxk zo1K$;`5u$UkNded69lKeu+6>y`&;q#*BgVlS~v6xsj7tT=#0}m&Z?;UJX@b-{ToUTxY%?$ zzc^f4{@;9_P3z@EgUy$GbsW~3y5fai z^5^co|Cb%i%)I9Jqa{o1c1$bnWSG!^u$GE9D2W{xz|M`}0Mq`@nIe8=P^fy~euNT{) z%^r#wo8&BGWMH^N%sE`}_|S!)-_#m(JL`~xK<)W(ANek>%q^_(le9YACbEh4?(>)s zV)pV;tez|=2T|#xBZDTCwcKyEr8x~rL7&8k_ug?*% zRc0)l$WuPiidWny;=VVde`4nf!E?PQA8m=5`OE44sRXw99V)kO2UUD(*&k&3Vv?Hr zn}nd*2I>p`%}Np%mN@vazH^$I=ko>wKI2-uo6^r-8W?`tb&H8>*KC$YDqF93&EIr! zdvI;4pV+UHG5cR?Te?m>QSTno`cPrF^}M%6qOa7JMr2P5tKW2Cf~t8a*GE@&Bc*$1 z!XHkrt=y|^qIXyDw)i_M)r-Lsn5<@U`xqSGHvNr9lN6tvW6jTGkBQCts{{lUcV=C^ zzj~#_>A5qXcduwDzx=Z+PV{uemMQ}&^VnTBJ9;N7xStkt7nCL zN@U+yLS=A&{yzB9SNeh3}Z(F0I?x*%2Z6%#y z`G$!dObiTc>_jYsg~z#GN%7f`+j+MfK>Ll{0!25p^b`fk&zYpf>*AEl!!%jEWs+}% zuy)1usP)nsPw!h-A8xp8((k%S?ERTJmc{4RO*VP#bZD2kfX!m3b?;6rS|B^izEfp# zjLAXPQ+pW&D-S)psI^$SgVlfGyWeZ$OgGjA7XP@uc%sui-4o$HlTx*vO?J(e5__pE zYGwUMe~PK2{;JrmQL6$zc{5*F$~IvJ$99*l6wcfk2CVJHH92$N9d>EjVDn7LRfYSf zw6S=1_pH;^k93abOqV!tf61Rsth-&dl=6N!?%~C}B7gO3-@S7;xTvKpWC)p2=$5M@ zY~nYK@tgLFdlzk*zsG)hzpk!lw;02A!~F|exexq&i5tfJlzCEw|asw)U6qk!-&nxL#haK`yI4c!T=P zg0@e6$C@4MB}%QoU;LmYbN5U8_V0YDCWgIJegwNOOIi5q^Ny~pHticcvI!+&GXrLS z)cK)p@QW)}aYu$tjHl;ipGATzCJV}QG&enUUDn4xcWSRf+1x(6wccH`7qjQjfBK9! zb#;xj^|2@S7QP4#_Q`2hzkOoXF6}D{N%N+=ry6~2`jj-~`LU3hWf4oe{#ZnMXo)nO zI{Z|({P4%O+uvP~(Kzq#c`bmIQKpW=raf`ps&=2#iaE*kjyGfMUh~a=sBNMnKk4@G z*NzF#u1?+-W$@S}y_EBsfW_-6K}mC6{56-@EcJQES#0+E|5C$R^(RX>`+ux4f2O$n zN8PJ?tNAUD|JYeQ-|2SH|LG6ZSL}HGY?sBBm-F7uy77Cu$%b3LaWAwQWK`2XX9k4E z^u}fNeR%gctCs7{D*O8A_f2o@k3L6h_4hk;g@Cq3ZzN_sqAasGGbuAC6FhI!JIOoy zumR8U_di8;rM6D8?Ry#Z;@TmeMC~owZ%p=tM6&kD=A>t9{Crm&r?O^g2z&F_>2*~% zrlfgATWp#dCYcqzLDp;cQ>Ejqvm%1irph@l|F%!`dfSul%T8=)aak?$M`1_m^2UEh z4m-!Pq|Q4uZS76r?i(R$#tPRzGKB#J*j>Ym4jax)=S-igmTASbF_p zl4@_OywMlerX0aTM|F$0Sv~Re!vuc$IlAGhFD*!70ur zJ-zgE`}FI=2Il9U67{F_w8Z`G=@m!|2K zAOE2>ZR)c!ii{W;8154j?&bMKDTtMclMd#ASC!Xt-M3PC@h(w8^rAB>bBl!aqBT3B zU1o>)X*sNTzi-mEElivbo%#N~J%4U~he>eC1EF;usUFW{-UZIm^)H&UsLvzCEl^%+ z>)EvynTqx1&w02zJeNMGx>x-p;su|q%}qs(&m#M#zkZpwiSLhOjEqL|#+dqF#S)F$ zqDyTGf1Blcm;Jk=W#970;DM*|xu%4x8SF>Sc=e-e@05q|7I-+Fg$&7KFHt{ZmGH9izEN8*Uh#fYUoFZrwU=8O9^A5Gi-^?AJG zS=FA6o(sd1cQHHM)J%>I^yeRqfU^4f4m8(B^67|=_%z! znI)Nd>AJc3DXE}#)WrMwO`x;=e`^0a<}+pQW0S(kIldpU%B0}6}a^K-t&Fs$2Y|^xmDY+&kVj`y=Re=K8OB`8LL<#)~S|U)V#gtET@K= z-Q!q4?ofnJ%9$8Iaq|`PrqS^<%xfD*LQWYx{!^iQ3v_Zg$&W zdszwUrKr{S@p0XYUxC$;#{+XTUH;^GxGi{ z^#5LJp7s&$nVa>kFH%&Z=0c!n-hk&IZZFUESwX!jqUK zPcpI1Gw)j8*?i#T*ZI$j7u&GJ`MyqSopK^yB>nSMKaFj&Q=??gtMjh^y6f4hZAZVi zy(?Ha!DyS?jpIs3b!B8OsA{@?a+ElKz+p|;rS_>)ER6&$dLKWyR% zvpyu@l&u)iXvg#I`<|oh&dfyzMbj&-Z<|H?-`3Uw~O|9+5XLd5TdnV1q!0?QXfx(K< z=n}#Zq=TBl=IWIc?+ppezHJ~-cYgha*{KC@bOa>yWT)u%IA|)r{3dj0mBp&ZVbe@J z_0Br|`|Y>M*m~RK-rP46giF5PtzGWlzA1()^+KKV${2~Ms%gL0D!gfsn|flR`<7*= zD#DwU-w6jkuxvYQ(6qGt&V=h8y8@VZtDP)f<#Nz`^PDAHCWg4T&Ez$mbWdN5uXg|B zFH?>+xVz`>X$h*`;bM?DdnNyjZwER2XX=_)EV|XQ(_`Mo-rcOGBE3SBa$W{VFVxvs zUn=+0qf=Als+frf%XPM`Dh00=X1Bz1XlL}8Xq*d{kKLcG?7Un}@2Q@7d`_QUaIN>o zg;s$#XC?V-DL$N6cKDL&!pOI*9J^2bW@K7@F!$r^1E-9dZS^W-vwGRzS%*)3#GbKp z)8Dxv+9HRKaeY?_c97Ygyxsni!!iHY0bvJk{i@RF+;;4QMb``Mld6jol|HRFSj!f; zo!R}x1l@y|x7uFmnpob*-vnc0pZ)oK{)6%%GVyvhSMIR-xJCbIW9l3sQyxnrCU^=F@U_lxQnU~-?KHl8$aSf(f-W*3lcCUO}@?9^@boVp2#V7Zr2nUzn^>LZJ=xad721dJ{ zw#6S+q@Bz;RebdVm^ET9pS(18vAgS8?e(22LYK(5@BLSjU>$Z}O8S9bM}iNNi1iD` zV{8JghksdE2Aqm+-am8WzYWJ0zr3*N_qpZ4%Djnt7My;~lEiP+&ziE(l>cZ;)8PfI z(goaK?DKXssVVfN?^)4&`+L_4MT3`<7Vr6a>Zyy{)z{`L*Ef9LvoxpvoNm6^ea1&| zAGKyV<}gJ{C5W<@Nnx zl&fF$*hQD+o^}hL?fmFr0$Zu>!H$PZXJ8$#EW0IF>Xou`@iA+QRcLVT&rLCt+Er!H#R+V^&X!#1sJ zGfOX0jC}X)Q_9rq-iAxwhJ`MRb!&RQI`8{j>)D~nvy696Sy`glxHXz5`yzv%`DTu# zEA8}{xsG1Gy>^$sXt(%{rTgNU(x3A_N@)z8U;eXy$-|!NZ%_7_Z_8P3aPpG$=DSs? zQ(m2HdU%IH_gm)Sj%eq~>z?mdHR@e<{JAxt+_YGK<4W#*Ro!eN_E&md89$kD&hO)! z#KMCc52>*(HaA<@HzQ=@;-HYtlb$0rTR;Wq=|`t4L>UUqSc#qS zxme+=d_1?Q(NVBi0g&_j|!G{qpwF3zOopy=Fi;XeSkf3d1B5?mDZA- zy7sE7$FiFBZqLq3*|V*0iNkH(a?S+aCmDumGONB!(O8?(@^4NTpCg~kw5F_8T+Fh$ zLEIv_FXp6c-f7#$@m6ok?+bDj$Et)yj#(vJ6bAErv2&LRJR)%L=kb>n-Aziy(rdZ* zUw7MgNJD0Eu_RaKf47@UF1kFts1kQe?*7A{@JU+C8V~u>p5n^9lEeyl^$4li^j4J# z%Ca&rxQe4*+5=TwkXTdy$ag*E01x=+FN_;YAZPO{e z-f?ARQKV$_f9vDZkCe?>lDUvST1bBWduwrddFz<`M=H+eZCUnje17(E{0yEOw~rU} zdP*I0`S~nv&fRnR`*sBXvpReIyT#9k?v`OKdvrKYI)Bx!=K24?rE;!)Zb5$gG|xS5 z>fdIs5wI+*vi&pL@U%mV&~JgG2iIHHN^bnAAJqK!s_pFib=GrtUjCVWeRjp1q`fn~ z{}p1%w?E@|JpNAQ>?c2pzI|P;r;*vJa`@yWeiid~&d17E2$tRW?*6K8O0U8BnEMuc zWB2abJcmW{N9<*dlXGMJj(%Ysa%6_g&@l>h5>!4Bl zk$bvsg6mP^z%b7}sge(EC!YELR*iGX582$ejAG2Iqb9=096p&wtRWe^Y^QVxZ1N zqjM*jd|2E++_*5)uyfu6{(ZM+f6Tle5u4VL5x9Gw;bohXg;zgul=Es&pB|UCneS84 z)ypf_l`P-K$yq&l`RYY(X1%Ywd;eek#*-Jk{gz|u_mKV|v%77P8-j$_WfeVGzT)de%Z_FkLgY8m&*@=UM#hQ*IlJw7~Q+3SAk;o)h!LZ1tI3hb~u z_Hfa|6G_io3NQcJFhv$LhU|2_!Ee3S=Kq6pQU>bW9tZU%?C~jWnN!oU^?Jhz(ZA&$`F68? z?6*c%il%8VTf?t%C|H*gTH@zaC~WV=Z8#o!@#+5FYZ?JymmM`<`6XpvfypPibQSp_at-^yke-*Wq4z2E6?kaM%_f!te8clj7C)0z&t zUf6Q7LV~qrEhn3n%UrW(`@i3NKiTg{rtw^($C5j4-~N8@?c2FemizC$ZE`nfw*UNl z_wUcYGk@Ni$+MR~zIgd&S@oX3#Z%kf_xpP<{gJoXFX;VYrO5lG*FL|#gDgDxbDkt^;-JER!U}VS^2$+zj{%<|L?fmYoEk!cd+7$^qv`g z?fQQmMYr1ImoKl%zHcw@zy6kTy}`Z@&2b9fdaLyH*36yz@M&sv&W;Rr@kj61-G3k3 z#ryW-m!Hi|8sw}piF~?Z$$+zb&$yU{Sz3T zUjM~cYqBNy_dPt%e7ri0 zi{*EP%Jm#PU9|NX&$;Dn>60!%-J)y=hcX>-S)96 zw841WvS%0WuYEKn>)71IC$;7^F3A#Em)EtzJMN%GazovWB}T~%vm4o4F1==9R4aB9 zTh4AfPp)LL-HKT?1{|Lb{Ow=fy7_C??5gK~zjeP%G5D1Dq4Zdt_=WY=_J1B+jbb|> z_j;Z5ZuaE=t@euyc!hbsDDV|={Wv)H@4W-_=l$+zy!AV3t=KjXe}gyQcTH~CVlK9& z_R0T8{Y>>*-|LMR|DD@yHea(?ck+L{L38Qv$|GTEw7ER+qk~a>-Vn1b^CR{*ZTghxZ(Nt{)KMuF;FaRF?+0%HOV$hC9jr6f{98Y_?srgl$lPMF zhAB@M*-j`qs`Dl3$@a$Dl8z3C1+gpvKI`w!-*$Y-_v|dS;7^{c722orm}eOq?F~#X zs=W8Te(pBw3p}eLzy4)r{rOF|T*^Kmv~ZWe&kYF)E)Dkm)pN_@O^$B&iGCcn|8x{9 zFKTb@eh-V99Qd8U8s&=l|952lCTToyBXHD{Z_8R1udc%GT&a^6gl$>06a zcTQ8^rydch8$n?o&((d2SpEI6r*=Ar>}h8=X}Fw~opO#hU?JPq6$Ng;UjNp3*>Zb@{8EMO z1yh2K{}gw(yqUx2u#)@pti@*zE;+sMMX&MF@5NfpUsuVNRP9}4raoE9{mUyezr_j6 zpG=I~mVGbV8|U`0E2{dK=DfKdpD=CEE0^5NBFlBwwkhUetjK{seVorugzYiS4!%Bx zVc|yS>DjG|ADuC<)JgHYkYc2~lw;D%^+F6n`(nisds1$CPI)(JYWT9<29``0r50A1 z$15xJ=PY=~v1#F1ksq5DPTHs^EtztXsmaQ*kV zADNu0;~AcK$;BuNG)aj+ERuex%WpBQ zztXl^EY_dnaQ4(GrrrrztsGBX9UNSa_biMv6!4xLtJ0TV^U8|VZ^5PY&b&_+=)N?& z!JVyh=Zx*LqR4Y>SEnv4uz=-fc1tMd5?&zoduMNcSV8@*q@3cI{Iu)4RxN!O zvv#+0Gk@x{9eOhw{f+$Rc~~{=I-uG+TjOR^V*a}59nb%n#krk%WW>Md>>aeRyr;lkac# z`ghUZu&;d+PlsrESzo>${$aKSW7N6K*>Wp2yN;gzvv|ech%FWjy7J2&KQ(3Ho!;fv zut<67;u|q6S_a3N`={Rh7?2>#Z&`l!=z)VjB1&4`o?v9V`tmY!zPkzAi@kdfJx$B0 zopZ9A&xw~k?eR1B4}DSwhk|pyOt2B>ZoTZD{KSvLV8tu{yNT}-7#8=wvgmpBVxM(M zQ7qS1@z+&(i$zcDU04!*M!lsm@?V)mk%bYzwSDSY(Kyz-o0$c}9L^kNOt_I?RwbFY zT|(`jwpWM2Y?Xh^SFZ2Tw32!fneQEOcG6v)SoPFGN1y!1Z#m_f#X>SU_xL{&$tlY# zUtRvi<*ZfMpHik3XAf=Bi3>TDp_LUSf9~{DHS2#>#b+-H>n`0;tNo$vvg9SlmrghC z%G!O<5|(mGYDjpPHeEZ`*G+)Kv*u!XM{E6^6K|*f(*wZAsl$wwQyVG-j&0N~y1LR~wno+Q zr7Ht#es#1}y4GZu!3JQ5RC3@BVren0|Rh`r?}(-?hzO7bTLz z+oZGeYs!b@Cy$SBY;|tuH*M*icH;JhXrnq==`>}Id*1R)4XBVinQ?hvL8 zYgq+jxM#HJN{1|d(Iq(dRyYs)ZMUyl3S9sE?D>l&BcExX*?-t<(Y^lruC9{(QeK@L zQ*SX|-w?S=?o&mun_|MNdw2aFTDo#G$DRCBAEYRycwE~xTyWPfn=bnayi7K)HaryO zmvKWW@kVB$U>CDOUl*=bjSg|qa@@Kg4aEt=YAx-Qn392j}(c#7!!+q*Vra+Z~p z|1S=-w`9@FV(iW^n7sY0!Jjp*J6(<~6<`02>)rXMSD!v%p0L0wwC>F`?>YCy9+udv z1#rKaW~sleyLrnbN_GmCTZ zzaG&#XPtNj57*+wceCVl56l*P=Kf&I(;e?j3ztt^tur;~pN{$h_1&UrUEX;ripPK1 zL@iT1?6d#G_EiQGzAs+FSR{P1rdYURW}=_qS?QMFw@$1qoyc61)jR9zGlOkkildJ| z_E~A;e7$U4b@3l$nwsp&{M$mG#=nS>VbeVI)51Yb5<>pvHmyt)a0iZ=Rf#6<+ua1l7zJ|JrA3XT~ z^Vg@#jNe@I(^1&DN%;TX;5_YfY74@)^gW*=b$=;GK;EzObLDbuUjBMgvr|X1HCyk> z!=h_!sXrgwoN@EaDowBX2ln&wz1^^Iv*x_?_=84n+?V`TFkQOh=v~rWx1PDFlE?3g z!^eN7HQJ{)Oa15wHJHaIFS}Az+2{E0i>qgf|Np@Eb^gP2=J$_6BbP>e72t|YaQb+1 z=DtN9tyg&`Z4FZ`xsu6XTwde0i}$GEb}QX=2cKohJ>Q#9&$X}oir1Rk-<+?T<@bNw ze1A>uzOvKXJwBT3%afgywy|r=y`>(fY#g>LKRohadEwHhbGCf!Ww`(AsoScjZ?)9Y z&aO*+9#Gx2PQjQjTl4n4%xf(DkFT+Iud25B^+!O*?!x^$)22*G-O{|y?$m)Enc7)V zKU3J$-(C^F^=78zsnDM(i)N^=c@){*o%L<0)9if`b~DfYT5{Dq+wZV>&1uopGOtCx zNfsIhf0wmDip8`vbK(xlfq7~Kxm0#08%5GkqwB>}jI;)QI_UFeoEHaAA zxTJBe5|9GuF#_0Fs{s#5P%=;(u&uTi~nX2{ZZk61c`xzNld!~e)opArbmtLEU zinVV%IjkKfJ?ih?Wv@2tf5nO?{WJXwrOIMn=pNzQo|IcT^A|shjc00TOWeA+*Pmzn z3c7j7`jQTtWu9{T(R1Kxv%vLX~n^e#4Zu7vT z-Db`Pg;Q^m{Wun;x}N*oILTf<-v2y zt;R*1^UWXle*ImQ%V_t_srBdGowE{tIdo1jH7oX3%ZqstcvK)-sqMVunzDsY7v8>W zbo6%0^ix5NCr;I#vs-<`U$XjQBr8 zY2>Sap#vg_$t%=xlxO}vkD@{A6IoDC&co0K8c`_%52?VX3DyS1f)2o{dB6U~heHz; z_HEzR87L&}YufekphR7E+j8%uG>&^Q1&3C}_Hr(XIpQtSm>9yvJyYdSj?NEm zquVScjk>A^J;HK{KFz6n^!(276?;joh!nq^o89#I(Bq~jS-aA_?|rx<&wBpg*QNGF z3-7tO>d%ugZQXeC=ofME*7u1@H)qS8lHxGFQ3TL0U{JINGGQasBqF!ywN{e4TjW3Me1@!YdseDS-&odw(nJnQ>7 zq}}%m+&tXmmc1cn`@B7ZVtcMVZcyupT6;?|@L2Etq?BVu4-eJ%9r~@brF)X(QN!*G zy_Yv8pWiwA$K8e3=h{^q?t9*^|DfGm#%F(JU4G4U#w*S)({jEVx|Ov3_~t#u`NhSA zzMWfz1DjTyJD4pRKHF6#@xs@t3vAha)9x(u+50dz{KhmHMdM}L_GH>f{yKEbYzl|N z7p?NJa}7GqS6YPI;!mXgZF<4$U%j3&UM1qQhPPX^_$8Gm&&+(iUkF6bd-)&oClefM8{m;e!4$SXkkGH))v9d}v`b^qt=YMSp%ap&&V&xPv zGTnJRMuhWM!hQCfr*>Z+GHaNWhOxgYsJxpY+T^wHyz|<_p^G2RX_&s?AM+O(j=-B< zJZHbH`V{bc*1uDBH@n=T6*9QxmI`j2udZ_Q$fEKKF<*H*qU@|?ywl$K&*oO&ZnP%$ zimHB~$fAZ7W#2x$edz%u4q>^% zRqeZs?Dl(o7M$lD`9#KRi(bF~uYYeIRG6iBEzRIOE^(omUnHbVr@Ya*ZN=RAGyA@+ zYrb*pkYd2oc5T)Zt$%t9HtKGW@l&qn{rr0w*UM>JB93UEmXJ>p(GNe9B~|y-V=+&C zhgeT>C$~_tfZs!d>`ybK_qYfDSeg1jO&T4v*S?DoeXq^{#MRdZ68#G_7dApPzR16ql-tZ(gUh z_uZ_`mf_|lY^hxF=f1xRR-S$5=fkT8?9)5MFL(WyU3U4R@%s2s!I`$p>e1F)d);L7 zp2^3+@X!=n@`qhhb5$y{_?Fw3>iX7OnbIB`m036vkH0&iAf43{!&rFNXIZ4@X~Tyf zICgr@`nCW2+jZY29axt4dtpV6_xpS0Z3#UPz*Bl={$hR?#8kFM~Vzdl;qwORG7dF`HA%Qk1v zaglg4GwI~s1v8qh`;;bqP}kCA)$aLLwb8FfIJtB`Phrs?QFpi3SK0mvy_q=C_=jn# z73(_vc*zfu3svom;sPFti*hNQV~%)PzCqw$@%QQdI}QaOnqB*Ba?ghevr3AJk~re! z=k>F{4LmPDg|qq33bs!bO^$~@e%&hHv*%oX?*8}e`wQPaj9%6-Dz;}Yznl1d`^5T} zoZ{j)Z^TdYp0f8?X1Ai^%_T*-?+*LUYH;^TJGvr`P3-0}>zk`Y58pZ|_AX_!mRs*; z(b$UiMf|E#^Y3ZtsvhHaW4dVNciL$fWD*M=Fs z`oH!%s{o@w;DV2*Hc0iRyC1GgGg*`Fa+vAAjk46&uEh6muQhppZm6Gk;N1Ufss5@P zVh!ii-@nZCA>z=#x7Y5zu4tb4=5A~1rMZ^RqHXR9{&bshGN%81f=R|=UcIU1@48pb zSU>-sh;g{*GTUhRe|sGvAFfD)+!fgX;%gdEH#&WIF9+ zL!gnpuiCNi4(AmTcFvsMWacdOEtl!WC5wlT^4uo5O|4w>z`0<#L;9wk%fZvUjZO;p zgx@}~YMa~i2Nlv&U0*ru(l&l&dQ$bV`{P<8-oTf)^$+|B`1y_ds;gc{<`es+X*0hE z)aA13vtKv^TBO(s# zr<6RO+>;nx%X=(5J#K=w!OOa1$5lSr)JDD7D!xT=<6Z9c8Z&gWJM9iyJSvKy~aO1iJkAfjOmF*v z_TxM&-kUKL{guDnAXmKa$0CE>$Hg>nY|(nHv-ODSLGjNqYL*LlS16XMvV1w*8F+R} z_LAic=P%1?Y_Z%K>Y=tfLg|6eX$ylOfv=M~8upv{{y%5)5B<;ao@*sDqoi5d6r3Uq#Xi5(iG6r>*`cU? zXLhW~Gva3S`SU46+qh0eoo9;CY4I0AUuHamvCmQO-dE@m-V{z z%;<$cdDUUY*kxMT8)K(M7z?WA$xU_nmTtDu!2D^u=;Pn-w`VTe;^O@-MfLCmImTJ5 zyt;1*JpUA>CHAc;H%fGIh2rhrB=aY)I4A63o7lc#qHxxt$Lz~07{#R@H#mK*SRVRd z@gl~v-Y$F(HziJRW8`h}P)S|TuX0<%w5wqC8fL}S7q0(ETK-4riTH{&Z!~uEXZIN$ z-}kq{US2ZCpuT*|(r+GTKmPsRINyDum#tF0)*n92?Hjt3Ke^vgtcsQ?Tj6_T${f)x z>%CL+_Fw;Y?pN~rjx*WMj?6r;{7^u1;QQwr%P+s}l@NPqkjM4reDZV$-^m3lE@+#- z;MH&!eoz?upiY+EXTq_fXJ7Rsb{J+JnaJ?H%|1QMx4?E4cg6kO_@JX-7B{fJU3b0k zclf^rlQ*^o>U{K(cpSJezpC!OwBVfPH3HF0;y=upncR4}BRv|9JO2N6?EA_-iS|C_ zm`i2W_d2w*e4->~GF>fg4t&tten@{=(`Us9z2c&rn`^biH0%q0=NO*#JREYM`SZT| z$E-24WAjZ5);wz8`%m@p9E(e<-!3RO{lXp+9J2Ph%A;$tB8P7lHhW(!_FIu;!Qc3L z2KRpc`)`?^sT53RGtz9u#`{ku;DUFe))Ds=tbw-=wH*3J@j)zbT56|?Z1$&#C% z1v{>8n(cA8a-G4^nJp8IFK$q=uTXjArz@sCG2>lmc2N6#-Hd$pDigjsx!-#e#h!5- z4;9trUc#!_na%XP^7{3?j?+@FR(T)h(a%qP){^D-b={7oW!--=IP#?ry^lE&vi{Mu zpo7o%9AeLcez~ezvZ>EEuXNoOvHSV+mc0kE4*JVIi|x{ z|CoKd&!kmN)3h)5qm*4Foq zdmR_foBx}69&?fZi|=~77R4{ND05iZHR5Juv0H(m&L@vC^BK(ca9%a{xREpW(+is>%uQ=iw+ef0b9UL| zEm5~W-1GZ$;`6VVx(v6k64@0;+dmxaGG1r1FMg&}$QssLMKQs1>=F_#v8F7T%G<;_ zspCMpUhx*k$ERL@ITrqFPV#NRE9?Ch=FJXV^Z3a#!9{M$>t5b?U085;`JS~=mwTLL zSG%niYu%a_dOX|O)p(0+Pch5&gm0~vywz4r-5h&c;FVuYuhQIxT+Tg`CaZauD{@rL zZHx-P8n*SSiE5ca7W=&Fqil9T{X(WPciH|0oT-UecP4-P-NgI@h6^v472Mxp|I@3# z->^&ci7tPmPW%c#2}Q|Do6Hj-3m!XM({6NTE@%4Q<7QQ|LG{s6K8_U!BUEJW91bXD zRdtqE=2#a{_x77@uJZv2YmSu}H^ma{8sb@(vhw&E)P!si)0;8zOqlPY^>?GiWh&-c zmJ6A)oGZS4_w~o0mCvF?wy9`IZ0@#eHp>Fch0!Rv47&Mb7^e)(l} z(|OLG7_- zxnWJjq2Kv8>?CJCX+K;%SE`#M!}g#XZz}(l-{wD}-kEPI6%S;-IC)j`&ylv_+hDnbPoJg)-zw+(g#d;;2V%0`W$NL<9gugp+{@Apr*oyE*Tn;ewf_gz9Gsfmy>Ii(Wqi|* z_TA81R(E**`UeJlYLYoD;^|+wYro~j+1#{PwmtZ-*_7@4t>&p=nN4PzCzO{LJelHn zV%-Jz-cKcA2}S1>8@l8#Z8|;qO^x@wDkBRU{YfgJUs>F2C!X^A_pm^I-k!AUpF=xk zj-;H?U%9ZI)AGzl84K~H|4)f3Chywb^U_c9;XR{6b9l;vwmr4HV&XX8Trlc^?Veb} zX|E;zpWrX(v1i;DapHNk!`y(zOSjj@9WBmy^5o#65LWlD%cVyC>8E*BZ`I6Me({Wk z;LBT&8Ce!qHP#*Hti1V(|~z1=)ar(;zdr% z?5l4t&HOOEU|Po3s2I+@`4+zGdhUg<{%ZT>XoTF8cM<&^`d4EUwQla8;PN@prdDF# zblHnlmD^_uTlYEK2#b~r|GItaneFQ%R6Z7OO)+e-bebrCIK26{>#vZ2)zj9xinh6X zb8F|U*!}mX!@smgeknf#P1$+k&Mn!v?6}F>1#^pTtz5#}y+uj*M(eE&97`e|CK%d( zyubL!{EGqqBbs~ES8*SbN^i;|vOng7V^o45PS|CI2v*Cut(UUMO8^)m4lcQ4&uciD*VTj5(j z*3!M}Z}+X$-XQxpVSdJ&pafIh+Q%!dma=YLRk$Ia;o-%=4Ss>U&Zq4)?T}CZ%zEzl zMB82KZayoV_)O$M(<_G{L1*0x0T#6ndUM|%i9Bv4=IQ*eH z`?RMg>(=7C7iGW6?|5<1Mqh7s`?d`^Z1zjvw|+RrZ(gn6wSUcl&{x_ElPBG&otL%a z=^InaqfgXlE^}S>!E;$adQ^f(R7INh`*q)>bvRxY?SI)Y;aH2vs|?M*{0hHfDz`25 zY!tjHbKrxjUHR#chI-QXCBNzI%RJ_OvXXsKPQc-5PbP(M<}1(XI@{giP`f&V&6knO zPcQk{rGjIt-MWA2d}ff9aeOw(YX1Y~Wf9+*+ZhEbtr=$t7&Eud7P=WE_JrF-a?#I8 z>T7)(S=sGQm^_vI+hfwYOFVOzds9=IspG@4eil(5#q>!hFS9LcVwNy0X1d=$bS-bd)RvTZvV$oXtNsUdVv*xJB)T?P% zR_%%JTL1ro`IH?p*|X=wcNN{b{WKz`uV(6s6Dnd`ZqJjN9(-Tz`Sdw+?tZ(nbIvij z*IFkkZ!LW#bmg2Y_X^!pfqAvws(W_+HYlI}VsB%^CUGY|->pGiDVKFq5+~|}oRHi1 ziyQa2j|9fJPg-eSa!F=>9{LDS)-OMMHYNs!M=T5sMtF4T78IqXWmf2dj%i6O(oF|l zs}8wVvp3{+-fRcZKGbFWybFSSqE@cW2|Vd_=S0>MkBzs*TNd%^RD|Ae-R=F<)& zu?>v7d##*q-9BTPu4ksLt!Qc!zxMil&%j`QEhW*vL7!cW4?Z^vuRgIvF36>B31S$-4_}6hh-Ywd0ck23$t!)o1*#CX1 z+IY(*=iNTNu!6!uF`1h)=Dlr*RcTE4;_I=aDSuMec&>TaHO|t0`y6S{)d0^9H1|g>FO(`<%hGm&^G$tiYG4eLr`Hm@)Vf{ACPL{b*!S8>+ zx>j=R*_q1+&fd*=G-L9mh7R7vx_9q7$rzkBVY;y8kHOrZr_9o?-|JP;ezN1dC*zcB zqI-BNly6Tn-gef;_1hiO*nHD@*2b4t-ipaq3iv$Jd{f$^<9i)uZOgs8%QC~NXwU3P z-bP$TOV3C@JO6a^6gyVAoyQdVI9NH`q)XP=#Vz3Qb8rr!Mg2 zlF3HK=v1}aNflx&WCOLJlB|rTTSV@ zSmqvm^k0u=@Av#kv;Fs<+f*GUCB`6WwSH~Y_7#sn3Ny#e)6QBWy@9X`{C2XsJw~hLC!Dw3b@~4)OKsol&tB}c z@wYg?T=3XGPxqGlS51ui@;Pfa)~ro?_{E>6cxlxxEyto0PX!GouvLG3t89BI^zaG4 znUPZtZqELkpBAX{wm&m#OU|22$=0wp^*Ns{QrVwJx`v|euOv3t5`wngg6o&vvTtrO#fRC9DDUAeGm;iXMthvw*Pjg`?? zt_}aF#(ev9`-I(JGTSH3Ea@_E&k72jYV`NR)!ur+3h~R?`Y$T$G8QX#PERWJy}|Rr zo9lwUzJzc4y!-s?O`IoMmCu^SesG1O+>+1&nc~KO6Lwc?acKpx&*aiA4-C|v9DUk$ zg4UPs4qgGzqvw6?;8=2l>GFYvmH)P;Zhh+aZa(w-2X7*x)V43M-kCf78_Nb(rM(O0 zJUsAy*TF6FCHia*fu%D}7ZsGBa?ZNW;+HS*rET6axA|pT5+XH1Ltn;}HGeoI-G2F= z#)rKQ{5rlCQ)3^QOV@|zWN>Q6_ioH@LCYG%^57QU@oe%k8Xrz)-;P7pfh8+9qi z?gi6Kky0z!E1SH|W_j;tJacM7>%INg-&pNW<<-zupSY@F@}&sVxOqag21;f+_on6x zc3M;_J1i^uoYWu`QT`*U!Oisc!l-7Jb4D&%D{Er!{n%Sz@QFKz-6VO>x#WqqcRH4w zswt~4*;N13F?3R-dYiLnMDK}B+lnvo#?L%o^zM~-$K1%fQNM$VB{q8eQ@?j>?hm&! zUN1wUY?^GD*&awI%w_FmNqpAGzFD%vY0~So>6;IITv_nX>Qm9NbKecORUd6C^2{&X z#+mcn_`sB^X?$-yf*xF$I5G82cYU0FS2RfcE^wB6ayu=ZfAQSx>F1}eHQAoaT=ySsbA6n~tcops z3=9|D7#NiBG?J3@!MBd5e1jN;U7|cTZf?_E(&pg2VJnZb zbA*AZijPo~Z0WJPuHK?u;(G%2OkWhbrYY)U&~NoyFQ0!C*gExJ`@G_Bzux5~m0iqA zKV`7{_nUd2}GoRifr2X=U0hV{ECy}WlMj~IIlC)shsu z_{n~$KW6=tnSUcuWA2$WuFep(`~c1W>YFX=YU@{haMjA6BKPXmE1xHy{y1n@&(i#` zQ_OCr-xHzM5Hn`iB}UAD{9-nC-S^CXl=RbP@s(FvDqJ?3cXu7Ezp#l ztIK|#2}s->xaLiEqRI3??ym<9`S1$An5sK%t=UzsDWBE&?_RVlel=t7$}`osf`u<% z=GKus*;f&&xr)(tuiN#j>$f)Fl$q|4_dUxj$4GLa&6jIJ3+?2uIDb0lT_pEAQjNcM zmEh93{OOY{>t>u6G2jkKSo_oY!oH-Q^Pj6!AA9e0nsxlBu;f;ab0PPp=!mE5&&m(@+St3b_@aUI+9Qm;FWeq=w%ucVk=r1}aEl|r)^3kn!^3JdtP9>V?&7(i+VG5V7V`@w2XO`~{tJ-}e#|D` z6VE-?6t>Geq^@vYtVU*9dweg4S6h)!(6U6G1eFAt1d%m+-Ll4DTlbi9rv=3;_-(@yE{KOTRXR%4lo74G~5BnZrsy(|#&o}jaNdKch=bB>nMj0=Ry1493 zbb@1D?P)LZB>{;a)`Y!{|JYQSU~;m0@1~tsvyOMmpZK>jZSTMDN56&43YWckMJIei z{HN2YSzC;*neJU9`hH7B+eup|{;T)0qmJLsN!1eK;jY<}_Vve%QZC~!d-XFs#Z5P) zNWWbilDJzYanIICzV&f-)stRbE7O@M`AB(6MTFAS{yx6XJ3}^gzC53HxkgEO|9Qt=f*VM&(5C7A=UCA8TTaxGkL!OXcZp8k{wju(yBvpOr1AlH1Jx7q=F%?l|L+Dj0gJ zYUY8!&2KF;wq>Pez41BP$iedV_U3yFe|+TIx%}8+lanvki?2|*zqdr+{&YA=c^Z6Rs)PXL~L>Bja|t^@@j8>dy%e+l5lBE=`p>^&);vZJW_j z5B0V!Yg6`0Z=7#+#pIFw<0p*?>YLdwZ&^C+!_`Brhu-EaIn&}K-OhDb*=MsQZ}YT7 zuZbdSF3BtqJ^8cuYQ)sLa`o%Y-ZXUb*u6{GbISJjX2xF~ ze^%bLitVftI+R>JNweyO+v|Oe*R7v58}1b_3>8}^7g(J+W7SueZx7e6+P~g(a${s@ zmRz*ye;%$g^G+>R@1L!1%6h^uFWB|j2UmUvdlY-h{ke#z%-bd}EnO6&EzO_*)WcI{uInS2 zNqkBl*M${%yZ0n7UU28K_v6*+dA73B@wOJ}KQ?mc=D&5!ea83Xxc;@N3X>%y9~v{q zZsES7)~lK>!2BnHsXaiiZ>mJL@%xK6*;TDF=9JcKTbxq4#eYNCjfrOyrM<4L+;(xI z{6Y2ws@V-(KlOuJpOhW*{=s0s*W*d$>5j`n@3>5Fu6<@x|7Q1##A}kG?5DfAT9X%Y zggf0hS($!lm))DRWYt#N5;Y&mpx;U|w}tw)T~c%wUpsNlQkR`ad#y^Y?{7X|b^lo8 z^urv>di5i!D%bEme{b~FqG0~7`1`v1@Bh|ZyiBEJ*&UgKee*sA=N+w@G(CQu&im+{ z<{LNt2`wxudwQ>=@c-7SsVjH9F&tbecX7gzu3gWJb*XCFQJ zvG{Vhod5kl#s9`xi`J{dNH2JyKzcPv*eY~!ID&X&@PiM`B8uF8*x|D zlOLkZyMmi$J!GF3-?m%vS8BS&&4Yy#?~1P1`nq=6)TjD}+ulfoN=qdD5uN?|NJRS2 zjS)2kJ1kU%^5uM{<#&~Y`+Pq4>+Y+!`=`nGO>($7Cp&rZ+Pf3Qo!`FFO#FI=dC&dr zT6Phl;$i-0*UsCwq3Zje`@)y&@5X(&an+*a?aSVySC?M)*Y~|qGd)+&cg?2f-?$2Y zGrrz5@!^&=TP99CdozpMoJ(AEyV2sa`f9R0#l2l@wtJWUZ7*Kfdi#{y%`mfPRX3mA zzj@<=!hN<_-L4nw?dPst^z~`2L)`sf$@7J^_iz1Wyrp$21yJ!5!^hx17udS5(P{Qx>ipjjjsew_I;tec^^l~>9OpxTk3Ifyb^Zu>B_O0e!J_ksriz@~BS958 z*GGo^i|9>u#Y~+zmF06E*$C+?3tp}j?YusrLbkKpV^8Oi+a7zG z`JEz$CQ^CY?*xPc&&=ve?6_Z%MGnEY;^LE?KI~ zb-%=_66+#l7d+SfQmRUA{}OGFU0q99Rbrhlm8#rzxTG{`+JutLcZ7~4elV!~@7LB{ ze5GI0>zK~hx;dXpT8oOOR<%hU<)5zE%H86f*>kCE!lyzx_l8R!OnDELp1Kw(#PZUj z_i(Dy&Wed_EU#xRUoubm;6yRa*W#HS@9fO~Ddn^?U-IcZ&;R3T^y1Y=^51uCD|7iE z@b+HQ0c*znMdg`?&CG6{+jZ~H(OG}L);)c9uBk7gV)o=CrZ3;;XDl$Eo?NAVPcD{fEuQ%9THg0plZ}3QPJeRz_6^fB&wljQ{d_j* z=Z7@CebY|xU;FuVw)Nj<^4u%7-j(|DCf@Fi>VDf_XlL8^EG*^qWM^P_CC9*^jHl0= z4jQ|K%mloR%JH|$rFHt9bsy6akA`Gfvc z$EM4EEy-?=viG)4%TxGqV3Ojzz&HGtT39-xWojf2v$pkzS2%hVFIMflGDGZjd!0o8 zBm*t}9OPd9!s1!3+a@QS5sI7esqbtV!k4};@zTzx4&n{B?p&9R8D%F zekc7{e%Wu!c{@%lXPj8Dh_8re z0dvV^uQbI_?ufe0?9*?zA7icvlhmr)mSEfPl8guv?(w+U2cy>R-x_-QZ0EP7H?>o|*`(Dic05hd^O}8t-|)*D ziHO40o$Cq&KgV3&WBaB$TVCDe_@|CzN4zs6J~(hE&rnz3zc+nX^Bmbq(~zz@=0o<^ zI&aplHL1?yFSg;7sXMfjMdj2v)18l`cCBXHZQ4_KWQtddtY`AU0KqjU7WmCnepwL9 zXRu%k%cT|rX~mU(-j#}%gIu}2-<*#xstuhb+2|fLJuxcu$q~1#1n=T8` zuAJh$^pIs|Tu_w4wvdU|+qQGfUX}W#?Y{*-s+ISjS66SHk?s3-#+hq!g`W<+b(kO&?mN9} z*FnQ?K@(0g%%8C4eTVUktB&PCCxqjte9R6jUKM?S#B=_6PP)uIuRk<_>+f+d7_MN$aBZ+YeOUYPuuw z^P{=nQO@f&>7OEbHuRf(oOa@3#KKFpn>d-z83xs@zW8pwjV!O*@_hH>?#+3M$L8`` z#R|DJN%D$yd@PsxP%T-w*7%6i6rDn){TWRjU3-t5ztAEOTnV~-W;T#?`jsOWruxkGUKwH@0O9RnsD6PPI(JF&shJb$|R(oRWp z#ao##bNzPL+q{|2ll5)BN7osbx;v+(lem^!er|}G#PTg6tb&2_>ZI)Yjc+?k3yO;V zbNV1>E0j}PvB_;T^u*xV(T64DQ}sy&*>@$L7uDx;%3`er3Ry9rr@)$jg~G|EOD?lCyX%oGUvJ^x%X{qPJ+~(Ig=f7YPA)k4O~~nD&Qa0L zs>&yq=Fa+mPdT#h=@+NggVRd(o!&Vkbn65uzUi;O#s`_1w9WExtv9o|?DJu#&K1FL zK_LcSlML-%b!LCE*cMo1!fn;m`@{d|vz5!Fg zVpOl@wk|s``<49E4@J|2f|BPR%D=CE@r=WqeNMAAzD4f3Et|A`cVt3BzWV{mzDav0 za8J71t~k+^N2loBouq0G$t9UwlTTQ`oBL*F$SpsvQ;J8+Hf8tz;;9W-v~m8l_a?C? zKV@yabtkv(f7F@>kC^s6U|*9WZF?!H#OQ*)dB)3|1v6uO_MV%zWY@%+iaM4kt*qEx zFTdV#oA2>$@mz^Q`EQ{w7yOqJGkrblMrzrsR|cEQ_$KWN{Qi*h@w)yWd{vni5&H|d zI}d(IVvPDd3F-J=I+pF9DeUF~TDi(0$nzGxY zPiME^=hY=>E3WXxW#>KS{0!7$r_b9q=IQEcl zi|N~WwI>u8ynAiy{;+KO)Jt0#c1tYZS!{m6(j~w*T&KJ>=6=P}oc>FXnA6O+o$2*D z|7NXp=(P*8BdWNRWU3|z?C|nrTPUA2yDiW=s3$4SQ|zP(}Y(p)~?3(UFa`8id-^eqt8Np`P2T{-1ow)o}TF1zD4GopkK zPujUxfL&mI%6VR+f1kyeFH7|ZU*)K-b8R$~n{7OsdBOX|9U-QE^INxkc*^p>79en!i5bL3$885cBC+q1JV7~SwkLu3P%lXd5w|gsO z9N(DExyCr-?bN;Iy0<2;4bt$R`_46+=cOEbL+gs$4kF#T(fJLZAI^%?ja}7gF5Y)B z&0E^@zig1yyLDpvzYg>&*XPWQVV#h=|HQFRiJ>lw>T6Qgu5g@n>Kunn@xqx)T+VH6 ze|)N0aKFq&xmWSLJ7ZE_-|twVyVc7@`PZB&N4}Ls-Fnx0Mo+{)up)okZj-ziuH!G= z)CBM7snzgYcx||+xz5un{RjW8pA#0DcUNEfXRo^7qtN)TjaK}WW0rZ#-`>aya9%xi zb5z)CwdVZVp1aL$9Q(C5rpweH{Q7gz%4b(z6iw7~dt&r?XIx3h(~VuZ(}G3Vx67zh z&*7gw&t#V9|D2dEJjJ3i`YZ3>Cf%461la?BbHdE&kI!R=1 z*R!^^MW@eJU z?1EL&%O0+GI>c^S(%@$2u*Z~HoKb03;(7NQF}FTcv@@SK-q3pE4eJHF!`J5DVAknc z5vicfv40b@iSZiEPvULuYHHtXR-M%`XjJ+rV`R7?bKL~S58sMBV}A9@T>r<*@-3kA zqEiJY+sBFdzeOZUViJx=26i`u+!JEEcxBs+NCpnw@E2_B^BM%^`Mf#sNpq@d8Sn42 z@{Helmgf4tJ*@d~6JN{|*1qK3c2PnsDrpIc_idLI&%fVLTJO$%zkJW*BFj0JEP-#Ioq`(pUB2_omr)O8^v%o9=f8Zw ziTgvE#sUc|b?@H=5B1MhdllDY|FA3U4{VBGa^Of=*gC`b9~$KE=`BHzsb3aGRpZ z&>r~grm_5@Sqy(f91q+S2zC&1N)giGoN?ORiOcw~irlH++)Z&k7MHr2l1)`_EOGRF zzDScLO8-rR>tt`6`=)ABf&?F^r5Uh#DsiMZO)|7oWKHAee{t!CK9imBj*bI2kM-Db z24CbXx%@%Q)x=bz)b>N-g~iRnNsLOND|iYSUgi`jv3;Cz&*_W+PeovGqsX14p5Jc| z#RWw;^c`IJHoY;MG5OGiWo8i*J}$N7Q11D4#?1G(SP740ba1oi!sK_S4I2{f{+qnu z#($I4UQQnOo9~bO>sz*m)i-wTsl&Y<%dWgKkkWo0BrTog4des>w zJ0$8~@Kt+AFP#1GLHgT0<$ZA_?jQax>afdreEQ&TL#B?z?e_mV*u+=d{@+n|>DTi! zrhDV_+V)=L;BE8Ry>I8Gs^_jtzMfxqZ+`aQ@)x=bzOg5lERD@hUSKU3Fi+5QtvQp> zlqaS66=j^tceme~YW6R}Mf}zd|C4bHclCC?i_3C7k!pf5_acw>vz%C-J-O3d5A3dAxacd|S(>CwJ9Z?y!1)@W*Yr zjvKYrR~PflY)TK0eY>mU-0tgl%Wc~%7@V9Lx>Hh~KHB8&u6bSWPr2DOw%QBqGrk;m z{-M)!>~LYvUq|V$T`R1wxG5MM-7mq{z$r09iC06m>(+Zir!zk^>;)C`mIYLd+$xz zsO8JK566-tHcnJ;_vbR@)1TfUY< ztW!}X=Xz?O^dTuj{m36>&X*QldvoH|mRXzPzJ{$yP;qmHWyc6`; z{p7ngA&;ciUS%)OKgv6qu4s8_3V5e{2@33Tc3vqYyEMjR%C3unht^B(Jz^lL$~U_x zH#2mq(&Zo}V?CFo*0Z%%oo3GWlIGpxDf9{q>?m1(ZE3o5m6?2nob894w?A^;zr6W# z)A^{U@!!_eEk1wibMfl*zdwJ@uQHoA|NQw{o0n+{Ry*n+Cy4uv}O?x46RQPDXq~sJ<@3}i%&)NNZ7PI(0)2}mI_`f^!6}tQI?-kl5 z=lAfsryirR=|7eRDXRxlE_jF7CVqZu#J)3^fkl{k?z}~}IuG!J* zuYl#Sy{f{w-^Wr6x>Rc1#dY)z_HJYBs1bUo@YVAo7i-9}Cbf+(FWSzzzx$uz?!}T9 z^B1H{jQ%)RzlFz_i9^Og!K>Id`;^;_z25Wqq#5FD8;@(6YVX-ob5cg=-x5u^s3+&n zKRI9DBYBGXVEFn;#}c0$@z?Qi30-ji`H}pC0hiR*PDoSr3HIr><(RRdF|(`lsxD8c z(0#VQ&q5w6pA6j4^6*8@r|SVT{WXv4J>%)v=i!j~d!0mW#hgzDP2YWlS9_^VVtUk` zer&z=<6y&7|MYotmMm0Zej()G%XUD>!8XX5b1E12)FstZCkWn44%)3QY3%c)@*2R!&Ou%Y7Y^U1D%MLhmY z(E5D*gvN$ahOAJwxh`dvN~SH6LYH5D5lZuDzGCE1AhqP5X0*uN$u{9m$2wo_n7(0N zhf1^Qmxe{l7@uc4tU6{R9N^C!l={WTVGi38t%ni@oSvJz5AT{3@bO5DkkY&hs>?pI zxSX}yG+$pP=w#@0zKtoXHdYsG+q2H9X`Q}eWEs5f=bqheF|SJYFwS1)>5;h4x;4tf z@&3F+woEBacKIRR6N6HtCZ2e9g+*s?m)0JU`!z-LOjmyPw9OX!>?z}`b=s3d;OP5> zb0W1`<|Jvg%*objnNy<1$^BloYUaH!UGir0YV)YJ6|t)<4}peAY7-<(%SflrmhJv#-Wz zw(k5h&rYdd_PKggsV>#+UW9!Z+tq~=V`jTlGX>YUo-Dj_e697Gg*zt)pHhim+*xr? z;QbcGFzG}2`=z5TFGl)UWqrTicl5FPY^#0^j}+^!JEzuX?A^~DzSj2L!=+#Dl)2BJ zxa0P_kH$e&w`1$wHrZccx5$=eOwUz+uqKyvPHYt;E2GRjtJkNa7{dEPCb_b7o3H#c z@6Fch6+bvMH|jHUMx>;fiS)ft-hJfs*!h zN-Xym?&dgH{=#Tp#%2G<%6hj??tS}AcdxPkp}cz!tgD2|K9}7;=y~Jp?E3V_@9y8+ z@weRU-T%3^<_tPHoAvDv?cVW&i$~)A`#Rkhx6kRnRcwvYb*{S>gv z`pNa?k2`Aecl@t*+~%#lski9M-e-QFK8kn7FF5%>%Jz@&w`c$79;==$mi~VFT=oAi z=H)Gy-sNs`iZ9#x^Gwv$Fx4o5CzxG>v|LcR-z9%yI?45LS^>y>syLp*%wZWS&=bpW0 zZpM>++j~a4Uec95ztjz}+rMtLTBZJG(zl!JwVmA)p6g!vEcUWHe_~f>bgylUUQT)X zhW@=5ziTaD`G0Zpdw2O-`}d3H zC5Uf*X7Y34% z&M`~#T#K_O?fgCY&V5*q3-izuB|JSYTqoOD|1L^@&BVZPkr(yqV)Uid2>nHgDVeCF zD|f?c^Jh6o{J-!2QRTFf+LW7C+3uHB{Whv@&S=n!lu8!x_ij6|qj+QXpLe^(=j^!> z>AhQL_li4rKUbaq{JXHKTcd~5#z(MAN2prw;y!)Zdfv?zF%DM`N*rBM6?gMRsm|){ zJTZ@28+n}7mp$rTw`h;Be2u(7`d7V2m!{;t@v%L=KZRdp!jzc;GG|*=#Qoweic{{p zd$8>?+PP|*Qi%T6#JiS)r_>%9?w+D;{^fAYx*F}JN$vLo4miEF5=!K{cmB-Es;cw< ztmLxN+>LW2*X8xBTUlx@9qDh!_3X(z4X2IXpT&QM-Z419QnB>`(-Qf<{VrMww|Wxd zn!EcatevgWxk>xZBks$`4y`DQmTzoUyt;VSA=Mpa^I9Id%f0J4`n26OYr^NC;pK6(bbf?jz
b3j;*wv?-kaZSTv}d! z!&UKHM1JTF*|#5j4sir;6cBSiWNpegZ$)O1Zj@BsdZ!Iv{%)l(Ihe zRyfbZxs90_&*FU2;`Zuu21M=B)sSzNBB zr=&%myKnEZ@V3+5-M_Y`Mf^H&V)}0tRvpP{jqV{U+*ZG;+A=+(ddAl8PJ(|bU-@oW z{xaL@zRk_=Xvb^+4p=z#0W$-G8y^FM7)IvH%uP&B1>Ni&4%u>WZvBU4o()G%e>YH5 zoK<$wXob*?>8p}5lQOP6aOvAzru*mn+{Y}pD;Fj|c8-2i{cc|MIlK77wL-yd_nxi$ zeYo4G#wA+nePFqX)bjnDpW}{ox(EC0p6r*ot82bNVHa-kHFJi+ z0&U~V z|L``kXJ>BSvDmDAI%drgrT<2o{k!KrJ~^L1QIaco@4t+$D*k!@w=6DnV`7d>J+iOy ziPZrMQQ29u7rva{Bz{z9u?u6bgu3lqzpXlMqPHfWD-3$9@#VLAAJeb-GE2Kk`xFyr zWc;sF;{^ViszP|d-%X6 z;noZ9tneQXeo9Au>b1yYWcOG2$bDGFBYU5bl%zvrTZ(yRp&gTYWy8lEPOsR6eQevB zuSuv!-C4hii&bUL#NDe_G|xKoJ;2R2=jBqY@6n<_RQPY)@E;edxIhzhka%=3TfWH^zDK zqRfq~M|gGnlaxCzPuS1k7R>cEkC)|u|I@&~HCFTDFKl_>v&UAVtk@yE9VO8E){N_*Wyz8%z)}-p5Us@Tv=h1|P zyZ7ylwbK5am0Uaj?u^+&Z|`+7?9utI$|*Vbc<=UI+vm88-&5JsS|?bd%J?&}-=xls z#a?;xr5WGq&Pg2p-QsxMRYy#;{aT~Zq%=-@cMAc>3)^}=eY`w-N!gzto0aZuboDwJ z9BTJAn`!%{nCm|ZSWn$>e_A=!1T;xGS6du+V+U;JVEUw&AH4B4Bs==1gtZw3a2 zPzJ1J0`$f;-!4n81_vI7>iCIy+VKiuc8PxvY--UsqH;up%kJLln^|p%9}K=WSnwvE zs;t{_m}4b}q1q0G(A2NzudTgsU!pZBbBp@DlT_I|^^A=_pUZ+p`Q*wC9le&M&Z_S=C)CsM3d{q*%t9!=o(8y<8|0cOyigx{Z`?&kOwGIxa z1HS(0oqZvw$U$8tYSUH25Z<5WZ4-3)6Fpd5E--uAG?h+c`4!OCRJ(TKcI}9lYG+Q} z2yJg(y2|a%TCru@yMD#V1h$mD_x*SO%P(vs zHb)=c!j_zz=eu88?ag=h5Jr{PseUo5b25Id_wtj?=KAv#=iJTQ`cd(4IAh!1>~y0U{`UJMjTIXYDJ#ue+h6T)`>y=@y3O=2Twg8=%#x_t*LLXcWXm~{hs)J3zK%PbL&X@09(7jj>i5tEEmU^<3O-+bv%1a^YC#Qe~sb{*@>&l^XdfWTwkis z_&Ln=t_jzS5A)>CFJ)QMvPO07!8oJv6W{hJY%M-ywL&Z6@w2H&_#!s4{ZjK3;Y+*Pj;;#4oickt>VMnll`dOPKe+qZ?!MhAE=AKj z&oXzN)K}ek*pZ8E+v6Rpo>{HGqcpwC2OlVA9(Tlegtb>(=Pi z-T3mkGp_f7(p1A&(^^y?y4NzX%&*GW_s6+Vf5#<<(EGBId)e=rCG-1SGWy%FlJ%|O z#$+|+mNt3o3fc3Au9|s^DYh;9$OL-*IU@O5OEEpyCm>4^3;YW`JD;<)jE ziGksuAOnLMM))9ffzN2ughR2Y97pzrQuRy!WnjJX@9 zh^^L{@nO32F*N}et$$y+I?isX3pvp4R~@a)+fwhHyse3SayiGsT{{C$J3I~B*1pSy zvB}|mp+@lA%`yq;87I6>AJ5oXoL6bb5d5~S^?5PJs>=@fbAP>#w0B?TF6(+;#_Ik3 z|2E}n)te_jE&5rRrha|WEa}Hf9v!@be$iwpG#}?jh77K{vv1JDmS|nR7!n+bL#KAXwCx?%@%#G z`xeK!Mg2aJ9T3;@=v9KDp;Owcp0G_T*PVTG_2Nr+E6s%*k8k<2&sh6DKXr!AuiS4j zJ||lYjs>^RD_7Flx9j7YkNVqq3z{D2?rJ;vK;%`Xx3Tf=Nvu7~C6a7}eVx-Bgc?@Q z4U13G;9OT0R6X&g)$Jdp0W2}Mjz>=EVBEJjCD$YF^A6sfCUZ4repbF)Ib~tbjteus zSSJ13zfk14!QMF@#+qN}yh|w)+W-6gTlcseYyG#if!TtW1Ap&Zuq7frKHO!yg>72s zyK}EQj8oQzotTpodA1~;F=F9PY140ukL87&XIc;!ID7ITqZc*nr*9JPE8nWLjd$z3 z-5jg@Ek4S9?Yb{rP`cPEpg29}-~1h!`voo@Ixts!M(#GTYZ+goLgk!uakyS`_XQ2leu;7cP0x1!$uAKc?8@5hv$*E5wJy?H!2LLEOD6F{P=^a z<<_{4t~TfW63Npxy$$`9wp(NQ!~MJUpRSm)?Y7gz%eU`1p0+dIZG8UCnRm0cJ^v-~ zXO_~F*u?u+HvG!vTdR7Z_Bvar1j~uzSC3B>eP{dSr2gu6Zi_wtHA%A{cD-F}Lfvm(!}xxw!M0eMIlq1sj;%If0o=a;vsHBa7l&${`pli%5}S+9bpMg4NwskpVuOLsc+eV(>gM_-(N`r^@3 z2id;Ksh(^fn(TgTVLK8%Bl5S-X0KCD?@u{I&hdY#Zn`{l^2~X51<$5!o$~ob*6R@O z_dRuo@_fPvBh!-dh=3n=e#WepUelNxkado& z!2Nu(xKB(^Y&2W7y5geW^Y|U@I!+$>By{3I*ZY|QX-zKr7mvzrExH{y(aiK<_AyhvUXFG9IAkS5X9aGuyJ&K6^cwTJIu1=)_4ohL%kx<|HTW$*PHAfY&9!Qa;Sr_R z2Abx!=RESOyp?r>XH3*G;K-fH?dfs)?J0rg_SNcDwi7kCuUYc#)bDbO-qnxG%DsP9 z^Xb3dAtc7f{c=fO#Fm22`=i*;Nj{5Mb$-98PE(oC<+?n; zQD>vZyCt(S^#YX2eGea6Ib+LO)lbu|WX%(-y?Ohq4_P1b zW~hI6`Rwl=lj6-LBtFktO|3ld-XblPO=n!DdMd|$aVmX0?@%?@cgIBU0^x!d86%f{ zy)K*P{AjJb`*~uX-m9N_=U&@iNJ)F-tJ#^);b&qpcR_KPl4WDSYah?2_KWp%Z_Li< zcX&T#pUtZ>tG%9$%7MS~miF9yyDNDi``Y5AWjd~Q!V@R(I9-?3k`=KQ6tQR#wG~g% zzq9GGG5>>R)faaqGv{`mz7yzGA+^(b$``+*sqZvYZD!t=^=ipC_IkGDitov{X@|<{ zk7b!IY?zkCTlyt9Zk6cM!0l^AZ|H4^b=;Wv@2U5d3t1CgC#E~TNjH3=^T2i5frt5l zhi_Qr^>Z(uCsecVwX2r>vo-U>?|=CZZ;)d)12~`+dP;t-K5Dh^p6=OdAi}`#B$R$Ma!!68?EKqiAFAtGH}P)XbF;`SA^we;>-3c94eMSey$=*| zRf^uSHguYer{ANsKi}`Rp0vp0)zrKj%rgQ_geq<1-aE?mySL8Ho%HP7`*^k87ANH9 zq|VBjGw*q?Sfp9kpW02Q-HTVf_T^Lg(8#>s;I|Ryx95VlH(1DbEDE;Y_tkiBz`lJR zOlsZxX4Zb+v*^d8)mQEtEdE(CvF-L9ofN_7h}NHWo1*^zD0Ou=E}Q>b^RsK{${7dv zxZHY@DrEEinj~j-GM}G(wQFf=nX0C=Yrv|;gB?r`_TKk4RXjN2`RA%$+>=Bhd++dI zwurwrp1jM_e_MQi?;bY2C1u|R^azI`s^?}G#edBH1lY-`6%#>h%v#+J}=%@GPp7%Pp z#S02t{nKza__e^xh+o{#=D2yD(c6AReC5A`H%@*s=?NG9x36n*h*tc!k~ceg?E7T{ zZ c`a;Cp>wo05r5)0p?qkYcv4+b;V&2BmP=kn5XSXU8S|(wjuV#Aa z>5;W68}9T7ze#V~wPN3=mi4`PjZSC9V$PKtd{-?Gj$k^*7k~D5@^jTQRf)&uDMaNK z+6vy$Ea9-=ym}$5QS7jwH~Z8@Y)|VrTs(qum>NQNHCW{3Gx{=qQD-uFI(bvg^kb8f zE-Z>qXe`gK>-bxFB;n6Fp;??-uJPvEe6CdNSoM$R%5JvXH!t0H{n@X$+qsWBYme@k ziT3e&(^xtnfXTUy9 z=%RK)GDqv7xHp$X*e&l{muJp6Y{b1P`)j~{1CJ-54DRQXq=<0{9mYwJ2}zxiLUDcg?03 z7QLRKV_CAanVH#+ZBv=ynJmSS?@J=A&Rp8EG%vSb7vE1k*UVvlvU-ZPOCCpf`*UxohyWh2aSu+3T%0K(+8(VUgx=GG5t==i2L$f$ML=q3sYrk zLcl+v8uum@r2^i-TdK zoFXlbZ(WtldCyJdkBWli_Pj0Uw7qh)Ecfip5%GVpzWFu(#kEbDS+~B4b5Bb>5a_o~ zFK8bl`~UlvyZ^lVQ+M!0^2LA;D^{`3?&Ox(E#LgX^tl?(FS*CFzb8d&Se16=rR1zP zFNkOjKetE1d_ud%Kh5Yj2^()r{vMRkC~LCf`LV@1F&!t(imRhyE*P6<$E}-f<@SXu zxPE!_P2sKQju!a{tZn{&Rlp{2_A1^RxvG1XuT{hOkk|a z>De9@X%Vwf_v_}JZP#~mGjmPU^;$o<>VqkFc=jxTQ}6dQI@Z>$KM>Y)UiG`etMkm7 zFHb#8GX8mIso|>Ziy>Z@nI?pOTc7dT(WGgL%va9ngyX9oWIQ;q-)J_QmGCc?0+kb& zxArzJiTceS^q=qdPpY*+GJt~vd!A@yj2lVev(WF=d`-%5ed>S>1$+1@`Gci3+7g5cRV`rBl_ zf6#4S>QH)RyHe49?w7?I?HN={z8yJQCD?E6+gqQI|9H)cPo8hL1zi$h))jb^#pAz1+5h+T2kedg)9#!txFr2rkUi9J zMedqMJar#V#PwH0Rg7rMRWaG~nMije7CGqc+S|O*| z!soBQei@rsVAOq{zPksFj{P~(?oy+AbzA)XE}t#iqGsgP{{4Hya@G2~=2yEe9Q5}% zaKl7t{Wl8+8Lkxz!*W;bS9~Pj`+N2?!_y8&lRvCdo)ETYy@EOxC!c zEAlw<#MeLMmpNZh85r~ToPY9+ZJV~$U8~%8_SoW!T4%2Bwv;)L>sI(fY?FB2^~ICS z4Y@YwutyrsuPyvyx$w=A>N^)6{F}6S)pHxu)E7OhCu7WbjcQ+Lyn9!zksH5akCt4# zYdG(>7N+famsah$cjdRPR@l{rf?Byb`oiHUQ?#<#+vBxw&a14`yu0t9@`Ym0EoLto zf(us)hHz}Jmhd{WXw}}G$pNy#Q@bX5*~hsleB5~czr;eesV>(=98I5luI+6vOJ)rU zHDf#!(yqPBCHYk_+mWg>i*tEzC8d~W-0aDEkf6tuSZd&Ey>B(&(aYNwo2i}H7`;n7 z`kLvVNsY^|Uz16i^w^T|+bcuc!^+e7I$mV3eT0kaSqx$8d=NBh@c_GyOd+MF%lO|UD zyrsk;su*{$dQoY>B1Ty*AI!7nRqxKij4C zxboZ8H-Af$hwbE|5wmD5$-srKzMGI5HWx&AkAz1y_)_pH;GjvVC< zS)PAuvi}?3<-2?rY!vd)f2WxtYH_mUV3SJt;>eIC4Odo6<(w{i#1Xisg4aOr+13>bw7T2$!lf8wnJ8*H|DNjNl7u@v-nTd;YQmf^H$lO zV!Iz$BpEHvId|&HIj2GmYkZVfM>1Xh;``!br`(U1r92WTwm;kZdsT^*yUJd>D(A-wm+}P1^2te_uUKK(u=a_`{6)8B{Eceh zeWKQ@^Us&}fA+h#rX5ZRuJ)^SY$ETy-`+M=eCcU}XKa(oZvFY`t`f3HG-qCkv#s*h zJfSVeWU_Uho~q0Cc)hYNe8PGKDZWMfn3ru#j&c5S?wr5=zUpr^MIZQI>?vJ&N!L3s zAjxb`f&9yS!-h4RSy^VN`pSY!?`1`)d36WdJ=6YVVJRbAUK;_?sXFV4FmUC`| z_9Qyxzt&k^;vGAo*ZH&AziXm)Z#!#qXReaVj%xWXZqZrPz4811H!Xp$;}0HIex&4k z(`E6*1m!#Dk1k=^t{%&;*LSZ$I;%P?kbV8LM{oWdU2|{i>32?EW?E-?wifsB*M9x5 z?!W}Syl=4 z4=*IR?Ka&juYW|4E8U@YTl&9C{)ZnOQaanK^nG@=Ra){gO%~@xwnq-WjF8~(YK+hS zs4stIe)7laH?y^P{Pz1^_F}j@r~csQmk*fEo__h0KYjZ3`whNv^X={b^XY#6@^kac zFSFcV%g<4h;PSG&BVnC?Cwu;?ycL(Y=02DcQ0-Qbus+q?k3A`$@6FQJ%WiXQnbGmn zvVYcPX{}J1ug?-%>@QV)db#mf%gIJNd$UJ|(dMqlv!1SDJ+Vnfr;xFXk!#+YyRBZl zn~e>+`%PXv)VjK&WI^7i#awPGa|(O)8Cs%VHy@lCD7_}`Y&y50!LKmZnuQ0#6di@H zcQD#cp0JU1W%^g$VuSWy*6;tEHhHyg_gQnfaznpZ{b_09XHULi(c?GJ4$OEK&1cHX zeeIjt*R{s)zP3hM@GA>mifCu7nDKV)sc^694{f<`&F;9pcJIS<_Yb%4ZQuT2drtM- zgy)`IKWkX-G3XdyZRPo?{l$3c&U3_xy|udhUms^VIbOKOB0krkG5am& zI`ciZ82!`dzES9#YMzi(`PWfNrn>p!iNnRtd!H5jS)*$a`or_~l-|(B59hZ^e7|e? z`gQP5X?w1NYh6o!&-k+A-F)*4hwofj^l`Glr%4~XU6x3R#A_aK&bsJ%c)LPR^}e_A zej1_TCo2}seiVDS^Jq!FM0&}#hAmq}OBUCLKUjaPH}hgyUc-+J!5f_wE>J z_I_Wvv+UAMJw4%>9U4~Z=O)&kob}?6Y^-3-jU2_)hO6xI39R1Si&WN{zFTs^eBtT0 za@P<1e6(T>>j#ZH>)Ri3yUy{hS|G1_wP24*&@}0FM~kkV5;>J{w5$JQ!n_~4Px?M6 zO9h1&)_!m}y^-VT$%!f7FZ|45TB=@fg|oih`8Atc;rX>0l1|^INxu?bx%|%DbJr?9 z`&oZG5%A@Zf#R&jQ;TjcbvYj_#j-#%{GgQKN9L~Si$X8fD0D=xwEA>}t6^W-_CNKh zzZW%s@=1@=b$`kF=cDF#UatPfvXP$gadM$aOw*DjO^$wGl`m>sYU0N9OJu=3nR&7E zl3I=mesGiKIUvg0dn|RrrwHwjCvIzBzAJO%X2V6cyrX;Xo_)CQt!qH@*JsWBb>@!p zb~`d%XTLSRu*WaMaz*R4r0<^VKTO`*z4F$i^!pF;KZv&K@jTFS_*l4cZ;eRt;?g7e z%l3{lgqc-}PoSP2+i(IouDeHIqn04}e zd*W*I51D4BcW=&5*Z%0gMf+(4!>-?+u6KK4PSr*(skWu}qTpx3u+OrkYGPDG z$o99+8&7`yT6)s9-8?sJ&okMw10E(N4jP(kTs0nT-|<%8Ow01+J9c@C_CL;T`|enD z8UNYX{QP^=+rzzY7ONEs9_`BBy{$;|WM^1fnCUhNk$m&G_Ots~0^e_K*3*3uS8d|p z^x-<&9-{{h+WXHUSMUy zn!7BFQ+Uy*U$1trEjYx;!0?ipfk6gooG~>g6*A12pI4HY2^ncD4L;a+%Rpf7bM0l* zSyiT$+>VaqGx6QNV#->NC1IO1oi?AJot zx}tB=6YI-tpZ#ZWE}Qw(YhL2&P{!tyDrb5)cb@rZF@w$5v&&stGgET<3jG@wf_|7~ zKMMcRziP@($;0=)ew-f1e$a2#mWRPpcdvT#%==faB)3k$t*NIrzEyi!_F7_f7^B>3 zuB?2W1v=b6_RO(Y`5?nEH$(RPWs93CFKvG9im=UDbUak*zfU4tc|kpY`D4BwyVKzt z-`kW;%$e4$a@Xcx#L?Gk-|Z~62eBBl?fG_XUDO1(Y1*2832!_X{AAEyn|P~r(*ASI z?eB6PTz@YoQ0sr_=ez&T+KV;cPk)#keXvYK>Giw-e`fK|Wv`4jd+}#q<9>OfH%R_> z`|M8n)-@9b;^7G#p#0G70j@pfGpCP#M6;=h=GBjh7pI~@^i5I zEw^t!Uy}n*`}gWDzeUYEyl3=uG9NoCZn3~cXoqsHp}?Wt)t}CHlU*=-xk`S1J|ZogB`?Quvn?on+`Ym448k1dDA_tXdB60R7*S65~& zViof(VhNsZ81!N4&f~(JhPF;QxsA72vKZ#Db6@R@xYd`Jclc>tZ(iUzqu4up^?i?) zwsP;{p0>}X=o>pTM`G9U|6MbEWz=$8&7xoU3$;D)ohO%L_MdZk@8frIUth7JhJ$x- zR@z|(28Jj`#J!fF$q8t@7V8(M=A&8Gy0{a8h{=SSm%9#SniWmyWZ9&o$GYYKzrptf`pN5c#Xs*ov9WPo^V6?~7qDMH z^Y4;?P1aleo-?zW6?qiR+KazB%I`tjUS93NJbMlU0|N&m1A`v61YeR`QIc4cnut9( z`u6fQ83-KzZg({Igu*)k_GBNKlEy zWY)6Ea*fe2k;`-A4+y!+Cw!|+KFow?dB$s%?P5`LI;5ZMmlSxnqKdmdAx%InqH66E z?s+j*YYt8le09%7?9`!GR+rKy%ddENCr;n{=ELYPy~xTn(uJYZ&sHsO-w^ty!8T>i zw8SvoJ8+5Jjv|14VjXeoCrtL1IyHDri^k+=$rx%LXE~_tzWf zZ<)A_VZFAima4R1q_OG4=oLmQZ%bZU(q)^F^!xn}?ZU@P<~f^`uC0A%dHwqJ>)Vg+ zu8j=*P(DxP`Ua(pzig?NYZQ*Aeq=BU-N~$czkZtFCm!+aFX<+hD}9?}-Uc{6Pjyt( z3n=B%TXZw^QiQFuP{FjT9n6nK{$8ARt7X%}xSAc!(IMJpZ?(3ZPi(uE?lrmcH1D|_ zy)%g)xhE;knYm%ZBsF=xc7sXr(b};ha_QL~MLAD9l-c`?wimS={Wpy-QsNogH;;`c zd#rZ2v3z*5F{eqii7zL_@$KooD<0%U?8@EYs{fMnw}VEjOyPm5T2CfT?Qap0tanoLV_L?VC{Eb?RFWAtUtmtx9mc}y3N=Am!CX1d-A;tvU42bW70ml z9Y3?YV0VM{v{wIR!3E!vHdH5Wy2RvXx6&oV)Ol&%;rG5(;vZRr7vGXp?_m6*dS!0* z)L7%qyZv{I-3(fjfAX#Wuemj~E4b%XW>lP<9C-2Tnb%?4mTg$6Tzy0N%D1(pp&j?a zSRY(nxP5*`kJ_oj%tbeKEjD?75UARg+INS?vc-Z+wsLU+N3M+JrDGQbqAsRe7;(Q5 zD4O_d-5VyGj(v91`j|{>I$BEL;5Nz?&V778_plXG|Hs$G|%X_ZwS*uhQQBmsg<-?N&akBecO1F!Zn6Q1xdH$d}@)?JiR@ANe zg`2P1-aTCEY4HD9+ckN^y+4IMdFQ=p{Qdi}+f%u;8TT5eKWG&_{%Lm;YohKGzO~VF zYz*V4zus-7%UycAW2@w|Pp6Xa_8NSvO1dTN?^($AYv1B+%2zpCibDNko~Ult-j;m+ z%+qD6rdD~&4%ly~EKHf%lQZ+Dch&av7cWB?YUkyycKxF^W5yXHld_`DjUU+q5A4&B znfRdnoNL?TEmLl=oeDYCdxZH$Q$^8#7k`sosV}!2)LEwN92*xO_2HUx+*iP(y!Sw-Vj)I z=MaB}s&!&qLvt-2Z0AuHC^6A<=c_lcDq4p%=l9!u8j3U|~l*&@C*CCl&DoBwv4 zY~3q!Vjs%zK1%gz-SE(&_41O}7uP=4h(s8?E*9^}=8oQ{(h!XKqSb-hI;JU9t0QL37AC$*F-}6W>3axtTYnYF_^} zQ^l?Ci>y{9Rvuw(li4WhAK4@P_Jzy2$YVbwI{wbGzh|#EE!o5MgtAu1mQ@!{Ewppt zh<{fkvhUcIVB48@4rgo(X4zgBT(v}koAcStDed|X{`78QViuURyyPugfaSiZw0WFm zIjsE!Jt|QhuR|+EI9HwccggK|X{h8KsW_GHmWJK$ZEhvR`yE*8BE>CX5$Cp`@4=F1 zCZ=shy{SSBx@Ou>u1!hU<(G0@ZR(fJ3#M-^Tc`9X{cNOT^5rFOLx1*M&N$4iBw2nb z{j6n!{KNCV`RcbXO4ksxyteS&7Q@pjqVpE5-?G*2?}g&oX(0`PANo#db#Lu|_<1V# z`t=1@op08)Z#Y#bay>g*b%V@Py~B;$Tc29refw}l-GPm5hgZ(Na(V&Fueq0IOf4uZ z44NZu9F!4zp3TI5RrkvUx3^yjbnC36?{*PPb z^}hUy_2;|vYradLO7Es=zC4p|eqFP=?sfd_dX1p@k=Z}zntJWs+j^v8W!l3S(^*W1 zjU{$^gq;3krnTZ;ecX;6l@WG#b@Jp7y%ekr)44ZSyZ2bl4mqopzYXV_Eqwnz_xt^W zmPQ)+;`6>9nDy7wfw}H|Z=s0jjons?Ru*Po?ke8spB3+*81%uGkXFu{=S!8yzN@o^ga1;&uo+K?o@4io!-o6`#$>ot*Rw*|5lc62+V3%zBOxR zZdb~-w_8_!Jfi!OOI+-yulEjP=6h`uyLAtGf0^ml&fu>8s><*|>&5Ae_YRmc{$!dd z!!-G8s37a>cIOSxu1|}3{=oWs`<5j3Eum-qC02?|>b6q;81#CT44?h|_|pM5w-~>B zdrso`rbi14bDJkQd|+gn^0MPdsnEkR=_!qCm&IT9b6dYt`L0W*>He$_SrS{9C;yaM zz%uA_~vzHb>Tj@6c%Sl<(oPExS<3cq91A{&zVw(VtV9ZNNtaP0>MZyJuat|6O55g^Kl?o+^j4(&it{&3V(hgu zW=x0{j*$E@kt^R+b^Gp^Pa@fhQ=^qn9$8kAtyh>AP+X(=<6qqKin~(+BNoKIxL|pK zeO*%L|2SvWM*LK;31d-@(N0^_g&70-3AS9)IQh# zBNpJjAf#4tZ->(rx1}ttIr>@JB0|hB+boLaFx~z1Ue@}YY+2^jcD2=_A1#gT9?#tP z_Pvsm#Td*U)N<)?ImRno-)u)wxl^ccb*)oDbfc!c?Cp z^IgeH$>-+A*AH(jtvkZeTdnP6>$BU?nr+9MOq06r$BgpRCO@snIC;gZ*mKk$-7b3x3ar& z^T9G9)^r<#57OV69N)QF?v@b>7d6*qe(oW3Et*r}hwh1xGZ%iWwX6Gn>(7=co}1i+ zAEz0*q<>u?{=%F4U@`mu%kA7!i)_+LllELs=RQ0~H}~^gyXrZ@*Bdu?wy%iqQCWDt z&7+gg%TP>hx9BqGFCjg1qV5+hjG6IseaN}e>^0}#T$KKsICJKb^uKLtUDsRQ$)vRe z9=g=pe)4uw??F$iOO5hERhp_lU-v9|SKFU^(OboS`;GfKk;^CW6>n&1(>PN7Qf)KO zhgl2VP2KPAS}Z4JbMBZ#r(F2QTj}oJ&Mp=mg~3lh?+=L-QxFx8Yk871DW=dc$dt;rkPpzo?=(`FgWk&VY8xP`?udKC8k?TK2b}%!f)=H7xY{HQs3;A0k$80 zALTZY*A7uQ;$wB zOPXf4L;Q(GLChXi#y2f5uIn$+c;LF>vd2fqlA5jkj+-ClZLHp{#O`|9(v`cAsnllv zl?yf&ejRfx?j073c`mW}XYXE_*}<2YqW&8c{Ma2g{R&G$*Ac1f{@gA6JfH0{7|M%m zW%^qV29{(`lUU2ud0~Tj%aq9s)`_Bi;mZwlg}j8t&E9Q0Uut@4LD0!Up+wI&8AsVR zeNB#J_egle%vI&}ZARZ*iS4g*G);|9?90}P%(&WgcKP`oi^MJM*B`ZcxW`D#W7adB zC8tszS@d3g^hIiNL%>8&{?Fn@l^3;aC*=yIy7^vs#9J#_=PRMS`}7ZyZ(U9kKlkhp z*Zg(DyzO)sOHIF0V|>Vzh5qXVHa)mMy_MsKoFdbH4VOhht{5lTDH;pdFRM>VIrwZw!@AER9xW+%CcD3To)~uXYHRh3YZvx5?R^)P?J~>k?|=Da z6MnWE&U?~WpY@jwR3vaQfPfH>Um%EvVI929wSwZ*(v*ByJEd5!q!?t92m=EHq;EXi z@AyXpMg|7Z7An}SfY{5$;<9v@F(|FR)Ifj#W&@G^pEd6tOc97JzyB!HaF>hzqRWqW zeQHtL5_@ZtuF$^S{Y=tEhZ?_qfB#o5NVex|fc?}J0l&9ioaf!Utx-N(YT2jqg)6yGF{bX_mCHA%p zuNoRBm+TWawWwg}RsT^}?R(KH?o@nIU&g)(eHSkrxt)6F^U{zlove57%n$up5WZMU z?eO{UXK!8bGF3gUBC*HLM`~Bwhb1?Cd;U*f&a-G^@b9zV?mV0}{mqHfTRD*(oCKiq8YuoCF}C6ZJQpbY-HJ%Ds7s1vi^M| zi_c~K_{bl%^FMz)rTY7$>2{U5o1VG-t}(#C902ardrL5`}dz=xr3Mq^YyIS z6wkDbgK`fiGhN?&^ZDC2@1MKw%7ksvI&JYrvUZsqo7p`1KHK;AcWWm|MK83|Xjp%?EoQ>{*oawl=l(QW& z%5$p#XI@a2!|bC8Aw_w7d~SYnc5!?>+EVG2EJ__`85kH+85tNZ`U8Uv60J`{ zU-liop?o{{5zoSv3xgJ#-IcaeIKlXxQ*`IP1;@Wne;!PyXz`ZnyZ|x!)H> zeT^$)Bmc2IKYI1YOUIi2sVfe+o&2J8$Ni|^f;sy(&V2S(YxOf-wpRh`o<82m`;_}f z_nEV34Px^rD(0Y3OB+T827YL8r03)(CFba*=#>=r_8US*f54KObF?L*>4=P! zxRT^@|%zr~I7-wdm#evG?zJHU@@L2?hop zs7ErPjnB0ak^Pqq1nTa`CpshrpI9c-)IO!jf>mh43f=3{d5^XvcHh=5kiulRsxY-@>xH_hizQs}nAX{s}Hl=k%$v`t*v^xBNV}(z4dQ z`sw*`=Ie`2ODZ1;55F{JPU)FSUvcj9C8thoXmGrrF-Pa*l;AdT6&K;4i?dXZbzghr za$;Vn*Nw}8b;qx^OwmkaS+usUw8X%YC#q%3zv5+Vwv*!y#Qj`kGrwMy(>#iQ<;f`q zQxC1%CcJ6tE0?&5+gN)i&Q>Y;%W`n>whzZ5*N>!32YhZIPcs3*BAg-Z+aG zBtKa=Wz~$M9AytI_2gbC*tMvN|7PrQR{Pv#Bd}Ve>`T;y3XaCGd7o!Z^-vA*fBpB3 zSi+5aX;!L|>$uN*%s68#!ohQaC-;-lQt6UZ8@KS+LPr#ry}a6Z@1iYdpzDqftDB!3 z)(SMR;*VY87EUT^u@n#V5M0h-uKMP*!{N6}3|Do2YnPeQTPv)be$Mo?q+QmO z3&l59^&83WHGBF&UQhUw*!qqG&!x{ypJ=o4mwMaM)d>xG3vO@EyDPO|_6x}dd##sj z0dj>_Q`OaW)XHzMuF^NFJT+Z0>QH5zlOOk_ga7BO`<`)U$3pMzt*f~Hou9v}t2|Av zv^x62shBt44y8R^q0axd$+V2Ar@JXH%|iUfnS#KH7o_$wZ8`r*bfKxwy?yL~8z*PG zZQm_n>VEXxl>n6m3$seK1m}gGF;{o9{pK2YCBLR4!E4U!+WE_S+mcm(eu+|>ADq4U zP25JC{)vBj<#y_C3}^6(ED;plwpXg^Z%C$&3U8w*zm=Hamsy>Sx;eZ`U3c<0lo-Kb9UT>)$e~U~S?S4ZEL{vO8`r|E#mx z!~Iin_^0CNBI(n{sqAGNE#mrmPl;A`2~QGll-a;}!Ts{o0OdZ9dB@^fUmUu&ZC%`q zT^~}WH?84XYqM{G_ce>!X!@m|cJH6zSi3*VZwmB2GZU zEXpM7`+|dk8x;Obje7pLo}qBY_3j6!r#PDn_o$n;MtzEiob)#H>yPzC`&_$n?e|;E zP?>EPbt-nv*Qx_v2LhXzm%h$PW&K;|zE9^t$UY&}3lrbjPw|so`1g; zkDN-spPBal;npl)p#-mU5m%d6sn;ERZzS;7*?kq~W#x8Fc7_ti(rmw=nHL_c=ySaC z%ILsvo}+nRj{jOEc~-6ezTbV@c{cUd#h>ljyFOUoZaot(sU!BUdFr~E+_|&fOvs(O zI4}6!k1KDM?$~~n*>UKTA8S)=v*WvKW|#Z(|0*W){B!)6%BWoY zpx&H=uOZ;*_ZvKjH_var$SEDECL>oL^ zJvVf_srA}>hn@edbM1X&_4CyQF6+&uJ6dJ$e!Va&EdAZ3nPscbLrP z-KA?;Uye&e>#Kj*;p@1gZ$q)P^}Ss_**}G&7M!1yS)_PJ$YHYLPLJ9}j%RWbtNf(H zc_Ux^woSnO;*iho{ zMps_d>au@sVpsn$1aoaYU+0$NzVv#?jYGy)cce62S-I`;#yc9TT)JPG@OSF`u2>`+ z%D#K0l6Jkk8Z*D(;>Q{x^V9x`);&mV-t|uM%lb~EmQTwvlCEB`pKsqBe!=slUdD`D z2iEK;N$B|Xgek5|Usvm@ow*2$()IgED#cH{l@i-d-{tCC!M%C!w-5d6g!Mlet)I3w z`EtgFykD!`=k}MiKhd$h7C9-Nw;pZYan4&a+XMy%hI$4D1_5}*SC&|mnV46iS5j=- zf)d%hP#cBlXKm-{EPFaYJ2G zl$w{4T7=dPyC+>cQG$bk!BC%pK>}4_Qetv;dQpC99;gL!H!89iRCk|WFQ7lcVb+0c zr-WNU3d|;R18tc541A zb+}Y8bI#&WAF~&6%P+^Oc$$CMrn2;|<^0{(^F1E@JshroY;pPV^ZR;^9Tq>FoE$4M znd5lL?(IiTDt%M-dbGs)yV!ld|p%+#d7mp=QnWZpE^5 z5(^AsB1#@z+^W?1V7m0V!`BZ_o|T}-WY3hwTXSq@V-@F~>VrzWHeI1Pb0!>FZpZ1k z@vlU6M#CX71HN1C^^uSv)+K4ha-eWK^C8~3#jJ&w)lFT~Zqg|Emga%(<#Anl%| zMDKjr-u*H?cH3?|`Xib=>)1kpTPKY+onL-ly3|#suTS0m_+%l;Q;W~W#CUTBr5w6x zb+c0R38Si3qP|uw$G+E!hyTAVTB6S~%P>SjW8QU^BFW$Tc|J(Ad&?XSlDx7h@0P?i zd9@kSvf<9XDw=Z|wbv!$)U^+iRYs0(A zYbFSl>Caw$Y0<_H0?`ueOP;@Yo7n#Fb8uut5zEu}JjV_l@bqyo_p0DKn@sMpD;kBCr_0vW zhm}TMyXmw+q@;3T=GiTBuB`37T2eOxFPAL1e~BSrVU&UG?nT?e9mo?3nyi zWAZfLE-MA)(CN=Nzn!MZR?$2udq>01lV)ESDi1uhX47|K3|r^wovA9~SEcW0Xdm)G z^^-uHu3_m;V7?ME_YjPK9u-*n*FHSrx$ z4h52T>W`GHzVq*<;hE%EN9V(@`gWHX9eKB7vG0K}`?)z4YKzkszmhl`JdaV8L2`w` zuBjF+2R5nBN|srqa)`VBDKlfif*a9gWxu;y4vO8|;H~^@ixiipd1F*z&twjv)}5_F zawmOylI~qBXli8%JDw%?iT6g%qA5)8O%iJw9BwXOee&6ztXnIScgH;GmsNlFx8r!j zYAwreZE2Caw#rmDM9Oeq*`T@YtvTFPzH(jHl2x0QrLKtf51*J6nfKHy ze7)uFq#3GLQ@Q=Ot}uSIEvR#qgrD_c_ODyq7T;g-=4b4@u$oZs(4)R*T(?#0t&!to zUDj>Nrr{}hX|CHsuO$nY>{uCf@YRu{R}~JND&+s&n#j0UYX651i6N3Fw_iHAXo6b7 z;#moLt8Ou}#Rc75bzE3m`|GpJ?XEkwSZ(!@Of^pyzjfs3wUutqdsn+vd;E(#P^|0F zvF-1!RY}nvA9nsS%YUH7 zs@ZXB;yl-*qDHF(DoXX)^ri>5y35bpRn9hZqp6>g)23;BqUX8SwJG>7ki9KnqGtM= z?QGfUFF89hs`K(^hOa+=Bc|l~CU^7gao*uQYq;vn&ah{{7E#&NcamS^k^BpdKF>RU ze(8OA#@H66{QmD7L-XR!&Z}UZg7&$GOGbdM_|8zum?;g(Hp8@=l|H~%Cw;XEz z@%GHV$xk-7O`nru{r=I>wab+@zkhu7?21DcKOY8b+&t^^lPMvgq$xMr>gMt3{kG}u zd2A;AYv0J5&YJq}#m(OO)v?d*>ThRlE$<91eihnV|L@DfDGqn8#KkpjIPmDh`q@Vg zPbvI$JZE>}*{u^J_b08Z*>y06(e-@RlheM>Pf7j>R#TJnCXwMCcXZa`&--W z>Y1sJ1OKh9+Z1ze$7_Zc>#yv5-F4%?W95(2O<%sP-I4hAbP&g{Xno#{zdnYd!MaIH z|8)!AJCUX&sh!h%bJpL^jlarPz0ZwQ^!{X@_*8fH!&9BbWhuYmGxd;x*R>3CY2O$b z7?Ri!8{nbMj-=G8%+w;?;k94x z=DsM`zJlM*uh==Ex&`UE5xoTv@@lp)DdUftmZn-=OphUw+GkJ~D`i zKU~c6C3r>kRfp#P?Q0{J^?#6jG$$^;p@+q;QEZD<=JxO(lgY|g3c1^fnw{s!7= z&t59f{qU$$h9U249-)Y1&R3VZaj$VVe0lcU`?ufT-G6rd?%y)g3up%-Hc&+0<8G$|K(!ow&Wt?WW4jHzIqJtaFW) zUr2kB6u*ljmBUzGd|lO1LyiKA-jQ;i1U=RZsVz4HGlF%~4yx$iPs;&cFZ~ zNh~f()`z8*{Gt@_0=BhNee;_f1lmBeyqug%y)JVflgqgj%)iW5iob?uQ|E>h`!y>+ z?k~UTdN8bIRrTz->FM_(AGD^hrZSa%c34vFA#pb0g`Jp|lpkCBlb%yHbyIVi_oN*v z`mA_qH}A9$l6ArLmVR+ReD)=>Cq5pbc3!^o*}|G4&aI3XRckbJ`@M)YfR=H6`!z z+(_@q2Z9U(WrD^Iu&|_F>v7_I+3PYgX&;|EHY0d$4XpIN##6 zPv6um-5H=cp|`ix!r{{%!IyKcpO9U=Kd!jIVC(8rdeht(Jr7tJ9$Gd3?gz!%W4bE- ztCyTkm6+UHq@Df5&Uyk%cHg%|H3!jHi`qGVCo#OA#PEJQ!@-pI|Kh^yOMBP;J)II= zKm8%w)%OQqEp(asa&^j`bxWQG#wafR81Ty9&w27E(Z6V&pd+hOH1ydR7%pgF=6_H} z0+Rhf6E;_)qVq2sh}7L*|3RAbbt~_@sN4>vJfRJls~#lI3b?e(+hvx>aZTL~|NHvq z9_hQR;wM%3GB~^b`KP(XH8r=c#OEtHT|6Jo825O>z8leK`|3j18rRg81)rT<{|2cg8NvhMfh($5`EMg9 z2BR6>1@|nx?K|05&b?S(@45HSVdc;;|b&>T5RdWe~_^ z(tI=Xc#!vpQ;nw@dG6nPx-Vt*Vqc-^X-7A8`w4dM?mfo(wrpz4Y1KPoLTf5!uq4m( z@|eHcl|x){s?wyVw{GrzwyEbids&%V&OxbOx0Oz&I{&yLh3Y!b9l1GCzvI{sr&V*Z zcHiC=`{?SsYbTscUYEU2ZH~I>HD7*9QRBaFlAk*CxH4Ux7c6t1>!5e&pTW`%Am#+*vtsj%bFg#a`1*1shB~ za~Hc5DeD;>TXx6g(=$cK<~`e|^JhhBKfkqR`r%udVhc0EbPL{=Y)+80;9cRhcW2V8 zpC3KuFS+2l(miI8`itxO85|$X?oM0(`~k20<#o#TOEylu#l1uLB|F#4lX}+~t13%B zf4q7&qptMt)4!{)7w=wD(&Y3EWM$UTv4Vd0MwpdD`N@ z@I!eD&4nqP#_2bL&e_c^Ecxa>r9}Alqw?;$K5h3Y@vUE8*wzGSs?0oLy~@wU&(NRk z&fkT3r_CncF*f z)y&Y%Rg*KE->-Zb7*^fj-KCgcq@^otxp!7bSH{QK?xPxCrkY+^^YFQ~qVAgJ6}W8t)9JlT@orUKw>{Ipy^c+5HSS0DT$)qP=F1pu zcyjj)!!=R%Ig9I*)A&{&lmD&Xs}L2^`#9WiZc6GWeSdY=Q)%Xb?OsOmFQYv zcJNHzD0$_U{GM$(mH97i65sw1k-VRf#rZ#5IOMur$rqQ@Fsq{{X8vn*Q_N> z8#89duKJ&-sr}QSyQQ#GyZq;U=}Tv%ZeCQ5ey%M1C93}Jy0`Ws*WJClEM zsZ`?^!yvQW8|E*b&#`=yG~2XuM<#KE?W(W{-drEg3>a%NNaNrtyi zql=d9v7i3lJn&7hJ(v2*)!$e&>%APrr>}f}=vH|8=E4~h(_a{F(uvFbXzsMOcy{X> z9=T(-TbFam`H2~yy%1B>bu9VDLKUtVsrD%ySGCgo?e}*4uzxVIbb;4<$6tFQtGYSA zinHF7weh=gaQ*u9m^|~C-=|X>L{G>mR5#2iK4I5f;PdD1a&G>}$Of+S7w1a6W`3!1 zUzGo2rjyf(WYalIlft-bkAFJa7o^O>8<`+gAamqGuSLV0DOx+u7-?Ur%P^~G_3NE$ z-p$6)o6(hgP5km!N%oRkK8yy-STD6@ZY-K!@l=KJZTrt#W@|2%8mmthc)Bzu@7T}( zOD;`5A9YlTQ>N>)y26L`QW^8O|2<#X`O9A``p@%c-}G|Zj8wMEthE3iv;+#fZuKV&w0oN^fJM3plEuGuod-WGpL*XAEuc}wb$yv$vS^XEI+Sk={P8oA#sKHuw~Yd-ne(g`fnC0<$v zD6QtO|GVap_M9ar5>s#ct(f9m+Ii~ah7Qe}(>0uRPV^T(jj}x;94feSqVDJHz=^_J zY@PqiN<7yhesIbbhs+yF6RlU={=MP-=BtN_Uq79tr14k7tNVs2FH z`~F*gk<8ZbuNxoO_6qwpyk-89mbLHV`zTvSm7?6dz0%ir^(p5adcLt<_T%C|({dJG zzhidS=k%eEGrxV7S{1v0>iaUg@>5wxu?*QM+!->TY`R@PtL&XtIpdmdg>k#3N0D{) zvSTH;|L>2?H$35E%ZMi{~(jtvAuIkbW+I{?|*l}qF zhTW48v%RS5A=-2yg5Z|lTiMLu$7Wxw_dCrkld3K}v~jnsQ)c_q8>wF6Y?qpi&Rnzb zE4Y4SgC{Yl!ar>eh_?R)bdc~%OkO*V44 zY@q*Wvr*4I^~oRe%rqZYMzVNl?{r^mv)gfQT>jbw)4LsErQ5i|bM8m3yW=>RPpQ2InzBwZ-2h z)6BCCb1(L02rvD<`tjwxe=nD(H-BDSE4)2(p6}y5F(vJVf=^FwIrAy$t=8%G!h5=> zT&27xl$}54XCT>LT{xw!GD7XXjtABh~Id}3zQE`yhZSS-7 z9m+G>ue_TkvckKpLb&fSeI&3O@hNn+igy>G$dehtC1Xsc@$8UDq3hFj8=*?R{@#WK5ECOv>XY4X}oJe~* zYguB!>bp4-^HW?Zr$&BV$iz5zx2BHAcdh=z--F^e#k|#0?q8lMarCg>W!apWyL?xv ztojiXxuJ5qM;-est!}fOyxr{yf)nTUX3Y8f;g{ls?%RdRH-fJ{R@_j!bi2J&qXyrh z8kxhtWQ`^)nZj{SZ-w;7%mY*BXjsi_3k=fBXl!xx;Yyi%a9L1<%M&fl_md83$;706 zpCt2u(ce3Y+xsie7r7T_H1xQVo}4?C6Lp32M~0cALDyD8W^LZef(iqR`tnWnyLSIC zb6_cWzUsuKNJ-(3tD9#Yn_O6VmvyNuqfO`$_TTor_ujsJFE{@k4`2WNeb!brud{A# zSm5Y5CGfMWi-((7$In=vYqpcrt(z{rm@U)k@f9y2kk z@_0do*@Ch^cOU#YQhF>iXv&o-<%X%E=XTCKWR$S=pPJvX>r0JY0<5nuY1jFb|E+v~ zZRsE34Zm+se$W2CtiQj{|KVYlV;5Yt9F|*Gw;#`&#QN8F=hdBw&tH7_8twjvlfNa? zXkLbw6-($Xy}zPas;@7tS+l@MYkEV}1ioGae*RA%o+Vnn1pp}R)UVg0e%hN5E8=UX~%?a2T2;_QRn%11*k{n~k}W?>!6sr1ZW zlh=NT(cR(J{MI7#ljr@hy>3@( zd>Fq4$EmyaPhx6Yc6U`%{+A{@skDnck8gJ~tey0ldA+lA$sb+bB1`rSmlSL_9G%j0 zA+z|#N5O_%mCX#tCJ0t^{Yq)s(=U>8ROjr>Zyb}E=DdI4a6x)$q^n`v*EO>&1(#+; zscD?LV-~R|k2B#&O4yCRpH9kig?+#LQ(w3C<^-?wuw~!&g?nmm6#FvwNV!pcWz9w1 z)!xgWOzc0U!<;hh=C8?z&F(Q)#ojCH3)Aj0JSx0kRaxSddC?CaF$Y8(sd&r~{3<3j zb>`iYEz?$66g^C~zY^VK`AtAYdyY%t)=eibyytzq`X(sXO^?-5w+uHusiG-t@O%Gp z=O-^uTbfVfXjK(z{ruaZCgRTT1=6RpSKVGHDjo4hV@X)n`}&o3MnQLjrwaNlf7#@F zulse0kdCTl$d`#VMf9uKjRRfeD;I$?Re$7 zKMAh3U2^W1zcSjjB5!Da-KFuuNeRF>Q^OtK5GTv!XIRCl(>*)nckGf@dpS>n_K010&M()Q?L74~A z?k#aHxwMk?xz*y%uZx~1w=X>u=WwmzPi(u$w|AO7Yk9)wZj*I8a^YBJndma>mtU-0 zZqM=Bo|x5~FMRmrngVj^(jUocL2>#Z$wwj-&I`ydKQ#nxUj3m%skN|M#!o zn)Wbu#*{6K zO$47+F8w@Fd`I4|{DTvp`7)NTaXiY)ICs|!=DbB`HrM(IH6>|0%)T>|XXX0M3pV^+ z`|Vy%*%s%!Z!(H&vVIHtTndbt<{<5J@Z^nV*Vam||H%zOJLkQB`SqlB?}cyQ%%avu zJLU)k*)T|UNBm-#Xy5sp>Gqt7VNCN>wrEW?=w9t3a5Ph}Tand$6DNbWR$QRrF8*Ix zuT*cG(})Yv{}?*S*ki`M!;(4bHcgt^Ul-a-@2kAFrLtk#tYwVm{ih@C6de}$o1XHO z5!-AtjqTIRGS|5$_Bwu)N)vo8QI&My_bKBuH+)ilpC~)}`19vCN0+XXKVkTy-N65l zr{OHcJ!`hw%;?=1 zCLHBi#$i^N(4ggdX6v>WSyF4cd#2cE8(#OWW7@{uaFD;eGKlGUJ$nYX-z2W7&m`yk zeDLYo;pU&qa*rKlVEHd}xFJG$jSq*|os$Rm#>%g_6aqa)j5`L8a$YU7Gq&GEVef&E4;vuQrF}{kU3nGKZ&I-$L4Ck;tDYQ1p?TGi5uALwMt4^Ahu{7;qMa-f5Yi_()(AU-b zR($hI<*l=O>q2hYE;zPg#r3I|*LZAr#avSA_c-*-U6=Fx8yt59zk@w&rCe|y(2 z>(g@Q2X9*)sxeFFf-&2%Hn{+?mi{wA64Do{=WJs9GJoM&RZ-sA=kKq`iz*dxJ{c-} zGH>mx{3?gFevd?b3nmuc5Xsh99QZf)W6{@_C8Qsqw)SYtIR(2a@JCxyNein4m{WwRQAF7ypFiJH(x=X^n*X1Y!y?_6^Z^5 zo$}>}ZK3sy{YT1|bSQ--_IclG{H>wh^++|kJg55I6*1vUITiU_(^VR(LwSVn8D@RD zvdYWi|1HH$C))Xa9=x`YuFgEs_Q}1<)2n$6|0Nxry{EjJt|{;xcoAtm(aYiNswzj_ z2S$3k+@{%;E}P6Id!X*3nAf%6|8!k^9`FBkVduZ}T;8`&AI3I1IkAc)vFG)y`{A2z zrZnGB^VS2NzVCe({TjR8urWMY@Owl5YS&` zyu9~C?^cyR)vp=7-u++~m;FB7bB>}9Z_^`ov2)@g-~Oli?9NKsZ6p8h{E2lvJ^#Pw zePE0Gzo+hU)`rf7x}RA$W-Mm;Cw~60$EWljqg~5iPP=}f`*F5-cll(V_LuRA@8u3m zJT0xMR312IiP&$Kv}Z|k*MN0~H{agNznf_OHYD-%oV~|2CL6C!_AZkt zdgbu_ZrbTxk2hahSNJ|&;K#9d_xl>}7bL6{JNwLQbMS#hdlFm3Lgv4T*zw3?vfuH- zYsWNwAL$wEIoF1$JPF@-aZPoFic5!`$Q$LCk&9NST>NZO7FGAV)o#YaD@*$2-a7tb zzRo$N>Lkl&_lYhkmu|2}2Ky%;)>dEo()Ij?^I?0Wh2Lebew`_mBy@f6{4W}k5&p&k zK3xpiyxch){K8WV<#a3dbg#eEe1F|!x8s#Yerk!gR3@y^yp_ki$auEs4FSJ}DKA;8 zHP0lzdQ`f6kyTqkmS^PQjIKx~?+ts|yfRRG@{pCe+&-<7 zC+~?p7`XO_n^S;Vf7ZHWk)211{s%V4eR$b%>a}`5m*OPGt5T7>ZwhR$%X!IcHm~>9{RT_&~#g> z-0^RB*=p~exIgXUapPOf#{O@Q%fHDKx$vf^;%NH4X%fC?+gq4Q*PeP~{r>iw4~KL_ z-!n+RI=8m;$Rw|bt24v0!k)Hmamm(pWvdrBwb*X-D3`gZ)gB=O&)cyKv;QyH#y_Ze(qtwd=#s@S98(s$?5a+awg5sulw10SSru1e$mH$ z1vBn_7JnVTr)JT|JC#?g?x>1A2}@u5Fl+Lpq#x05|E4yU+Me9T<~Hk9+p~v1q)U18 zN<^EpnXOBrqT4E6beB!8ogiMto6GjjHu2?Lt4(718QrRHik_z1Te_T|<<_u-@uShk z?EhiQR@dL^jAh+)RWxRq<<-1{%VWfE6fNAds-XVrML#3mC#_#{YA^V$_q2-sy0*Mv zmt=<5_A2JMM=#e&Uc9ky{!*Uk_rX`=c3hlsvot2}*l81ZCIcFfwA;s(yiZqf}6$8zF20M!Nnuqd+{L4>n)X=xPoUyeu-#^n72vpq?eVMU%rEo~9yny`@Zi<&AVCh-*DD2*YB#^T{e8ow1@HV0 zx?8g^)hWIc&BMZwzu@!TnzPHM-d&RLGu3^I5tm+~9aQlpOgVQOFvLDQ+@86A%viR9y59PF~ZL);{aVV*&Nt zgG=PSKM=EH;_pQ zci>c*>$7Wu@ms!mp6QdbDjz8tWX^JZzni7L#n_(dPl1z_!W#qkmYBby%VX56d}Gz- z`SPhhd8l=4LFuE4Ezc_VzgRfe4of+q=nP^k1jbZ^;`4O%U|4eG}QtoSD8eA;kp0h7)3#dGiPR@q-B z-@4i8^?`t(i&gH^16jX0woN?Mb%RT*qGije!#bKf`9;DjkEFU@pAtUVq-n{iCoH`_ zca9n6oY0t>@O`37h1Dmf7q8Dqo{D&~is{Jug!i+h9&qHDD0Z&mDLALOfz#)vW z&u=X5Gu~Se_RV6w5&N;jkLNt?%$~Y+bJ^0bmHcxhZ?jqP?TRuv#T>apg~`_RnD*i2 z9lDqE4+Q*(z0Nj`bK8PTD}20cCk1lah~<25ydEZ|D4yFcShMIZ$L)-JEk~<`4s$lH zQ~&V#k&tugZrc@E`I~N6OsKpOn7*2=v~zXupUvLp=9kZzeTk3Xx3`fmt#`wb&&y0! z&2#J)KX29%$i);F=q(_9xI$!OY0WgrIbU;K7{fclKi!*BC(M?Zp=>#uqk_fvM*2TD zi>Dg#%bgs5Oz3F(b5XfBaDJVUv)u`gw0FC$+LSL;Jy2R!|G-AqFMq<5rR9yu#{_Pu z-R$@ndR2x$!`l9|$f4h59jZ)IVxskCMKu`}G;zF*dAOD7mF~0-$(5Xs*loQlrf$~y z=cZW`H2=e#SoJT;zYf@SuRpoa|J~}Ew|gGMoZELp>P+5e<3bTBW6RAKR|hhty}mnN zcV;$g``^-a!d&-zgWS{3Hn%vObw59OK}gDmCH3y|JQ~}MbBAaOC$4?8IfGTU=}L!z zuJLE3+}vL`&KejU6-z1K@cU+S%bl9XC;orC9lR;~=J}KPd#5cvKk+*Ao=msz?yAMx zcP+Sna=%a}bL*TfjWbWXE!8}{+DvxAhaDF-$_D>QTyB5)>@xX?^UlgfKesGDb-D4U z-TV_8EO&EzXI?+XESY`$FVo*CYCa`UYlaKq}y9>L)Nc5A~Rnu zpAq|IR{kNL&kM7s|H>>WN}6kZjct-xN{5Nd4vAy?j6Rjb-d-GcLht;nnBeKAN?+>P z)&F*1j$gH`v~#_2;pTqvQknGRpyhSBDGxrLoNOJn%pvXl zw3~r-AN}|azun{VDa4~cH1hPfFO_S%x>=uxS3Q#t(KMNQ|8VH3V3oNlRZssPnf$-+ z$YhQ9Kl7_U*-x(Cq*Y$GFHh#g^pjqi)26r_d2%E-cD|JBkF%ZfQeXI3ZcM$g^t{_D zk#AnbRw5m-jUtt$4{!ch%T%RybsKx0)`Ii73mSiXouIjYd!wA9kJ1*lp3B!vX5Nu= zc+Hz(Q{-9f!Q>E|bg*SN>tzFl%cbw!_vL0Z&p+$_h5z}zD<_@X-%cuwO$(FEOp4S< zoTu3TOj{}7g4WNqn(`Sfw|6C|vxeDe?3^6jP@Qct>rdyb19{gKTN!v&7D_ETzh74_ z{&wJU@%j_q`xm;LeV_Q9YgYW+x1J0dIW_zb#OAqu$SUBNb-Czg+U(_ClND;0TmR+z zZM@&?aO24vV{4G@8Yx#gKlZsb+qL-Z8u<=;k3qgYNpms^C$XWgJXKR&YxT}EcWCovjc~>vb{ac+oKv)c;4~u zg!)~#y2NbWe@TwJd~z0>9HV5>0qq;Qs~=oZt9ZgUFHMgvLByZK=#c#W#$d?$*U}UJZScxg(EZ-CbUJ+sBhNmi<^=9odtwdqurDIh)s^ z&k2jx^qv04yL!&Gv-+HW4Q4(IthVF%tnVZqoWxnu z`0wq>E3SDT56$AL3!QXUx4hxG$by-_?sB$Ge77&dzcl}RZd?b;$2^XZXGa2DOm8%> zlzjRa%E9OS=X~4!!ph};SE#S|Qkh)i?D_5CkB3YDUYp>Sn5w)la7!%1BdvWCRiBsd zVpq$LdQmL)W=Y^%v-cvJ*LK}3nIsqLUg*R*A+o7q9|u+h{i!QHA^$(^TAT5Mqfd?4?W@%N53UeoGhpr7_4Am_ zGso+z6;C7_eczqckm%3LET}1A(Gi`o(u#SPi<|te%h_Je!Q8RpX)mN^2` zPMA6T-n4xhZ*2cJezrW5l>QGaEB~Qw4}2-xwB3f8fnftD1A{i|b_2)^pnh6@QEFy- zUVc(mYH|tkeBj)$`}w!PD+WItR+%)lZKc2Pnz?Gn!c<;pz1a4R*WB>n?2QxOPBEDB zzxKJseox7$BhHU@|Ne8gyxgp0vuM!b!*f0~oS67PTgpu+TWg2c1+OmNS5>w0pT*bc zrM_}WDZg>@@iHZzP92-Qc7JLVJ?A$reD>12$@C|anbfEJ^ZPs2i`(zp=Cba9{v?;s zq>WJ-o|QK~gsoctJ$jKsALr)q_9K}M=Qjkc*mm9KaUY*7OaIx7hCKPS%Ynb_(o&pv z$VF}LV?VKOd1G3!fcxZ#_}XoHN!cz;9cC}vWVxBwN4f12yJY$Dy;RiA#n(eBr^)X+ zw@p*b&P<$5L!d5YMs?Uj9scFnvb)}G=;K+*n3F23uX5`dn`5}+bytRJqwhjG%E6y> z-fWdEGr5pmFe`EIw=f@(q9@C?yGETl-m~MaLAJ*8sO=A*Y%^}0a?dzuL)1RYD>qbb zS5zwL-CiX&W9`io%6oOi)&H)!_&Rbr+tbGtx`yFAU9V%F?K9l9Vu==Oa=&dz#CP{= z%emIcMg@Q8{G$HMPBUdm<7?I@mRzzQ^yWYP@v+Wk{+VOt{PUN6J?LaKU+2`yWiu|? zt2p=tU;g2t-D0XG{&>lEwfT*oG(WA36cT@{F-7m{>ZXZx>ZcExds}W4J(zX=Z_iri zH&bpfD@f0*;pjf;`bxFMX#4ya)5aY4yn>wWm}TdfH~!9K(eRG#dgQcwo5|6e2ez-A zAXyZ9w|Ief^-bsh;@?W#B9>Kbwcy?Q^4N;r@)=o2Sop+T(%z;#X^RSRe!+EBwer}% z_W@lU?8?Q`PmbEmS}K~FllV;H+o|_cFMAxm-uUbS_qpA2P4T<#*DHEfcm6l+ogimm@cCJju+^5Y-co6$8mz`@ z{so6mUeH){`uL;ew?a$vlms+eSvXE7-B>wu>X);s0tvnBR}5Y_?-T#n#{1WFTGxx$ z9_#h)AKvM*PV7(`JsVQN}hYH|r^f!^MbSpP%dlLL3;XEiz3-Z{deF15(WC|$|$*p-_v zC)F<1Wo!CVetYZeXm*yKME(ok?o^-qo3&WwJV*0U+a#4)x{4~1Ozg{U@@l+&rgGXa z@$=(yA7-JaI?7v4b9iOWnE2#?nb&*6r1?|x&7WKINmYa`aGVyzW-(XIfWukFpnhI* zgv0^+`>*@u*2TV;t17sp**9nR!x``Wo}QEaK6j!0L-zDzMa-&7@-s9qo!(bm{di~p z;?>u^ukV(s;BVqTciubbxa`m7iR=BO&jg+_;0$a?wCE^o3J&9#a)o8_cb(qn2P*i& zbmgO)yDM}a2<}Tvy6)UBxb(qv*N``RCIt2VTc5{#=Fo(sZ(9WCu5p~4WT!KsI7ymm zYOccNhVw@ky4}0@(@N^-#1lTXZ;Tf$JHb=6^~#wni#N-bPI`1KYpc-wkRL9GQx#;U zaq~`9{nNYT#w_i$SGzM8ws~#Rjg~Ecq;Wre^HJ3;4l!q*PZxc=>hRo2D<@tM;}r@o zjs5z>$+=bQ_>ud&tae&*DVg0*U%4+JM)9rJ)eH9GUVD?QuXepI>iECPYwNuI9a-x+ z%CG3tdlDr-tY|knWGx@ z^(8y%avBw%K6`ma28LuN1_njc^pKXHSE38Kr8K=LF(oqbJ`j9q?TA`$}GVTKcxEY0*g$OCNs> zm~ePaa}c-Cnl(2Mu-}xt@_2{l+-$AM4;S6z`zG>~?bLz=a-BX4A94reJ?VFPZ!O9g z_v}S#T2hz0(RTBfwfSFue7R8R&HgvBb@Jr16Dw|>xZoLN8C`U?Tyg)hl;>*M9&3yA z#U3mQn0(FRo87K!1zB5l&pI+Ke7w}Hob~x8w+S(awHV@CY}A<-?thbYyqiDx_ic`i zZpKgjJtnc}Eez9sm{@A~u(EObwQQExr8~FQA6Wd@vRdYG({8WVO8JS)w@&?AIko*A z=kMziuH=7GxjaAP{R)S$I)iz4Z2h^?uJD7j1pxd}`R45=Ip@Jr!}AY1Xk zp_`jpQk0nt%5rN%efw`22<-i=U9&A8&OPPUiH{1^5bfJVFKBhM}DmZ#SWslV2=C)YgoTl`?r;)7pgLwkO@`g3nuCwqUd!ts6ic}KZ)x4*tB{yElV z<=)p(u}k}T?l6az_)A_jn4GKrD_d7U*ijm|xs zhPJwiqUXfkKeAx++t)T_V&T&p6V8{N4!vZ2uXn9Yw(kF>F70d|R_5|GvMsc`Ih*_N zlhbU?+;SUrDc5Jl#ku-A-)5PG?5GnoeNZE+>*4)pb8s&I+WAcf zb`&o2RZEY$%axiO&mt*XExktILde|HIyLc)_qrr%Ek3U0eSh)!=GiVzDtsTfq}FRy z7aY-jnQEEGCvJ4;&90e&o;;Uc%84I+dg=PYb9clRZ(hqK8+Rj2s6@%q%ll-0*ObV@ zhUf1YzTBRB%Rk}yzmKs|?WZRE)NV@Y=iOUla_zW8%l#dVZ&-gcynEM^!Kwa0`qGh; z`m2AvJnTQMk$Z*SLPPb)@FjT{f)?g{kw+~hdUKEV{AXrhn8k~cjvBP1Acn9{4}mVF5FXkdsFCUegO|p_A^J9o%Q_x63THI$2?3 zR(bZ@`TviV*~U!?lKf%Gv};|QU8Pp>N~P~pJQ77Nx2-T$T>ZgIDs98kH6piPwLG1- zDWGPmWMkBJ6}@$VmAbQLdX-Mi(6L;+{372pFEN25A5|_^C2doE<(VYQmA3fUMR6PR z6-q1i>+_1)ZVWm(=}qqYWh;b_dMi(zr=z=TwVU*`KNXY0Uu{az-gEKOCZW!UK`%O) z@5c(vGU+LJ9HMf>(BYa8*R{+>%a%P6z7Maqo3EFBTG_|SCG416zw3Jy&*$U6r)V$9 z;Sku-R{n(Px={jy?X>6C-Bl_IAj4=}x~p<;oU|lM`2JTiyN;$MPl8eL`Zo$ZpzYT=NVz0`ufUiclC#_KRnM!v*=`ybwPyMLt_#*T| zX0og2^Wf(_EpR3p{vAs-L0*p!DA zr};;BF_%i6Wsh61#>e7|T~qt_cf3LSluz}=N;~w|xEa);jg+ zy)$+8pT2II&oHg#xu0^e#_{h(d$hyPi+X=eV*l&*IKjCo<*k{`g8lPU?kEPHp2h2O zHOnyf{>dea0^jL#U0Ut^^1gWbyKEDOb$M=+zWz2}AntxqJf8o+Zszs3-%WdE>Agvr z*N(?|{Z3CKj@#GXt6$>J?iDcU_B2@Qc=+@(zGruN)l|Hmq@8wLU#^umfw{`}?~K0( zd869jX>I49Te9TtoO6#oH@D3#TjDtHq~QL)bH5yMKL5Duww3?%j^?>59qugqZT|FO zL;7mI?eX)!9bEKpJF})<F)iUVjhGoNt)BeV&2g?3Wi=FZv!`FmG;NtNNFmFKE@@a$d)Opaa?;Ff%X+ zp*AiG5=%1lN{Z)BhHmls)3sAH$b0Kv#}|&yig~;k1tXf|~*ldfjn4k-;SXY^(a@@VcpcH<&J+yZF3jcIwie zR)*_A4Ve=UFh5*>3-Sv8j zn*KD~4_8)eeAVckxa7edQ?*3_8D}Lfe_wU3@IW@xVj+`mMg0h_BWE(3cEx>DFw$~~ z4A)t-H1M|gG^<||j#zTd|JE3I&SO^l6xCBXYV+3K7yi8c<7G#id$sbcS!p?MLt@Xg zr0kj(_|g4{KxRX>rH6IaKgq&+)~dbg=C__smRXqp_Nf)G%C(&Og`4tj)N3|x)-1T` zQ^QsMxqgmUPk&+l`DgPE3X6XO&_6=m1!rrz>$6iuCb z>yxfY>y^7omsD=NyV3CPeT?;X@bt3tP-B(iW?v z)k!2jTJ-$#WbtmJ4A4yYtu;*G75P+b7b0m?&!d_)VT&JU>?R{Q19EwR-fAI(6RrJ!hc}mqibgUDu(K zJ#mj4c53{O`}IWV@cPaT8zW+c?3cZ6$?0O>e1+esZ1(>tO$Ptho|>^&^89@N^!(6Z z&9_TcJPi}K_lM13Q}Ky(%Fy3=EJarJzDU`h(%0_03^o4*Y0UE#dwzNH_uLQRioVMK z!>;CKv2C{MS`htZh32Bmp3w_5a#*-H+8p{65_l%7?n`V-d}DTW%l3Q67`Z3U_}0`j zNx9?$Z##$5{Gcrfbst!Z6fd*iXDDDl=`X6FGRnY(9RKiVGKdPVr1yK_hpPYIW+`2wcI zUq#Czja%i8X?2weu6cB(>3UDB;P$;KSJ~cLS=%l)JbK=+>v|a|xd0flXWOI}6)Vo-f;di2fQ zkeiQ}^L$m>llDS!RrB15AB=kJ-iw{g;$4pN`gi z`}OAL(U&>@a%|!k%(pt4QMOHHzV7a}z1AESN9)5xCsbPO`7-79xe41ORbTxO{vx4t zL22dIY3tZ>_jfIr7s8uyu5$0GohvSHUA^#v@3WN1*Cw{98!z`_St97mb};7 z{nGDc>G!!R$#>O6zGmK-)5vyDR@m^jz?p3-dPg+hx(b>)8QKR-XG!juGdVD&f34rm zr`jR;+Gp*8103b$b)y2)&K_%d$ZKkS)=!*w{v!Dmp+l-o` zh2_E<#cf$xZYDlG+xu*t%w1iX)ai1Ii(W*DzEpK{5%1i1X||u(>F$$7e@@+~G|EwG zEM?v5a>e&y&*prGBRjuaWNg~;K3(W;`zDFZrYPOYBxHo}g!{%YC?` zW8?N;40!(UT)KMy`lpsjuTD;P_%yLmN0{GOq>J;7hVhkuD;^rVIrXbazp*G_N)^%L z+Ff;aMfgiiAs^Sb6R)l~WwoyE?{X7GnJbe|zS{e3VeMtjx8M4-XFs-k`Y*eg=W&d1 z!Sdqrvt@S=9=zze*-qX3kgI#%yf3PKxf^->U-#TxEHwY~|7R_Yf*(HqpKB7?Qm33* z_eS)>yGa`MKeJl5N*~b5GTv|5^oZ~3*4A~R)BScP?SI?4t9n)Qn`=b}Uzo{uN7^5c z$)3wy%5OQL|LD$}>#ZtvRTf)coVIlBrPgYtl@@D_W<1-uKCU6`YiREBD&}eNbL86Y zNSG;mEwk8u>1tc{>-TN9zBey(f4J4XL33``w*UnbFFmhwc?xVw+$z$YlX`Z(S{58q zmR%;;?o#;ciFnZi9)`ye0cQ_p7B);a-E{xZg2!`CF-tMMO$>Zr%*uI0;Lt`Zfz>Im zS3ec^k}CSac#_}!M^%u-)Vrz2roZ8mv#YtW_1P+cIm|Y!ACG^SY?ruV;=?14ei)l< zi7*1o`X7zmu@~) z*Y^06)F-rhZr%KCyYDkFFkE58*mzu)np~0(x;Y~^z?ZwpL1fQw?R&b)8+Qjye{o@D zM~BU{J&RhAJg@1gTd#t^s>0f?Yz?s=IO1}VJi5Zn^D*#o}U%bdVAW8 z&^MJgv@64wIvtQTzFDBq&6S-J)KJuTk=bez&!VGRVKMTIuV1Mb@v^gvo|95jd7lC%2xfvM5Fx0}%`gp4W5rL*`yfn`*T(d%4N0Tkh<72iI;?zHQQ}^zXOc3>JUAM4hC!|2HCITOV(3PbO`NCX z{ij--+2H%Zmo1a&P|r@0UY3PEACjBn8ZI8&_VMb`t50^Z@o$lv`ITwIv?oUG-`+XS zPE=}UU(Vpcs;u|C_{?Lut4+7>&RLh5YKUw*@D_lCpRsYRVeP>zhlx)UpMW}tqTt_3m5B8ICS8YNo3_s?#f33 zB@1rw%&9wDR{f^ejm0GWMBNFKxrHBC!af(Uo(P|^&ts#(Jl2Ig^XA-jo}kR5acZy3 zT!tMD(2~?3LQ-(zyNEm+uzbii1r{$@ z?&LDc+nYX$T>n#Fxq=~E%wZc>w8ZlD$umDqh)_E@Y37W8jkUA>F_^D&b@=9*KW|53 zN08=Dwd2*-W{T|FvCxL~_zICbr$kFUBagT*mwAr8JmZOFZA&miq2pG` z;B$(9T6?W`_g;Or@M^x!#eF^t4$m>WYW(G1%v1NWIr7!>W!5bEly!L>^C?ErO!uUO z&k|dI)LiJ(jm$`zFC$lSBrIaX-d*ovT&um#eaq2%lm7=zMW@~EzK3`0 zzHRpZZqjqBIZdhe5Bi$<-i|cPa_w2<^tk1qj_EG$-CJLZYd-&duKK1q^QM<)ru=1Y z+mt`+VM$R@<=@XKMP-(?U4PR%d`uJgeWZ1wPI*o<ZMuKHTO# zuxWA4zqiYJpYEJvx!&TJ#=_VN_Om$;%}*#TIrws>M@i*JxAU>OPCIqBXey_s2JEYS z{qJB*t!(ez)ccLs{%`)8yIC~$>yetX_WKzFycwD78AQP68n<@p1c4b)f(3l?DTpHg zBA|JRi$MX*V`PwEP|jb+c=*LNMi}jUWCoaz&s0tnQLVK+95Wki6pSvm zoC)S5jE3yB^jrTv#hZbFA&h~6K^Sf{$cdiFMki&KlxG&F>Xj5DTfz_|biHliHOA%y zh~Y3A-ICCCy9FPyGcatF0v#9(Iu8~U1RzU}B3qK2n4FQSTU?S~l$egos>$sTt6(&` zRm*o*ANa@0z`!I1D*KVGy2ru506KyK98_tki6x~)si0w5Y#v&?4q^+8Mz=-H;FMAhGi-Kjz6!AeMx)zd-F59&1_J{_F8IVbWIGB3k?bff z$;`nKErAKsJ!3sM-3u#Hf>y+ySRR>LF5Tk$GL!2qLS z1q8wpNW8Tb6fb|m#=zjDf*x;jq9|U1Swc*_B{xGXhtcSk8Z-$`@MmOTFaqCvi5!m3 zGFUCe8;&q9!f1qTnBm9)Pnw|ge?}H+TX8W;srtBn8933y=*@0(z=YmA3;Atu9UWE+Hd`~n%Zjpqh2GBDV( zqNnhMnh3Xh1{CENq!yJ_x}+8-7iAVe+P=v4!HV$9Qy})iXmtA=xw4j)F)%Q6Fo329 zkbR`DjbvX)WkD(hc45W~KT5pZMY1a=u`<831SNWuBj$pm14cjI4e=DZXX^RpoH)qH z!0?=vfk6Up0jNIW(m{9zYC#g{WE)%+1v3}KMi`B5(~9B;h^+C1njG412H_=+4U^R={XjAR?@S6buhnO*@#wz`)ST z0J@b1*-P>Gtil_NFgsy1!cI)@$)b3V+Zf@!0%&5_O@XI&)a+|~X$d%e!06;J5HBOF zgLu)5>8IXIMg|5o76t}=xOJfP;YP?h#Hp&f$vK$?IHI#ydLGyzFdE$fhjc~!cQ7z8 zI5ILY7@#;n$CQA7ksXkq1Mcf$%kRxj5EsB`bQe6g+`>@F$iU#ojP3#>GvZwUo;k!3 z1~8jpG`b7Ue6y17WMp8t%gn%Fh!O@r&53tGeoO@xbaLBhN-VsHq1*wUk zVSQ|gVe@u~6JRvD6J$HD_HJNgVED<5o*#N_iFZPAW)-ehJxzp{p(#R9i8ld%neqIO~f`DsylJ{bL=7?JyeM_CGaj?4FDa z4AmI68@do@dwFIGcp?y6%7fVoqtR`z=apWX!NkCDnw^0`3nl1}xDvFzLbo_0F(tno zN3?$qhWH#tquYJPt4Vw{0|SE&BLjmfirpn11ndScsls6`b2Y?T7>#bN-8>#&(CtP` zm>3w;QLIh!Bxr4331kTuwy+jn1hE)Kqg%Xd)1@c|CI$v6jDqI67eR|l^GZ^SbTjjk zi@-FdE(B9lJW7J2EjaOk-nUP(z7xwqODl!l0`31>h15Mx$H1HO**ZFe3xQ4JHN# zZ4~cvhZC|Ev|=$WF*h>@S6BzzK`e*S=$2O*X3X8lz`&rz$iSe9V)^n2Vl2l~jU}f- ztcKC(R@d-Myk^PBz;KWWy)>I2NtD%i%JJeJh}|$6-EQ$Z-#5P)7#L22Z;?l?LKUKk zu^VyK0k(wJycuFUj7GQp&(RMX%orIMUSPy{N(?c!m#1c?XW+`#lW##RhtcSk@79W6 z1}elKGchpep~U#(SOS*kq?Ulnwc>)rWXPFw*gU^kV<9-X!)SE-WhKx22Q9$O$B6dm zctZAP=B4T;6{RL-<0x4U`$O!8(dc$7aMW+E1|8YVjGomFB@knG2EKrYSqh`kEid~d zTB6Iyz)-}5-ic^SB4~MWi7vR^qKmH+ak&iQeHe}I0uS4CLr_NVVM1@gGo}#l0s<}g z$8#Z$fYIoVxOFA^pcg9xgQ^62^SLIKct?~}7T|2d5pY0>*t7#5K_l%<=+%pGHUS6Z zCKloDKM4yj0w;VJjqY*A*DU|vFfcG&XGE`0J#q-xn-9uyCAi9XWh;oqFdE(BdGk$O zwHX;0nwijB?3KBMEG|neO3TSF*DWtf#MON_PJ~zwqtUJZ5~{Tq)OnuE%)p?5QsBJL zBhq>t*&Svhj7GOveZn3tCPoH^=@^a2bp-@$E=WvC!Cl5VcR;)hqtPusbK}e&A4UcS zVGJ*SEhJ3u;SRqpn*Q!#GK4@Tw?;oe<60mXmq>xWdFOfhk=2?ixG6^J91^7 zRZfiEDXGc%MTww63A`O+nDsCk-5E;Kvl~DcfwHiIZh1y_Mr8$w&dABk!&SO9t1JeW zGB6t573Zg?PTbDGzyNBrxCml7j7GQo$dBM@JE22`sKaDJbwpWSQk0*air4>}XF{9+qtTrZ9PYLZ zlp38F(MPDH>Pc_{zHx%X2Ov&>(dbTKFJ%k@jZ%PCuAnyYR2ztKLP=3#UU6D}Q7(>h z_wsXy{V*EcelcT{oMns*43{v5OLsLAvL9x(F5a=q$9#~nYZ#600>knR6FZm~7}(g+ zyEW^YiFE-E&wnW9wKx~K6$hL!K0Dm44QHW$@V0ed7)iZSyu)Rtb+ted$Do}Yf z#9|nYuo!bP1~EVdnrm9nL%?FhqBG=vRI>Y0Z~+gaVbe1Riy_l8PUd#cq?s5Pp0P17 zSfMnlo)T{{()|ut|2zxNY3$aQ86kgx=qT7>JnwJb} z&ZCCh=4wdD!DwUW<=~J+*aQi=tY3ciY)lLck60KOjNmZ?n(x-#hubFIf}+&4%nDsl z(><|B7ij}Ia^ViERh+XRcEf0NH(XqKe4`jM1A`qnRZGXsMe7kWef#bXMq|^yC}su*4t{jIAKb@nH*u+JaR|h27>#bX z?nf2{eO?BJ3SacemZrzJ?FR46(@n}L1?^hEma$;g!DxiFn8k<^JadDJkuM)`TZ_G- zxqK4hMHme$SP*tX3KZ+#Md`1Z7#J?{q7Mw1d?dzB;sPAz3K)& zAo_se5j-}5YjV8pADF!`8r@!*TjzdfvM?}g)IhiQ#edvB1Sd=~?ZpgP4tNxT3V^9B zjNm2lDfzi5EplVkHQN>Lih+PAj$BhjpfT}3=9l^8PF#Q{IFS(lc`Ir=ZXU$ zHo|ChnG&?#}m`g`&`i0v>M-S%n^=Gk)?7#KJh(dU8f<*?We>YXPRr6v;XdsvLaXmtB$ zK2DjtorQtnhA4VHp)QZGKQBKeRTppfHJAzFPZ*7`2Q%?Xp(I`*6@)!SsRcQS$*CzY z@1dq*;p?lxsTf8d7KZQ}5wEjtNx7=jF__qQu-(qMh+L5#kIOjqZ#^L3fv0T(vkOL}+j%Yi<+g*&3=ADS z=stf&z|Mlw5~9POc>~1fFdE_tF3@f=2GE!&m;ohp;3*%}TKTDq&*ufjsii6TFlXRe zs0qspFdAYzX!9Q=FUc~AYsf}U7>#ZL$B(^#&$BTwlu9r#@W6u(WWhdThz0lt=#zyY`}$xsy6JP? zn%O2WFfi0Jpzl7~ZUr+P|8g$aZc!MGumLmbx#8{yMZK#v#0KaQc*q^r&p+3Ks}vX= zTntGA2*V+9eowk~q67y6gP}h9IBy=R;fSGp9MOSwq)S^#vk5b~p_VjD{84Ozv?@^gGQu8^ zJ=QQ9mimx=gL#K3vTv?nwF6=m@(ISsK1;5-?s4SBHAZGdi0v>M*;Y`6g?eBzvaNTs zvH1+=@yW=R!;&P-%P<<RDC5m0pv)8cs=kjWZ9WWZ*4ttbS!jSE#u12u~JTeKI#7@r7 z12rgc6j?BPU^KdYsHe3c+n3ygVjukM7HqzHd%$6vk2issj*6$A+}&2R5G3h z$-pogc2onxd`P8*dTa`^FBV{!kFRcr*#n~kyjj^m4cbWzlNb`67#QxfF)}bP003$* Bf-C?4 literal 0 HcmV?d00001 diff --git a/src/css/index.ts b/src/css/index.ts index b7fe5fa..0eab690 100644 --- a/src/css/index.ts +++ b/src/css/index.ts @@ -45,8 +45,11 @@ import {overflowWrap} from './property-descriptors/overflow-wrap'; import {paddingBottom, paddingLeft, paddingRight, paddingTop} from './property-descriptors/padding'; import {textAlign} from './property-descriptors/text-align'; import {position, POSITION} from './property-descriptors/position'; +import {textFillColor} from './property-descriptors/text-fill-color'; import {textShadow} from './property-descriptors/text-shadow'; import {textTransform} from './property-descriptors/text-transform'; +import {textStrokeColor} from './property-descriptors/text-stroke-color'; +import {textStrokeWidth} from './property-descriptors/text-stroke-width'; import {transform} from './property-descriptors/transform'; import {transformOrigin} from './property-descriptors/transform-origin'; import {visibility, VISIBILITY} from './property-descriptors/visibility'; @@ -73,6 +76,7 @@ import {counterIncrement} from './property-descriptors/counter-increment'; import {counterReset} from './property-descriptors/counter-reset'; import {quotes} from './property-descriptors/quotes'; import {boxShadow} from './property-descriptors/box-shadow'; +import {filter} from './property-descriptors/filter'; export class CSSParsedDeclaration { backgroundClip: ReturnType; @@ -101,6 +105,8 @@ export class CSSParsedDeclaration { boxShadow: ReturnType; color: Color; display: ReturnType; + filter: ReturnType; + filterOriginal: string | null; float: ReturnType; fontFamily: ReturnType; fontSize: LengthPercentage; @@ -127,10 +133,13 @@ export class CSSParsedDeclaration { paddingLeft: LengthPercentage; position: ReturnType; textAlign: ReturnType; + textFillColor: Color; textDecorationColor: Color; textDecorationLine: ReturnType; textShadow: ReturnType; textTransform: ReturnType; + textStrokeColor: Color; + textStrokeWidth: LengthPercentage; transform: ReturnType; transformOrigin: ReturnType; visibility: ReturnType; @@ -164,6 +173,8 @@ export class CSSParsedDeclaration { this.boxShadow = parse(boxShadow, declaration.boxShadow); this.color = parse(color, declaration.color); this.display = parse(display, declaration.display); + this.filter = parse(filter, declaration.filter); + this.filterOriginal = declaration.filter; this.float = parse(float, declaration.cssFloat); this.fontFamily = parse(fontFamily, declaration.fontFamily); this.fontSize = parse(fontSize, declaration.fontSize); @@ -191,10 +202,13 @@ export class CSSParsedDeclaration { this.paddingLeft = parse(paddingLeft, declaration.paddingLeft); this.position = parse(position, declaration.position); this.textAlign = parse(textAlign, declaration.textAlign); + this.textFillColor = parse(textFillColor, declaration.textFillColor); this.textDecorationColor = parse(textDecorationColor, declaration.textDecorationColor || declaration.color); this.textDecorationLine = parse(textDecorationLine, declaration.textDecorationLine); this.textShadow = parse(textShadow, declaration.textShadow); this.textTransform = parse(textTransform, declaration.textTransform); + this.textStrokeColor = parse(textStrokeColor, declaration.webkitTextStrokeColor); + this.textStrokeWidth = parse(textStrokeWidth, declaration.webkitTextStrokeWidth); this.transform = parse(transform, declaration.transform); this.transformOrigin = parse(transformOrigin, declaration.transformOrigin); this.visibility = parse(visibility, declaration.visibility); @@ -261,7 +275,8 @@ export class CSSParsedCounterDeclaration { // eslint-disable-next-line @typescript-eslint/no-explicit-any const parse = (descriptor: CSSPropertyDescriptor, style?: string | null) => { const tokenizer = new Tokenizer(); - const value = style !== null && typeof style !== 'undefined' ? style.toString() : descriptor.initialValue; + const value = + style !== null && typeof style !== 'undefined' && style != '' ? style.toString() : descriptor.initialValue; tokenizer.write(value); const parser = new Parser(tokenizer.read()); switch (descriptor.type) { diff --git a/src/css/property-descriptors/__tests__/font-family.ts b/src/css/property-descriptors/__tests__/font-family.ts deleted file mode 100644 index f98a9ce..0000000 --- a/src/css/property-descriptors/__tests__/font-family.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {deepEqual} from 'assert'; -import {Parser} from '../../syntax/parser'; -import {fontFamily} from '../font-family'; - -const fontFamilyParse = (value: string) => fontFamily.parse(Parser.parseValues(value)); - -describe('property-descriptors', () => { - describe('font-family', () => { - it('sans-serif', () => deepEqual(fontFamilyParse('sans-serif'), ['sans-serif'])); - - it('great fonts 40 library', () => - deepEqual(fontFamilyParse('great fonts 40 library'), ["'great fonts 40 library'"])); - - it('preferred font, "quoted fallback font", font', () => - deepEqual(fontFamilyParse('preferred font, "quoted fallback font", font'), [ - "'preferred font'", - "'quoted fallback font'", - 'font' - ])); - - it("'escaping test\\'s font'", () => - deepEqual(fontFamilyParse("'escaping test\\'s font'"), ["'escaping test's font'"])); - }); -}); diff --git a/src/css/property-descriptors/background-clip.ts b/src/css/property-descriptors/background-clip.ts index db83504..21fa019 100644 --- a/src/css/property-descriptors/background-clip.ts +++ b/src/css/property-descriptors/background-clip.ts @@ -3,7 +3,8 @@ import {CSSValue, isIdentToken} from '../syntax/parser'; export enum BACKGROUND_CLIP { BORDER_BOX = 0, PADDING_BOX = 1, - CONTENT_BOX = 2 + CONTENT_BOX = 2, + TEXT = 3 } export type BackgroundClip = BACKGROUND_CLIP[]; @@ -21,6 +22,8 @@ export const backgroundClip: IPropertyListDescriptor = { return BACKGROUND_CLIP.PADDING_BOX; case 'content-box': return BACKGROUND_CLIP.CONTENT_BOX; + case 'text': + return BACKGROUND_CLIP.TEXT; } } return BACKGROUND_CLIP.BORDER_BOX; diff --git a/src/css/property-descriptors/filter.ts b/src/css/property-descriptors/filter.ts new file mode 100644 index 0000000..699933d --- /dev/null +++ b/src/css/property-descriptors/filter.ts @@ -0,0 +1,53 @@ +import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor'; +import {CSSValue, isIdentWithValue, CSSFunction, isCSSFunction} from '../syntax/parser'; +import {isLength, Length} from '../types/length'; + +export type Filter = FilterItem[]; +export interface FilterItem { + name: string; + value: Length; +} + +export const filter: IPropertyListDescriptor = { + name: 'filter', + initialValue: 'none', + prefix: true, + type: PropertyDescriptorParsingType.LIST, + parse: (tokens: CSSValue[]) => { + if (tokens.length === 1 && isIdentWithValue(tokens[0], 'none')) { + return null; + } + const filter: Filter = []; + + let hasFilter: boolean = false; + + tokens.filter(isCSSFunction).forEach((token: CSSFunction) => { + switch (token.name) { + case 'contrast': + case 'hue-rotate': + case 'grayscale': + case 'brightness': + case 'blur': + case 'invert': + case 'saturate': + case 'sepia': + for (let index = 0; index < token.values.length; index++) { + const value: CSSValue = token.values[index]; + if (isLength(value)) { + hasFilter = true; + filter.push({name: token.name, value: value}); + } + } + break; + default: + break; + } + }); + + if (hasFilter) { + return filter; + } else { + return null; + } + } +}; diff --git a/src/css/property-descriptors/font-family.ts b/src/css/property-descriptors/font-family.ts index 624166c..b544aee 100644 --- a/src/css/property-descriptors/font-family.ts +++ b/src/css/property-descriptors/font-family.ts @@ -1,6 +1,6 @@ import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor'; import {CSSValue} from '../syntax/parser'; -import {TokenType} from '../syntax/tokenizer'; +import {StringValueToken, TokenType} from '../syntax/tokenizer'; export type FONT_FAMILY = string; @@ -12,26 +12,9 @@ export const fontFamily: IPropertyListDescriptor = { prefix: false, type: PropertyDescriptorParsingType.LIST, parse: (tokens: CSSValue[]) => { - const accumulator: string[] = []; - const results: string[] = []; - tokens.forEach(token => { - switch (token.type) { - case TokenType.IDENT_TOKEN: - case TokenType.STRING_TOKEN: - accumulator.push(token.value); - break; - case TokenType.NUMBER_TOKEN: - accumulator.push(token.number.toString()); - break; - case TokenType.COMMA_TOKEN: - results.push(accumulator.join(' ')); - accumulator.length = 0; - break; - } - }); - if (accumulator.length) { - results.push(accumulator.join(' ')); - } - return results.map(result => (result.indexOf(' ') === -1 ? result : `'${result}'`)); + return tokens.filter(isStringToken).map(token => token.value); } }; + +const isStringToken = (token: CSSValue): token is StringValueToken => + token.type === TokenType.STRING_TOKEN || token.type === TokenType.IDENT_TOKEN; diff --git a/src/css/property-descriptors/text-fill-color.ts b/src/css/property-descriptors/text-fill-color.ts new file mode 100644 index 0000000..9781f5b --- /dev/null +++ b/src/css/property-descriptors/text-fill-color.ts @@ -0,0 +1,9 @@ +import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor'; + +export const textFillColor: IPropertyTypeValueDescriptor = { + name: `text-fill-color`, + initialValue: 'transparent', + prefix: true, + type: PropertyDescriptorParsingType.TYPE_VALUE, + format: 'color' +}; diff --git a/src/css/property-descriptors/text-stroke-color.ts b/src/css/property-descriptors/text-stroke-color.ts new file mode 100644 index 0000000..ce4e0a9 --- /dev/null +++ b/src/css/property-descriptors/text-stroke-color.ts @@ -0,0 +1,9 @@ +import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor'; + +export const textStrokeColor: IPropertyTypeValueDescriptor = { + name: `text-stroke-color`, + initialValue: 'transparent', + prefix: true, + type: PropertyDescriptorParsingType.TYPE_VALUE, + format: 'color' +}; diff --git a/src/css/property-descriptors/text-stroke-width.ts b/src/css/property-descriptors/text-stroke-width.ts new file mode 100644 index 0000000..a4861ec --- /dev/null +++ b/src/css/property-descriptors/text-stroke-width.ts @@ -0,0 +1,8 @@ +import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor'; +export const textStrokeWidth: IPropertyTypeValueDescriptor = { + name: 'text-stroke-width', + initialValue: '0', + type: PropertyDescriptorParsingType.TYPE_VALUE, + prefix: true, + format: 'length-percentage' +}; diff --git a/src/css/syntax/parser.ts b/src/css/syntax/parser.ts index 83b8a6c..0acc3bc 100644 --- a/src/css/syntax/parser.ts +++ b/src/css/syntax/parser.ts @@ -141,6 +141,7 @@ export class Parser { } } +export const isCSSFunction = (token: CSSValue): token is CSSFunction => token.type === TokenType.FUNCTION; export const isDimensionToken = (token: CSSValue): token is DimensionToken => token.type === TokenType.DIMENSION_TOKEN; export const isNumberToken = (token: CSSValue): token is NumberValueToken => token.type === TokenType.NUMBER_TOKEN; export const isIdentToken = (token: CSSValue): token is StringValueToken => token.type === TokenType.IDENT_TOKEN; diff --git a/src/css/types/color.ts b/src/css/types/color.ts index ebc17c1..1115973 100644 --- a/src/css/types/color.ts +++ b/src/css/types/color.ts @@ -302,3 +302,156 @@ export const COLORS: {[key: string]: Color} = { YELLOW: 0xffff00ff, YELLOWGREEN: 0x9acd32ff }; + +export interface RGBColor { + r: number; + g: number; + b: number; +} + +export const contrastRGB = (rgb: RGBColor, value: number): RGBColor => { + if (value < 0) value = 0; + else if (value > 1) value = 1; + return { + r: Math.max(0, Math.min(255, value * (rgb.r - 128) + 128)), + g: Math.max(0, Math.min(255, value * (rgb.g - 128) + 128)), + b: Math.max(0, Math.min(255, value * (rgb.b - 128) + 128)) + }; +}; + +export const grayscaleRGB = (rgb: RGBColor, value: number, mode?: string | null) => { + var gray = 0; + //different grayscale algorithms + switch (mode) { + case 'average': + gray = (rgb.r + rgb.g + rgb.b) / 3; + break; + case 'luma:BT601': + gray = rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114; + break; + case 'desaturation': + gray = (Math.max(rgb.r, rgb.g, rgb.b) + Math.max(rgb.r, rgb.g, rgb.b)) / 2; + break; + case 'decompsition:max': + gray = Math.max(rgb.r, rgb.g, rgb.b); + break; + case 'decompsition:min': + gray = Math.min(rgb.r, rgb.g, rgb.b); + break; + case 'luma:BT709': + default: + gray = rgb.r * 0.2126 + rgb.g * 0.7152 + rgb.b * 0.0722; + break; + } + rgb.r = value * (gray - rgb.r) + rgb.r; + rgb.g = value * (gray - rgb.g) + rgb.g; + rgb.b = value * (gray - rgb.b) + rgb.b; + return rgb; +}; + +export const brightnessRGB = (rgb: RGBColor, value: number): RGBColor => { + if (value < 0) value = 0; + return { + r: Math.max(0, Math.min(255, rgb.r * value)), + g: Math.max(0, Math.min(255, rgb.g * value)), + b: Math.max(0, Math.min(255, rgb.b * value)) + }; +}; + +export const invertRGB = (rgb: RGBColor, value: number): RGBColor => { + return { + r: value * (255 - 2 * rgb.r) + rgb.r, + g: value * (255 - 2 * rgb.g) + rgb.g, + b: value * (255 - 2 * rgb.b) + rgb.b + }; +}; + +export const sepiaRGB = (rgb: RGBColor, value: number): RGBColor => { + if (value < 0) value = 0; + else if (value > 1) value = 1; + return { + r: value * Math.min(255, rgb.r * 0.393 + rgb.g * 0.769 + rgb.b * 0.189 - rgb.r) + rgb.r, + g: value * Math.min(255, rgb.r * 0.349 + rgb.g * 0.686 + rgb.b * 0.168 - rgb.g) + rgb.g, + b: value * Math.min(255, rgb.r * 0.272 + rgb.g * 0.534 + rgb.b * 0.131 - rgb.b) + rgb.b + }; +}; + +export const hueRotateRGB = (rgb: RGBColor, value: number): RGBColor => { + while (value < 0) value += 360; + while (value > 360) value -= 360; + rgb2hsl(rgb); + rgb.r += value; + if (rgb.r < 0) rgb.r += 360; + if (rgb.r > 359) rgb.r -= 360; + hsl2rgb(rgb); + return rgb; +}; + +export const saturateRGB = (rgb: RGBColor, value: number): RGBColor => { + if (value < 0) value = 0; + rgb2hsl(rgb); + rgb.g *= value; + if (rgb.g > 100) rgb.g = 100; + hsl2rgb(rgb); + return rgb; +}; + +function rgb2hsl(rgb: RGBColor) { + rgb.r = Math.max(0, Math.min(255, rgb.r)) / 255; + rgb.g = Math.max(0, Math.min(255, rgb.g)) / 255; + rgb.b = Math.max(0, Math.min(255, rgb.b)) / 255; + let h, l; + let M = Math.max(rgb.r, rgb.g, rgb.b); + let m = Math.min(rgb.r, rgb.g, rgb.b); + let d = M - m; + if (d == 0) h = 0; + else if (M == rgb.r) h = ((rgb.g - rgb.b) / d) % 6; + else if (M == rgb.g) h = (rgb.b - rgb.r) / d + 2; + else h = (rgb.r - rgb.g) / d + 4; + h = Math.round(h * 60); + if (h < 0) h += 360; + rgb.r = h; + l = (M + m) / 2; + if (d == 0) rgb.g = 0; + else rgb.g = (d / (1 - Math.abs(2 * l - 1))) * 100; + rgb.b = l * 100; +} + +function hsl2rgb(rgb: RGBColor) { + rgb.r = Math.max(0, Math.min(359, rgb.r)); + rgb.g = Math.max(0, Math.min(100, rgb.g)) / 100; + rgb.b = Math.max(0, Math.min(100, rgb.b)) / 100; + let C = (1 - Math.abs(2 * rgb.b - 1)) * rgb.g; + let h = rgb.r / 60; + let X = C * (1 - Math.abs((h % 2) - 1)); + let l = rgb.b; + rgb.r = 0; + rgb.g = 0; + rgb.b = 0; + if (h >= 0 && h < 1) { + rgb.r = C; + rgb.g = X; + } else if (h >= 1 && h < 2) { + rgb.r = X; + rgb.g = C; + } else if (h >= 2 && h < 3) { + rgb.g = C; + rgb.b = X; + } else if (h >= 3 && h < 4) { + rgb.g = X; + rgb.b = C; + } else if (h >= 4 && h < 5) { + rgb.r = X; + rgb.b = C; + } else { + rgb.r = C; + rgb.b = X; + } + let m = l - C / 2; + rgb.r += m; + rgb.g += m; + rgb.b += m; + rgb.r = Math.round(rgb.r * 255.0); + rgb.g = Math.round(rgb.g * 255.0); + rgb.b = Math.round(rgb.b * 255.0); +} diff --git a/src/css/types/functions/stack-blur.ts b/src/css/types/functions/stack-blur.ts new file mode 100644 index 0000000..20724dd --- /dev/null +++ b/src/css/types/functions/stack-blur.ts @@ -0,0 +1,1057 @@ +/* eslint-disable no-bitwise -- used for calculations */ +/* eslint-disable unicorn/prefer-query-selector -- aiming at + backward-compatibility */ +/** + * StackBlur - a fast almost Gaussian Blur For Canvas + * + * In case you find this class useful - especially in commercial projects - + * I am not totally unhappy for a small donation to my PayPal account + * mario@quasimondo.de + * + * Or support me on flattr: + * {@link https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript}. + * + * @module StackBlur + * @author Mario Klingemann + * Contact: mario@quasimondo.com + * Website: {@link http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html} + * Twitter: @quasimondo + * + * @copyright (c) 2010 Mario Klingemann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +const mulTable = [ + 512, + 512, + 456, + 512, + 328, + 456, + 335, + 512, + 405, + 328, + 271, + 456, + 388, + 335, + 292, + 512, + 454, + 405, + 364, + 328, + 298, + 271, + 496, + 456, + 420, + 388, + 360, + 335, + 312, + 292, + 273, + 512, + 482, + 454, + 428, + 405, + 383, + 364, + 345, + 328, + 312, + 298, + 284, + 271, + 259, + 496, + 475, + 456, + 437, + 420, + 404, + 388, + 374, + 360, + 347, + 335, + 323, + 312, + 302, + 292, + 282, + 273, + 265, + 512, + 497, + 482, + 468, + 454, + 441, + 428, + 417, + 405, + 394, + 383, + 373, + 364, + 354, + 345, + 337, + 328, + 320, + 312, + 305, + 298, + 291, + 284, + 278, + 271, + 265, + 259, + 507, + 496, + 485, + 475, + 465, + 456, + 446, + 437, + 428, + 420, + 412, + 404, + 396, + 388, + 381, + 374, + 367, + 360, + 354, + 347, + 341, + 335, + 329, + 323, + 318, + 312, + 307, + 302, + 297, + 292, + 287, + 282, + 278, + 273, + 269, + 265, + 261, + 512, + 505, + 497, + 489, + 482, + 475, + 468, + 461, + 454, + 447, + 441, + 435, + 428, + 422, + 417, + 411, + 405, + 399, + 394, + 389, + 383, + 378, + 373, + 368, + 364, + 359, + 354, + 350, + 345, + 341, + 337, + 332, + 328, + 324, + 320, + 316, + 312, + 309, + 305, + 301, + 298, + 294, + 291, + 287, + 284, + 281, + 278, + 274, + 271, + 268, + 265, + 262, + 259, + 257, + 507, + 501, + 496, + 491, + 485, + 480, + 475, + 470, + 465, + 460, + 456, + 451, + 446, + 442, + 437, + 433, + 428, + 424, + 420, + 416, + 412, + 408, + 404, + 400, + 396, + 392, + 388, + 385, + 381, + 377, + 374, + 370, + 367, + 363, + 360, + 357, + 354, + 350, + 347, + 344, + 341, + 338, + 335, + 332, + 329, + 326, + 323, + 320, + 318, + 315, + 312, + 310, + 307, + 304, + 302, + 299, + 297, + 294, + 292, + 289, + 287, + 285, + 282, + 280, + 278, + 275, + 273, + 271, + 269, + 267, + 265, + 263, + 261, + 259 +]; + +const shgTable = [ + 9, + 11, + 12, + 13, + 13, + 14, + 14, + 15, + 15, + 15, + 15, + 16, + 16, + 16, + 16, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24 +]; + +/** + * @param {ImageData} imageData + * @param {Integer} width + * @param {Integer} height + * @param {Float} radius + * @returns {ImageData} + */ +function processImageDataRGBA(imageData: ImageData, width: number, height: number, radius: number) { + const pixels = imageData.data; + + const div = 2 * radius + 1; + // const w4 = width << 2; + const widthMinus1 = width - 1; + const heightMinus1 = height - 1; + const radiusPlus1 = radius + 1; + const sumFactor = (radiusPlus1 * (radiusPlus1 + 1)) / 2; + + const stackStart: BlurStack = new BlurStack(); + let stack = stackStart; + let stackEnd; + for (let i = 1; i < div; i++) { + stack = stack.next = new BlurStack(); + if (i === radiusPlus1) { + stackEnd = stack; + } + } + stack.next = stackStart; + + let stackIn: BlurStack, + stackOut = null, + yw = 0, + yi = 0; + + const mulSum = mulTable[radius]; + const shgSum = shgTable[radius]; + + for (let y = 0; y < height; y++) { + stack = stackStart; + + const pr = pixels[yi], + pg = pixels[yi + 1], + pb = pixels[yi + 2], + pa = pixels[yi + 3]; + + for (let i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + if (stack.next) stack = stack.next; + } + + let rInSum = 0, + gInSum = 0, + bInSum = 0, + aInSum = 0, + rOutSum = radiusPlus1 * pr, + gOutSum = radiusPlus1 * pg, + bOutSum = radiusPlus1 * pb, + aOutSum = radiusPlus1 * pa, + rSum = sumFactor * pr, + gSum = sumFactor * pg, + bSum = sumFactor * pb, + aSum = sumFactor * pa; + + for (let i = 1; i < radiusPlus1; i++) { + const p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); + + const r = pixels[p], + g = pixels[p + 1], + b = pixels[p + 2], + a = pixels[p + 3]; + + const rbs = radiusPlus1 - i; + rSum += (stack.r = r) * rbs; + gSum += (stack.g = g) * rbs; + bSum += (stack.b = b) * rbs; + aSum += (stack.a = a) * rbs; + + rInSum += r; + gInSum += g; + bInSum += b; + aInSum += a; + + if (stack.next) stack = stack.next; + } + + stackIn = stackStart; + stackOut = stackEnd; + for (let x = 0; x < width; x++) { + const paInitial = (aSum * mulSum) >> shgSum; + pixels[yi + 3] = paInitial; + if (paInitial !== 0) { + const a = 255 / paInitial; + pixels[yi] = ((rSum * mulSum) >> shgSum) * a; + pixels[yi + 1] = ((gSum * mulSum) >> shgSum) * a; + pixels[yi + 2] = ((bSum * mulSum) >> shgSum) * a; + } else { + pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0; + } + + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + aSum -= aOutSum; + + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + aOutSum -= stackIn.a; + + let p = x + radius + 1; + p = (yw + (p < widthMinus1 ? p : widthMinus1)) << 2; + + rInSum += stackIn.r = pixels[p]; + gInSum += stackIn.g = pixels[p + 1]; + bInSum += stackIn.b = pixels[p + 2]; + aInSum += stackIn.a = pixels[p + 3]; + + rSum += rInSum; + gSum += gInSum; + bSum += bInSum; + aSum += aInSum; + + if (stackIn.next) stackIn = stackIn.next; + + if (stackOut) { + const {r, g, b, a} = stackOut; + + rOutSum += r; + gOutSum += g; + bOutSum += b; + aOutSum += a; + + rInSum -= r; + gInSum -= g; + bInSum -= b; + aInSum -= a; + + stackOut = stackOut.next; + } + + yi += 4; + } + yw += width; + } + + for (let x = 0; x < width; x++) { + yi = x << 2; + + let pr = pixels[yi], + pg = pixels[yi + 1], + pb = pixels[yi + 2], + pa = pixels[yi + 3], + rOutSum = radiusPlus1 * pr, + gOutSum = radiusPlus1 * pg, + bOutSum = radiusPlus1 * pb, + aOutSum = radiusPlus1 * pa, + rSum = sumFactor * pr, + gSum = sumFactor * pg, + bSum = sumFactor * pb, + aSum = sumFactor * pa; + + stack = stackStart; + + for (let i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + if (stack.next) stack = stack.next; + } + + let yp = width; + + let gInSum = 0, + bInSum = 0, + aInSum = 0, + rInSum = 0; + for (let i = 1; i <= radius; i++) { + yi = (yp + x) << 2; + + const rbs = radiusPlus1 - i; + rSum += (stack.r = pr = pixels[yi]) * rbs; + gSum += (stack.g = pg = pixels[yi + 1]) * rbs; + bSum += (stack.b = pb = pixels[yi + 2]) * rbs; + aSum += (stack.a = pa = pixels[yi + 3]) * rbs; + + rInSum += pr; + gInSum += pg; + bInSum += pb; + aInSum += pa; + + if (stack.next) stack = stack.next; + + if (i < heightMinus1) { + yp += width; + } + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (let y = 0; y < height; y++) { + let p = yi << 2; + pixels[p + 3] = pa = (aSum * mulSum) >> shgSum; + if (pa > 0) { + pa = 255 / pa; + pixels[p] = ((rSum * mulSum) >> shgSum) * pa; + pixels[p + 1] = ((gSum * mulSum) >> shgSum) * pa; + pixels[p + 2] = ((bSum * mulSum) >> shgSum) * pa; + } else { + pixels[p] = pixels[p + 1] = pixels[p + 2] = 0; + } + + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + aSum -= aOutSum; + + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + aOutSum -= stackIn.a; + + p = (x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width) << 2; + + rSum += rInSum += stackIn.r = pixels[p]; + gSum += gInSum += stackIn.g = pixels[p + 1]; + bSum += bInSum += stackIn.b = pixels[p + 2]; + aSum += aInSum += stackIn.a = pixels[p + 3]; + + if (stackIn.next) stackIn = stackIn.next; + + if (stackOut) { + rOutSum += pr = stackOut.r; + gOutSum += pg = stackOut.g; + bOutSum += pb = stackOut.b; + aOutSum += pa = stackOut.a; + + rInSum -= pr; + gInSum -= pg; + bInSum -= pb; + aInSum -= pa; + + stackOut = stackOut.next; + } + + yi += width; + } + } + return imageData; +} + +/** + * @param {ImageData} imageData + * @param {Integer} topX + * @param {Integer} topY + * @param {Integer} width + * @param {Integer} height + * @param {Float} radius + * @returns {ImageData} + */ +function processImageDataRGB(imageData: ImageData, width: number, height: number, radius: number) { + const pixels = imageData.data; + + const div = 2 * radius + 1; + // const w4 = width << 2; + const widthMinus1 = width - 1; + const heightMinus1 = height - 1; + const radiusPlus1 = radius + 1; + const sumFactor = (radiusPlus1 * (radiusPlus1 + 1)) / 2; + + const stackStart = new BlurStack(); + let stack = stackStart; + let stackEnd; + for (let i = 1; i < div; i++) { + stack = stack.next = new BlurStack(); + if (i === radiusPlus1) { + stackEnd = stack; + } + } + stack.next = stackStart; + let stackIn = null; + let stackOut = null; + + const mulSum = mulTable[radius]; + const shgSum = shgTable[radius]; + + let p, rbs; + let yw = 0, + yi = 0; + + for (let y = 0; y < height; y++) { + let pr = pixels[yi], + pg = pixels[yi + 1], + pb = pixels[yi + 2], + rOutSum = radiusPlus1 * pr, + gOutSum = radiusPlus1 * pg, + bOutSum = radiusPlus1 * pb, + rSum = sumFactor * pr, + gSum = sumFactor * pg, + bSum = sumFactor * pb; + + stack = stackStart; + + for (let i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + if (stack.next) stack = stack.next; + } + + let rInSum = 0, + gInSum = 0, + bInSum = 0; + for (let i = 1; i < radiusPlus1; i++) { + p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); + rSum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i); + gSum += (stack.g = pg = pixels[p + 1]) * rbs; + bSum += (stack.b = pb = pixels[p + 2]) * rbs; + + rInSum += pr; + gInSum += pg; + bInSum += pb; + + if (stack.next) stack = stack.next; + } + + stackIn = stackStart; + stackOut = stackEnd; + for (let x = 0; x < width; x++) { + pixels[yi] = (rSum * mulSum) >> shgSum; + pixels[yi + 1] = (gSum * mulSum) >> shgSum; + pixels[yi + 2] = (bSum * mulSum) >> shgSum; + + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + + if (stackIn && stackOut) { + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + + p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2; + + rInSum += stackIn.r = pixels[p]; + gInSum += stackIn.g = pixels[p + 1]; + bInSum += stackIn.b = pixels[p + 2]; + + rSum += rInSum; + gSum += gInSum; + bSum += bInSum; + + stackIn = stackIn.next; + + rOutSum += pr = stackOut.r; + gOutSum += pg = stackOut.g; + bOutSum += pb = stackOut.b; + + rInSum -= pr; + gInSum -= pg; + bInSum -= pb; + + stackOut = stackOut.next; + } + + yi += 4; + } + yw += width; + } + + for (let x = 0; x < width; x++) { + yi = x << 2; + let pr = pixels[yi], + pg = pixels[yi + 1], + pb = pixels[yi + 2], + rOutSum = radiusPlus1 * pr, + gOutSum = radiusPlus1 * pg, + bOutSum = radiusPlus1 * pb, + rSum = sumFactor * pr, + gSum = sumFactor * pg, + bSum = sumFactor * pb; + + stack = stackStart; + + for (let i = 0; i < radiusPlus1; i++) { + stack.r = pr; + stack.g = pg; + stack.b = pb; + if (stack.next) stack = stack.next; + } + + let rInSum = 0, + gInSum = 0, + bInSum = 0; + for (let i = 1, yp = width; i <= radius; i++) { + yi = (yp + x) << 2; + + rSum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i); + gSum += (stack.g = pg = pixels[yi + 1]) * rbs; + bSum += (stack.b = pb = pixels[yi + 2]) * rbs; + + rInSum += pr; + gInSum += pg; + bInSum += pb; + + if (stack.next) stack = stack.next; + + if (i < heightMinus1) { + yp += width; + } + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for (let y = 0; y < height; y++) { + p = yi << 2; + pixels[p] = (rSum * mulSum) >> shgSum; + pixels[p + 1] = (gSum * mulSum) >> shgSum; + pixels[p + 2] = (bSum * mulSum) >> shgSum; + + rSum -= rOutSum; + gSum -= gOutSum; + bSum -= bOutSum; + + if (stackIn && stackOut) { + rOutSum -= stackIn.r; + gOutSum -= stackIn.g; + bOutSum -= stackIn.b; + + p = (x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width) << 2; + + rSum += rInSum += stackIn.r = pixels[p]; + gSum += gInSum += stackIn.g = pixels[p + 1]; + bSum += bInSum += stackIn.b = pixels[p + 2]; + + stackIn = stackIn.next; + + rOutSum += pr = stackOut.r; + gOutSum += pg = stackOut.g; + bOutSum += pb = stackOut.b; + + rInSum -= pr; + gInSum -= pg; + bInSum -= pb; + + stackOut = stackOut.next; + } + + yi += width; + } + } + + return imageData; +} + +/** + * + */ +export class BlurStack { + /** + * Set properties. + */ + r: number; + g: number; + b: number; + a: number; + next?: BlurStack; + + constructor() { + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + } +} + +export const stackBlurImage = ( + imageData: ImageData, + width: number, + height: number, + radius: number, + blurAlphaChannel: number +): ImageData => { + if (isNaN(radius) || radius < 1) { + return imageData; + } + radius |= 0; + + if (blurAlphaChannel) { + return processImageDataRGBA(imageData, width, height, radius); + } else { + return processImageDataRGB(imageData, width, height, radius); + } +}; diff --git a/src/dom/document-cloner.ts b/src/dom/document-cloner.ts index 2e93a02..9603ef2 100644 --- a/src/dom/document-cloner.ts +++ b/src/dom/document-cloner.ts @@ -5,7 +5,6 @@ import { isElementNode, isHTMLElementNode, isIFrameElement, - isImageElement, isScriptElement, isSelectElement, isStyleElement, @@ -116,7 +115,7 @@ export class DocumentCloner { return iframeLoad; } - createElementClone(node: T): HTMLElement | SVGElement { + createElementClone(node: HTMLElement): HTMLElement { if (isCanvasElement(node)) { return this.createCanvasClone(node); } @@ -129,14 +128,7 @@ export class DocumentCloner { return this.createStyleClone(node); } - const clone = node.cloneNode(false) as T; - // @ts-ignore - if (isImageElement(clone) && clone.loading === 'lazy') { - // @ts-ignore - clone.loading = 'eager'; - } - - return clone; + return node.cloneNode(false) as HTMLElement; } createStyleClone(node: HTMLStyleElement): HTMLStyleElement { @@ -265,14 +257,14 @@ export class DocumentCloner { const window = node.ownerDocument.defaultView; - if (window && isElementNode(node) && (isHTMLElementNode(node) || isSVGElementNode(node))) { + if (isHTMLElementNode(node) && window) { const clone = this.createElementClone(node); const style = window.getComputedStyle(node); const styleBefore = window.getComputedStyle(node, ':before'); const styleAfter = window.getComputedStyle(node, ':after'); - if (this.referenceElement === node && isHTMLElementNode(clone)) { + if (this.referenceElement === node) { this.clonedReferenceElement = clone; } if (isBodyElement(clone)) { @@ -306,7 +298,7 @@ export class DocumentCloner { this.counters.pop(counters); - if (style && (this.options.copyStyles || isSVGElementNode(node)) && !isIFrameElement(node)) { + if (style && this.options.copyStyles && !isIFrameElement(node)) { copyCSSStyles(style, clone); } @@ -486,7 +478,7 @@ const iframeLoader = (iframe: HTMLIFrameElement): Promise => }); }; -export const copyCSSStyles = (style: CSSStyleDeclaration, target: T): T => { +export const copyCSSStyles = (style: CSSStyleDeclaration, target: HTMLElement): HTMLElement => { // Edge does not provide value for cssText for (let i = style.length - 1; i >= 0; i--) { const property = style.item(i); diff --git a/src/dom/node-parser.ts b/src/dom/node-parser.ts index adf4256..791247b 100644 --- a/src/dom/node-parser.ts +++ b/src/dom/node-parser.ts @@ -102,7 +102,7 @@ const createsStackingContext = (styles: CSSParsedDeclaration): boolean => styles export const isTextNode = (node: Node): node is Text => node.nodeType === Node.TEXT_NODE; export const isElementNode = (node: Node): node is Element => node.nodeType === Node.ELEMENT_NODE; export const isHTMLElementNode = (node: Node): node is HTMLElement => - isElementNode(node) && typeof (node as HTMLElement).style !== 'undefined' && !isSVGElementNode(node); + typeof (node as HTMLElement).style !== 'undefined'; export const isSVGElementNode = (element: Element): element is SVGElement => typeof (element as SVGElement).className === 'object'; export const isLIElement = (node: Element): node is HTMLLIElement => node.tagName === 'LI'; diff --git a/src/global.d.ts b/src/global.d.ts index a94aa96..5cd3a36 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,6 +1,7 @@ interface CSSStyleDeclaration { textDecorationColor: string | null; textDecorationLine: string | null; + textFillColor: string | null; overflowWrap: string | null; } diff --git a/src/index.ts b/src/index.ts index 8331f84..e6bc7a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -25,7 +25,7 @@ const html2canvas = (element: HTMLElement, options: Partial = {}): Prom export default html2canvas; -if (typeof window !== 'undefined') { +if (typeof window !== "undefined") { CacheStorage.setContext(window); } diff --git a/src/render/canvas/canvas-renderer.ts b/src/render/canvas/canvas-renderer.ts index f6d3a4a..34f597c 100644 --- a/src/render/canvas/canvas-renderer.ts +++ b/src/render/canvas/canvas-renderer.ts @@ -10,7 +10,7 @@ import {BACKGROUND_CLIP} from '../../css/property-descriptors/background-clip'; import {BoundCurves, calculateBorderBoxPath, calculateContentBoxPath, calculatePaddingBoxPath} from '../bound-curves'; import {isBezierCurve} from '../bezier-curve'; import {Vector} from '../vector'; -import {CSSImageType, CSSURLImage, isLinearGradient, isRadialGradient} from '../../css/types/image'; +import {CSSImageType, CSSLinearGradientImage, CSSRadialGradientImage, CSSURLImage, isLinearGradient, isRadialGradient} from '../../css/types/image'; import {parsePathForBorder} from '../border'; import {Cache} from '../../core/cache-storage'; import {calculateBackgroundRendering, getBackgroundValueForIndex} from '../background'; @@ -22,7 +22,7 @@ import {contentBox} from '../box-sizing'; import {CanvasElementContainer} from '../../dom/replaced-elements/canvas-element-container'; import {SVGElementContainer} from '../../dom/replaced-elements/svg-element-container'; import {ReplacedElementContainer} from '../../dom/replaced-elements/index'; -import {EffectTarget, IElementEffect, isClipEffect, isOpacityEffect, isTransformEffect} from '../effects'; +import {EffectTarget, IElementEffect, isClipEffect, isTransformEffect, isOpacityEffect} from '../effects'; import {contains} from '../../core/bitwise'; import {calculateGradientDirection, calculateRadius, processColorStops} from '../../css/types/functions/gradient'; import {FIFTY_PERCENT, getAbsoluteValue} from '../../css/types/length-percentage'; @@ -38,6 +38,8 @@ import {TextareaElementContainer} from '../../dom/elements/textarea-element-cont import {SelectElementContainer} from '../../dom/elements/select-element-container'; import {IFrameElementContainer} from '../../dom/replaced-elements/iframe-element-container'; import {TextShadow} from '../../css/property-descriptors/text-shadow'; +import { processImage, isSupportedFilter } from '../image-filter'; +import {isFontColorGradient } from '../font-color-gradient' export type RenderConfigurations = RenderOptions & { backgroundColor: Color | null; @@ -99,10 +101,6 @@ export class CanvasRenderer { applyEffect(effect: IElementEffect) { this.ctx.save(); - if (isOpacityEffect(effect)) { - this.ctx.globalAlpha = effect.opacity; - } - if (isTransformEffect(effect)) { this.ctx.translate(effect.offsetX, effect.offsetY); this.ctx.transform( @@ -120,7 +118,9 @@ export class CanvasRenderer { this.path(effect.path); this.ctx.clip(); } - + if (isOpacityEffect(effect)) { + this.ctx.globalAlpha = effect.val; + } this._activeEffects.push(effect); } @@ -132,6 +132,7 @@ export class CanvasRenderer { async renderStack(stack: StackingContext) { const styles = stack.element.container.styles; if (styles.isVisible()) { + this.ctx.globalAlpha = styles.opacity; await this.renderStackContent(stack); } } @@ -172,13 +173,56 @@ export class CanvasRenderer { ]; } - async renderTextNode(text: TextContainer, styles: CSSParsedDeclaration) { + async renderTextNode(text: TextContainer, styles: CSSParsedDeclaration, container: ElementContainer) { const [font, fontFamily, fontSize] = this.createFontStyle(styles); this.ctx.font = font; + let fillStyle: CanvasGradient|string = asString(styles.color); + if (isFontColorGradient(styles)) { + if (isLinearGradient(styles.backgroundImage[0])) { + const backgroundImage = styles.backgroundImage[0] as CSSLinearGradientImage + const [, x, y, width, height] = calculateBackgroundRendering(container, 0, [null, null, null]); + const [lineLength, x0, x1, y0, y1] = calculateGradientDirection(backgroundImage.angle, width, height); + if (Math.round(Math.abs(x0)) === Math.round(Math.abs(x1))) { + const gradient = this.ctx.createLinearGradient(x, y0+y, x, y1+y); + processColorStops(backgroundImage.stops, lineLength).forEach(colorStop => + gradient.addColorStop(colorStop.stop, asString(colorStop.color)) + ); + fillStyle = gradient; + } else if (Math.round(Math.abs(y0)) === Math.round(Math.abs(y1))) { + const gradient = this.ctx.createLinearGradient(x+x0, y, x+x1, y); + processColorStops(backgroundImage.stops, lineLength).forEach(colorStop => + gradient.addColorStop(colorStop.stop, asString(colorStop.color)) + ); + fillStyle = gradient; + } + } else if (isRadialGradient(styles.backgroundImage[0])) { + const backgroundImage = styles.backgroundImage[0] as CSSRadialGradientImage + const [, left, top, width, height] = calculateBackgroundRendering(container, 0, [ + null, + null, + null + ]); + const position = backgroundImage.position.length === 0 ? [FIFTY_PERCENT] : backgroundImage.position; + const x = getAbsoluteValue(position[0], width); + const y = getAbsoluteValue(position[position.length - 1], height); + + const [rx,] = calculateRadius(backgroundImage, x, y, width, height); + if (rx > 0 && rx > 0) { + const radialGradient = this.ctx.createRadialGradient(left + x, top + y, 0, left + x, top + y, rx); + + processColorStops(backgroundImage.stops, rx * 2).forEach(colorStop => + radialGradient.addColorStop(colorStop.stop, asString(colorStop.color)) + ); + + fillStyle = radialGradient; + } + } + } + text.textBounds.forEach(text => { - this.ctx.fillStyle = asString(styles.color); + this.ctx.fillStyle = fillStyle; this.renderTextWithLetterSpacing(text, styles.letterSpacing); const textShadows: TextShadow = styles.textShadow; @@ -234,6 +278,20 @@ export class CanvasRenderer { } }); } + + if ( + styles.textStrokeWidth && + styles.textStrokeWidth.number && + styles.textStrokeColor && + text.text.trim().length + ) { + this.ctx.strokeStyle = asString(styles.textStrokeColor); + this.ctx.lineWidth = getAbsoluteValue(styles.textStrokeWidth, text.bounds.width); + this.ctx.strokeText(text.text, text.bounds.left, text.bounds.top + text.bounds.height); + + this.ctx.strokeStyle = ''; + this.ctx.lineWidth = 0; + } }); } @@ -248,6 +306,9 @@ export class CanvasRenderer { this.path(path); this.ctx.save(); this.ctx.clip(); + if (isSupportedFilter(this.ctx) && container.styles.filterOriginal) { + this.ctx.filter = container.styles.filterOriginal; + } this.ctx.drawImage( image, 0, @@ -269,12 +330,13 @@ export class CanvasRenderer { const curves = paint.curves; const styles = container.styles; for (const child of container.textNodes) { - await this.renderTextNode(child, styles); + await this.renderTextNode(child, styles, container); } if (container instanceof ImageElementContainer) { try { const image = await this.options.cache.match(container.src); + if (styles.filter && !isSupportedFilter(this.ctx)) await processImage(image, styles.filter); this.renderReplacedElement(container, curves, image); } catch (e) { Logger.getInstance(this.options.id).error(`Error loading image ${container.src}`); @@ -545,6 +607,7 @@ export class CanvasRenderer { async renderBackgroundImage(container: ElementContainer) { let index = container.styles.backgroundImage.length - 1; + if (isFontColorGradient(container.styles)) return false; for (const backgroundImage of container.styles.backgroundImage.slice(0).reverse()) { if (backgroundImage.type === CSSImageType.URL) { let image; @@ -570,7 +633,6 @@ export class CanvasRenderer { } else if (isLinearGradient(backgroundImage)) { const [path, x, y, width, height] = calculateBackgroundRendering(container, index, [null, null, null]); const [lineLength, x0, x1, y0, y1] = calculateGradientDirection(backgroundImage.angle, width, height); - const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; diff --git a/src/render/effects.ts b/src/render/effects.ts index 1bb4283..243e468 100644 --- a/src/render/effects.ts +++ b/src/render/effects.ts @@ -18,37 +18,42 @@ export interface IElementEffect { } export class TransformEffect implements IElementEffect { - readonly type: EffectType = EffectType.TRANSFORM; - readonly target: number = EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT; + readonly type: EffectType; + readonly target: number; readonly offsetX: number; readonly offsetY: number; readonly matrix: Matrix; constructor(offsetX: number, offsetY: number, matrix: Matrix) { + this.type = EffectType.TRANSFORM; this.offsetX = offsetX; this.offsetY = offsetY; this.matrix = matrix; + this.target = EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT; } } export class ClipEffect implements IElementEffect { - readonly type: EffectType = EffectType.CLIP; + readonly type: EffectType; readonly target: number; readonly path: Path[]; constructor(path: Path[], target: EffectTarget) { + this.type = EffectType.CLIP; this.target = target; this.path = path; } } export class OpacityEffect implements IElementEffect { - readonly type: EffectType = EffectType.OPACITY; - readonly target: number = EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT; - readonly opacity: number; + readonly type: EffectType; + readonly target: number; + readonly val: number; - constructor(opacity: number) { - this.opacity = opacity; + constructor(val: number, target: EffectTarget) { + this.type = EffectType.OPACITY; + this.target = target; + this.val = val; } } diff --git a/src/render/font-color-gradient.ts b/src/render/font-color-gradient.ts new file mode 100644 index 0000000..7575921 --- /dev/null +++ b/src/render/font-color-gradient.ts @@ -0,0 +1,13 @@ +import {CSSParsedDeclaration} from '../css/index'; +import {isLinearGradient, isRadialGradient} from '../css/types/image'; +import {BACKGROUND_CLIP} from '../css/property-descriptors/background-clip'; + +export const isFontColorGradient = (styles: CSSParsedDeclaration) => { + return ( + styles.backgroundImage.length === 1 && + (isLinearGradient(styles.backgroundImage[0]) || isRadialGradient(styles.backgroundImage[0])) && + (styles.textFillColor === 0 || styles.color === 0) && + styles.backgroundClip.length === 1 && + styles.backgroundClip[0] === BACKGROUND_CLIP.TEXT + ); +}; diff --git a/src/render/image-filter.ts b/src/render/image-filter.ts new file mode 100644 index 0000000..19feb72 --- /dev/null +++ b/src/render/image-filter.ts @@ -0,0 +1,99 @@ +import { + RGBColor, + contrastRGB, + hueRotateRGB, + grayscaleRGB, + brightnessRGB, + invertRGB, + saturateRGB, + sepiaRGB +} from '../css/types/color'; +import {Filter, FilterItem} from '../css/property-descriptors/filter'; +import {stackBlurImage} from '../css/types/functions/stack-blur'; + +export const processImage = (img: HTMLImageElement, filter: Filter) => { + return new Promise((resolve, reject) => { + if (!img || !('naturalWidth' in img)) { + return resolve(); + } + + const w = img['naturalWidth']; + const h = img['naturalHeight']; + + const canvas = document.createElement('canvas'); + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + canvas.width = w * 2; + canvas.height = h * 2; + const context = canvas.getContext('2d') as CanvasRenderingContext2D; + + context.clearRect(0, 0, w, h); + context.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, 0, 0, w, h); + + let imageData: ImageData = context.getImageData(0, 0, w, h); + + handlePerPixel(imageData, filter); + + let blurFilter = filter.find((item: FilterItem) => item.name === 'blur'); + + if (blurFilter) { + imageData = stackBlurImage(imageData, w, h, blurFilter.value.number * 2, 1); + } + context.putImageData(imageData, 0, 0); + img.crossOrigin = 'anonymous'; + img.src = canvas.toDataURL(); + img.onload = () => resolve(img); + img.onerror = reject; + + if (img.complete === true) { + // Inline XML images may fail to parse, throwing an Error later on + setTimeout(() => resolve(img), 500); + } + }); +}; + +function handlePerPixel(imageData: ImageData, filter: Filter) { + for (let _j = 0; _j < imageData.height; _j++) { + for (let _i = 0; _i < imageData.width; _i++) { + let index = _j * 4 * imageData.width + _i * 4; + let rgb: RGBColor = { + r: imageData.data[index], + g: imageData.data[index + 1], + b: imageData.data[index + 2] + }; + filter.forEach((item: FilterItem) => { + switch (item.name) { + case 'contrast': + rgb = contrastRGB(rgb, item.value.number); + break; + case 'hue-rotate': + rgb = hueRotateRGB(rgb, item.value.number); + break; + case 'grayscale': + rgb = grayscaleRGB(rgb, item.value.number); + break; + case 'brightness': + rgb = brightnessRGB(rgb, item.value.number); + break; + case 'invert': + rgb = invertRGB(rgb, item.value.number); + break; + case 'saturate': + rgb = saturateRGB(rgb, item.value.number); + break; + case 'sepia': + rgb = sepiaRGB(rgb, item.value.number); + break; + default: + break; + } + }); + + imageData.data[index] = rgb.r; + imageData.data[index + 1] = rgb.g; + imageData.data[index + 2] = rgb.b; + } + } +} + +export const isSupportedFilter = (ctx: CanvasRenderingContext2D) => 'filter' in ctx; diff --git a/src/render/stacking-context.ts b/src/render/stacking-context.ts index 5cba00b..2dde610 100644 --- a/src/render/stacking-context.ts +++ b/src/render/stacking-context.ts @@ -1,7 +1,7 @@ import {ElementContainer, FLAGS} from '../dom/element-container'; import {contains} from '../core/bitwise'; import {BoundCurves, calculateBorderBoxPath, calculatePaddingBoxPath} from './bound-curves'; -import {ClipEffect, EffectTarget, IElementEffect, OpacityEffect, TransformEffect} from './effects'; +import {ClipEffect, EffectTarget, IElementEffect, TransformEffect, OpacityEffect} from './effects'; import {OVERFLOW} from '../css/property-descriptors/overflow'; import {equalPath} from './path'; import {DISPLAY} from '../css/property-descriptors/display'; @@ -41,10 +41,6 @@ export class ElementPaint { this.container = element; this.effects = parentStack.slice(0); this.curves = new BoundCurves(element); - if (element.styles.opacity < 1) { - this.effects.push(new OpacityEffect(element.styles.opacity)); - } - if (element.styles.transform !== null) { const offsetX = element.bounds.left + element.styles.transformOrigin[0].number; const offsetY = element.bounds.top + element.styles.transformOrigin[1].number; @@ -63,6 +59,10 @@ export class ElementPaint { this.effects.push(new ClipEffect(paddingBox, EffectTarget.CONTENT)); } } + + if (element.styles.opacity < 1) { + this.effects.push(new OpacityEffect(element.styles.opacity, EffectTarget.CONTENT)); + } } getParentEffects(): IElementEffect[] { @@ -74,6 +74,9 @@ export class ElementPaint { effects.push(new ClipEffect(paddingBox, EffectTarget.BACKGROUND_BORDERS | EffectTarget.CONTENT)); } } + if (this.container.styles.opacity < 1) { + effects.push(new OpacityEffect(this.container.styles.opacity, EffectTarget.CONTENT)); + } return effects; } } @@ -119,7 +122,7 @@ const parseStackTree = ( } else if (order > 0) { let index = 0; parentStack.positiveZIndex.some((current, i) => { - if (order >= current.element.container.styles.zIndex.order) { + if (order > current.element.container.styles.zIndex.order) { index = i + 1; return false; } else if (index > 0) {