|U8IxnksQb5C++>Y(^0>5~24k21%dfwHbSiOvVb(v?cpPu~Mu(JtJx=#{%AQcK
z@hB4L3)CnY6jG)HX}g66+Efg|JAzAw4&;ph=PPpdzdO+3mQWM~_=z0JAIsf}%;q$T
z%dT@BfRv_cLRWra=&)L4J;x_%!zDB`tF}5qC}pbcWj3WKk6}ifg7T@aR)c8!!-kv0Ve2{-|KQq1j^{(DRU|myp`4i)U=7l
zLH?#)QPOa9ANQEUvIAr)B`NyIX=AaQ%uj`^$l?arD)9OSMZY05h#yTuB_8u&5?miCR=H;TS1c+#b%ChQWyTMOL6
z-dGc)FA?}=h#P{ei763u5Wx9YPshQ{H+Pb6hUOhIKZ0d=^mVI94OW*5<{%}TGP{4GNotOewKbdOZMiub
zJlQwgZl!1+PSXQ;dlH$mh<@7$YMw9u_J9T?(GoFj09Ea|kZA(fp89dyS?gq*!1(@c
z=7g6(`erG_(rhKZt!Rgc&Z=H;s92!LGa5FEy4A$`IFtx%;IBsRcN!Xsu_%*qluYKp%YyIanxr8CERi|>lfV7&~_k26MRJqh6_tQ|JwHF`PUMqgJZd)mMeBookJvzb_M=a7=4Vl3r;L@
zwl25V?TOjZ5-if;C1d)U9zAoOvt@nGDCx`F(E`O_bXme-!D<0Pp1r
zW8CsNbPN{q&7O(^!p_tg_@j_ZCgMnK+)98oWS0dPK>jt#1KubRY(ultD8WO2dlB$m
zCbfA#|(N}nQCM%>8*^UWh=ARFAOKltvTZf(n`JtkK1a0R8WgGYn0f{gd
zP1H$gDs#htttmH|2<<+waU;Z;V>WV^p8nPe!Z6(1p{K$!#hg`1aQt81`oqgaMMszE
zP~fUeC1`MooG0%Ab6dGRK~9l(;Nx@Opxo!Ewd!ve`mpS9qu
z*hiTRkG(m9hn}2q*-6f!rz=Cd5=4@^oc`yc`Lf5J=;O?3F!dzQ7=s%00nI0PI_Rml
z_vCM)__?_?Kp)C(AHR)xPbC3)&|D{Es?yf;O;(I8Gl?n@G)e1K$=O~%Kxe-;%4~oM
z^t3SxuPNhxRC!Zsbh!b`6`P?_)UQXFucSd&Iu$5W9(=0Su&31^$0_Bj4IBAyH1iRJ
zD>SzX(Xh{?!kd6UC~7lYoxu|L2Wrqv-$b5L#`#>uS!rsRkk@WNoK$&@PKi1mkiU`Q
z$nY3~fExmCK`d?(SGrEIDv4gmpHwMm|VPx0*8Z#Y!xip%Aaf=oH
z)*9T^&TfY%_pCEF`J|wssq_FC_LFb2xFP=;L5E55{ot;C))>Z>F`JIXUT4yqN5Tfj
zp%j9pqtrv#jl_rUrCcnsmLOXpzcqIc+zWn$))v#aa#W&bqzh&_Om$fIPqr4>A+6|2
zXs`QqTtK_0sdD$SJf3E*b#*nIcoCoi8<;~*SOW;Pl0}pV&erfwNK-XPhJX&|aUDzv
zcQrg+Bt;2oLuJk#Y~KS(W{S^^x@_*Ie|+Ilqfw}siY3Pk+wkpZ!yPP8&ysrW3y|IM
zD4TIt1nWDn?G|$_o
z{+nyfLxbUDsMrl(*e7<)g3-edygRpOHtb}P9x!nvWy;DbC!kfhf?+vRam-LW4^Eacxx0?goe88;$wp@O~D
zJ$}4Z7KWRYcwxNqPuRAGQ?qtEVsvIe=kJq!rKN9>ZH;u3q%syb?(y#NCU2kLG}#-qUbk*8Vu&V{zKW
zi%U-M=E9jo?vqp-cNLbtTK)pjPa#tqwyjVCDFfyBLz!8M{OcvT2aW%6m)iFi&IxBc
z;GR$tq6d3gk%RkZ52q7Is%S&*L%3;5@ux_#aN&$qFCfs87Bd_Y&35WaubWvSh(w#m
zec~Bl{KujSeUR=MsGzax+>AU!BUlG(4oj6@SyC^2PiIi|3^m09SN!MJ-oXm;FT
zB2bOClg6r*?ocKJ(Y=~aI^kR**nuvyrq4p&S1K3}vM+bzKHSY!2m2QRdT_cJP$NV9
zQKT>4)ez?~V6iww9m2(Fje1qLoE8i;e+DYO42Fq16kS;oT~1D$hz46CT_uCD-|76-
zN}rGw=JVd;LhF$0)h{gMT_pjg@E&z**7u8CJ(jo3gE^E)51fg%;qzYf(7VIfaXepf
z3LTD!J%Qivuqf=)9wreuAPi6kwbB43SVsp9(?}im7#}?6L$QM?6n+zZ?bP*SpL^^>
z>~tEsMMGg>W9VFY87-+Q+Ze?;lg@3x?oaDeo=;<
z$9OB@42FJzs^}7rVuZ|72Aw!X$K5!8!I)}Z%=C;GHK<;ztsqNLv^1V~40Imr$bfAJK3m5@61S9CzxBv3($gx
z0&C+;K}64N$adp~DfYF!FP?2F+f~Nhv*)yypYs_d@{8108Tp#nKNxEj^-*OStaia9
z>=#wQSbyiY4(^A18(Q3}Ib+E)GtsfcWmOrj@nHBI7`gWLf%AZm3Gj{gVI|VAl)OtL;R5-(;6LIv35WI
zz}JMLik1{z#h)7DMejRl&^jb9e
z-$*l^Wyfy`q1kaLl|kMZi8k8^D)&(tf|d4Xv$3nF%0_aVEsdQ0BzEf09Sr_#+UqAD
zi|k-(9QTROqgY9=r>o?{D9ek+lEbv%KV=?shrSc(wJRg3@&~yR3=ghkW`ubSQ_4nOn*(~F2^^iUpi=0?}
z1Xv|oN@v;0#8(QEu{QJ+b7joE=`0VKD`w>6YY3eyZ;SqRDrr}7kf>k%jArgDaRr@p
zm;G9<=pibVkT&CC^a*)8=Xdisll^fV45S5pQiFLzA_iF?O++iyzLE$Wx~^!2xc+A!
zY}oQ^7Jd=i7nbE53vz7HcRAI5kw#{>7|a>MI#AbaI=qGR7gvrNsv6?_8W%i!i+nF+
zQ>yPn1v9tK_2m-gwm)2pJI3pQDQa)@qsaEiw9e{8QAc%SiksxyCQIuHVYL|i$t<-M
zuNZt8rm#R7VQkHP@;y)Udy$?O_BS?#ERk+5LFH39bif`)v$+M2!Hy~4HdtfjSNHpr
zjbGolqz`x0w$&c6c(+Vg=oS#+oq>rwHXx<}O9@V2V;0OSJbEld`3wrfr_|BhF)f$|
zNOSMdVZ~@6LH3G$)XC46FS7^Ve9#(_mTR%U4%jqC?@zhw{9tK_^W;p|L
zeG4Y5V#^c!^(^J539py~&mPt;`{6rO(u$ho+`=CVe;t`_ZCC)^HG{|Lm`*jE>K{%y
zqIgA)uy8$uzJKb7#Wk!71Ei}$#)0Y)|RCl_e0#mY9%b?oom&mU7Xw1z4ryPQ<6gmLGd5C#-Lw@h0T)~
zU;ufk+2HZqT{I{jSS+rG8*Tir-B^P7IVG|d%rkrZHg0R>K_wQYo2U3rW3eW{-mIh7XG!LwE0~tmdROTy?OP6||-)k(qfze=D;V^70)r
zQ*Aq{iR9?C+^&eiYQkT&yMTP(hy+k0E-Vs?i8C
z`yVWWq!k36H=LP_rjeZ>O|&!@T5-LJ=Ev5$?5{^{iiF)-;f~QS}PGWTG44M~1{>+!Yq2^G=g1yU=|t
z
zIUV*zEM1tl;r-=b3sUttdeGi)xqjxDstaX{B(`ZN_`~Jv{5;a>E$~AH@&dMdmdOqq
z$UhXaQszykjuQMk(7i0Dytu)3EQoWa&G|HTsA7YDm9~9d#Amb;bnvFK=S+N~RX!Iq
zMn#rwem);6k3d&*st)&5j_fQ!DvFpHHHlqb0nK}O$E9h-cim7*GQL0RV5eGi4O+>m
z?i|1pdZ0u(z(?9PwCh*WIb&?iqK?m(Cbwc%r|AD|+8E=KqEa*zkYQ;gpA3dt1Gy2d
ziA|Af7Akh9c6@4+ge;dea=UX>;eM&f7T`~UzudSn1|jD$1;7&XCX81$(*r-cSvv0a
zSZD2e03+jJEI{nDE_rwShOQhnvi40>uJC?bF>hLiPlP%0JXM9a
zaPi6Z*#DDKxg}ob)@v`ce-8^?Fr`CbO#RW1r0lxV&ESK43H)dfL=22y;pFZ@A$&
zKxONu(8Jx9W8$%^-<=VnVoEx-&p2L^CQAs#GO^RGBqixlAt@FlM9L7M;X$-B@!KzGqig
z)v+Cp?>ozfR0We;D&iHVUDQ5=|xKFj1MTGSrBT5hq6Wji}9`VY$3kn&QfD=b9>X
zuG~*G?rVSBJ8wYd7A=u|s5~TXt7d|i2DG7nf%UmTE4J$_1O6s6!)hY_nWabblbjs_}9PmqdD15#(Px#*IV%Y1t@g~us$qD{d
znG5tCg1#<0r0;1Tl>IUNmH#2fPL)|kgAUWCS!^!qQu0_dD)-MV2eq7Ty8K*Y-)`+T
ztDU}eX3($bi;~N}b$tZ?bPX2DFTNWO&Fb)aE?_H++FbqeB|J6rgY>T5ZW!9dH>gY;
z{5p5gtmkLdkIlM`oNbBzO;<*7%32ja5^RQqr?iH?9nc#3QclW;&S^F7-Out#tIFuT
zbul-{&Y?9UzD+0RZH~o#L*?EmcDZAx^>zJ6T*H#fS@4UJ@&4x(AA*Ame=VIi8n524VeT38|f6n#@ks~t_&TY=cH?2cIfX%lH?!Cv6Ah_YO2&$
zp&mUTX{=w_^x?agEP#`-RIz7MY*Tbps@A2~%nMn{9GPp3BwGKpK3BZ5Ns-ghDkz(%
zvqhYnJ3LEwJyE#n8Z2=R5w+4s*v_V$@lHr9Y_3
zQq(+(S)8_O-`2z{oV9i`{TGTOcl-vPmfEnp>a-d|IjpSP!BrVgWvdF^zx;Y;Gu>Nk
z=EkaCv?3|uP5QJIS=ZJqtGr`iFZ6ZDYtQlBJy5}5(ZNm=vS}h4P}$q}&{H!={MJhq
z3NUPrzDsq9Cdo5Y(T;~T9h*1sxk$J3bIWpB|4vc0!>+E%@b3?W-J3leL$eoeoS+z&
z7Jkuo&8aRbBJd`6tMvl~+@lzMB{?Fo>r>Kg4xVRT-2yP1tzd(t0w8dkBp^Ij
zu3OaZk;tv?vAf6dK6K^Djm5m^o31&XK^F4C4*(0X5`e+^RN1w`cUxn_Wb3EF%|)0+
zd&Hr5_q3)dliF$Z*V39+*~wH&6~C?h2CB;(ho$9{x3!wn@26%bV`S_Rj$8J~%2Ey=
zSbt|xZ`8?z_5N9@O-R6apiA!~tMAc1S$b(PUxm_bhhUUnry%OFm1@!rc=&
z`?em4m1!g>f<3@G&C6|ui&Xl(8wUFN@bZiq=%kCbGDq8+<{mJbQjjS(cSNY_G
zL}B`${t^H~=0v$P!p|vlGehekHajHak6;aB#e+nWm@@{5NC_kvVAmhJd8&a|`O(U-5Bb5aq5A74Lzpz5;Uz!xXFW8kMH
zMOgteU%Ecu@a^M=5m1|_+2efpW>ru}wEMj^x2me9{E(dVwBJ@S>YxwZc(}xBM`!s-
z%KD$R(FdC6BRlRv^UGDbXecJ*e
z&fF?(l3+lq5f?LZqbxn}1WBLpGwgZ2lIh6YKJ-D@T%fZ8B+0uN8=~sQd3bueAy&>q
zn|n%Hu}oCdpeS>jd`WttmA5rIg|o*erHM1&=~r&RtoPa1(j@&=;(jkNYoovH*Dm!-
z;cu1ZJ$USzBXU1LS`CLT9?%@m8A-eA9PJ*z^|b|J%SxYWln4+K&WpeE4Qa
zL(GlZ>uykd)#G_ngTXuVo16>GN$>A32w5O|bI@F<{N!ydiQ2Gg#mR_(8Y(rF&ra0_
z`8|Wi%PHF1dhhR4Dp6c&IN&fxmYn=&$gk83T{RMwv<(P$O5uD8Cls?0e3f@l5ZEe?0))(IPaPfV2jJM>>n?-_e(!`vX
z>QPm`w96mn7yNifm93ildccnJ_+tc=48CbwP#}{OiF9<1nYP+SJmyB-_uS#56;vNP
zx%hhGBzewGZJ{7!gz%Gh=YHGclb@&QeVusPK%UD>JQ0wmYWG)wrVe|f?LqR0x(#-f!Y}l^KU1^tf)FOEH}3>>?{WMivy<5NJs0JC75gYdVhYRh1@nIvHj>Tr!f9KkIuu1LT|mH%s|rz
zlQSzee7KIq$?6A*5!)*Iw6MpSvc_hT7U`eku+^~@^J&Y8NxhQ}?ypcP@u3-wQWsV`
ze6S{lF*1pSm!zn(oCq8zTRhmuln4*5I-0xlm{gC@Gqn3-U|S1QnoeA1$D}#$U6**J
zY3iIlqc(-?Ly~i(U&@C&+bj0Bs2}V}tMieZ9&m}D@A$jhrcs)ZRJ?ab%9hA?0lIL)
z4Rf1B&)zIXLRqLJg6|!2w?^GJ+kZs!NyJ`^fWKn0FVBFNBNXjd8mqH%8fT_QUYeWf
zS`%dvIKTTrE4R9i2#$(OEdHgfJMULe=C|A&;d@1!(}IF8CrvJPyqhQXnb#YCbkFD3
zIA;zgMc%o}VyN$`P0^!k$%fbzn4%xK^hm%2SY5`IZ70=KPn3nqp7QH$vwH%~Mc1)>
z>jF1rmSb-bUgiVa23sz(9e?Fpm^|5ezq0edbo1tfp?%WW=Y+LyL)P;(8tqmgoi!C*
z-rZhi3uI~RshNX09VVL(~IB@C0iS~AHD#u$G{V0opO|G72qV2Lb9MbDUJZGh>
zOjjp&yX0)5gRr)9^IfHGgh$=e0dFgj9*jH8jiIXWCsQ;tHX6nMGgY$QLg_r!L}9^AjVBJ^r6aE@D!%1r8!W2HG3}`F
zL#yykZEuEO`1eb8>CtyPYHZEo+HLEf{V=QAu@+l-u3;}y>BkH96kqn7dF~Krp+oVE
z4CcRV2&IT|+lSpI%_ocA*4n$ej1SU;idub`c9He7qFw*BSiSp&U88DK{x3F!n}
zn%1uc$FHS5oaZzsNsE*S%il1LYZR}x*oc6L((wx(Oc^XyfGU7WAuIiJ|5}WSx
zOr!of_g6WyMG4MW8Qk~j*Ejj!+%WOqJa^d<+g@$Uf*wg6XQ)|vj29&n;W!V
z9^|*jb5rT*_@OL@b{ywgDSr&iv$9eBZXEM`3ZmAk)rZzWb+vO|lal*;u{$Y>!fZ&@3r#u&Jm2E=I8`l}KU+@rLRU#M%
zs}Ha~#B*`E?Xw&eQn9DnOwq7=#=3J~pq0#~j4RP6-*%Td4FraZ4V1}{Ebf>Py|-%Q
zL|W^2V*NX#d3{Z#f+nM&UC1opYrws-x)cndxHq~*e&?}l^mK^|c}W^SS1C?vaENSY
z(DhtcMgCdW&V3YKc=yeJ_-OucFSagEI#!26ry|LW154D}V&G*wm5|7s*>PM`rQ;mO
z^x-9UMMYoskIo=qtuwIn;S>F2_aOF%{WkuX0h_O-QBV}@|DF{**e3QN$lXe3hT;Ns
z(Vi0PPacuvq1uG-Zy@C)d7DAB)&2}$|K8V~
zSDf>MeqhgV`*H7@FTOt;5FAvg%RMVj(ImyZHFLl|LXDn26}#@-su1;M3s}|PiMLpi
zzE-gs6MiN(Ey}R*S|LJ^+5%J|RZIw0mte~(A|jXI=Sh8BS#>E&C!KZjcWpj-WxpW;
zJ3i3Oe>N2hu*~uJYfGLTRW0oMIEBtH$3n?pO)A~9FrP!J16)1+7?%wRyqQfa8|tKSvSCcWKL9)rA#_
z`up`+epEIHUbDJ6822|P92WFo1&ZbWDU%*J4#6GiQ{*?U$m5&TbbhaG7T*HQA58P-
zOZF1)5eaIi1liBKu%Jdy=uRC?nNPJ$3TYxhoW3b)PF##-V0A1VbM8glsI^}vBQWII
zNUV_V0RD^iu|}LttpV0DZ!kA<*aq$>$_TX~rEgqW#@NQnRjoTjb#HkogJyXHYE0b^Cb5Met(RxIf?
z=z*V!1EZ-Wbd&UtWvJf#_%p1g9?94K?5U4<)6yOe=AN$V0OjH#aqP|qb+!e2}cI`Ia6Ekcs!kDX5G)P>T
zfRX_D3=qHGpe*H&8728KYT)^B#)ZoFUiSXR%hybD9dq_(hZA*SlBYz`P>GfwXM1c{
zVaCz`+1eEOuSYgRJqMEfKOOB(Q6_JHGI^YFZK@n=*z08;bXHvj`-3-$VSf7&>rp^H
zrBe_G+5O+EQZ4(KuzyMqZN--Tc59t$ZiL^WIsW`a-kp=Rz|+6}I&zO0L+-+8}BFb{i$~G$L@|vJYcJB6poqrk^s!AmOW7z;7mC
z1jOkkX~2H%@_7#zHws-P>MNH?bODyC
zL@)3%A;S(Pm!P(UHJNt*A-{i@)-iOHKjKHYe)WUY{dcLhH>m_SqxTd~g}+J~GrQx9
zyAz7Z7hJMmVV5^iEj6t5B3Uf?=WYtsZZAKUg$2|hTkSP=V{G0vr5?Tj_prkwgiFqP-@8@>yesSz0p?r#yBG=b^q|O*$BvCCXwP&k8aOORWGG@B)XA#$#BwDtA9!JBH(7Up@tU
zqCXsTL6sY>QDVDNtR2nVUbR3Zte%m0R8o-3Pwu`n@aw7b!y=)P8f8>}x4+
zBJEp|a;OrZ$v1s6U$GY{&Ok_T9%8$*7ZnLaTbF;Fp)|gA9Ou-@fP1aJbgV|zJTd+Nul-twO9IhB;80e
zXpWM2A7N_K8kZ-R&%NQ{8B&h*2FZixh@NfS<>LgUSw@H@->vi)+qzpCOmYkSMc#On
z5m1oPh0yoJ;=A36A+ksOzusrq#^f%@c^mx(5AdQ5L36vk!f5F=7Zk#Cld%V8z+ipI
zf&_ZtpMM@~iZ+74I(;hkLkQXDZiC0J{8{DQiM;kif6cXXKWCgwiD>`YIsQn%Tx^Zl
z8Cm;5-1BSd|Js=8ZP*EVl1MXk<6orjkW||PKa&6z8f)f2gGI^#=(wBq>aDh3*M|dZkRM-llJu8ZC@ou7c_Y%J
z@@IXoIVJSB3D%l%kK2M*N;e-e7rt;@#Cq^KGxAd9jV!Yd;eVvZNH-%`HV}b|LCVk?
zuxs7Z(M4*ZFOE7E-N)5&UWhPLni-{+zFn3qUl*1*igACw4zF3cQ~&Zq8%}EaX9`bv
zD(-KBm9B>pWeW+u-dP5Dh(*?bN-Z)-z`3`0x$N;Ftfn}U=2j|$A&lI1$u(a|%AM@H
zN{GT+2XRN2IRotuPFLMx(!G?4lZy_Gzvejf8#}6J2*BRzA2#anXkmb>bn`*hhqL6p
zn#>*LBTi`It)q`!*Z@lFiRM31R|97ndJ}fjrnx9QYYgZZ48bTkeFzO8z!G;+F2GK2h0!C*=PmdkF@%;o-Ms^IE(`T@L^Wd$V}wlo~^s2C&9
zrtY=n++L8=IW^jeZC19qkp>h2p#5hNS_JE8R&v_ICBgq
zM$byG5n_=bqc=9UJx?1ueRb6BOFm^2IKMC%5H;>dqz@LO%~=>i*I2NDpone<*$gyg
z*==dPA{U9^q^7yo?pfv>aqz?SVZzip$q%g+N;kDJ7tKC;XD%X%vGEwkyC`Lh1h1Tr
z!m?C6y9$h3Xrtvm`c-1F7K}a_Q`KD`7Ve@wDU{{{7lI|->E_5s{!HjG=
z-NzVO#VN{2$6#5TH(M*Prtm$FD)hiNNO%8hHh6uNh*nCx53t7594{AXD|4D}XGSWw
zYa_PpFSomfaGU}OY&eNfdml@
z+BhXd+II5jDki)>f(#04LO-ul=$OVin~T9sRA@WKfob8sAtL=J!1dHxExx;?!~Me(drZA
z35HlgAXLYdNYYJa=+gW<_V4T4qoRgvqpyX^f3Jr}Y)iRu6jm(`wqtxjZgD>DM?u;c+g~_wM=unvBRP?1O
zHfZ>2iKJM{H;ZNyK9J7Q8U1U`~#M?XB#C4y=}k_3u47B#izAN
z``tL|>A-*h`T4r6}&EI^sqc1jrL7}hoL=#dn$9T!NGf9CsP
z_3Dvu&LcF|~3=@qQz?Z)|&g-}ZE;^OiI<{8?1;g5HX1r)o*)|
zmGY!BMVfdfsJ0BS*8#F9*@SbUf>vT%{_*k~Z5ZQnONEqlCn6{;3~f2WK3oa@kZ9%i
z6Ut<{7eJqfw{)|gvf;_6W6E;duTPL7$aDKw0%r>5QiTM3J^>NSonV;;W;qpMhq3T-
z`e3N!wLP|UNoAb%U!w(oVycbg-3cYCUYcaR%@P%y6Ei@ij(5g=wQF$4C}keWsu5PY
z#MeoldqfdQ1|IW3Cy}=d^(Dfb#elo>Fm$Y=bs0pAqS4^I)7@?)>Y5nG%Ua=11pC*j
z0Wcr=P8@oC;jS&8ZcOpqQKc8MtGxbemVIBy^Mze^XFr@4^4(03>9SfAU5_8Eb)4to
zW^=1bcd5Y=!KpXfXO5|**p8`tLyG~bebIMw)%M~kDA6MA@G%fqGDGzsJ4CR3rqRr4
z)24;=Miq6p@*Y(Mc@@qxgA9c+g_J&j{(L%B*MD|IT_}=wi880kd-ZKLxQ`l@PfMDG
z54YRWaMfVwm&L#e{2PuGC$vc#Ad(w%k{F4FLv0c?&T<4mn-6!
ziu)NC50lc0bLgyi^5S{8ZW(_3wA5elgORTtgtOu;$X@{EPWg!r~9M|1&+H^BIE#(Q
zk8FtB^t;00C9wK@MKtuC3&`87&m*~wEqMqZo~=s>;!(PJL!Q~r4o8I5#QA9QRB0G=
zG_cp9&;2bb(#Fhaq~bPLYs}S=BLe;7bh$Hplwq@l*VoUZaJW`Xm@m8h265H-Z@b?V
zXm($zK}RTi9!YxzhC||CfbeNQ0S&r7x}0?|
zdn>!j7hW)<9K0~h(U@}Nc6-2tksER9^q7**%qH(x+@%^}ZW`l*zA3b}1#|9M486xR
zudaA%p-Pj5RjN4MR-vR6Co=`mM#cQPNo0w&mb+7A_pi`r@7vAdvNw20y@3^JsLRb8
zZxnz-LHLax8z(wQZLt2)bV+O*r58AN>e8)s60UYO&eN2W16FC*F`h55TKDCno2>If
z(BaJs(q9Tv1_ip`DKT#O7&bOgxQxXs2=I`$nH1eOf-uH|_MBrli+WjgVo!h1Jk(lj
zuN$*3w4@N^i&X?$^hnGgzP_LWjnW5ia}G%BA7`wvIdot;&$*vxfB_mnxhoacLNob_
zERJ59J`N!1#CYT0{C@Te52N73}lV@k*BT4-0eK+nAjfhwt@Whjvdzk}xWs
zU~OKzS=mq=xru`7;|};w+kdK|tJ8=g*e#9^AzwOrHx|UxX2?~U6)1Houn)f{#H-#|
zK0S|Ew5cYsPV&V~inL}|ft9>VOYE?TN-A+`ZVH9<6gQv8Sn}vn`C6U!1<=pTdPN6g
ztoEh*VZ|APir7(bBb}UrvlEXONPnxqY^?pT3Qak>7!2X$;bu$U-a`kEE1e%}8poTn
zK-#M{JW{l=&sw$)bbd=WXu+9dciP>Q;}4Qka0GW~L3b@)q5+n(@39*W*d8P;)6%d?
z=TLmD$d=!8Sl+I58@m|6#XV5k}94vPMCkf39L-30g+8=Wk6R
z&>UK8Hm@P`H+623{Gvd}F1B78q1`Ly!xcK;%r~4<3%)v3HX$B0C4E}A~jbrImIU_l2MD@$F!EPRo4IX`!=7SYO
zZ1T2zr?VapL-TR8Mq|~C@8C5)5Xkzflrfx#h^9kSWOD*S$Tn`@YJ!zo4G5hvF!@=I
z3e-Nw%mXKVsTsxJcAJ2||6;ERG@N=gN!Ei1X8Cm7BzshA#ZbnHjRoUPz|qEo4z*Nx
zJ@%Td0ql8hi!mY!?zCkh%-lb3g}D#6DdKa`hcoJqbo($^OV?pQ!av+{nt4=cf+GMp
za@nz7`i5q%O8em!+y;Sa8aiAJgtlP64$3zB2W6f5!UXfd(Hvc+
zaYw9G-xB1cJQW>|Mw1aAEHk4
zhjBxZ8!!}__VxsUNDXsM<-28RPCqbl9+_|6Oz@@Jb0^>k2g=f?hW&JMs?vENGk@a{
zXHtq^Ri#jgbxQgZ;926vZ<^3d)N0H&-umLiLhEyrT>;n3VgtTx+WQ$oU1|*Rq$pJPICxQ-og)
zaOjSetP}#L${TQ3$jyx}R3P*46wU!=f4=?Y2gPR#9wY{DL1#Iy_a%P}^2c|p^o?bD
z{fM9;3lGO=KdbPqCNoa)!iBztGsnQ>whEleFnj)56AX{}RL4hS0=(tH;&)q;jZPjt
z^MdD(xzVmjc|8OlV@BSd^BmJZOtr8ufhD_(==GzD{DU5@w+|IIyy@rqXgRG%rf@7Cw)8L~Gd(mgi>}tuQ6e7J2VcG3GHshUV1|5w
z&A?V&ahV+t=lH^2-0o0kpW3Yahp0cc{g)etV+-ly)eh&jCu-p(Q@7m)JMr|0vj)+%
zn1a0WF>R)vX(de<0hT9tO5<%LCc9yTk3QByC}k5o
zQ6?v;>S&r|glv(<`zu$97x<0;?`9PL(od^TG9wS`a;9M<`F~ot`k1D!D1Iq~B0LMs
z%snhEuM<^u#qiULwn$v(m#x{m|~1q7WiY{=3OhwT_Erh`@zHANVRGh|c7Bxy$G`{!Df`{W{^MTGJ?EZ}drr^2@0^Zf%EIC^*{K0X(C$Xvrq5y+274E{o;KVYrSAPrR?{>ON_+g`eyOFpA;n;
zTn=2*oTc4J%n$HdERh^`S=A#iZGL}Um!P~V-^@Ax&k4O->2Y%iQpYqV%d5d@HuZFZ
zv*r)};~aW2(~Rh`6?ATAsNr0s|4&akvh45-QNSt4rhfLP{T{9OJEI-7LhsHi-=TD;u8g*tw0HsPrwj&kSqVAQ}EiO@N__OK~ZVHqLa404_poZbi8UcFHx^
z5L2ZfOLo>&Qg>X7p&-bToHfR$cZ#oY@XutZUoT}Rp=;&_!gZh^M-Blmnjm+iy%j-4K39x-Rfhkh9TlL`u-v7hFAl&_
z+ItgctxUyzhxq)-niryoso%|dD96;<^Lp(z`vPv8M;)qdlx=w7nAE$U6StRgikMW8
z#41_}P+6ev<_))^5VEMI`)!w!_mX{67x!&5e}}`PgIH}r~oWvSGXdq-a$Okr@$^&K{I|D0nDT*n7V;uoRW>XJ56#O11$E4H|Q?4NE
zix>Re7~aqk(_KOKZNA{op~Gro$`h=;#{oQyA%MFD>$On;V^|B|IKg^#0l;)fO`~Sf
zVSJt2wx9`lsxKyl;W>Em*e8pEKW>O)R*=raI2HS7AC@k?pg83CzsT`0YH-o9V(^A;=`{Y~Qgx}?Ik
z5&jAo?x_fK1b)_*>dQKdRrb!rS((f{3!=JptjcB^Y*?}Y8(DK~neZo#ttYD<8ZUt!
zrw+y!0lq9WkI~t`#NC#BHHO9XSPOIy{8Heb!_`i%Yf~D)gQFAlNB{|Cs2jT5RyZXl
zsv8V);ZhR2Oz}0X$k2VqN$18IGw_IN?8Y=(!S^CZAAAARkmEGkT!mAnbW{GsS^v%w
z3esc5*P#DX0hCCDf`A6ZDw3dZ_9lEeOtJF;gf628lVYW(Qe2LHSMjxZyN80EA0mAq
N`FU?^`VN*f{0Bv6n1uiU
literal 0
HcmV?d00001
diff --git a/src/all/hentaihand/src/eu/kanade/tachiyomi/extension/all/hentaihand/HentaiHand.kt b/src/all/hentaihand/src/eu/kanade/tachiyomi/extension/all/hentaihand/HentaiHand.kt
new file mode 100644
index 000000000..d37b36559
--- /dev/null
+++ b/src/all/hentaihand/src/eu/kanade/tachiyomi/extension/all/hentaihand/HentaiHand.kt
@@ -0,0 +1,256 @@
+package eu.kanade.tachiyomi.extension.all.hentaihand
+
+import android.annotation.SuppressLint
+import com.github.salomonbrys.kotson.fromJson
+import com.github.salomonbrys.kotson.get
+import com.github.salomonbrys.kotson.nullObj
+import com.github.salomonbrys.kotson.nullString
+import com.google.gson.Gson
+import com.google.gson.JsonArray
+import com.google.gson.JsonObject
+import eu.kanade.tachiyomi.annotations.Nsfw
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.source.model.Filter
+import eu.kanade.tachiyomi.source.model.FilterList
+import eu.kanade.tachiyomi.source.model.MangasPage
+import eu.kanade.tachiyomi.source.model.Page
+import eu.kanade.tachiyomi.source.model.SChapter
+import eu.kanade.tachiyomi.source.model.SManga
+import eu.kanade.tachiyomi.source.online.HttpSource
+import okhttp3.HttpUrl
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+import rx.Observable
+import rx.schedulers.Schedulers
+import java.text.SimpleDateFormat
+
+@Nsfw
+class HentaiHand(override val lang: String, val hhLangId: Int) : HttpSource() {
+
+ override val baseUrl: String = "https://hentaihand.com"
+ override val name: String = "HentaiHand"
+ override val supportsLatest = true
+
+ private val gson = Gson()
+
+ override val client: OkHttpClient = network.cloudflareClient
+
+ private fun parseGenericResponse(response: Response): MangasPage {
+ val data = gson.fromJson(response.body()!!.string())
+ return MangasPage(
+ data.getAsJsonArray("data").map {
+ SManga.create().apply {
+ url = "/en/comic/${it["slug"].asString}"
+ title = it["title"].asString
+ thumbnail_url = it["thumb_url"].asString
+ }
+ },
+ !data["next_page_url"].isJsonNull
+ )
+ }
+
+ // Popular
+
+ override fun popularMangaParse(response: Response): MangasPage = parseGenericResponse(response)
+
+ override fun popularMangaRequest(page: Int): Request {
+ return GET("$baseUrl/api/comics?page=$page&sort=popularity&order=desc&duration=all&languages=$hhLangId")
+ }
+
+ // Latest
+
+ override fun latestUpdatesParse(response: Response): MangasPage = parseGenericResponse(response)
+
+ override fun latestUpdatesRequest(page: Int): Request {
+ return GET("$baseUrl/api/comics?page=$page&sort=uploaded_at&order=desc&duration=week&languages=$hhLangId")
+ }
+
+ // Search
+
+ override fun searchMangaParse(response: Response): MangasPage = parseGenericResponse(response)
+
+ private fun lookupFilterId(query: String, uri: String): Int? {
+ // filter query needs to be resolved to an ID
+ return client.newCall(GET("$baseUrl/api/$uri?q=$query"))
+ .asObservableSuccess()
+ .subscribeOn(Schedulers.io())
+ .map {
+ val data = gson.fromJson(it.body()!!.string())
+ // only the first tag will be used
+ data.getAsJsonArray("data").firstOrNull()?.let { t -> t["id"].asInt }
+ }.toBlocking().first()
+ }
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+
+ val url = HttpUrl.parse("$baseUrl/api/comics")!!.newBuilder()
+ .addQueryParameter("page", page.toString())
+ .addQueryParameter("q", query)
+ .addQueryParameter("languages", hhLangId.toString())
+
+ (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
+ when (filter) {
+ is SortFilter -> url.addQueryParameter("sort", getSortPairs()[filter.state].second)
+ is OrderFilter -> url.addQueryParameter("order", getOrderPairs()[filter.state].second)
+ is DurationFilter -> url.addQueryParameter("duration", getDurationPairs()[filter.state].second)
+ is AttributesGroupFilter -> filter.state.forEach {
+ if (it.state) url.addQueryParameter("attributes", it.value)
+ }
+ is LookupFilter -> {
+ filter.state.split(",").map { it.trim() }.filter { it.isNotBlank() }.map {
+ lookupFilterId(it, filter.uri) ?: throw Exception("No ${filter.singularName} \"$it\" was found")
+ }.forEach {
+ if (!(filter.uri == "languages" && it == hhLangId))
+ url.addQueryParameter(filter.uri, it.toString())
+ }
+ }
+ else -> {}
+ }
+ }
+
+ return GET(url.toString())
+ }
+
+ // Details
+
+ private fun tagArrayToString(array: JsonArray, key: String = "name"): String? {
+ if (array.size() == 0)
+ return null
+ return array.joinToString { it[key].asString }
+ }
+
+ private fun mangaDetailsApiRequest(manga: SManga): Request {
+ val slug = manga.url.removePrefix("/en/comic/")
+ return GET("$baseUrl/api/comics/$slug")
+ }
+
+ override fun fetchMangaDetails(manga: SManga): Observable {
+ return client.newCall(mangaDetailsApiRequest(manga))
+ .asObservableSuccess()
+ .map { mangaDetailsParse(it).apply { initialized = true } }
+ }
+
+ override fun mangaDetailsParse(response: Response): SManga {
+ val data = gson.fromJson(response.body()!!.string())
+ return SManga.create().apply {
+
+ artist = tagArrayToString(data.getAsJsonArray("artists"))
+ author = tagArrayToString(data.getAsJsonArray("authors")) ?: artist
+
+ genre = listOf("tags", "relationships").map {
+ data.getAsJsonArray(it).map { t -> t["name"].asString }
+ }.flatten().distinct().joinToString()
+
+ status = SManga.COMPLETED
+
+ description = listOf(
+ Pair("Alternative Title", data["alternative_title"].nullString),
+ Pair("Groups", tagArrayToString(data.getAsJsonArray("groups"))),
+ Pair("Description", data["description"].nullString),
+ Pair("Pages", data["pages"].asInt.toString()),
+ Pair("Category", data["category"].nullObj?.get("name")?.asString),
+ Pair("Language", data["language"].nullObj?.get("name")?.asString),
+ Pair("Parodies", tagArrayToString(data.getAsJsonArray("parodies"))),
+ Pair("Characters", tagArrayToString(data.getAsJsonArray("characters")))
+ ).filter { !it.second.isNullOrEmpty() }.joinToString("\n\n") { "${it.first}:\n${it.second}" }
+ }
+ }
+
+ // Chapters
+
+ override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga)
+
+ override fun chapterListParse(response: Response): List {
+ val data = gson.fromJson(response.body()!!.string())
+ return listOf(
+ SChapter.create().apply {
+ url = "/en/comic/${data["slug"].asString}/reader/1"
+ name = "Chapter"
+ date_upload = DATE_FORMAT.parse(data["uploaded_at"].asString)?.time ?: 0
+ chapter_number = 1f
+ }
+ )
+ }
+
+ // Pages
+
+ override fun pageListRequest(chapter: SChapter): Request {
+ val slug = chapter.url.removePrefix("/en/comic/").removeSuffix("/reader/1")
+ return GET("$baseUrl/api/comics/$slug/images")
+ }
+
+ override fun pageListParse(response: Response): List {
+ val data = gson.fromJson(response.body()!!.string())
+ return data.getAsJsonArray("images").mapIndexed { i, it ->
+ Page(i, "/en/comic/${data["comic"]["slug"].asString}/reader/${it["page"].asInt}", it["source_url"].asString)
+ }
+ }
+
+ override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used")
+
+ // Filters
+
+ private class SortFilter(sortPairs: List>) : Filter.Select("Sort By", sortPairs.map { it.first }.toTypedArray())
+ private class OrderFilter(orderPairs: List>) : Filter.Select("Order By", orderPairs.map { it.first }.toTypedArray())
+ private class DurationFilter(durationPairs: List>) : Filter.Select("Duration", durationPairs.map { it.first }.toTypedArray())
+ private class AttributeFilter(name: String, val value: String) : Filter.CheckBox(name)
+ private class AttributesGroupFilter(attributePairs: List>) : Filter.Group("Attributes", attributePairs.map { AttributeFilter(it.first, it.second) })
+
+ private class CategoriesFilter : LookupFilter("Categories", "categories", "category")
+ private class TagsFilter : LookupFilter("Tags", "tags", "tag")
+ private class ArtistsFilter : LookupFilter("Artists", "artists", "artist")
+ private class GroupsFilter : LookupFilter("Groups", "groups", "group")
+ private class CharactersFilter : LookupFilter("Characters", "characters", "character")
+ private class ParodiesFilter : LookupFilter("Parodies", "parodies", "parody")
+ private class LanguagesFilter : LookupFilter("Other Languages", "languages", "language")
+ open class LookupFilter(name: String, val uri: String, val singularName: String) : Filter.Text(name)
+
+ override fun getFilterList() = FilterList(
+ SortFilter(getSortPairs()),
+ OrderFilter(getOrderPairs()),
+ DurationFilter(getDurationPairs()),
+ Filter.Header("Separate terms with commas (,)"),
+ CategoriesFilter(),
+ TagsFilter(),
+ ArtistsFilter(),
+ GroupsFilter(),
+ CharactersFilter(),
+ ParodiesFilter(),
+ LanguagesFilter(),
+ AttributesGroupFilter(getAttributePairs())
+ )
+
+ private fun getSortPairs() = listOf(
+ Pair("Upload Date", "uploaded_at"),
+ Pair("Title", "title"),
+ Pair("Pages", "pages"),
+ Pair("Favorites", "favorites"),
+ Pair("Popularity", "popularity")
+ )
+
+ private fun getOrderPairs() = listOf(
+ Pair("Descending", "desc"),
+ Pair("Ascending", "asc")
+ )
+
+ private fun getDurationPairs() = listOf(
+ Pair("Today", "day"),
+ Pair("This Week", "week"),
+ Pair("This Month", "month"),
+ Pair("This Year", "year"),
+ Pair("All Time", "all")
+ )
+
+ private fun getAttributePairs() = listOf(
+ Pair("Translated", "translated"),
+ Pair("Speechless", "speechless"),
+ Pair("Rewritten", "rewritten")
+ )
+
+ companion object {
+ @SuppressLint("SimpleDateFormat")
+ private val DATE_FORMAT = SimpleDateFormat("yyyy-dd-MM")
+ }
+}
diff --git a/src/all/hentaihand/src/eu/kanade/tachiyomi/extension/all/hentaihand/HentaiHandFactory.kt b/src/all/hentaihand/src/eu/kanade/tachiyomi/extension/all/hentaihand/HentaiHandFactory.kt
new file mode 100644
index 000000000..eee5d1542
--- /dev/null
+++ b/src/all/hentaihand/src/eu/kanade/tachiyomi/extension/all/hentaihand/HentaiHandFactory.kt
@@ -0,0 +1,15 @@
+package eu.kanade.tachiyomi.extension.all.hentaihand
+
+import eu.kanade.tachiyomi.annotations.Nsfw
+import eu.kanade.tachiyomi.source.Source
+import eu.kanade.tachiyomi.source.SourceFactory
+
+@Nsfw
+class HentaiHandFactory : SourceFactory {
+
+ override fun createSources(): List