diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt --- a/documentation/CMakeLists.txt +++ b/documentation/CMakeLists.txt @@ -9,3 +9,5 @@ endif() ecm_optional_add_subdirectory(manpage) + +ecm_optional_add_subdirectory(zealdoc) diff --git a/documentation/zealdoc/CMakeLists.txt b/documentation/zealdoc/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/CMakeLists.txt @@ -0,0 +1,37 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"kdevzealdoc\") + +include_directories( + zeal +) + +set(kdevzealdoc_SRCS + zealdocplugin.cpp + zealdocprovider.cpp + zealdocumentation.cpp + zealdocconfigpage.cpp + util.cpp + + zeal/registry/docset.cpp + zeal/registry/cancellationtoken.cpp + zeal/util/plist.cpp + zeal/util/sqlitedatabase.cpp +) + +ki18n_wrap_ui(kdevzealdoc_SRCS + zealdocconfigpage.ui +) + +kdevplatform_add_plugin(kdevzealdoc + JSON kdevzealdoc.json + SOURCES ${kdevzealdoc_SRCS} ${kdevzealdoc_UI_SRCS} +) + +target_link_libraries(kdevzealdoc + KDev::Language + KDev::Documentation + + sqlite3 # FIXME +) + +install(DIRECTORY pics/16x16 DESTINATION ${KDE_INSTALL_ICONDIR}/hicolor) +install(DIRECTORY pics/32x32 DESTINATION ${KDE_INSTALL_ICONDIR}/hicolor) diff --git a/documentation/zealdoc/Messages.sh b/documentation/zealdoc/Messages.sh new file mode 100755 --- /dev/null +++ b/documentation/zealdoc/Messages.sh @@ -0,0 +1,7 @@ +#!/bin/sh +files=`find . -name \*.rc -o -name \*.ui` +if [ "x$files" != "x" ]; then + $EXTRACTRC $files >> rc.cpp +fi +$XGETTEXT `find . -name \*.cc -o -name \*.cpp -o -name \*.h` -o $podir/kdevqthelp.pot +rm -f rc.cpp diff --git a/documentation/zealdoc/debug.h b/documentation/zealdoc/debug.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/debug.h @@ -0,0 +1,29 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include + +namespace Zeal +{ + +Q_DECLARE_LOGGING_CATEGORY(KDEV_ZEALDOC) + +} diff --git a/documentation/zealdoc/debug.cpp b/documentation/zealdoc/debug.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/debug.cpp @@ -0,0 +1,27 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "debug.h" + +namespace Zeal +{ + +Q_LOGGING_CATEGORY(KDEV_ZEALDOC, "kdev.zealdoc") + +} diff --git a/documentation/zealdoc/kdevzealdoc.json b/documentation/zealdoc/kdevzealdoc.json new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/kdevzealdoc.json @@ -0,0 +1,25 @@ +{ + "GenericName": "Zeal Documentation", + "KPlugin": { + "Authors": [ + { + "Name": "Anton Anikin", + "Name[x-test]": "xxAnton Anikinxx" + } + ], + "Category": "Documentation", + "Description": "This plugin provides Zeal documentation integration (zealdocs.org)", + "Icon": "zeal", + "Id": "kdevzealdoc", + "License": "GPL", + "Name": "Zeal Documentation", + "ServiceTypes": [ + "KDevelop/Plugin" + ] + }, + "X-KDevelop-Category": "Global", + "X-KDevelop-Interfaces": [ + "org.kdevelop.IDocumentationProviderProvider" + ], + "X-KDevelop-Mode": "GUI" +} diff --git a/documentation/zealdoc/pics/16x16/categories/zealdoc_abbreviation.png b/documentation/zealdoc/pics/16x16/categories/zealdoc_abbreviation.png new file mode 100644 index 0000000000000000000000000000000000000000..9f72492f8e7cd5a55dda5d99d56db26364319a10 GIT binary patch literal 354 zc%17D@N?(olHy`uVBq!ia0vp^d?3uh3?wzC-F*zCiUNE>T>t<7UmHIcj^Xn z^F3E>x?_A^MD$j$`1vWPCWrd^uHJS1V7a}5R5E|xT>t<79~iqDj?&An=hi$; zFTWX+c`zpH@X-_ZfeP{~w`Eq`{r2tGx=okkbB~?B^du-@U3S&O#Ow{-lMdIm?43U6 zL~_xkgxu4MSDbtN4-1hD%oVDUM1%jwXhZQu7R&2SRT>t<79~iqDj?&An=hi$; zFTWX+c`zpH@X-_ZfeP{~w`Eq`{r2tGx=okkbB~?B^du-@U3S&O#Ow{-lMdIm?43U6 zL~_xkgxu4MSDbtN4-1hD%oVDUM1%jwXhZQu7R&2SRT>t<7zdEo1j;^OKe44xJ zX8PiTF%u5QOuB#c5>Uam{3&-cm;UAsbv1X$&YeQ&nR}TVb;xCZo22HZb*1_ qwtc*2{kkjB)z(G~9P7lLA{h7hus3g7a&8yU5e%NLelF{r5}E+C?3;A} literal 0 Hc$@T>t<79~iqDj?&An=hi$; zFTWX+c`zpH@X-_ZfeP{~w`Eq`{r2tGx=okkbB~?B^du-@U3S&O#Ow{-lMdIm?43U6 zL~_xkgxu4MSDbtN4-1hD%oVDUM1%jwXhZQu7R&2SR4~+Ta;1uMri}Xg`sVQK zbEb>aKPv=HcaobNK}XxrBt_ToE==E-`U9 zSAt(cQc4=mm64T^lSjykC@3l^tH8OcYU&!AT1XaY>*(sixIiGKZ(s=GqB#=P3jod} V2gb~J8h8Kz002ovPDHLkV1kqNv!MV0 literal 0 Hc$@*V0cwW;su<+YlLrG|2*h;#e;`ta)J zriXLn+S9O;fx)Vq{r&y&?C7S4a{K!E>f+tEos6=Ugs6#jwVQ~=t)5(xxs?C_00DGT zPE!Ct=GbNc0004EOGiWihy@);00009a7bBm000XU000XU0RWnu7ytkOZAnByR0!8& zU|?i|17-#WMiy2$U}0upVr69m0(K5gAfE}$<>KPv=HcaobNK}XxrBt_ToE==E-`U9 zSAt(cQc4=mm64T^lSjykC@3l^tH8OcYU&!AT1XaY>*(sixIiGKZ(s=GqB#=P3jod} V2gb~J8h8Kz002ovPDHLkV1lTqvWNfx literal 0 Hc$@KiJ289Bv11qRe){_T|Ns9#e*M0B=h@U1ho8K9ziQX{MVn9UKYeq` z@*a^(6H`>N21JW0JSKi@?-PygEP*XMsm#F#`j) zFbFd;%$g$s6pZzBaSY+O9@`%&)S$rQ9KB)9rX7p!{a>Kh-0uH1ug|-z&AF?~x~$E-uFbx%(6FGyrIft7t<1-|)Wx~gyROZtnZLB9 z$giHnwy4PT^Y{Dv{N>^7_x1SG$=$W3$Goo2sF=R@_4=-#!>O3RrINd;n7+8F%fq+Q z|NsBZ!`iK$!nUW$xvI;yrpVIB-Mp{P(a7Duw9&t_(Am-9x~t2sp2D-G$EB6M@a^^e z{r#nryR4kRxT?ytqsOzP#;%^h=i~1C`uzF$`=ykL;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_005dvL_t&t z*G13Q8iFtk1>h7?5uD&a1q7rDC~i^QgZFps`Q&eqtX|VSW*|Au3I$zlJmL^}Kaerh_|HYfdkw`6-BFZ^H{(m8dH^j>g?$Gh7 Q(f|Me07*qoM6N<$f`AnLhX4Qo literal 0 Hc$@CfKFkmc8* z;m?-sSH2!r+y+*@LRre5KcZrrUHYfr{Pp~;+?zTm$vM`(*OVeO>_%)r1c z48n{Iv*t(u1!Fy3978y+$M#1GH7M{nM{iiOX~&{_{}<>rH#~dqd&`HZA*6*tBW0q4 z!p#o3kXr&b?G_i=9kIC4bL;rV!sV7Tmj3%!;QQThr}@pL+FE(lZk02xa<-32xiu|>vCK#LeWUHx3vIVCg!0E2mkSpWb4 literal 0 Hc$@SH2!r+y+*@LRre5KcZrrUHYfr{Pp~;+?zTm$vM`(*OVe6N?jo<6#@b~jhjBIS8hI*-x z=<)4Mifd(^f0n(eeyo(a)W@R4v|*WieXo{UlXm_6{lePKQH^f-`}t>_fOxEtRFH7V z-qV=9s@>_~ZK8&Ru$lY)`<1<^Wu1V8w3|?jZTI>2T$OfPm3L5$Zc>hKeXy2?6{3&; z0004WQchCmZf0HH($9dQ}_iYCuOwwowVVzQH0I*N# z8X`QC&6ymme8J`f5pasawZJKrDPEb zd9+xreuVWV-0pmqaDNcT(-{(8t~YTHQo`dIy+9GivKf;%mQVjrcmurH4C;EyDCGbE N002ovPDHLkV1m&;-N^s| literal 0 Hc$@z-T>t<7Kdm8q$%ML%3n#6e z-LrH`^U4{W8|L@V>#3R7Tf23|?0uV7>|VEc;iRTH-Bs%rO#bxY?a%Mu9^JWd^TNrc zQ`^6N`82z;Vs=;M;z^B%wyy@7c4FU_b#wccO>Mh+_Sof<2UpMP{`33yq3vs?x0SA# z*}bs8;lvT^y*7G0EHA zg`tC0)&t1lEbxddW?fApihC^3XyRJsLZbPH-#!~l{@A)llyZ5|UdT-z5uM;@enTIB-oeG~M m@pwqXU literal 0 Hc$@{EA-efj?B|Ns9#fB(4g?CPAuQ=h+ovf=!y6{i**xw(J# zp(&d#ty^|-!I!982Y`mNmIV0)dp>*a^(6H`>N21JW0JSKi@?-PygEP*XMsm#F#`j) zFbFd;%$g$s6pZzBaSY+O9@`%&)S$rQ9KB)9rX7p!{a>Kh-0pm!?w*c{{R2~^XK=|C$}Ho-MVaH!=(!c*DWtQw5w&_%#vgK+uyu? zcI4pN1v7KEuB(;~o_Zc=K5I#kU$E!1=Uz`z52P*w3NR*lySwn^?=|oTaySb-B8wRq zxP?KOkzv*x37}w#r;B3<$Mw>FZ@vZt4%U@j3$9N4_5aTQdE2A{#rLtEWMN`%aO`C? zP|fsW_BgUK`06wcWwW*29}hh|#<#Kj?*a=pG5fP!;eVZ_+gD%LDh%4sr>lKiSid)S k`I3(l&nTRqC*Jjk;jzopr0564r7ytkO literal 0 Hc$@Q&gA+&L|0Vwrd0i+j(XTz|Z{abA{v+W-ImkE}b`996%6`JN4v*W@^)A6&IR z-#M!xtR~MX^ZoPp6Vtkj+;XR6_0K4qlIxfeS9D7qXbWRWkY8}a+PG*p5GTpo-GwiI zuYo_1!&%@FS^;opCAQC$&c z>(?o(*c(zA4Ep%zFggduOc6chJ=M-|t7wI`>8iIY>@Ut-;>tXg-4J-a9N u^U7S$V3%7t)vfn?R^%^vVraF$fWc`W>*9V@p5s947(8A5T-G@yGywn?aB*1x literal 0 Hc$@;rs4T>t<7f2zLmSXueWx`rDw zX5L%8| zAsp9p``Nh;81OL9O;mju_rE^-u@*~|mc^My$88;-Z5@xezU_3Zk!6^)@Se$Z#x^^y z9^r-ObbU9jS@Lq*PR~y0 zac;v{*yyYS`j zHSh;=I14-?iy0WWg+Z8+Vb&Z8pkT76i(?4K_0oPzp#}v37ICqu&eym<{QGY%sw={5 z{W@h8dqXONK_CAdM(4noDWa#mr`j2A6|K-VUG;W_{3I)RH{0IgORRg9RqL*>XZOT@ tUYY9|>~broy7hj~iu@%{46XJTFgWdFUEHtAa~x>r0aX7{%We!;qw?=wfM536fc2q(Vg(HguN5>Dr)agyJHWAB5(vZtrgHeu3%S zbVGECqrqevgA)6Yp+%;cGG)TZzDR?iIVc8$)4RW+1J84KzWg}nF*md8iwcVi34$nM zH85HzKc1f>hpAm&d90PPWbsXn83&gj7Ee) zn4QJg7>0(huzk$lMdK&RKG8qgEK&yq@jYI;0 z0Mu%Xj>6@_$_jEhB$F_iFf|303VuHv4)pg!sl@Owwzl$N#>cU-0f_{IgGi-7k`Rkw zwW6;NJw5RGU^YXeL3=x7GIVvJyBh)l6~y!O&?%}Z>vapem5`DOk|d?%e<+WT_Jls) zrJ%HqNA(QCL@#Nej_)kS1Mb=NrqwTUZ56@X5go13a^5iLwi7zjk2|ZDcx(@h^sYrM z>n$jE`7hnKw>W;&cr{Jo1EEjo)9^)qUd->*6>*}XCk|maEms*`j=gtj-pQHmpeEIt z;M3CLvbQBOnhwsV4bl)>tST-0C9Mvy7hhIejNWf&SjE>WMcIp|Gv|1stF?{qBi!?> z&5Bp{K(Zooi?5i!8LPX{c|EtDRUXit&Vsg&rZ$6QPWH2Ld|x}~?sD#KU;gU9{oz@5 ed$V@m{nvVLRZx1PSKoPs$Ul(DW<(x7*Zu>-o<@-X literal 0 Hc$@Q&gA+&L|0Vwrd0i+j(XTz|Z{abA{v+W-ImkE}b`996%6`JN4v*W@^)A6&IR z-#M!xtR~MX^ZoPp6Vtkj+;XR6_0K4qlIxfeS9D7qXbWRWkY8}a+PG*p5GTpo-GwiI zuYo_1!&%@FS^;opCAQC$&c z>(?o(*c(zA4Ep%zFggduOc6chJ=M-|t7wI`>8iIY>@Ut-;>tXg-4J-a9N u^U7S$V3%7t)vfn?R^%^vVraF$fWc`W>*9V@p5s947(8A5T-G@yGywn?aB*1x literal 0 Hc$@pm!?w*c{{R2~^XK=|C$}Ho-MVaH!=(!c*DWtQw5w&_%#vgK+uyu? zcI4pN1v7KEuB(;~o_Zc=K5I#kU$E!1=Uz`z52P*w3NR*lySwn^?=|oTaySb-B8wRq zxP?KOkzv*x37}w#r;B3<$Mw>FZ@vZt4%U@j3$9N4_5aTQdE2A{#rLtEWMN`%aO`C? zP|fsW_BgUK`06wcWwW*29}hh|#<#Kj?*a=pG5fP!;eVZ_+gD%LDh%4sr>lKiSid)S k`I3(l&nTRqC*Jjk;jzopr0564r7ytkO literal 0 Hc$@t<7e>(5TwdV8pXWY2e zd-+uE;q&Du?@haYvHBEH>eZnqKVE*j(Q)bbm*2nM{Yh7ML)4Yj0jj@+uQ19dTTm(NtR zUVrV@+{1t<7Uzm5gz4_YQ8TTjk z-YUpFRaJg|*0g(d)fa(M#}2)E_3}q|$Bi#ve!qYFYr(vGWhLi;;$hmpyMQJLmIV0) zGyFpX>-dzNfs%|#-tI089jvk*Kn`btM`SSr1Gg{;GcwGYBLNf)^>lFz;kX`pKANvV zfyd?Iv_}EjUBBbkK1^Ku{k;mWWQT>z5zjqGj!fDvxrFD{J>w1?xm8^U>SE?DpQ&iQ y{@Sg%hYcHkRePB~fAMsd;m=;3mHmD36%4sz%vF7+k0t^wVDNPHb6Mw<&;$S(V0n`O literal 0 Hc$@t<7f4VjA+RWzrYi8V9 z)O)HW_k3^py;ajLPOJt>y*hvB$J>`T=5+l2_T|^7xA)e~JJ(qP6fX!U02wV<666=m z@DB~F<5PA9N-`#SySp%Su*!M>Ih+L^k;M!Q+`=Ht$S`Y;1W+*4)5S4_<9g`%Xubvo z9+!*L9tCK3{f=MzFmdho_bR-S9TqM}Jog+qGHJWy5}sH0j5~DXR&^byikCFyx9cSM`}bnh3Oj!PC{xWt~$(69ATbdKUly literal 0 Hc$@t<7Uzm5gz4_YQ8TTjk z-YUpFRaJg|*0g(d)fa(M#}2)E_3}q|$Bi#ve!qYFYr(vGWhLi;;$hmpyMQJLmIV0) zGyFpX>-dzNfs%|#-tI089jvk*Kn`btM`SSr1Gg{;GcwGYBLNf)^>lFz;kX`pKANvV zfyd?Iv_}EjUBBbkK1^Ku{k;mWWQT>z5zjqGj!fDvxrFD{J>w1?xm8^U>SE?DpQ&iQ y{@Sg%hYcHkRePB~fAMsd;m=;3mHmD36%4sz%vF7+k0t^wVDNPHb6Mw<&;$S(V0n`O literal 0 Hc$@t<7-|5QT>B7A=n)h%L z-}V^Z`TjgJy?Hi7@Lt-z8Yuhl@`(ijJQF>5R)p}(N;LoQ^2zF&=oj~{A70$MJcMU= z9Pj=FK9C8n+(5m9ajB(ZTs)U-T&TrTnS44b522V=ajn> gMHl?jzw?3N#zvO+lOlHA0NTUg>FVdQ&MBb@03#-bX#fBK literal 0 Hc$@JW&==;x~fojg&yw!2wFp%-_<*kW8y(}d`e!&dSouY z?a+g18=s%KnW<2vKV|>*rMA3E`;>WS?k|s$n({u7ZKD0%X-sD9N7`rn?r7x9>1Slv W&Z*%cy!uNa1IScYKbLh*2~7ZQ>RxRC literal 0 Hc$@t<7->GNUscW~zGT@MH z*aBmZ)n-1sdZqxySLapq=sC9r4jnx-bHT%N7iJi`?6(P>XyEYS z`O6I!ew!@)H`TVSF!S!v1sMg@`*0V>4WJb=B|(0{3{U^z0M8!pz4P)JP#t5Ex4R2N z2dk_Hki%Kv5n0T@z%2~Ij105pNB{-1JzX3_IIf5G-xg|C;BlGm(V`HPvGnVw|Nmd6 zdgYwIJ@c%J^nnRU43{I98Y`SmKQLW*Mftymx{y8-9>XKcY?d9Lv4r>ej9N{9-}vCK zpZ>i%KF>=3`>L3%xnCxDE8UxTjbmcx%l%Kd)_mudE_-%{Re71xVIH8h44$rjF6*2U FngG0pncV;Y literal 0 Hc$@t<7uV>t;Yt(7!u*KHp zkg?SQGn>^ty}N+oc?GNWOnQthXPa6rws)HJ>GStPhi=WBedye|hlXY|Y+Uvm7*Blu z;)8|V221-*we_3KY*y$RbpVY5>Sg-TYy`AIrXr%{@X&$3Op{;Jz5lkGM0Y*^#A|M zRIi-#w`ZPJkv=dXiQ#hOQe%bF=?A6@uPFc5P#4l?!ee-3na#4}GnVi^pHZvn?;9Wd z_0zvs$LCq;e_s`oHTTN|Z>4(^uW?N5e7XM#*P8F#(q+%iuqrQ8I?MyKmci52&t;uc GLK6V$yqEF- literal 0 Hc$@5$ Xp5orCLT|VN&1CR&^>bP0l+XkK(H~%| literal 0 Hc$@Ate8@y43Y-`_qxKC5$ Xp5orCLT|VN&1CR&^>bP0l+XkK_|;-9 literal 0 Hc$@5$ Xp5orCLT|VN&1CR&^>bP0l+XkK(H~%| literal 0 Hc$@5$ Xp5orCLT|VN&1CR&^>bP0l+XkK(H~%| literal 0 Hc$@Ate8@y43Y-`_qxKC5$ Xp5orCLT|VN&1CR&^>bP0l+XkK_|;-9 literal 0 Hc$@yQGV@ zpzHJR?DOx(-OH=KA~lhP0fAw4H;onSrpEd#jR^yQcsD|JLW; zp2e}g*2$g1uhHV#o4~Dmt(5ck_5A()fv=aF!>^9Fq2lf5fUuX2x}>YiyTI7YiMF4G zw42M{)S$z%lDnpUt(1tgos71jn!>Gbqlf0;ZE^qr00DGTPE!Ct=GbNc0004EOGiWi zhy@);00009a7bBm000XU000XU0RWnu7ytkOg-Jv~R0!8a&sPq@Knw&>XXAy?O$|L@ zLN5uu-~U={Q<%ReOQSJ>C$jhe9!i~DJ}4B?2VqnySE@CV!m{3Iw%VQ0vD@nphNE$0 zh%}kt<7zcC?sV|?<7*7oxg zC!cKZ+FMk*x47(3WzF{N{D()50u}5juR7h;bGEPl>zg;fzJ9&5WZA*W>g($_9`B!U zpsaFte$lhD=MUG^o$H%$qNV*rYX{IYAOdPkmK93_S|nEzTXC6=` zW0JSK3quF1tOt<8S>O>_%)r1c48n{Iv*t(u1v5Nd978y+`}W@!YEa;CnW$rYdQ*4V z#{d7%I{Drfe|F{&69dm?35L}bVMiJ62bmufk>QNKsTpK1kY(w&Y{%zC*L3-Wqn2wp zsF**>yOfh*XB2d$>BqzECp>%i8^8JK_P2kJO@qGvoevD$PgrMf<(}dUw3ETp)z4*} HQ$iB}K=+@% literal 0 Hc$@GjXYouQ?vVLfxUof$KXP{m#u6$;mT`R0$E~|ZKp;arYVlk&=G^b-Rsbe#$ zWi+c~Gpb}Xt7S8*V=$|PcA9xxu#RoSpoPqvf5?}5#*=l$k#WO}Y{7|VzlLPJg=M>l zX}rI%-?5<4;@tAp$?Vp?`~3g=`~CXv@9wgo+igs&q>b8rQO$Np z%y>)8bVbUJY1Es0*rbWs(a+bhpWc36!?u_8ntSn-bMB99=Z$OUiDco1Ufgy_&%wLS zt(n|?UBkAO^_Y3^mU!@+eDR%s@t1k=lXC2MOVPf#%czstd|bh`k@cQ`@x!wB_VfPy z`~T+N`>>hxy|>AvjM#fx!L*L`!msxF`To_&`L&+(u9fqjgz|Yz(Ydn4p@!CZSirE4 z_S?|<>EryDckrf)@~e^alymH~t-_pt)OJOb3?_Qm8pwp%xOrlk8I|ZbmGmu@%;P#|Nj5)>iV;s z>4sw0a6iA7kD`TO$!0{Vi)P=4V%dmf*^Fn}j%wM6WY&aW)qz>iYdX1+iJgB|#b7+3 zhhW)*T+@PE(t=yjfLYIfSk8V}&U;eHXEw5qhMIOuz)dHFY(%kWIk0U$wQN4Ma6`Fq zMZ0xM!F5T%Wizjdfs|!5tN;K1cQN?R0000hbW%=J07uH@^Yiob^Yiob^Yiob^Yiob z^Yiob^Yiob^X2mfG(cj0oy5-g`e-WP$xv0|hvI{rm$0gMxxXLc_v2fdX6+kx|hxv2n5S35iMEKmnfQ zl+?8JjLfX;gv1L_>!U@|-`*duTt; z+ErW|*Kwab#{2N@vH$=7Z(0>|^f1r9Jv;|?)L%KjX3IwI@ORceKyz42g8YI#pFQ__ zl6oL@8Bl;R$=lt9_uH1luRsoGfk$L90|U1(2s1Lwnj--e^!9Xd4B@!m+V9BMpdi4! zJ0UW{RpkHw@Lu*7=EA(mYQEeY(o7x~+ynxW7p;`a4YixKZO`<_XTHwTy!raH$={W) kN@f`Llze!%d&3XuJ8A4Z+V=blfyOd;y85}Sb4q9e01nt+kpKVy literal 0 Hc$@es12b687){DM87J@e z=8$IcxZoxbki2N6RBouByV>Yu8a#`!hjsk0*}aI1_o|n z5N2eUHAey{sN?D47{YNqIiZA+k&*49i2z4Rfw%X24R3G8OOnR7H5Ms8b2!7Bl)!X5 anSnvpMKzeO;gd2@AA_f>pUXO@geCyDHbX=J literal 0 Hc$@Q zL70(Y)*K0-ppK`DV+hCfWB8HN3qUFG(8T)>x$Y%;5}gQUcTM aWCjLV7u8_ChEK{seGHzielF{r5}E*57DRIZ literal 0 Hc$@Q zL70(Y)*K0-ppK`DV+hCfWB8HN3qUFG(8T)>x$Y%;5}gQUcTM aWCjLV7u8_ChEK{seGHzielF{r5}E*57DRIZ literal 0 Hc$@H$6>uK)l4Z_keeqODWPjx26m z(3*K*LG6UEal|aXmR9go%-nEm2&+ASIx%FqEsXkm=G( r+0+E7x&Au57ugNV#hAr!%;RO4t1b02ppmNusFlIf)z4*}Q$iB}qkKt5 literal 0 Hc$@ByV>Yu8a#`!hjsk0*}aI1_o|n z5N2eUHAey{sN?D47{YNqIiZA+k&*49i2z4Rfw%X24R3G8OOnR7H5Ms8b2!7Bl)!X5 anSnvpMKzeO;gd2@AA_f>pUXO@geCyDHbX=J literal 0 Hc$@ByV>Yu8a#`!hjsk0*}aI1_o|n z5N2eUHAey{sN?D47{YNqIiZA+k&*49i2z4Rfw%X24R3G8OOnR7H5Ms8b2!7Bl)!X5 anSnvpMKzeO;gd2@AA_f>pUXO@geCyF$U`^) literal 0 Hc$@O>_%)r1c48n{Iv*t(u1=BrU978y+ zhn_z!)S$rQdeI@tapQs;(y#viUu@OakUw$e=G}}5_ZTGHtYhN^dqesxp zr>u2Lf`dw&RAgpB)I)@d{n7{Bq@er_)DHM9g|Pf1`Zelauo^{H$6>uK)l4-x6`)NZy%CZP!m$ zoL?8Rdwas6i%r*n;$QB6z0h=RWB48*`|6t}NuU;vL>4nJa0`PlBg3pY5L9->B-5iY>Ah*BqTp} zQn<^xgtv2{bIhItB~iD;1nyqEIFV_QGZ64dw)8X}h?&Tk#m{h}MXXlp+ngGpX$+pO KelF{r5}E*#h*@O- literal 0 Hc$@H$6>uK)l4KhP3!W=7uiHErh? zR_v||InH$6>uK)l4k2p}2cc!E5`n-zs zlR|dqCLF44x;6(We*f#Urfajq_Y?xzITD9|0JZRx1o;Is{6m210$xR+IAfBxy9-a_ z=D=hihqJ&VvY3H^TNs2H8D`Cq017&Jx;Tb#Tu)9AVPRxsQxq0YPfm7aOT4@#A^EYB z!d=cKyqyc3WA+>;rs4T>t<7f2zLmSXueWx`rDw zX5L%8| zAsp9p``Nh;81OL9O;mju_rE^-u@*~|mc^My$88;-Z5@xezU_3Zk!6^)@Se$Z#x^^y z9^r-ObbU9jS@Lq*dX&eBp3YQju2pNUgqg}-b+iBf|Av^zbBMoymB?XuwEg}4!OY`ec(rYW zy_TlX(%S6#`utpTvRQAj)ZFaX-R_m8(2ASNX??lt@Ajs%*M5-1)!ppH&gGu1)2q1J zp|RDNsL}iT{pIQNbB4a6vDIdKx8djVkDtwaj>2kyyK#oTLD-7+00001bW%=J06^y0 zW&i*H0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_003=CL_t&t*JEH{WP$@` z1_nkJRybf`W?*7vV`F3IVC4i-tV|#-7Z*1VFCRY_oGTzGB+LcpiinDdOGv`GQe4t9 zvT|^)yn>>VvI>H$s-~`?iQsB+X|o~8vgzou!DZRl^z`+$AkIK@B&rtx|Az<`PW_=k P00000NkvXXu0mjfJrAvr literal 0 Hc$@+Y4x(2B##X|K8K_V%XI*M7Xj)$8oV;^m&s)2r6n zq0-fu%+dS&{pIrWbG5#r(bZy25I+yK%O@JFlNT00001bW%=J06^y0 zW&i*H0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_003=CL_t&t*JEH{WP$@` z1_nkJRybf`W?*7vV`F3IVC4i-tV|#-7Z*1VFCRY_oGTzGB+LcpiinDdOGv`GQe4t9 zvT|^)yn>>VvI>H$s-~`?iQsB+X|o~8vgzou!DZRl^z`+$AkIK@B&rtx|Az<`PW_=k P00000NkvXXu0mjfV^p(P literal 0 Hc$@HvMDKTcR}f~ zj-LPj|GzwQ?qFTh!~F-ZE?x2K%a>alHs4ye@$8J*rzcK%dH%xLX|sO3d%rz9|MaA( z+q3fbm(`r>oxFSD`s+Y9FqQ=Q1vjjXi*^HXlDyqr#A+_CkOXo#3p^r=85p>QL70(Y z)*K0-V3DVbV+hA}-}5(xniY6hFEDIm)!r6*a8cOq1^@obbDi68r#%Vhv@3UY^t_c%{AyqRX=T3Fz`miWGpYt?JAT>t<7e`!JS%{6WJch0!D zpz`jP$(I&Z-`g<_DE{Q=>gy}pZf)$lxuNghpWmOK-n_P=`Ni4o-`_m?^XuESmF?eN zKm76j*_$f|e}8%Z>G9RaM^?SMeBkZ%BkyjXxw~!puaB=TEv|iZaQWRGv#u;_ys@V9 z^5R;cvEIcKDuA}DmIV0)GyEe0%(l4M1k}Kot<7e`!JS%{6WJcg{FF zqu}D)@_XB-U0P6iZ^tyC)W6@qZf@v*bN%SAukWv~>bSC`?#IVhU!UK;w6OZztfE(! z_rJe$_T21}^K(jpidW8Pp93^jq9n*KnBgCGFwgwWBcK|_ByV>Yh7ML)4J?|;jpuod?!AX1T3Lc}1|Ff-Zm)?BxH_6#Zh4TElIuV6g|rMr>?F!7^xnMnfy9q?trc9Wo}{KwZ&#XqM%p)Y1ek zQ;XBO_2MF**vf8Jn9pK1MhUoepj{Iyx{s43i1n-B2jtaA0>g`Qi9D0s*wNU|;~T z7&<%A)P&w%w6~+70d_m|dUSO`CWAz>AQp?snyIhYtQz9*&moCB3HuHir97!9qZ7JQ z*UHJ|Ono`Gks!`yB$+}uhO@}f&!m@dvUaMr*5#@Kf)U*d7JW=BeUWopXw9OZJdo91 z4MYFqz@U^hr~b_@9wlgo;xG`5PLU($^y8V(dnL^`qkkjt)CaCqeM#^%i;68t zQOq1aN8#A~-t_10ys^0XcV_SPDS7T9?cY&0vuEO{wXo7s^40hBUWH#MDa8N) literal 0 Hc$@t<7f2pJR=JdAv%V(Tz zEV$TKesAftOI?-smQ4dn{rmOn=G^`_XOI5+^#1y^jw^k2KiRMXyfm ze}Codxz>{NZKXiPrN$E#fyPRd1o;Is{KF3BnZJ1iRKu9$?e4vL>4nJ za0`PlBg3pY5VP@V^r~fww3MDn@|46G9`pC^c=On z*5If#OXgGPXXlUH%Ilqe1Z@)#oqWDpGG_IbbvI^*9Nm;YpUtw(SEMlNkGqmvc}!_> g&z!fmQSlaxM>nvw%#477{E)78&qol`;+0RKCY<^TWy literal 0 Hc$@t<7KR3O3ZOxQDlUHnR zo4clB;eCs_D>UUbFC|FL`S8t0O0uYG>X3H0c!{5$_3$G&yvW}V5k_d9n!`M%w; zf=b0dqDoJCqHX(kNvbS6(ILUOOUre-{vU>$KiDtYGjpf_9mC-1>gTe~DWM4f!ydjT literal 0 Hc$@t<7Keso1?fjZO8zygF z*0yF|#kQ553n!#5o}9IFZST%?{R{fjP9IwF?c2xqZ=Nojo_BEjY@n7SyXMX7OPdt^-o{w(Uv0 zpt`9}AgF6$`UDHV&x@`Y`9F5gUE^GG?X}NOIe{LXm4D|St<7pFV4D&8oGNckOB0 zu(@Kzn$FGJQs*wrn!mVr+s^*2JJaSaSaIU?$1mTWzI&gyaM|pA2Z37V9Xygeb6))P zIai-NdHwlw+Uy1QUcE}1HLq^XhQh@wI<{>4`TO_ga~FEH?wGV|PxkyJxeJy8b={g% z$_=zvxg^LhnBgBmaCUcu7En23lDE4HLkFv@2av;A;1OBOz`!jG!i)^F=12eq8$Ded zLpZMco_A+zQQ&b1G!Wd_(3Bm_EV6@AIN7M*ff8bJsYRTzl>FQ%;~qXXW4d2RZhwJ2&f0uD##6^U3$^ zjuliY{t;Dr(i3glze`eO*@+Gb#$8&j)Aj!_-2B0Q(Vm$@1?U(CPgg&ebxsLQ045N? AF#rGn literal 0 Hc$@t<7e`!JS%{6WJch0!D zpz_9=?n{enuPkf4w{7b0FYke}zdpWta&+~B{fqzn`gVW!{P%axURqfF@6YdVuOB`? zz4^+rrdu0&udQgly?Nrj9n)@a?gwf)ULElQXufnwkY6yvKb)YzurnU0j4{dE-G!lp zRn`N@;VkfoEM{Qf76xHPhFNnYfPzt;E{-7_*K^N%@--N6I0R~3@tXU}=Fa~GVY3yu z|G!gJVPKfWaAD#zfe9UZzl*ldS`m;mWf`y6$5{P86SX(W1oD=Lz}ywck`dA~xg2N9l)%EH!Na7etA)eFrLMA#_V@Gu|Nr#$ z@6OS>v$m6`s)fPCq^z)wxV)Xn%d`9Y`KPIbtFMf%vXG~$gsZTN#mKJWs;-Fe z@#($4ovE#dq^E$evyZ#Jo1>L;#2d9Y_EG z010qNS#tmY3ljhU3ljkVnw%H_005OqL_t&t*G<8R5`kbC1<*^h_>`L^OW7++KT@Q4 z|My~=VVnar{s2v_^`kY>)^)=)P0Q$r-fhEnoE}}5{lU<2Y>Yf_OirdVjy-P#UnC%d zIXM^cu@G63tF^n?iYyRDQOq6p2kRI`ArhrxGLBDYrBs4TnqJ9kTDu_2^V|J?%WE03 nqIi51wG3rdd_AiYc>Tc}5)u~F5hX%H00000NkvXXu0mjf-PztR literal 0 Hc$@9l)%EH!Na7etA)eFrLMA#_V@Gu|Nr#$ z@6OS>v$m6`s)fPCq^z)wxV)Xn%d`9Y`KPIbtFMf%vXG~$gsZTN#mKJWs;-Fe z@#($4ovE#dq^E$evyZ#Jo1>L;#2d9Y_EG z010qNS#tmY3ljhU3ljkVnw%H_004YRL_t&t9Yw)Q4goL9l)%EH!Na7etA)eFrLMA#_V@Gu|Nr#$ z@6OS>v$m6`s)fPCq^z)wxV)Xn%d`9Y`KPIbtFMf%vXG~$gsZTN#mKJWs;-Fe z@#($4ovE#dq^E$evyZ#Jo1>unH zl%LF~qTs7~Z`bIel0_>$x_Hg5m$8$jP167o7uqzns8AtvE>t(-d3joST;EA|ayzqN z^aU9q1I!W%WK?lWCLbA81d@+OG@r>=WRX38hc}zDB=enbNsa&j002ovPDHLkV1g|x B%~AjW literal 0 Hc$@t<7e`!JS%{6WJch0!D zpz_9=?n{enuPkf4w{7b0FYke}zdpWta&+~B{fqzn`gVW!{P%axURqfF@6YdVuOB`? zz4^+rrdu0&udQgly?Nrj9n)@a?gwf)ULElQXufnwkY6yvKb)YzurnU0j4{dE-G!lp zRn`N@;VkfoEM{Qf76xHPhFNnYfPzt;E{-7_*K^N%@--N6I0R~3@tXU}=Fa~GVY3yu z|G!gJVPKfWaAD#zfe9UZzl*ldS`m;mWf`y6$5{P86SX(W1oD=Lz}ywck`dA~xg2Nt<7Kdm8q$%ML%3nxu) zD_uIZeRfynyq=nMbNfDhcng&M@aEON%`0}UUhwn#x2-E?-@J5sW=Hv--@jixeQjC6&7I;J!Gca%qgD@k*tT_@u!6;7`#}JO|x#vCk8Voob0yVC9&3$Ea=l_DR*$UkM z->IrFFwA1OF!7ndgpR%6McZes2uPZ;jMwX9tp1;g+8bqpd=?i?s*E%@KJ(kLE%l>* k@%nQ<<(cm+Z|y5!?h0hd2t<7Kdm8q$%ML%3nxu) zD_uIZeRfynyq=nMbNfDhcng&M@aEON%`0}UUhwn#x2-E?-@J5sW=Hv--@jixeQjC6&7I;J!Gca%qgD@k*tT_@u!6;7`#}JO|x#vCk8Voob0yVC9&3$Ea=l_DR*$UkM z->IrFFwA1OF!7ndgpR%6McZes2uPZ;jMwX9tp1;g+8bqpd=?i?s*E%@KJ(kLE%l>* k@%nQ<<(cm+Z|y5!?h0hd2t<7A6~!PyL5Zil;fTA z&V^*J3(Z*5uc~ASCGeFs2zyIEO^r2?L;jD(;SMR(@uGrQz<@oR4f1kbl9Ff1_ z;>}l)1slV1*T4Pn{mZvsw;#T*>OHje;N6XT?<7@j`|#^#nLW|5GVl8}^`$jW3^#^)y{ zre-j`ySsaK`*i+uPsIg9gv1()U5_47RXO@FMl(}VFVfN?JkBxDGm>>-M5u3UGwZ?7 x<_MmrS&IrHBey2Rh`47N8)PW&S#Xwtq3r|PZoW4q3xW1Bc)I$ztaD0e0sx~bnScNQ literal 0 Hc$@GphO?ZCwX2%JzOc~$|Ns2_{p;rNt(?HFp2Nku)T^7o^zruG)8wO) zy1cH=z_ZZe-s;!T;hBfDoQSlVh_suDw9CNQ#JSV`{r$tY(x;fd_V)SC#M}1t_|(hZ zoQt%sp2E|~-nOU6u%X1Spu?Gmv#_DXp^&+?rO2a^zAB~6BLDyZ0d!JMQvg8b*k%9# z00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_0001aNkl<39`xuSQ;{KcYd&NA4YM4 zc(6<`p8%*W3M3G1+eYY+n3sEz=_TbleZ4Wq{v~>A!hRB27IFS4S!UwY;F< Y2iwU9;UXaosQ>@~07*qoM6N<$f+zU8_W%F@ literal 0 Hc$@9l)%EH!Na7etA)eFrLMA#_V@Gu|Nr#$ z@6OS>v$m6`s)fPCq^z)wxV)Xn%d`9Y`KPIbtFMf%vXG~$gsZTN#mKJWs;-Fe z@#($4ovE#dq^E$evyZ#Jo1>L;#2d9Y_EG z010qNS#tmY3ljhU3ljkVnw%H_005OqL_t&t*G<8R5`kbC1<*^h_>`L^OW7++KT@Q4 z|My~=VVnar{s2v_^`kY>)^)=)P0Q$r-fhEnoE}}5{lU<2Y>Yf_OirdVjy-P#UnC%d zIXM^cu@G63tF^n?iYyRDQOq6p2kRI`ArhrxGLBDYrBs4TnqJ9kTDu_2^V|J?%WE03 nqIi51wG3rdd_AiYc>Tc}5)u~F5hX%H00000NkvXXu0mjf-PztR literal 0 Hc$@t<7Kdm8q$%ML%3nxu) zD_uIZeRfynyq=nMbNfDhcng&M@aEON%`0}UUhwn#x2-E?-@J5sW=Hv--@jixeQjC6&7I;J!Gca%qgD@k*tT_@u!6;7`#}JO|x#vCk8Voob0yVC9&3$Ea=l_DR*$UkM z->IrFFwA1OF!7ndgpR%6McZes2uPZ;jMwX9tp1;g+8bqpd=?i?s*E%@KJ(kLE%l>* k@%nQ<<(cm+Z|y5!?h0hd2CiAet3F(h>DGUe}bBvpOu!E;^N}(@9){# z+I@b3o1LJDiHwkulk)QN(9qG0jE{SLf0>(}d3t@z&CU1s_lAgxhKP%Me1DjlopW_} zd3$}9n4EHSc6WJvl$M%xcY4{`*@T6MgM^36%guLqdy|!!frEzo`}@nw%Y%i6h>42N z&(L*ud6AQrkCBvgc6ptipW)%*t*)+aaB_EfduVBEbar@-j*@zNer|7afr5mFhl#<# z!qL&wcX)bqb$Ee;g@%WUadLHge13L!dVYU{a&vZVZgFX9ZU6uO5MX!e00002bW%=J zfPkQ=)>8lg00Cl4M?_P4XB`Fr000SaNLh0L01FcU01FcV0GgZ_00026Nklc^@V_2U-w(V1i1!%i2#)8x00000NkvXXu0mjfawr8W literal 0 Hc$@Pke zj^^5t!F{HLcS*Y5|Ns9#zkPXNcKzEY_ck@h{QUN1SuEH5NY3NS`>*U>vn7@L;l*QX z6S+a8u#Li73AB#2B*-t=^VxH+C#eTgmjMMBlf2zs zc)x8){0ih`c)B=-a9j`Vzsc92z{AqbRk1MQpzzfH|Gm2T8-8C%F}j(;eV~GeIl<`| z2V2Z3s~O8wpS8rU*EoE`?Mls|g`O7DE|ViyPcwa=_j_Vw@AA{T)L;Gcs)+vjgFo}w ln-@P4>Xo@Qy1zJu?)zBHzB1*AUOmuG22WQ%mvv4FO#l+sf5HF& literal 0 Hc$@Pke zj^^5t!F{HLcS*Y5|Ns9#zkPXNcKzEY_ck@h{QUN1SuEH5NY3NS`>*U>vn7@L;l*QX z6S+a8u#Li73AB#2B*-t=^VxH+C#eTgmjMMBlf2zs zc)x8){0ih`c)B=-a9j`Vzsc92z{AqbRk1MQpzzfH|Gm2T8-8C%F}j(;eV~GeIl<`| z2V2Z3s~O8wpS8rU*EoE`?Mls|g`O7DE|ViyPcwa=_j_Vw@AA{T)L;Gcs)+vjgFo}w ln-@P4>Xo@Qy1zJu?)zBHzB1*AUOmuG22WQ%mvv4FO#l+sf5HF& literal 0 Hc$@Pke zj^^5t!F{HLcS*Y5|Ns9#zkPXNcKzEY_ck@h{QUN1SuEH5NY3NS`>*U>vn7@L;l*QX z6S+a8u#Li73AB#2B*-t=^VxH+C#eTgmjMMBlf2zs zc)x8){0ih`c)B=-a9j`Vzsc92z{AqbRk1MQpzzfH|Gm2T8-8C%F}j(;eV~GeIl<`| z2V2Z3s~O8wpS8rU*EoE`?Mls|g`O7DE|ViyPcwa=_j_Vw@AA{T)L;Gcs)+vjgFo}w ln-@P4>Xo@Qy1zJu?)zBHzB1*AUOmuG22WQ%mvv4FO#l+sf5HF& literal 0 Hc$@Pke zj^^5t!F{HLcS*Y5|Ns9#zkPXNcKzEY_ck@h{QUN1SuEH5NY3NS`>*U>vn7@L;l*QX z6S+a8u#Li73AB#2B*-t=^VxH+C#eTgmjMMBlf2zs zc)x8){0ih`c)B=-a9j`Vzsc92z{AqbRk1MQpzzfH|Gm2T8-8C%F}j(;eV~GeIl<`| z2V2Z3s~O8wpS8rU*EoE`?Mls|g`O7DE|ViyPcwa=_j_Vw@AA{T)L;Gcs)+vjgFo}w ln-@P4>Xo@Qy1zJu?)zBHzB1*AUOmuG22WQ%mvv4FO#l+sf5HF& literal 0 Hc$@Pke zj^^5t!F{HLcS*Y5|Ns9#zkPXNcKzEY_ck@h{QUN1SuEH5NY3NS`>*U>vn7@L;l*QX z6S+a8u#Li73AB#2B*-t=^VxH+C#eTgmjMMBlf2zs zc)x8){0ih`c)B=-a9j`Vzsc92z{AqbRk1MQpzzfH|Gm2T8-8C%F}j(;eV~GeIl<`| z2V2Z3s~O8wpS8rU*EoE`?Mls|g`O7DE|ViyPcwa=_j_Vw@AA{T)L;Gcs)+vjgFo}w ln-@P4>Xo@Qy1zJu?)zBHzB1*AUOmuG22WQ%mvv4FO#l+sf5HF& literal 0 Hc$@nzmQ-i}zdbmq=x!~a7WSZVzmEB&H++2~^Sc}h7gv9Uf@893wYMDUXs~bjnMe``0()XZ=~jFpX6wsTRRwZKCL7m*&Ua z_kytNUzFWlj?q_##!!5?cdhPnsO@p5>}{m%W1Z@9pzO)s_*s_cVwm4wk;otu}J z%Gy{^2qa)*Yinok;HU)z!Xh96Cue6DS2uSLPcLs#F_3_dub+QFU{G*KXqdPJNFY2S zGAcSIHZDFPQBn#dkd&N~nwFlCmYJ0;Edvse&B;y6%P%NQD=IDl3&@q0l~+_&RoB$k z`G5uL8ycIMTUy)N8#_9|0$trby?y-?CQO_(c?wuy>a=Oorca+SbLOnsb3g)ObEgBr n46wjFd7yxTq7ob^Dl-58e4{|Z<(R^T00000NkvXXu0mjfmyflu literal 0 Hc$@t<7U)i~;Z}yHwYmUxc zd1&U6{R>tfX`8&MWy1QdXS52C?bLqOHZ{B_W`t|3pUwpG@vUb*Qc(5U;5U(Z~+ueNi|>}3Zg&E0wF`r~!mPS06!aQdP>t2UjS zxpe>31$+Bv@93PmrFGJ#iF0=Xwfr@*Ndr1Yt0c%TnBgC>K)~a8JWv;7lDE4HLkFv@ z2av;A;1OBOz`!jG!i)^F=12eqn><|{LpZMcp1;X=NI`%#AR$Halu}da>Qd*`|Nl2H zYFzf&$nxY_ot6u(f=-eQYb>5GHT2SGn7}1i%Kl-w$+gJ*Yg4Uq43=GyXuf}}&byPIDt6DQC$44$rjF6*2UngHJo+S~vD literal 0 Hc$@t<7f3Z5~=ER(b%iHeH zt-m|B>fz$%tKG>LyArSVXPj>hy)mu$-pcMDZ=e17@#*jHA2+5Ke0%=z$ESCfIwG!5 z%fG*}^Kxh8)xOk6Yx{vleYt=2_RQ*w?csOl)!vv<{Nc)xN1LbLn^SjlR^_9O6YtKd zx;v-z=9Ii^{b|>_6K_r}1Zw%i<~9}R7_E{ZzhH)c!~y}2{$}w1WMWXrsu{!hi@9zq1 zc%OSMJl6gF`o5r2i#vO+=3bE~ElOOrHFfgD-imq^Rn`utY4g{=w5=^V_on>Ir&j%j XsqAiktxTLi*D!dx`njxgN@xNA(dXIo literal 0 Hc$@~glp(S(^Trcqiz=T4*8X5sIPR&Tg; zEUPTxAz#j#DI&`XK1`O_C&uZ>{?T;3#fKf=RpZw!wtsHqyY7(_XO-=Y#$&T}yUTm{xH-)4WQh_Prb`)A)hn_oNVdW3c`n?2K-%m3b}HKx7c d$^PT@%ugcBodv{mt<7U)i~;Z}yHwYmUxc zd1&U6{R>tfX`8&MWy1QdXS52C?bLqOHZ{B_W`t|3pUwpG@vUb*Qc(5U;5U(Z~+ueNi|>}3Zg&E0wF`r~!mPS06!aQdP>t2UjS zxpe>31$+Bv@93PmrFGJ#iF0=Xwfr@*Ndr1Yt0c%TnBgC>K)~a8JWv;7lDE4HLkFv@ z2av;A;1OBOz`!jG!i)^F=12eqn><|{LpZMcp1;X=NI`%#AR$Halu}da>Qd*`|Nl2H zYFzf&$nxY_ot6u(f=-eQYb>5GHT2SGn7}1i%Kl-w$+gJ*Yg4Uq43=GyXuf}}&byPIDt6DQC$44$rjF6*2UngHJo+S~vD literal 0 Hc$@t<7e{p%x&Fwi4kGI|5 zUw`*N)x#srS2rhL+?aTEYsUEvp*MCG-#ga* zTXpw9>CK&a*S4iy+md*5XCY7vJ7;?t&@oyiL4Lsu|A++w9>?Q>x)_tZ-CYEal|aozX)O};}40;~ZEDUzp@no3ufI1AC(r7%TyPb1l4Mw8@qDSFmqx<`F2Pdv56exiMdn|dYL#QK?21J5{bP0J?cd)O z*zi90T6nDc`}KW6r51PgT+O{AQCgI^Y-{S|iM&J z5YO`N=EXmM{@%Iy#Q*>QGvnLigPS7#YJ8pY=S>+`A^kv+}`l$8exy7)yfuf*aPxMZ1AGN#5=*Ql;rnjDQ@@0*}aI1_o|n z5N2eUHAey{*yQQr7{YPg_k6u@lL8N0Lc+u*j*ft3)BXk+?)%T)WA*Ze{qFSlZ}Xfv z(pv@87z!4Ak&r&f!7QTsc#lxwhh*KUc_z!PTQe;tzd0Wk{V?Xy%R8mhSGCP;-MlRP z@Qh0zt7iYIo>{iD;zrNf$}H18LQiet<7U)i~;Z}yHwYmUxc zd1&U6{R>tfX`8&MWy1QdXS52C?bLqOHZ{B_W`t|3pUwpG@vUb*Qc(5U;5U(Z~+ueNi|>}3Zg&E0wF`r~!mPS06!aQdP>t2UjS zxpe>31$+Bv@93PmrFGJ#iF0=Xwfr@*Ndr1Yt0c%TnBgC>K)~a8JWv;7lDE4HLkFv@ z2av;A;1OBOz`!jG!i)^F=12eqn><|{LpZMcp1;X=NI`%#AR$Halu}da>Qd*`|Nl2H zYFzf&$nxY_ot6u(f=-eQYb>5GHT2SGn7}1i%Kl-w$+gJ*Yg4Uq43=GyXuf}}&byPIDt6DQC$44$rjF6*2UngHJo+S~vD literal 0 Hc$@m9R$i&6RwYa&pxVf~qxwN*p#Ky?P#mCCa&DGb~!^FnZ)z{hD+|bd}%gxWh!^P6n z)z{hE&d}1y%gx~7;^*k;#mC9w;^e@=#L&{z*4Wy}%FX5F=Kufy_V)I_!Nkwe)7945 z!^Ozz>g)OW`Nzr3zQDuB$jidS$L#Fw%+1ifzroGV(ZLm@4~{x(bLtv zzQD}Q&&9^b!NJ75y}!%M&cMOL>+I~w%FMdFzt7Op>FMgh!o|3|zR1eV;^XA>^!2^J z!nnG;#Kp+N#mBt8z_z%$#m33X%g(~Y#=gM9y1c%$wzxFkK+pgH00?waPE%m-_V4fZ zVDFK(M63V+00Cl4M?@b54G_Hm007lVL_t&-(@oFW8i7y%Md59(P$DyBsNuv!dCLj#3Trz-*AeH8MA@e8jd^!a&o0IZ|VyRRtXQW(KM5S7*%SwY` z8j4)6RV!$=+Nwr6s@fr%+HN&LCEe%}!!!-jHFSxhXZ3AoFdU7B1IO-LJ&Y$)+tFr% zF#Cn+WQ_TOb(h3ltz5En*~J`dZbP?ZN9m4i>4sZlf3QSPIi5~O#S^W=9%tRWTs`@A zlfCQ3)X%s-eE<3Sd;0$4j_5mh`;H<02};Dj$Eha37d3#&00000NkvXXu0mjfCb~^o literal 0 Hc$@5$ Xp5orCLT|VN&1CR&^>bP0l+XkK(H~%| literal 0 Hc$@t<7U)i~;Z}yHwYmUxc zd1&U6{R>tfX`8&MWy1QdXS52C?bLqOHZ{B_W`t|3pUwpG@vUb*Qc(5U;5U(Z~+ueNi|>}3Zg&E0wF`r~!mPS06!aQdP>t2UjS zxpe>31$+Bv@93PmrFGJ#iF0=Xwfr@*Ndr1Yt0c%TnBgC>K)~a8JWv;7lDE4HLkFv@ z2av;A;1OBOz`!jG!i)^F=12eqn><|{LpZMcp1;X=NI`%#AR$Halu}da>Qd*`|Nl2H zYFzf&$nxY_ot6u(f=-eQYb>5GHT2SGn7}1i%Kl-w$+gJ*Yg4Uq43=GyXuf}}&byPIDt6DQC$44$rjF6*2UngHJo+S~vD literal 0 Hc$@t<7KVwzpiXDABjxO4A zXzqsnGq)XCuy9k`y!9=Mw{%TgRlREGq#Z|>e)#<6`_Hex|NL69W5U}HFTQ;JICE{? z^6mXQjxL$Cu71(xjy)$=0*!k9`u>J}GiR)+-FRU3%AJ$$JifI5^t#Ol=d9f`eb32N z8~4v#yLak}9sP^9bS~V~x@zY{pqA6~UoHbWMyn*qFPPyUu|UA%csx)SW0JSK3quF1 ztOt<8S>O>_%)r1c48n{Iv*t(u1)Drw978y+`<}nacSu2iH6S5H@|03j>FQGF)&KuD zFKS%&*~s$bS)G;(u7XaI3~MZ&FE#YiXqdnySjzrkxyiN2{A*LKatxMTk!Zeutj@gs z`?~@g-sfHmk9B{)zAvcM;?AC{xmP4gixQV@O`SZkx1wG}m9@iZ+Whq|ZEK6py($0l asa3yWD!ZFsD-$QsH4L7velF{r5}E)Es@<~y literal 0 Hc$@t<7e{p%x&Fwi4kGI|5 zUw`*N)x#srS2rhL+?aTEYsUEvp*MCG-#ga* zTXpw9>CK&a*S4iy+md*5XCY7vJ7;?t&@oyiL4Lsu|A++w9>?Q>x)_tZ-CYEal|aozX)O};}40;~ZEDUzp@no3ufI1AC(r7%TyPb1l4Mw8@qDSFmqx<`F2Pdv56exiMdn|dYL#QK?21J5{bP0J?cd)O z*zi90T6nDc`}KW6r51PgT+O{AQCgI^Y-{S|iMt<7e{p%x&Fwi4kGI|5 zUw`*N)x#srS2rhL+?aTEYsUEvp*MCG-#ga* zTXpw9>CK&a*S4iy+md*5XCY7vJ7;?t&@oyiL4Lsu|A++w9>?Q>x)_tZ-CYEal|aozX)O};}40;~ZEDUzp@no3ufI1AC(r7%TyPb1l4Mw8@qDSFmqx<`F2Pdv56exiMdn|dYL#QK?21J5{bP0J?cd)O z*zi90T6nDc`}KW6r51PgT+O{AQCgI^Y-{S|iMt<7f3Z5~=ER(b%iHeH zt-m|B>fz$%tKG>LyArSVXPj>hy)mu$-pcMDZ=e17@#*jHA2+5Ke0%=z$ESCfIwG!5 z%fG*}^Kxh8)xOk6Yx{vleYt=2_RQ*w?csOl)!vv<{Nc)xN1LbLn^SjlR^_9O6YtKd zx;v-z=9Ii^{b|>_6K_r}1Zw%i<~9}R7_E{ZzhH)c!~y}2{$}w1WMWXrsu{!hi@9zq1 zc%OSMJl6gF`o5r2i#vO+=3bE~ElOOrHFfgD-imq^Rn`utY4g{=w5=^V_on>Ir&j%j XsqAiktxTLi*D!dx`njxgN@xNA(dXIo literal 0 Hc$@w%(-N#5=*Tp1U>gaJ96 z1s;*b3=G`DAk4@xYmNj^(8bfmF@)oKa)Jm8BO{w)vOqwJKpA7B16$(uEnBvTII|^w z|J3(MNWr$)tNpo;LT=8bhdDkXv3!eKdvw$N%tBKdZZR_mz7gjs`Z-w}XdZ*7tDnm{ Hr-UW|YO7Oh literal 0 Hc$@t<7f9rJDjpOwHPKU$%-Tp9(TVGBt=%T&T)O6;^YfCs e%JGkT_UP9fzRObk?~7D91IScYKbLh*2~7aQDrU(5 literal 0 Hc$@w%(-N#5=*Tp1U>gaJ96 z1s;*b3=G`DAk4@xYmNj^(8bfmF@)oKa)Jm8BO{w)vOqwJKpA7B16$(uEnBvTII|^w z|J3(MNWr$)tNpo;LT=8bhdDkXv3!eKdvw$N%tBKdZZR_mz7gjs`Z-w}XdZ*7tDnm{ Hr-UW|YO7Oh literal 0 Hc$@t<7f9rJDjpOwHPKU$%-Tp9(TVGBt=%T&T)O6;^YfCs e%JGkT_UP9fzRObk?~7D91IScYKbLh*2~7aQDrU(5 literal 0 Hc$@w%(-N#5=*Tp1U>gaJ96 z1s;*b3=G`DAk4@xYmNj^(8bfmF@)oKa)Jm8BO{w)vOqwJKpA7B16$(uEnBvTII|^w z|J3(MNWr$)tNpo;LT=8bhdDkXv3!eKdvw$N%tBKdZZR_mz7gjs`Z-w}XdZ*7tDnm{ Hr-UW|YO7Oh literal 0 Hc$@t<7f9rJDjpOwHPKU$%-Tp9(TVGBt=%T&T)O6;^YfCs e%JGkT_UP9fzRObk?~7D91IScYKbLh*2~7aQDrU(5 literal 0 Hc$@t<7e`|HujV1LrS9d(w zx8UWmRo55S0R?Zb?)>%b$%-Tp9(TVGBt=%T&T)O6;^YfCs e%JGkT_UP9fzRObk?~7D91IScYKbLh*2~7aoEoFWH literal 0 Hc$@H5uy$3h8_8eWhYWK7`yQj=PFni(p=C03g-aa^g z>FdW&8{2xHUcdGF{-Y!Fmu>5ryt=M+|BU$o58vGeTESQnpR=^O^}x zH*UL5-k!}EA_X)|$8qO2> pki6@i%^?+^nSU}Lrk2z{WL>e8wN&Vr_BWtS44$rjF6*2UngE_2fPer1 literal 0 Hc$@|`M&CNht5*bSoRcY#JJxF;VI=bY{i>xE8xv3S-L2*BQ&#tCQ`A_q(t<7zo0Q`^`x@x3%fVW zXj;&gw{3ps`sod8r`9a)EWCg13{b(=1-)yh)-LZWJF{=Y(>qreb`-3cQnP!-)H|1s zZ=Kh%q^s!To99P1FIw4O@$37ym6NJA&T2ljYxSd>7uHT~c>VO=w=W-8O{%(hWZSad zvOm9nT|2pNX?O9L4{w(BlmZRU>Tz%bI!3D`$S;`TAF)8d<9Iw!7h{sQy9+}HtE>l* z!&%@FSbriMdE?w=iE|7teA#?ov?KprLfAj=W0$7ioA-fn?+msp Unys77fi7Y2boFyt=akR{0PN7$g#Z8m literal 0 Hc$@Ylq6Ky3Vk_mRx$Oc9F}iy|#y+_3`thaEhpOkL%y%_we+LPHv)Xh^cy$ z@#yS|O>CWIgT0ojtbUf%xyHnzvYBCiuYH!2T6g{Z{o2CMjZtv>`TLw_gRFRykW_Tu z$<)1=tLfe4qHT$=gq!{Q{k@f`on?Zwgq@5~Z~6E6m0Ww3TX~I8Z;n!Nuzi=MaQ68C z0004WQchCmZf0HH($9dQ}_iYCuOwwowVVzQH0I*N# z8X`QC&6ymme8J`f5pasawZJKrDPEb zd9+xreuVWV-0pmqaDNcT(-{(8t~YTHQo`dIy+9GivKf;%mQVjrcmurH4C;EyDCGbE N002ovPDHLkV1jx9-*Nx| literal 0 Hc$@t<7zo0Q`^`x@x3%fVW zXj;&gw{3ps`sod8r`9a)EWCg13{b(=1-)yh)-LZWJF{=Y(>qreb`-3cQnP!-)H|1s zZ=Kh%q^s!To99P1FIw4O@$37ym6NJA&T2ljYxSd>7uHT~c>VO=w=W-8O{%(hWZSad zvOm9nT|2pNX?O9L4{w(BlmZRU>Tz%bI!3D`$S;`TAF)8d<9Iw!7h{sQy9+}HtE>l* z!&%@FSbriMdE?w=iE|7teA#?ov?KprLfAj=W0$7ioA-fn?+msp Unys77fi7Y2boFyt=akR{0PN7$g#Z8m literal 0 Hc$@Gmbgn@h+}h`YI>edV2gHysa9u@W_O!PTZmq6 zm#ekP|NsAMd!Vbc%CNZ3Tx^zhg{rN#%ZHS<@$&U>ex!7RsNdr2@bdRgUyN*fqI!s_ z=<4xJUW#RQotB}!ev7QRz|^9u#9?uneTuJIYLor_{ldxGQDTkx`}=2ioOp+mZf0HH($9dQ}_iYCuOwwowVVzQH0I*N# z8X`QC&6ymme8J`f5pasawZJKrDPEb zd9+xreuVWV-0pmqaDNcT(-{(8t~YTHQo`dIy+9GivKf;%mQVjrcmurH4C;EyDCGbE N002ovPDHLkV1i$*+!O!+ literal 0 Hc$@^)T0RMo1pjv>{sKU5Ki;*^GsA-G!*6Z%G z+v%3h=!wbUZMxZAuaqxkl|*j%o`S10}Se%g@TPFYj zjx=K2sm81>bb1X%mqKgDdZo#Psev3%kT_$qP=>v1mw*^cg(+2{H*csqb$bd#c^OBX zIclFaYIOxaauGnFNpp}JRc!!1aS1|m4Lyw>RGlASe-lQP0043eL5>tqp#WgB9CM`; zV}J%c{{R4Z3PQgXe9Zuf*AtQANU7d8pV1qPfC@^00Dyc8N1P2^tP*U34NXk|B90SS zvnhJUIF89Tk+>&*krG+}008#Gb%wa z?e_D+%`js{{H&u^!Lr_^M=s$cFpmO(ddT8(oCSQCUnk* ztnkz2^w#P0&*$>8-1wZ@_i)Vceah#4!p%LF$5xn~A7Z|0meG8!?8MyfzTM<&yYQ0F z^qbb@T(#Fkp}!k}$77qA09LJ1fWB3c+LOK2ZmiT#qS!;C-D9rde!IjMgt{Pp!(o_{ z09BqoZM$EQzEF?BM2*5bio`jK!6AXYA%5A9xyo~+q6K7>C|!&sRciR6b*jBCez5>*uNrNo7G#YYQ-Tac%WI(Tzuebnsm2F^#vg*f zB7c(;Rh|}HvmbKD7=W%5Y0n^t?K!FXO1%G2!~8+H;U${?|NpnH&ocl30A6%bPE!B@ z0Umj~+S3Ntr>VO0~MCwywURv8lO5tyP^tqfN8DqqD2Kr?;bje1*|UV+NB|rmNSiUAKP2#!Z{Im~Ay@ zu-Im~eaFsSyZ7wfxBr0EL2CvZ+e3B}4<9*t?D&b3_NN>i85o>SJG-1Yd+z*&i>_{$ z+!+`=7(6du@$&ZZ_4B`aEr1~qo;!Rx_u`k6zo?<4~DSth{&kum{^8DMg{Xd9!7HvvrKTWPY<@d$Mqcxng{>Lv*h8`1|ef_g{IiYJ#@%^!fk) z|9gFxDIcCg>%@caAyovqMlg0{EA+>fEj!OP)=nZ#>_xN?lWt-9CM-0O6XzQxbu zyvN_h(By=k$(gFo;N|geiMx1`!GD>?W{tmsoX3Ei$ax;Rd;kCd0d!JMQ-G~rTSfo? z00Cl4M??~Pylemf000SaNLh0L01FcU01FcV0GgZ_0001)Nkl761#EbD~9M0@{U|S^q=`%V)XWfANm~%+vx6qZvX%Q07*qoM6N<$ Eg8a3@?f?J) literal 0 Hc$@n{UR(*k@#Tvyaz`v3obZTwtZs54{r zlG?LCW%DXew5P3HT6dv2c}Zj9!WqT;mQUUV)VpEcfvJVNFP*%7^U}k)6-PUAmrSf$ z-J7-Hz~-|j_Fd`9T=U`W=O5pG)+H>MoWJAI?dNmK4lk@a)t|d%$I9a`pS-Dwn>VFk z=e+jKvq}!OrLO48ShKk1%1cNfP0 zOuF}g9L@rd$YKTtZeb8+WSBKa0;qu5)5S4F<9zL;*FlF21l*W21?`k3o>tm6)q82u z-bcUp&yhZxH#76Y@9A?+zB&0$%6N;U(8)Ii37-VlrA(3TUb)-%h;O2An@Vz*H1mtX zXN>ljl?xc(KU;X|km^Gr?M0^VCSOQ){=zW7iPh^-h=$My!RT9pxf|a*Z$HquiuYn{UR(*k@#Tytxl{{R0!Fm^RA6q&Xw zyXrnrSw_XJ@RS`nwU0uRHV4P8PbxUyJ?St|@ANq*67$X+J#qj1r6=iS*Aufhan z-}~oCpUs<@`Qi8UIVazod?#hRMN;VGn}UQ-g6mSINO!N??R&&G(YH+{xl5Y)Md33> z`^(A&jPIW(|40EBs+g$nBTaxSe_FED7ZxuR{ZXpyC0pa>%6?|&I<3-ze%aj&scXHs{A;48FLa(e>5%xAC7oVO zJJ+t4?eSuAj-GAtIBW__+qP|jtAbh7Fk)8T=_fsB_NqSM9T_GV$@RHX81nyX3-=XU+1umvpS7PwZX*3?l|lS3j3^ HP6n{UR(*k@#Tytxl{{R0!Fm^RA6q&Xw zyXrnrSw_XJ@RS`nwU0uRHV4P8PbxUyJ?St|@ANq*67$X+J#qj1r6=iS*Aufhan z-}~oCpUs<@`Qi8UIVazod?#hRMN;VGn}UQ-g6mSINO!N??R&&G(YH+{xl5Y)Md33> z`^(A&jPIW(|40EBs+g$nBTaxSe_FED7ZxuR{ZXpyC0pa>%6?|&I<3-ze%aj&scXHs{A;48FLa(e>5%xAC7oVO zJJ+t4?eSuAj-GAtIBW__+qP|jtAbh7Fk)8T=_fsB_NqSM9T_GV$@RHX81nyX3-=XU+1umvpS7PwZX*3?l|lS3j3^ HP6n{UR(*k@#T%YEy`v3p`>c9qEXjf$S z{p{sHWw$bx>OC=i{h7q+_m5tBa{ku!^o1J|ySC*| zIS}1{dEJ4VJ5TM7>iPQS1cNfP0 zOuF}g9L@rd$YKTtZeb8+WSBKa0;qu5)5S4F<9zL;*FlF21l*W21?`k3o>tm6)q82u z-bcUp&yhZxH#76Y@9A?+zB&0$%6N;U(8)Ii37-VlrA(3TUb)-%h;O2An@Vz*H1mtX zXN>ljl?xc(KU;X|km^Gr?M0^VCSOQ){=zW7iPh^-h=$My!RT9pxf|a*Z$HquiuYn{UR(*k@#Tytxl{{R0!Fm^RA6q&Xw zyXrnrSw_XJ@RS`nwU0uRHV4P8PbxUyJ?St|@ANq*67$X+J#qj1r6=iS*Aufhan z-}~oCpUs<@`Qi8UIVazod?#hRMN;VGn}UQ-g6mSINO!N??R&&G(YH+{xl5Y)Md33> z`^(A&jPIW(|40EBs+g$nBTaxSe_FED7ZxuR{ZXpyC0pa>%6?|&I<3-ze%aj&scXHs{A;48FLa(e>5%xAC7oVO zJJ+t4?eSuAj-GAtIBW__+qP|jtAbh7Fk)8T=_fsB_NqSM9T_GV$@RHX81nyX3-=XU+1umvpS7PwZX*3?l|lS3j3^ HP6QvXzp%qKCAZiMN|*p?+(kfpw{gkGY?CtBZ80hykGh|v$gza8mZ-|M@AdKe`}&*0t9`GKpTw@;>*S5NoxIk? zvCzHF;?;w*mABNwaHoaY=-WUcl$EYm z=rx56F1I5m`L3n{UR69RlfT+f&B{{R1fPBOL-SZa~;m-SzYSBt3U5RF3;4bpqGtw=aKw`!YY0 zb5nB+(AWdB>vv{yKfHKsNxI&OIIiQ%`=8&wv>=jmZaC+*bnat?JeyLu-#)pwES76` z7WeT&o|DDA^CbhTfo|g}3GxeOU{GEAQT_dAjZbx=af(1u#w2fd7smfgy7z!=VNVyw z5RLPtlU|oK83?p2W?apzJAHw~#T7AJSzF)#FZ1h<1aXG*dy?Mst>#OZny5;dF<_FsH8Fsv9nUHx3vIVCg!0B;)R A6aWAK literal 0 Hc$@EhJSy`GVyeQd0Wde+0cx}1;5uB5Y)gvzw2_3-ce`uf7Bo3D(0#H*j{1w z(7LhW)6TPzgVeycr+{$i-r3;K%CV1t?&jpsyRxN!ZK#8Cu#SI8kaM^I000AYQchC< zP~TReR?h$c00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_0002lNklWUcl$EYm z=rx56F1I5m`L3 literal 0 Hc$@n{UR(*k@#TsQ8&{Qv*|?uC1Cp=qlQ zuibqSsBFpBGt*Wc>0PvM%JM^V)*bI#ynofM^OIK`+J5B9u?u&9{Q3~!%UzHxk`fkf*BZ8*M3xg|5@WxooJjQP?Ryr z+ueonKa=h~AcwQSBeIx*fm;}a85w5HkpK$5@N{tu(KtVK(o4Q31A&&+to<7otY%V@ zlUelO?eG0_bURzJ46W+-zbPrV>M7mH+N^W$t-%g;R;k?fl?0x*^v9pY+c+o>|4-KF4x>lx;+hVC zh5d7ltpa)rmhe54brVkz)|hnaM@h8krA?+4rh8Of*O?!l{`YRnde+A~q+dSPTWy)4 k|FywzHotGiiMQv3GvDjXefK1r;P)x=-@Kp8mvH;{^w6Jr z)uw{kuaM)ztKPbu+qRk3sD;y{fZn;A+Ow17$F9?(f9TG*)TV*UmT|&|Vak?r(4c?W zvzN`9cF&%8$dPNri)G4`Z_uE6^X1s`;nTt7pqBsu00VSVPE!C--&Ubk&j0`b0b)x> zL;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_009w6L_t(2&yCaDa)K}r2H-$M*%Att z*am}D3l`eiHe%0*_kXP}LY)xM@xNihH%XSAg%Hc8y|FDqEP5~&vFX4W_QV-bn^4+o zsFWh^mm?RdoOvULE2g7=4*LYv3l04c#upLid~)fU$U?Z{qE2k02k}%0nar+}X$M1? z=cy23OC+@Tb1U8CgzAaWLg(a#R72#Ufrpzg$~40hy>s+ zxW`VlkpV>bnGa3-+Bi&Mja%j8Q(Qb3Ci(yi*|aY=5lez~RI%hM1C>&nvMjeRuWws? zZcJ3~A7!1-uWtjz?!=$p370~t9{i7nPIIiDX29yv>>%5X=Fy=(_B;FmV>T)lGqjwN P00000NkvXXu0mjf|3f0@ literal 0 Hc$@T2 z#mBta;@s=<@b=r(=GDyMy|B@jgtnlLyx!R8{QLa>|No$*wEyfhq!w%HV?EnA(19VbOQvgukR-snU00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U z69E94oEQKA0VzpDK~yNut&`h!f-n$815qMOP^2K28j#dtMGC$D|EG0`<&rMyQ_o}e zn#ttM2_c3_XJs0M81zpW#H5R*HKXN%nuOAxv6bxr%Hi&sA*B@I6>VIY%CoEyZn4O} z3qsT`in#_i38eRX(JRv+hcw5-FpA?tQ0UMD`GD-m)qVU?Yg@tz*^`8GRoTg}ff5LW zxMWaXhjX0?WFSK!a)3>5!IllsgABXYz%`Hl1EK*ta0b98(*isTQK;7`R6;evGaRsQ z1IZelY)gFS1Xkc<-@@;A!l#fWaKzH$B}J0G2F(;=seJ3!alw^gk644~t~}*G3)|=K zIz$T6kE6Q&Xfl1EP97gaVz-v{so)SkwyHSiZq+LPyMT4k{F;%9=FL#w`VQZ`swgw* SdQr~+00001r;P)$dKdKtKQL_+s&EQ#D&wrfZouX+RT&W+OE^Uf9T)1)Wd zL;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_009w6L_t(2&yCaDa)K}r2H-$M*%Att z*am}D3l`eiHe%0*_kXP}LY)xM@xNihH%XSAg%Hc8y|FDqEP5~&vFX4W_QV-bn^4+o zsFWh^mm?RdoOvULE2g7=4*LYv3l04c#upLid~)fU$U?Z{qE2k02k}%0nar+}X$M1? z=cy23OC+@Tb1U8CgzAaWLg(a#R72#Ufrpzg$~40hy>s+ zxW`VlkpV>bnGa3-+Bi&Mja%j8Q(Qb3Ci(yi*|aY=5lez~RI%hM1C>&nvMjeRuWws? zZcJ3~A7!1-uWtjz?!=$p370~t9{i7nPIIiDX29yv>>%5X=Fy=(_B;FmV>T)lGqjwN P00000NkvXXu0mjfvmz(S literal 0 Hc$@1r;P)_be!O& zz4O`Q`04NV;_2$b($$Bl|NsBce4+a7@&5Vy@6X%cqP*p_$nMJ6-=De5aG3k__t1Ku z)q zL;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_009w6L_t(2&yCaDa)K}r2H-$M*%Att z*am}D3l`eiHe%0*_kXP}LY)xM@xNihH%XSAg%Hc8y|FDqEP5~&vFX4W_QV-bn^4+o zsFWh^mm?RdoOvULE2g7=4*LYv3l04c#upLid~)fU$U?Z{qE2k02k}%0nar+}X$M1? z=cy23OC+@Tb1U8CgzAaWLg(a#R72#Ufrpzg$~40hy>s+ zxW`VlkpV>bnGa3-+Bi&Mja%j8Q(Qb3Ci(yi*|aY=5lez~RI%hM1C>&nvMjeRuWws? zZcJ3~A7!1-uWtjz?!=$p370~t9{i7nPIIiDX29yv>>%5X=Fy=(_B;FmV>T)lGqjwN P00000NkvXXu0mjf>lz{~ literal 0 Hc$@n{UR(*k@#TsNM%{Qv*|?k#(9p=rAh zuRVDYsBFoRGt+h->D{_-%FaV`_8;%twtv-$^OJWS+J64Zv72{){QeCzXX1_n8%|$Z zcJ%b?PoLMFy0HA{*?9*}JbCy2)A#Rl4;){4{QUl_H&+}xH+9$H)yK~-I&|vpi&qN| zojP~#(Tu%ECu~13bMMhM7mH+N^W$t-%g;R;k?fl?0x*^v9pY+c+o>|4-KF4x>lx;+hVC zh5d7ltpa)rmhe54brVkz)|hnaM@h8krA?+4rh8Of*O?!l{`YRnde+A~q+dSPTWy)4 k|FywzHotGiiMQv3GvDjXefK1r;P)_be!O& zz4O`Q`04NV;_2$b($$Bl|NsBce4+a7@&5Vy@6X%cqP*p_$nMJ6-=De5aG3k__t1Ku z)q zL;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_009w6L_t(2&yCaDa)K}r2H-$M*%Att z*am}D3l`eiHe%0*_kXP}LY)xM@xNihH%XSAg%Hc8y|FDqEP5~&vFX4W_QV-bn^4+o zsFWh^mm?RdoOvULE2g7=4*LYv3l04c#upLid~)fU$U?Z{qE2k02k}%0nar+}X$M1? z=cy23OC+@Tb1U8CgzAaWLg(a#R72#Ufrpzg$~40hy>s+ zxW`VlkpV>bnGa3-+Bi&Mja%j8Q(Qb3Ci(yi*|aY=5lez~RI%hM1C>&nvMjeRuWws? zZcJ3~A7!1-uWtjz?!=$p370~t9{i7nPIIiDX29yv>>%5X=Fy=(_B;FmV>T)lGqjwN P00000NkvXXu0mjf>lz{~ literal 0 Hc$@tcd1426M88E{Z8 z1A{WaDTv$N|M`}-FdV4))#jroIc=~k+Yt}Lu`SCMPs6qxv2o>ta5uuSgmAq#6TZ#{ zX5+a+Fq9!oL>g|*Cfpeq14){u5dsA>yr&6}O9N$rJrO0?GV@R#=!i;AP$%dEnKU2P z1-w7VQVr3e)_x+#+G!PZTUt)U@Zt3{=F6de^*j6m1JWSC=gq@}00000NkvXXu0mjf^!WNO literal 0 Hc$@btI^shEhxy05;or`FEKs+^9( zwyUa{hdaHZkN^Mx19VbOQvgukR-snU00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U z69E94oEQKA0VzpDK~yNut&`h!f-n$8fhZ9s2vU$+4G6VZBZXf7|I<3eBE&^~>Uqpw zGnt$@(KOv4vodr|)5)LGHG?dc=8TpLVrYa6jIJyjP&RYc6e%GHFUWOZD$g`WxWOX- zAqY|1C{7gUrjR}E_@G>Y46*_b!zhmTL8(FyswZTJMBbM?D{X5yB73ReB!rdT43t8^ z`8kLBGMr;iAcZRAyZ~?q3pVr$J;g1^Wu=7#v-K~zr@4|M) zrwWlm<;PLee6_hcP%n>-A+b7~TEc7i=t?lg9MMVt`+)V){GO4F=G{==`wl-5b13Q^ SCC9k{00006Gj literal 0 Hc$@n{UR(*k@#T(@1``2YX^$$PtTp?Qa= zZM?V^sBG1lC3BBVo4T)e_Ms_@kI$L5zi-3&RkIIG-g9O9sk_I1{{8_pXXb&4TQ6-` zdwSX1&#$*!Shwcv@}(!{J%9h?%lA)9kI!9qe&vyy``4aZG3W5q_2*ZwIJM}(tGmlj zExh>X+=8PsW*nHX;ONY`ho^12xc0>D7-^sjxk`fkf*BZ8*M3xg|5@WxooJjQP?Ryr z+ueonKa=h~AcwQSBeIx*fm;}a85w5HkpK$5@N{tu(KtVK(o4Q31A&&+to<7otY%V@ zlUelO?eG0_bURzJ46W+-zbPrV>M7mH+N^W$t-%g;R;k?fl?0x*^v9pY+c+o>|4-KF4x>lx;+hVC zh5d7ltpa)rmhe54brVkz)|hnaM@h8krA?+4rh8Of*O?!l{`YRnde+A~q+dSPTWy)4 k|FywzHotGiiMQv3GvDjXefKn{URO9OmDTu&bC{Qv*|v$>&e+rcX#axU>1$v%5zQu3fXVbk*XLm5YksyneP|X716w?K?Ks?%lO`|Bj}0 zt9q6#Y&f*5<RN-Y=%q~lU+%xpQlEdLal-i}i?~(TLS9b&)mIy9B+R__ zuwbiJvZnfp{ROMy%$%p4Vk+jc)xUH-Jz~O+BVMQUl7z0`_|w_HDf$f$|E<#^ia)2u z)GzHnai>L>Po>MrT*JWBE;g}ZNuf@|<%>3NUi`6qbA1+z`2Fg&C(pA!-Z80a_o}NU kXT9GVJ^NtgTKX=cJU~t_Cy>*8InbXBp00i_>zopr08A(IK>z>% literal 0 Hc$@|FbdUi{3=-%hq$kwry zt%F*BZ$oO7YLEZ_|M~a&x}UacKW24Gaif8rns}CSM{SU5i+D_PY(Qu9?(@H=yn9b| zi)DtDagl^sf4ic%@8|DsLTQa-iKmI9r-!4Rdzc|_L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_007=eL_t(2&y|zu4uUWcgbUR& zSml)B0V?2qy#MRD5aPu`;{2NAOWW-1R0zwKtZYjNOZLhVwj7NWqwz@ELQ0kqm69MC z2QRr3J#FBOfwjDbzM&tq3Zod9C*RP#lrEN7b>KS7vM3-!)cJ*e{azlD+tf#Y;nv&1 zFN_I+E_77lqBN$+$#$WtkhPY98hf$_nw5cG17jTjy*An5WX$=3D?3U~F_duG5H2)H z8l&RkyVddCfoV_h#bXK0ZRJcWquaON`}~xvRUe$_M7`la!FsAWYpOX|>Y?xO1f`B1 UXnFAR(*OVf07*qoM6N<$g8nw!umAu6 literal 0 Hc$@n{UR#sNMduK)l4-gqn$(hgMf{q5Up%a%VobqdJ-nql|@sGYea$S;^7;C|S` zu=7AJW0JSKi@?-PygEP*XMsm#F#`j)FbFd;%$g$s6rAem;uxZFe(B_Bz7_=@SMJb- z5vBik%gQmHV(}Dyf8OxBN{c(If!OkmYzH1>^F-YFmOQD}nY$xcTsLXT)7wvf9BbaS zB&h9FM5*MRlhcbsEIr~+9n<^lWo|zy>SX=J(=)pLJ@=kb7r1gte|PPVvPo7mORO66 zMa1XF8BbkUG-08QVJP3F>6L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_007=eL_t(2&y|zu4uUWcgbUR& zSml)B0V?2qy#MRD5aPu`;{2NAOWW-1R0zwKtZYjNOZLhVwj7NWqwz@ELQ0kqm69MC z2QRr3J#FBOfwjDbzM&tq3Zod9C*RP#lrEN7b>KS7vM3-!)cJ*e{azlD+tf#Y;nv&1 zFN_I+E_77lqBN$+$#$WtkhPY98hf$_nw5cG17jTjy*An5WX$=3D?3U~F_duG5H2)H z8l&RkyVddCfoV_h#bXK0ZRJcWquaON`}~xvRUe$_M7`la!FsAWYpOX|>Y?xO1f`B1 UXnFAR(*OVf07*qoM6N<$f@MwEumAu6 literal 0 Hc$@w>`333tqx z5^ohP(l~3PUdqy5tGSY+LCw0(Z7q|ktIjYx+q2}4*yrwfp3hIu=kt8-sf~#ax3YAw zL=eO(GJ+IqoKB`XWNz%F9^Mp!nEAv+T@C{y5CrV)K`1nlk`kz^0~`*dQt0UcfdHDC zU}OY%Jm~C%;bBmzpsx=G2B5YUIyykD2Duy>8sOSBxN!rxTxf5H?rvyngPIzU$>7c% zP%1$rg8qIm7~tT*q`0&MRaMa13PVHC)dkO_Dy zWP(@>_wU2{I*g5(4y>pE1_O$Vfkp!s3v@atDuVfWC@h460?5w?GT9VGULMftCX$>C zxw(*&V>F(XI_6?b@G3PnIu5Z;WW~@~iLCzv#;tL57R!(+&o=6rrGFMqLbw>m2n0c; zMv{n^1E)(HzKi=Q#4e<%KMU(tm50qQCfTMCpFWa`Rh_9;9CvjkOM5}F32tJ`6D>>rQa3^Oa2 z&daaJ+AcZ^8Xbi#i~l;0k4r44xZ)HCZAIfMsys>j4Sj7?XT#xzm6FNi72g@X*|{^U z3yhf4RvCEJ*QdG*MhmD^>bph4H)3QGaS{?6b*GO$8@tVZV zH*aR@sTXH+vuaDD4MSJi$`7kgge#UdlzShb$QbmMV0+biptbA9G4a`Hr=&jD1olb7 zjw`zE(|JXjz=tsLFp%6*Eb#G|K;r#c>nyU>3PFS-yH)QQx)8PI>6{=L9>Cf7eK~+3 z?tbMOaN?eqt-(Re#rmsj>9Q)#-r0|IO0D^j=L;k@;T8MG++V0n{0_r$F7LJ9O9U(^HdQFlMF7{*N;#+P9r4{#ew)+nVQjc415%Asc6@BXIm_ zA*0XJeh^uhyL8N!L_44|G3efx>;MaMyT_=gk+f}o_Nia(R|9;%brwFQ5IlHCh2Jwp zH8xusnpPM(r>AhIUP_c3UHBIjWq-26-}iPokCr)zoHf47lN}Ym@!q3|_JUUK5;G3D zH>6`UL|^?GA0+tm;~2BG;lSojgDR(xa)76MOwW6?=%gbLceME6y~UwrtnT;x0|FbdUi{3=-%hq$kwry zt%F*BZ$oO7YLEZ_|M~a&x}UacKW24Gaif8rns}CSM{SU5i+D_PY(Qu9?(@H=yn9b| zi)DtDagl^sf4ic%@8|DsLTQa-iKmI9r-!4Rdzc|_L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_007=eL_t(2&y|zu4uUWcgbUR& zSml)B0V?2qy#MRD5aPu`;{2NAOWW-1R0zwKtZYjNOZLhVwj7NWqwz@ELQ0kqm69MC z2QRr3J#FBOfwjDbzM&tq3Zod9C*RP#lrEN7b>KS7vM3-!)cJ*e{azlD+tf#Y;nv&1 zFN_I+E_77lqBN$+$#$WtkhPY98hf$_nw5cG17jTjy*An5WX$=3D?3U~F_duG5H2)H z8l&RkyVddCfoV_h#bXK0ZRJcWquaON`}~xvRUe$_M7`la!FsAWYpOX|>Y?xO1f`B1 UXnFAR(*OVf07*qoM6N<$g8nw!umAu6 literal 0 Hc$@n{URO9OmDTu&bC{Qv*|v$>&e+rcX#axU>1$v%5zQu3fXVbk*XLm5YksyneP|X716w?K?Ks?%lO`|Bj}0 zt9q6#Y&f*5<RN-Y=%q~lU+%xpQlEdLal-i}i?~(TLS9b&)mIy9B+R__ zuwbiJvZnfp{ROMy%$%p4Vk+jc)xUH-Jz~O+BVMQUl7z0`_|w_HDf$f$|E<#^ia)2u z)GzHnai>L>Po>MrT*JWBE;g}ZNuf@|<%>3NUi`6qbA1+z`2Fg&C(pA!-Z80a_o}NU kXT9GVJ^NtgTKX=cJU~t_Cy>*8InbXBp00i_>zopr08A(IK>z>% literal 0 Hc$@n{UR_5nU2uK)l4KUr}2R2~o=Iahl8 zX4mEWGj81Ky>cq|=+&n4_oiOIP;&~X=HA>JKVE)2S90Qf`NB?GXwf{)JKr)j?Z%!1W#+qdg$;mpCGXt2F zvX^wUY{|H5>R&y#<=c@9^N-%QXxV#YQHk2a8MD(Kp1x*R*tzpalIDgTe3o)YjqhKW zzGVJ`RpFfz4=;|dcrDGv942{IuYZ}vl}p7p7^Y{7N*nRs&IY=L!PC{xWt~$(698 literal 0 Hc$@n{UR_5nU2uK)l4FD^J)n0Klm?^I>! zx!$gub7$P2(0eOC_f%`s)frRo)zw@8s#!4i-m8~C%1X{vm7niwzcFpn-S=;Q{rve) z^XSwAK%^EGkiZIAeC&!_(L73OjcmNz&Y~gU?d#sPX*^ z)0fPDuqwQB;^D>d6|bean8PH`>h&+PxN@oZ2E+7hQE4OI+u1<3FnGH9xvXn{UR_5nU2uK)l4KiO7rsx|LaOWwJj z(wpWB3-gcOw`kdWWKoIQ!x^*F9-h8tSJ=7pNRsA;9ekE@M~&}a zn7(BGgH_?36Av$ruXruZ#T+JiRn{UR_5nU2uK)l4FD^J)n0Klm?^I>! zx!$gub7$P2(0eOC_f%`s)frRo)zw@8s#!4i-m8~C%1X{vm7niwzcFpn-S=;Q{rve) z^XSwAK%^EGkiZIAeC&!_(L73OjcmNz&Y~gU?d#sPX*^ z)0fPDuqwQB;^D>d6|bean8PH`>h&+PxN@oZ2E+7hQE4OI+u1<3FnGH9xvXn{URMFBn`uII~m|Ns9#C!7-(T9v?c zs+bq3>})CT`XsK!(OfIyxQ-R_e17{9sBu?+-oZSc=eIAdE_FY*Y2Ja^^$#x|TavE# z^V^r($9HZ|=Uy4lb)typa6Zqrbnev&Tqlcpdp?*i2ine6666=mz@WPJqx$>L8lUP! z;}n6Sj7i?^E{y+~bngK<2RvOILp07$ophV8$w0t`o3-WPH7C(M+K>MKU&QJavP(5l zu6mR8bP0l+XkKMB}X? literal 0 Hc$@n{UR;Q>A&t`pCk|NsAg_Vk&!P}#B- zy~j@hl}$KxwsQ5_f_V!{7BA~Md@Os$tnS0d-hciK)V}`cv8L@iTb8fvIeMaO*~;Ey zC%t|<&jVV)RTAVE%)p?!_M`gy&l;cVMB@~JqKrx2?k~InzB|978nDPo4CV zugQU@h5N3nL;zp@$G`J~uV}5z6rUMCyXNJW8;Yka4+K20SsZa7P_Q%5JlffpcNxQE zUlD1hdFx|upRN1*VNt_@u+rP|B5w+8YDa`L)LP>%Kia`rm$q{qN&pM^of9Bn^&bT>5R{y7`N9tdQ$Eoshyaa&cVhxfg>? RJb_MR@O1TaS?83{1ON&yn)Cnw literal 0 Hc$@n{URy#YQUu5%px|Ns9FBypitZegc_ z($0ru9t%ue;Tn1(Fm-imK2XcKRqN+F2CjAsKj4?RBqZVI*Kb$0?b_lQeSF@Mxeoqs zA3c74{pKdms9nw7pWnPa?4Nu#IAgJM@a;oK4@{h~-aT@scieXG*oSA&@9|Ao<`Qx$ zDE(wmTIaeir-2URDhcunW?)cV`%(S1cNfP0OuF}god2FKjv*T7 zdndgPI%FWw%A6_a7oD_iiHPW}6TSj9|Bn}k7syO1IrdrEcy@mN%{4*Ea}^i*^t)@# zPjHwVlPQuqH6YSqHIL^p5r3`~Y%CmeX5TkTlXLuhy@m6kvCiGE0gvZz*ta}3;o+%c z2TTO_gqGW<3p+HYdcPHlxX7hn_(c2TUeSKF433J!vrgpQxE?C_=7MnLrvue@E|j&~ zR?8Oj%sX`X_k;^!O!5yr<<3n^7h2uXp3o=r!gj0J{EsaR{(l=Y`tO>}$?cu>exu^6 zogW3FPHKFUn*PGx_2}7mi+l|}X(q*6Wo8r{T(>7d>fNlQ_`dLBt36kOmloN2FPwSK a{r>0uTBVCm-wpu=1%s!npUXO@geCwGi{zF7 literal 0 Hc$@n{URy#YQUt`5#~{{R0EByk}(&s9NT zr$Qpn2ZkMU^;i)Yb|N)xHBif{Rp%UC=ev2W_6s@?61wE;x1Za#UGenYGH>2-2j{tu zAHBVC{kf;lrskGiZ{B?N4>=qhan{*=@u5SvCr&!x?!DgIf2X(q_H$<+`UdTB@mLlV zekv&ZWNb5A6VPE?B|(0{3=FDkKdQg~tnsN%G)@sH%9!Ns?!x$=N%tO*!&%@FSw@&y9)cikQ99|$ZspQya zW#ifT`8U@DCC^n{=+p16H9x^&a!jU3>ePTpht)iu$3*zhU3<*o23tjvX)&+!I=EpDygsoa+5nDB>cQe&G}Ck9$S?)iO9L4$nG~ zcjJ1f;F}A=m7fk&-?>oMZd)x|&@=DQ<=+!7gfYoK@RU0@F*sHYc}t*87c$uXcVEh&rk9O=|iJd)K39-!1Yr_@tQ>Z>xt30_)c>%DO1IrsaY_iL3dK7Bg`7!(Yiu6{1-oD!Mn{UR#sNMduK)l4-|pSL!)F2z?GBxM zGI#m;+Vuy+r=P1@2bB8p>dVR8rDuy*A4!_`=hvU-n=ilId%dZ{U@}lUb4ie2Fhjuo zu!Ujgfn3HUZ+91l4pvzYAcwQSBeIx*fm;}a85w5HkpK!7c)B=-Xq?}A`8HRB0#Cz3 z*(+!I-v8gCD>!L}cJn{&kIF2w89dgwaWvfFX}s}s!4p|wgVk!MbSl=hMQ;+-n{UR#sNMduK)l4KRK=7)U>=)Q}fQv zDZRP2>;BFe7v@*q+cp&__4~{F8*935Z=P^vS>wMyzu#Uv{O7{=Qcy|B* literal 0 Hc$@n{UR#sNMduK)l4-|pSL!)F2z?GBxM zGI#m;+Vuy+r=P1@2bB8p>dVR8rDuy*A4!_`=hvU-n=ilId%dZ{U@}lUb4ie2Fhjuo zu!Ujgfn3HUZ+91l4pvzYAcwQSBeIx*fm;}a85w5HkpK!7c)B=-Xq?}A`8HRB0#Cz3 z*(+!I-v8gCD>!L}cJn{&kIF2w89dgwaWvfFX}s}s!4p|wgVk!MbSl=hMQ;+-n{UR#sNMduK)l4-|pSL!)F2z?GBxM zGI#m;+Vuy+r=P1@2bB8p>dVR8rDuy*A4!_`=hvU-n=ilId%dZ{U@}lUb4ie2Fhjuo zu!Ujgfn3HUZ+91l4pvzYAcwQSBeIx*fm;}a85w5HkpK!7c)B=-Xq?}A`8HRB0#Cz3 z*(+!I-v8gCD>!L}cJn{&kIF2w89dgwaWvfFX}s}s!4p|wgVk!MbSl=hMQ;+-n{UR#sNMduK)l4KRK=7)U>=)Q}fQv zDZRP2>;BFe7v@*q+cp&__4~{F8*935Z=P^vS>wMyzu#Uv{O7{=Qcy|B* literal 0 Hc$@QvXzp%qK>(qfUuK$td4T0hj*!pkh-9Xx0{c-pLwc`k-MV2*2UoM zpmZ8S4v(dn>&bzA1x9aii zh_sr5v6Pv>sEoLs%HGk)-qFqB)w0mOb*PEg=iT!7^n9(5_WJjLu#$eSk(9orrpU90 zw3=m{d0m%uW}JE{JO|4F000AYQchCw)5Xu48dd*iqo3O*kE5 za0Nv1EJ>1Uz_EwuA)POTN&?tJ%#hy*vAA8z)p~?J?r3OJuk?Eugkgp>5#m0FeAs&M zS`6#EcQiOx7bHawb4ht5Ts3I-P>L>3|H8}4XJ{U)5)ZHc_h7DodykQTm>MY;Ovi#_; wR0XT5>9n=F(6p*%RqD^LCf~zS|Mffk0B*P;_GBw|m;e9(07*qoM6N<$f>-n{URlLLH0To06%{r~?TNa8}ro0_lB zn}2ih;)|1~oNQ}9TwQalq48{Q-{l!IZ!B7Ly0hz0W!2T$vyar&UR%BT{@#7xKYRch zc(k$U(!zzGU%YsI{l@JrThC3Oeqqw&lRdpBnp>WqKYy-&!qwSx4pmhC{{H>i+_|UP zI}VhU-&nZt`uzFV=FR)ISxpM)Jg$-;zhDLi)wLhh-+$KlR3{pz2oz;Z@^*J&{LiF& z56JoB>EaloalUlY&1p>r0xpXgrwf%RaXGtO3=Amz`+xmr;Tg_N3*~;#TT-QYtnE&- zvz$EN411@3H@n9(SQk1Ulyxw=RuYKGScW>%{PV?o@#dCHYwJJJL<#+SUjm9ktnJ>mX&b`-o zl6iOU#%OM{@RcT-OSh~OYCGRr>3?^}+?GeTR2KFz9lY1DS0h2>?DX!vp(g5o7tS+y h_IV|@?@s$43{O89aR2(P^9~pe44$rjF6*2UngA~4_;vsQ literal 0 Hc$@eHGz* z3&+}@IQ3Xhdhy zvxsR#g7ESS|CufOo$mwF09qk`LGtS2=f8fEFMR7!LI}7!AqXJ|01|`{KtzxrAU=LB z%{(H_m4F~bjZ~wGJZs@7vvVzeckS&l<6{78{g+l&P9NdPr$5C@&#&{_Yp-zW!>fGg zV`te|8&G5!ODkh(+Qj&j)6Pv)(q?GciHZ5v9Y;{yY2@N#1SAX0$cY= zuD|#Oc`?J$)n#s8-{Q?zOWaaU#|wD6Up(GsZmA$v&fHSLiE~{>L!&hVC(j<@@N&hl z>=Q(AoE?&;00;$;p|v9+r@L^NR=Y!sf_A$@$ZO&#vP(8;B8Sbj5u0nH@$*+-e0O|3 z8aQX3Xz|pSb9#$lW>c)~G7xftWW-v5KyPuLUhjR(*aCCC8Qyv^zRZXmFBm}gMK*fzeS6uk0(>p4NKa_!-K!!%g%n8z{+ftNcpg_no@_c6eY-YB* zFaHt*Q)B)18rvJ&m}NwTXo^haaeS3$86p9TjwyIUc4*4%y$&~M&16UpF=`XhbU;l$ zlT&DP+)^>SJF3$Ryw{;>m?~xuOMwK0tYN@0lF4AQ4?vc~`713}&gCo~&**f8!M1Vd zmU82jij6hh$C@;r;qGJqm;#VVju09fO(kgt7Sp*A3JXUASD)$d$fHHF*Rc*V+B3rD znsV>15#uxfOZLfL>mLJf0z%S&swYQlM7Wc-!wZ2cpX_kzVzanIIdrO_)9wm`ZKuCw zX|btt70i?v4SiS{<{`rg#M*J)t%5qHbC$!g)r{p+8CeeZ){Wm^8?gRnqPu7dovtt% zCf!WtIWb^FjhNXawh0Y{oIs$i69eu^gi%upS{-5KTuyI3(7$gy|C2uV)^`N6p3Ot*m}o^F}2&HD;X>2^G4zD?%T?p*CWG$;g$~EP*cn_G0Oo} zAVF{hZBl?ZYA~uHDij5Dxe3UP0>@TcT>W%sJmJmPDmL%b3?CR~+AN;vk5Tva zf?XpbfQRvJ#@#S8R8thHDe9snNS5ByL^PFaPqe8!4k*&>0J!*36j+&ubqpGMW8kM?= zO&R*Z0Pv(Dj}F>6kU4dom>6y=ci*U}O6SN*!Bfx9B3XKu#O34m>mbTQYNdxXF3ROiM-nv!tw?A$%^OWT zo0ybuF{NS$%pRAT0FYctYe(y(@T7r=#q^;LB7gZq#l~966Hm``;&g}Je2xR<-l;; z?RHLMzn1i%s@MVMIR;?viKggSOtoy96f9<#PxSK%FoOq4fHLu}+50v^j47372-971 zT7*psJc8CrJt~_Wj8ezMz)q>|oP}LbJ^{epagmh9#)7r!1CSluj>jp8P?i<Hik>U1#yl57lFb>t_t)fLZ>Rzzd|5XX&Uc@?zju!R Y0_OV07*qoM6N<$f}dl6ZU6uP literal 0 Hc$@n{UR$pJnguIJD4{{R1f&P+~RXw`DA zQzv+V%FdqVUB8BF@qDfoOSz66;W>PeXWM4(xidMBALaS{@g>lZ;|Kb8@8sUTh5Ov` zdC#9*TDgpC^$M<&$9erzeWQWaag_x51v4^-hu4`u#uW%TM}PFn^z#j98Ch_pf=e mrpfgylan{UR$pJnguII~m|Ns9#C!7-(T9v?c zs+bq3>})CT`XsK!(OfIyxQ-R_9M0$2md-slobz}g&*!%bI7Of+W0JSK3*&z#-FrYz zpQnpsh{pM&mv#y@8}P6MH0CgIKjZQ|Gxz`hQx*XZipI>Yo2DLLzu{Qaq6wlc8hp%` z1Z_G^3@@GGI2mN1+3`%ooT*3sKv!iTQ|RBlyTadHd9yJ>t-@yR+QTN+Iv+P}b4~Z@ zKX^;<^7(?3tapmU*6;s0Uw+cZg8BQ@WW;(5yMN7#HBGKxnXGiS(}nZ(1Lm36bV?t; S?Dzt78H1;*pUXO@geCyWtBWB3 literal 0 Hc$@n{UR_5nU2u9r7%`v3p`-tJyp=C_*5qv zrw9~fO!9VjVf@dedk@IrEbxddW?QGnpf3sJTQ;Y~-!koKLba%ZF$TSm3iqPJ~H`&6wM}S|^u;HY`mUTBcBurBkmIpS9 zFMA#$v~j_n{UR_5nU2uE#EK{Qv*|>b>2#(B{L_ z4qsdgRJP~z;*E!7DjK%=-yg8YIR7*yAORDb_j<5Qhz zoFY(^G0EHAh4DX=?mZxfv%n*=n1O*?7=#%aX3dcR3TAq`IEHAPe|yQBuR(!_<$@Dq zszY7X|IJ=WPB9{U33Ki~(cSezAk$1BDMEV_-((B#9szzu!-kU%Th`s+kT6YESRU9Y zzU+C3(8dMZ!UF65u<6a$J(FT-a7^=6>9@>xp99~!Oxx=+Z|yF|-cPEV_xRWYZD(Nc MboFyt=akR{04ptcV*mgE literal 0 Hc$@n{UR_5nU2uE#EK{Qv*|>b>2#(B{L_ z4qsdgRJP~z;*E!7DjK%=-yg8YIR7*yAORDb_j<5Qhz zoFY(^G0EHAh4DX=?mZxfv%n*=n1O*?7=#%aX3dcR3TAq`IEHAPe|yQBuR(!_<$@Dq zszY7X|IJ=WPB9{U33Ki~(cSezAk$1BDMEV_-((B#9szzu!-kU%Th`s+kT6YESRU9Y zzU+C3(8dMZ!UF65u<6a$J(FT-a7^=6>9@>xp99~!Oxx=+Z|yF|-cPEV_xRWYZD(Nc MboFyt=akR{04ptcV*mgE literal 0 Hc$@n{UQ0X`wF=QmFL|NsBYrfghj#e}j` zYkPspj;-uk)?adLW%sP+?49$Pj_qC#)VOj&*~vA%+pAW^0uAIU3GxeOU{GEAQT_dA zjZbx=af(1u#w2fd7smfgy7zz_&H|6fVg?3oVGw3ym^DWNC|Ky};uxZF{_P}dz6Jvx zXXZ@rLhiqe-|e+hS4uDmIptJro+rXz>0+zR5qPJ339nv~iQ%02Efwr)tP?}K8XLl% zGqmgseZZ1zH7!Ua(JgXX(;u08^ZsUhZePDoDWgbRB3bn1wlLGTTdz#>_^h#TV(E6~ YhV^Qx7w#pc04-n{UR_5nU2u9r7%`v3p`-tJyp=C_*5qv zrw9~fO!9VjVf@dedk@IrEbxddW?QGnpf3sJTQ;Y~-!koKLba%ZF$TSm3iqPJ~H`&6wM}S|^u;HY`mUTBcBurBkmIpS9 zFMA#$v~j_n{UR_5nU2uKPA#{{R2~-0r=&(6VWV zcdxw&RJLjH>7`Q+t(tZ0_SI)VEz74J-nI6kUaFKn&?v5wAirP+2GzA6)!%>C_*5qv zrw9~fO!9VjVf@dedk@IT^mK6y(K!G1k~d$20uRdtC&pBVx~l)1y^@?_MEDZs+xDq3nLtv6_9ni`7T!Gq{ECJRCmpt|yTKu0nyRoouu**3^AMqp3$}#?*8O4Ao3DE& u#nRxI=Bv_gneRRazIU0n*Ja+?U5ve-R5$POu@?qf%HZkh=d#Wzp$Pz#$8!+? literal 0 Hc$@P)s#&A+eu_x7@!hxG95 zwVj97%Dvpt$FG-wrHyvDq>;X>n$*U*s*!r1fMKt!t;PTV00VSVPE!C--&Ubk&j0`b z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_008JoL_t(2&wZ0^Z^AGPgwq;) zO^cU>wm>&l25hj`|Nr;q7N$+qtv^I3J&Np{FU}n+rUeX3BGzJ@T%5VoqS&inCY>(-2zl)p6prs;rn{UR#sNMduK)l4-xRrjOT>XK5&L&0 z963>T{!;7p134$xh3vl2boF%m(MsGYea$S;^7 z;C|S`u=7AJW0JSK3quF1tOt<8S>O>_%)r1c48n{Iv*t(u1+zU}978nDzrB2tuR(#w z_2M)mkE0L&?+*(!5a7PJbP0l+XkKG}Lh? literal 0 Hc$@n{UR#sNMduK)l4-`^a0pe15|OT>}x zg!A*uuCHl5IW=c@UC7l{O_x`+Us&7-RP*)K{qqYezQ2Edq(A-Fw(i?N?aU=Xe!&a@ z_rn&3od%<`V<6PYIl3Vril S^H~gNErX}4pUXO@geCym8E=&U literal 0 Hc$@n{UR#sNMduK)l4kKA7raiAz-e{sT* zse+|#RTTy*Kk$GaE&^PZj SpT&UIGI+ZBxvXn{UR#sNMduK)l4-gqn$(hgMf{q5Up%a%VobqdJ-nql|@sGYea$S;^7;C|S` zu=7AJW0JSKi@?-PygEP*XMsm#F#`j)FbFd;%$g$s6rAem;uxZFe(B_Bz7_=@SMJb- z5vBik%gQmHV(}Dyf8OxBN{c(If!OkmYzH1>^F-YFmOQD}nY$xcTsLXT)7wvf9BbaS zB&h9FM5*MRlhcbsEIr~+9n<^lWo|zy>SX=J(=)pLJ@=kb7r1gte|PPVvPo7mORO66 zMa1XF8BbkUG-08QVJP3F>6FoATUZ_`Ru62aJsIl4N=Jd3^-%?|%Ykj?$n3+QW000AYQchC< zP~TReR?h$c00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_0002fNklEW>IKMCmu6LB-l||Q^(ry-A?~VS; zqU$};iFu@6EY1PEB<_|12=}5$gZPeze*W-Lj?VKJYX&SZn(cBlYlph-IlKVqG92=I SVJ#E@0000UO%FX8Mu+`ko=Ie2-yp+b! zlElug)ZAR4v1O>Y>GbwbmZ(>ru647&sLEW>IKMCmu6LB-l||Q^(ry-A?~VS; zqU$};iFu@6EY1PEB<_|12=}5$gZPeze*W-Lj?VKJYX&SZn(cBlYlph-IlKVqG92=I SVJ#E@0000_}3bh~MEzi@iVgNx6PmA7hduU%rpe1gS)g}-)u&W)3`W^B7~b=Ce!?euBAgbk(M*?%CSmzrnO-YsQF-)1IOD?(fo}q}Qvh?%3Hglpz2B000AYQchC< zP~TReR?h$c00Cl4M?@D3)|ig~000SaNLh0L01FcU01FcV0GgZ_0002oNklHW77zu5&%~WgdLvkeQ5mN5M0Q*$WhtpeKDH zC2OQo5-J5v`t)tktX0^;A>rtY>W4@fL!{IMrZKH8oXufDtxMz(6Ig`+DTBK1T~n*J zaidLd1Y6wMxX%y|bjl2m4CaU@fU}M-44yS!3&=4o83fZi&+x{g=}l-ii>CKZ|7Fqi zUg^X5(@7&A$_x0GVitpUb)~SctuaD}}zSX9I z(w}+Su#UjdyoCS&00VSVPE!C--&Ubk&j0`b0b)x>L;#2d9Y_EG010qNS#tmY3ljhU z3ljkVnw%H_006y7L_t(2&wbO$5`!QR1W*v2b|Y~~+_NYD|BFGWvSJ+U+wd+;F$f_8 z#U_wKNac+bfeIsH5``)dO0i~CDT73?iIe#(bpt3{A>#ZOX+f>^;fNxiu{6U!>Z-=c zi~8Ik^`dTp&Wrs3j4V#Q)|ad4$zj>L$~-nVSD8iBm95Mp?uWUu2k|ha`iaF676oqRw#1_)wJbtQJT{fqqKHQ}kBC=j1R;cq zS3TkpqGa(*sKb1*{? z#4^C&m!i)&1JOo$^=&ViL=e>HpDD)8#0p;aTRtB}T(B2Mw zK8Qr1P(Vouw6wtRFbobtcQ^F(KyfiNH^a~nbaX&f6;xJ2TO06r(9{HCF*G(pQ4!p` z2Ll7p*9QUtNTs0Ff=*``tB3ty$k#MP*(>W z4)pdybv2Zh!u|W8R6>3})YQP(80_vsZY~@gz}y^2B+%6bIXNH{LT4v%xsaU=3kxtk z4Qw`4RKUapY;MAX2Zm(}3V_9e%uGm6hrB$PorSbCn4bqG6H-$lB?Xd_fWd%_3`k6b zgoOVptcLXGGyR1^3Py0U<{g==b0;;Q8m(`lVy<5CMR>-i{SA&}e<~9}%xw%rA@z+I z1ot5E)*70K0OK! z^xQw3f1wRMC!wHew*+mmL%CDiD#`@rBjHt}%-{MW-+W=-Z}Gxwl$IM2`hP?z?N9tpJgYSG zB6Oc~qPy3}TGH5laptQJ_4VSdUYd3yg@5E5lyK6O+(EZgok8%Vbv65U*DL zDiPg3az?)LM)PGQ164dl4sPI$P|@s1OwCq_m-Xn}Y22@Vj=b0FQL4irnI_0(QF-F< z@zS_-+wA~(n_Wb`Ez$T8tfl8^TbDWne63XKW>Ta>O<#M8!@tW>{Chd*nT9WpZB_Hn zgfXqEM^7Cb(_LNnjvj8bJ0)MoM|(QbX|DLEt%wa{Nhfx=pc5~fn7CLJ(cOi%ex@qw zxN07g@%N>hDkplJyLVLpQS(+Ua3p+p{MgN+cgl(&akfFb1`h9Rv?x|k%d50M4-?fG zj&?8%m2A!bYStY2VSbX3r*t7n=pLW_h4s=|VQ2Odxa&;kNrmH4!N$0!M|0crnA*sh zWH~-!X03F9D_Yw#A|?R4An|=x(z|eT-TOzV!w{LL?a4Hb{sEByKay{scQogJ;E^0l literal 0 Hc$@ppe#+gx;o=-lUcP|Nr{(@6(Te(1&;5tfBSb z+tP}B)Q^Ac%)`!wbm+jf*qDg^`ugIqq|=Oj@6*ft_4U}9itp6T)|H3ZoR8|rzSWU| z(usN6osL>S$`b$p00VSVPE!C--&Ubk&j0`b0b)x>L;#2d9Y_EG010qNS#tmY3ljhU z3ljkVnw%H_006y7L_t(2&wbO$5`!QR1W*v2b|Y~~+_NYD|BFGWvSJ+U+wd+;F$f_8 z#U_wKNac+bfeIsH5``)dO0i~CDT73?iIe#(bpt3{A>#ZOX+f>^;fNxiu{6U!>Z-=c zi~8Ik^`dTp&Wrs3j4V#Q)|ad4$zj>L$~-nVSD8iBm95Mp?uWUu2k|ha`iaF679Z;GRAhOmE_pk{-dV1Bueq^NR@pl5@#gq+K& zy4JhNo@9Z+o38KW>;M1%t$dWwvcc-!<(^}K%BZ>f`TN73u%&H?_VM!6xyPYth0d?P z#-Ow0)ZE_6)v0xlzm}@ewZquI%(#rByOF1?dy}z(nWAfkwuheK&Dg$`s;YO9+r!ZF z>+q*?jl7emxs9W?h@LhLuZaKv00VSVPE!C--&Ubk&j0`b0b)x>L;#2d9Y_EG010qN zS#tmY3ljhU3ljkVnw%H_00B`+L_t(2&wZ0?dx9_wg@Gy{$6G@U&RT{Bp#ZQhA?iy0bYCuZ3l$8l`yL>VJky>l1G zaCw>V8t4N-w-<)JZUFdYs)jy9KayoceNoX996$^yKMKPUJ%$2y1HBkyDJN3_i0MSi zF=DTV1I!XB=RleQFH$Bm3~K0LQD%HqT%GH|sUeU_xh%D)!cxi%ID^z_s7@=DkUGAG z3v?7*;q?xDtG?{0`q8ays$hC8>*Adz0OY2b10&U(TSFgfnn8gy&r=jMg|(`Ggc}5; zn|&xx0=iWcfH$*sv8D4_Aw=;>8e64WLkrj?$`R#EvIDGh%Q&$AIw;2u^F6>X{4{w? z<-UJ@XXJIvFU_Vq_;Ix*@hUePSTCQovF**DdA48=R?{n2^XXDQ`yKuP1GgoMrAi(Q P00000NkvXXu0mjfz6~X< literal 0 Hc$@L;#2d9Y_EG010qN zS#tmY3ljhU3ljkVnw%H_00B`+L_t(2&wZ0?dx9_wg@Gy{$6G@U&RT{Bp#ZQhA?iy0bYCuZ3l$8l`yL>VJky>l1G zaCw>V8t4N-w-<)JZUFdYs)jy9KayoceNoX996$^yKMKPUJ%$2y1HBkyDJN3_i0MSi zF=DTV1I!XB=RleQFH$Bm3~K0LQD%HqT%GH|sUeU_xh%D)!cxi%ID^z_s7@=DkUGAG z3v?7*;q?xDtG?{0`q8ays$hC8>*Adz0OY2b10&U(TSFgfnn8gy&r=jMg|(`Ggc}5; zn|&xx0=iWcfH$*sv8D4_Aw=;>8e64WLkrj?$`R#EvIDGh%Q&$AIw;2u^F6>X{4{w? z<-UJ@XXJIvFU_Vq_;Ix*@hUePSTCQovF**DdA48=R?{n2^XXDQ`yKuP1GgoMrAi(Q P00000NkvXXu0mjfyYeVk literal 0 Hc$@SLOosL0Cs`}>~2!)>6Y@$~k&)6{95p|8!( zpv1=1L;#2d9Y_EG010qN zS#tmY3ljhU3ljkVnw%H_00B`+L_t(2&wZ0?dx9_wg@Gy{$6G@U&RT{Bp#ZQhA?iy0bYCuZ3l$8l`yL>VJky>l1G zaCw>V8t4N-w-<)JZUFdYs)jy9KayoceNoX996$^yKMKPUJ%$2y1HBkyDJN3_i0MSi zF=DTV1I!XB=RleQFH$Bm3~K0LQD%HqT%GH|sUeU_xh%D)!cxi%ID^z_s7@=DkUGAG z3v?7*;q?xDtG?{0`q8ays$hC8>*Adz0OY2b10&U(TSFgfnn8gy&r=jMg|(`Ggc}5; zn|&xx0=iWcfH$*sv8D4_Aw=;>8e64WLkrj?$`R#EvIDGh%Q&$AIw;2u^F6>X{4{w? z<-UJ@XXJIvFU_Vq_;Ix*@hUePSTCQovF**DdA48=R?{n2^XXDQ`yKuP1GgoMrAi(Q P00000NkvXXu0mjf@kJ_) literal 0 Hc$@gvC0fz_vj*{zD+wUpblkA6b?Jpcdz19VbOQvgukR-snU z00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0KQ2?K~yNuwbR)Wf*=%t zVHWMAMxd42LfT%oy#M0`RFfm%bkl$3%%_9G5g|;dGlCJq)Eh>G2I0U_7*K*Jb(&I2 z73wSaHjLtzwlFb`G_!FRxzMFNn@oYtoM>*yp|G(`=8L7$6@ZOWt+!5v;BKrKlwJg= zGe6cFwdNKo8+ZGIdQR{)M4nGMo)V~H8_!b8OAhZnaJ-PJu=my=Yp%EZ=UBSfw9oAS rX!;prgFOSLNAuyxjArjp_x%n}e61XBcE5YT00000NkvXXu0mjfyCmn# literal 0 Hc$@#ip*Z zjQp{J{c!o{Vnv5olo_QAxcwYiq3s)fPCq^z)w<>uPByq&wgq0rL4@$&2H z>*3<#*0s5r!NR1hu#Dm3)~>RTt+9^e=GwBgl&7hLz`>)duZ!X1)!^dO-QUlqs)WD7 zqN}iq?Cs>t&$!>=(zLjjrm2IguZsQs{O9T3x4WCJv5mgLq4V_a)z`$p!=}5wo~5aR zzQLffwvxTTpQWgRzQCZXu8OR$i?Fkhv9yu4xR$)Woxi}JtFDQ#vyQ8-h@_{2zQCWV zu86O*j-;o7zQCUF@#)>(&abkKy}zBQt%tq7ousFLwYZd{r+}!eg|xVoyuY2gzMG?_ ze-(@54gdfE2XsK6X=F*rwKKNNkSqGkyW)8=-A&o_Yl{&FOPL;hjf zd_D*SMi65dN5{q|Cb3R2Mq40&u@$LtnlUyrh<7tan;K`4AYhym<`;xTj7tJ-7=yuO zB#IP8QJnW;6h+#CL2Z<BxhqN5T+p*0i^V_e_Z^wM{>!o#$MLJ*12L6*P1vc0n_ zNB3xpM6_{VlB9z}Sr(4gDlry2MrxE~)ye6xDi(uy{0t=$6cgtcmsi(k74bO6Bub?y zrf!lN-!Yx0nEo9znJiKiipp(PBa?w#E{_!Dj_2={hrC8Ehp~Vj%c2DhH5L(97A#ip*Z zjQp{J{c!o{Vnv5olo_QAxcwYiq3s)fPCq^z)w<>uPByq&wgq0rL4@$&2H z>*3<#*0s5r!NR1hu#Dm3)~>RTt+9^e=GwBgl&7hLz`>)duZ!X1)!^dO-QUlqs)WD7 zqN}iq?Cs>t&$!>=(zLjjrm2IguZsQs{O9T3x4WCJv5mgLq4V_a)z`$p!=}5wo~5aR zzQLffwvxTTpQWgRzQCZXu8OR$i?Fkhv9yu4xR$)Woxi}JtFDQ#vyQ8-h@_{2zQCWV zu86O*j-;o7zQCUF@#)>(&abkKy}zBQt%tq7ousFLwYZd{r+}!eg|xVoyuY2gzMG?_ ze-(@54gdfE2XsKNm~L4ft^GWA zg$IgD#9wM0a(C}L=etYXsFfgKN&mn&Hf93=mTZ^JQ=fsF1c52}*7Ur8G@;dOiwVBK zi{$S3*+0w*057juW#iZ5KR}&0#SL7aaLei0VFj&9Z~-ooBwGV$#f*SC2h4zt0tPTN z08pobfH4OEl%cAsir3xGEvl-js;a80io$|7j9;GsV5b=H33xRCfV`7E005|#dYc3Q rfE>C%Ru%vNz(`_|_W%IkL+}0r{7+X=crCaf00000NkvXXu0mjf)>y7o literal 0 Hc$@#ip*Z zjQp{J{c!o{Vnv5olo_QAxcwYiq3s)fPCq^z)w<>uPByq&wgq0rL4@$&2H z>*3<#*0s5r!NR1hu#Dm3)~>RTt+9^e=GwBgl&7hLz`>)duZ!X1)!^dO-QUlqs)WD7 zqN}iq?Cs>t&$!>=(zLjjrm2IguZsQs{O9T3x4WCJv5mgLq4V_a)z`$p!=}5wo~5aR zzQLffwvxTTpQWgRzQCZXu8OR$i?Fkhv9yu4xR$)Woxi}JtFDQ#vyQ8-h@_{2zQCWV zu86O*j-;o7zQCUF@#)>(&abkKy}zBQt%tq7ousFLwYZd{r+}!eg|xVoyuY2gzMG?_ ze-(@54gdfE2XsgvC0fz_vj*{zD+wUpblkA6b?Jpcdz19VbOQvgukR-snU z00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0KQ2?K~yNuwbR)Wf*=%t zVHWMAMxd42LfT%oy#M0`RFfm%bkl$3%%_9G5g|;dGlCJq)Eh>G2I0U_7*K*Jb(&I2 z73wSaHjLtzwlFb`G_!FRxzMFNn@oYtoM>*yp|G(`=8L7$6@ZOWt+!5v;BKrKlwJg= zGe6cFwdNKo8+ZGIdQR{)M4nGMo)V~H8_!b8OAhZnaJ-PJu=my=Yp%EZ=UBSfw9oAS rX!;prgFOSLNAuyxjArjp_x%n}e61XBcE5YT00000NkvXXu0mjfyCmn# literal 0 Hc$@d(f!s+^9jn~ScWlA(`)+||ygnTo=;tL*9J;@sER(8-~Zf|`hUnu&SV&c@Tp!R6rF zzOtv)&Bgrt`{3Er@9gNvy|kW-e4mberj&)QosO!Rhf6XbuK)l519VbOQvgukR-snU z00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0KQ2?K~yNuwbR)Wf*=%t zVHWMAMxd42LfT%oy#M0`RFfm%bkl$3%%_9G5g|;dGlCJq)Eh>G2I0U_7*K*Jb(&I2 z73wSaHjLtzwlFb`G_!FRxzMFNn@oYtoM>*yp|G(`=8L7$6@ZOWt+!5v;BKrKlwJg= zGe6cFwdNKo8+ZGIdQR{)M4nGMo)V~H8_!b8OAhZnaJ-PJu=my=Yp%EZ=UBSfw9oAS rX!;prgFOSLNAuyxjArjp_x%n}e61XBcE5YT00000NkvXXu0mjfP7d59 literal 0 Hc$@d(f!s+^9jn~ScWlA(`)+||ygnTo=;tL*9J;@sER(8-~Zf|`hUnu&SV&c@Tp!R6rF zzOtv)&Bgrt`{3Er@9gNvy|kW-e4mberj&)QosO!Rhf6XbuK)l519VbOQvgukR-snU z00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0KQ2?K~yNuwbR)Wf*=%t zVHWMAMxd42LfT%oy#M0`RFfm%bkl$3%%_9G5g|;dGlCJq)Eh>G2I0U_7*K*Jb(&I2 z73wSaHjLtzwlFb`G_!FRxzMFNn@oYtoM>*yp|G(`=8L7$6@ZOWt+!5v;BKrKlwJg= zGe6cFwdNKo8+ZGIdQR{)M4nGMo)V~H8_!b8OAhZnaJ-PJu=my=Yp%EZ=UBSfw9oAS rX!;prgFOSLNAuyxjArjp_x%n}e61XBcE5YT00000NkvXXu0mjfP7d59 literal 0 Hc$@A+~M$&p3YKZt5s&LVso}~gu!%(#A<-Qana^Q00003bW%=J08rmn zp;pfT0004EOGiWihy@);00009a7bBm000XU000XU0RWnu7ytkOzez+vR4C82)7cV& zAQXUM7VV@)pq1L<(q6W`|JwyrlOy1C(|_g6r-Q-~Axx+>f)T>h8AgP9{=ky&QGzJ7 znp8?9sw?<548oVTFw%__vvC)>P{ll%Oo7d;sBcIiGck|mi>1^RfQf}%uZ)G@cB~lW zP6VhEH`Z&p<_0Pgx4XT3PVf=}$EO^Q5mcdxr=lp%DZKZ<@=`gMnh#5QG<%1-?{|0tlM@`L)LY*J00000NkvXXu0mjfq$Sj- literal 0 Hc$@KrpGtx2BZCm|N~e00003bW%=J08rmnp;pfT0004EOGiWi zhy@);00009a7bBm000XU000XU0RWnu7ytkO*-1n}R4C7-)7cV&FboCILXb!?6cNE) zabN!b|4hrER%&0oPfX9yWHPA`K?u%-fe-=wOdvvt2C*Z92toniG*$q{IL0o+M85SK z5z|p0iIXXnkFjv0&ZNYt165VeGNL&qF64qa(E^uzE>|o0jccOKtF@y4=eCuqLbP$$ z5bbfU)cBB*g~xTy{Y*~!Zoa`rlVfcPH|Nb6@$!j0gR1T7deU`oc0)Y9yT%fWw1+(R zL?dD>lhj5%mJT@=AHx4=(&FpWhti;D!SrfA95JiuU25NRcmi$N8@(N)Mi2l1002ov JPDHLkV1n13;;aAw literal 0 Hc$@#ip*Z zjQp{J{c!o{Vnv5olo_QAxcwYiq3s)fPCq^z)w<>uPByq&wgq0rL4@$&2H z>*3<#*0s5r!NR1hu#Dm3)~>RTt+9^e=GwBgl&7hLz`>)duZ!X1)!^dO-QUlqs)WD7 zqN}iq?Cs>t&$!>=(zLjjrm2IguZsQs{O9T3x4WCJv5mgLq4V_a)z`$p!=}5wo~5aR zzQLffwvxTTpQWgRzQCZXu8OR$i?Fkhv9yu4xR$)Woxi}JtFDQ#vyQ8-h@_{2zQCWV zu86O*j-;o7zQCUF@#)>(&abkKy}zBQt%tq7ousFLwYZd{r+}!eg|xVoyuY2gzMG?_ ze-(@54gdfE2XsK6X=F*rwKKNNkSqGkyW)8=-A&o_Yl{&FOPL;hjf zd_D*SMi65dN5{q|Cb3R2Mq40&u@$LtnlUyrh<7tan;K`4AYhym<`;xTj7tJ-7=yuO zB#IP8QJnW;6h+#CL2Z<BxhqN5T+p*0i^V_e_Z^wM{>!o#$MLJ*12L6*P1vc0n_ zNB3xpM6_{VlB9z}Sr(4gDlry2MrxE~)ye6xDi(uy{0t=$6cgtcmsi(k74bO6Bub?y zrf!lN-!Yx0nEo9znJiKiipp(PBa?w#E{_!Dj_2={hrC8Ehp~Vj%c2DhH5L(97Ad(f!s+^9jn~ScWlA(`)+||ygnTo=;tL*9J;@sER(8-~Zf|`hUnu&SV&c@Tp!R6rF zzOtv)&Bgrt`{3Er@9gNvy|kW-e4mberj&)QosO!Rhf6XbuK)l519VbOQvgukR-snU z00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0KQ2?K~yNuwbR)Wf*=%t zVHWMAMxd42LfT%oy#M0`RFfm%bkl$3%%_9G5g|;dGlCJq)Eh>G2I0U_7*K*Jb(&I2 z73wSaHjLtzwlFb`G_!FRxzMFNn@oYtoM>*yp|G(`=8L7$6@ZOWt+!5v;BKrKlwJg= zGe6cFwdNKo8+ZGIdQR{)M4nGMo)V~H8_!b8OAhZnaJ-PJu=my=Yp%EZ=UBSfw9oAS rX!;prgFOSLNAuyxjArjp_x%n}e61XBcE5YT00000NkvXXu0mjfP7d59 literal 0 Hc$@`z-dV79xa&^7EzK)QSi;a=Aw6=nThmn$$o}ZwOkdlOkiIbI?kCBvc za&&cec-`II=H}*pe}dH2)smE#Z*g;qjE&*p;*gP&eSm@G=H`2SfpK$n_xJaJf`yTj zmWqszZg6t1udwUu>!YKjT3cOYWoM9*m5GawZf|mYeSd<3g{G#c*Voy>!or4!iEwdr zYHV(Eba!oTae;z_rKYEbh>C)PhH`Utj*pRvii>-Ee{ytpg@=l4Zg6jKb7p61VPa)o zlW2ti000McQchCi{2GPVK$JDN^X)nO4l}&4{2q(p4)?AWW zKxtrTS(&|&y}GhD#rk7=7*ywUsLu~O+w(oUyLSk=8d~aVa3SPs{U;Y{BuJ{Q4vHii z5ka)K)zwZA$m7{jMUx#nK?g}!pS>Or^7(dE_;)kEb^4u=&sU37fa%(^HwgB1cQGtg zi=mL$>*acS`}X6S{=VJ=91KGt#Pfc?|DZ51h;i`H;Uh=G7!M8z5g773ibgq(62*RK z$3$=(>IqSlI4&A>;z{w;5XPrPd3abBM=%~bBc634%PPt_^*qL5UcI0wF_ni`FRCqy z!m=nHm!wf`4C674kw8L&uV2zGOHw?J0)b>w*Mk_3>ueIR`grX=5cq#gr6@`;CNQ2b z1PWX+u0k|=C@Pggrb*M2)-^bZU`^3<+M0w7UALk%ZJJJ;wr1eZ%v$n|h-}TmQD&^0 zPL$Bm*?ayDn+V;2D2E?XKb8G64bgN!2 ymsdK|TjpjHavOi!{{Cazxp5=pZZ_&_cB7w!+Or2L(#P!p0000n{UR(*k@#T+f&B{{R1fPBN6>(hK(z%Zp^2`nAJdn!+)cE}NrQ^%{uk2m3t3U7UlY2|j^-h=Yt}b;y zn8&j_i+fXZ%+Ug#pWnXBkK{bJY2Ja^^{W%O7DRGBym;*M+n1YCxYs3d-9El^c^ucC zY@V~Fyz7&=7DscPEao+ysCXUdLave^zhDLi)wLhh-+$KlR3{pz2oz;Z@^*J&{LiF& z56HRe>EaloalUraOQ9wMo|bObe%3V^E=Pqe_pJY3Z<&2i^5~Vno9F&ibDzv{agv_I z&P`7_*7J7Xnm6G?^Mt?$&RpU;mqlb5Tf`Vm->fcIHSL%cP-|i&W2)e=^_Zc-CdWmb zBFv>{N|xCkoYNYSF+b_#K~bT%**QN>%+NSGZT-y5(}6d3`4&%UY!)oR<-Hem!Tu+Q%hYzl~dj)IZNOX4h9MG+lhmu060~P1!HeOg+b!Ue|B$ zt=s$HKJ#Rah+i6CGRxk&-xWPH*_Q9?lIJ#zxl^?Rrnm(;06og!>FVdQ&MBb@0Qs}a A;{X5v literal 0 Hc$@n{UR(*k@#T+f&B{{R1fPBN6>(hK(z%Zp^2`nAJdn!+)cE}NrQ^%{uk2m3t3U7UlY2|j^-h=Yt}b;y zn8&j_i+fXZ%+Ug#pWnXBkK{bJY2Ja^^{W%O7DRGBym;*M+n1YCxYs3d-9El^c^ucC zY@V~Fyz7&=7DscPEao+ysCXUdLave^zhDLi)wLhh-+$KlR3{pz2oz;Z@^*J&{LiF& z56HRe>EaloalUraOQ9wMo|bObe%3V^E=Pqe_pJY3Z<&2i^5~Vno9F&ibDzv{agv_I z&P`7_*7J7Xnm6G?^Mt?$&RpU;mqlb5Tf`Vm->fcIHSL%cP-|i&W2)e=^_Zc-CdWmb zBFv>{N|xCkoYNYSF+b_#K~bT%**QN>%+NSGZT-y5(}6d3`4&%UY!)oR<-Hem!Tu+Q%hYzl~dj)IZNOX4h9MG+lhmu060~P1!HeOg+b!Ue|B$ zt=s$HKJ#Rah+i6CGRxk&-xWPH*_Q9?lIJ#zxl^?Rrnm(;06og!>FVdQ&MBb@0Qs}a A;{X5v literal 0 Hc$@n{UR(*k@#T+f&B{{R1fPBN6>(hK(z%Zp^2`nAJdn!+)cE}NrQ^%{uk2m3t3U7UlY2|j^-h=Yt}b;y zn8&j_i+fXZ%+Ug#pWnXBkK{bJY2Ja^^{W%O7DRGBym;*M+n1YCxYs3d-9El^c^ucC zY@V~Fyz7&=7DscPEao+ysCXUdLave^zhDLi)wLhh-+$KlR3{pz2oz;Z@^*J&{LiF& z56HRe>EaloalUraOQ9wMo|bObe%3V^E=Pqe_pJY3Z<&2i^5~Vno9F&ibDzv{agv_I z&P`7_*7J7Xnm6G?^Mt?$&RpU;mqlb5Tf`Vm->fcIHSL%cP-|i&W2)e=^_Zc-CdWmb zBFv>{N|xCkoYNYSF+b_#K~bT%**QN>%+NSGZT-y5(}6d3`4&%UY!)oR<-Hem!Tu+Q%hYzl~dj)IZNOX4h9MG+lhmu060~P1!HeOg+b!Ue|B$ zt=s$HKJ#Rah+i6CGRxk&-xWPH*_Q9?lIJ#zxl^?Rrnm(;06og!>FVdQ&MBb@0Qs}a A;{X5v literal 0 Hc$@n{UR(*k@#T+f&B{{R1fPBN6>(hK(z%Zp^2`nAJdn!+)cE}NrQ^%{uk2m3t3U7UlY2|j^-h=Yt}b;y zn8&j_i+fXZ%+Ug#pWnXBkK{bJY2Ja^^{W%O7DRGBym;*M+n1YCxYs3d-9El^c^ucC zY@V~Fyz7&=7DscPEao+ysCXUdLave^zhDLi)wLhh-+$KlR3{pz2oz;Z@^*J&{LiF& z56HRe>EaloalUraOQ9wMo|bObe%3V^E=Pqe_pJY3Z<&2i^5~Vno9F&ibDzv{agv_I z&P`7_*7J7Xnm6G?^Mt?$&RpU;mqlb5Tf`Vm->fcIHSL%cP-|i&W2)e=^_Zc-CdWmb zBFv>{N|xCkoYNYSF+b_#K~bT%**QN>%+NSGZT-y5(}6d3`4&%UY!)oR<-Hem!Tu+Q%hYzl~dj)IZNOX4h9MG+lhmu060~P1!HeOg+b!Ue|B$ zt=s$HKJ#Rah+i6CGRxk&-xWPH*_Q9?lIJ#zxl^?Rrnm(;06og!>FVdQ&MBb@0Qs}a A;{X5v literal 0 Hc$@n{UR(*k@#T+f&B{{R1fPBN6>(hK(z%Zp^2`nAJdn!+)cE}NrQ^%{uk2m3t3U7UlY2|j^-h=Yt}b;y zn8&j_i+fXZ%+Ug#pWnXBkK{bJY2Ja^^{W%O7DRGBym;*M+n1YCxYs3d-9El^c^ucC zY@V~Fyz7&=7DscPEao+ysCXUdLave^zhDLi)wLhh-+$KlR3{pz2oz;Z@^*J&{LiF& z56HRe>EaloalUraOQ9wMo|bObe%3V^E=Pqe_pJY3Z<&2i^5~Vno9F&ibDzv{agv_I z&P`7_*7J7Xnm6G?^Mt?$&RpU;mqlb5Tf`Vm->fcIHSL%cP-|i&W2)e=^_Zc-CdWmb zBFv>{N|xCkoYNYSF+b_#K~bT%**QN>%+NSGZT-y5(}6d3`4&%UY!)oR<-Hem!Tu+Q%hYzl~dj)IZNOX4h9MG+lhmu060~P1!HeOg+b!Ue|B$ zt=s$HKJ#Rah+i6CGRxk&-xWPH*_Q9?lIJ#zxl^?Rrnm(;06og!>FVdQ&MBb@0Qs}a A;{X5v literal 0 Hc$@ zb1f}xsbHa8iWi~?fr8=#Po$V=P!kgpc_2QR)JUQdd=?d-s4qqzFzO4+3lcRNW57g+ z34{=+fJ%YdGBdPiEqy#v$#pM=P$N{YBbUml^3Y>F?4i=a}$$I&qFk?Xs(}#%wWQt+&iocijK* zv19jrW2XSZ#v4&9e|H7udqcXt5|f1BEUhN0Dw5oB5-B<uAsE3FQzOfaBF}m5`PZ4Bn`3cli7$Nd zzPq3J)z6;VF#xo3F^CST3ZlqGyQpHSh#3ixF#~x}iacjxVuFVreTwVue1JoD9OHwZ z`Z~WoF?IB--+bgZI|G2B0+}f)<1ZCZ5kzVh7&EYMHUnxVEGQx5w5Rv5_ll43f$NTN z*|kS#?>)@3uXK4UyW_6gj(+7k+qxj5rh8O@1;v2mKuUFVhlrCR+;+I&b+B&7(F;ch4u&CUd;+Bbd{`1bz8VF{mIf1hXmfI8hfXhm@4egz1?Mv66gS1^|j28qGj&kT7vlQUq{!k~>x99KEqX zROmF7tM=!dI8}leAvZK8EIBbkbfw6Y&SZzYIqe%`=~e(xaB<88QAO3zpoj^>Qn>k` zarm-YglCr%MN|3c<$?Y{P*Kbb)eGXRtu(A4Z)9nE02yQfiad~9Nv;ekSX&pC`oi8R z;nu4&=6lZCNO}51q%2`-SI(im#_}2rDq$FvB#Mht6voUXNS1y-<5UCivH)^TtAAchiXiA3qrQ#a}Ac2abv}7)5TmR`}e< zGX8NU^5UD3^Q*$HX#*21GqMasWcw6QGFCiHG=x@ATAB0ZTa zC}TYz2yo`z$l-m;mp&O7tSe`G!ljeOC<%Un-ksRc0^~WIUV^2lbkCQh1CbKpwK;h1 zq|?j=amUBCVK{;VQ*g`Gk$)~EPMmTq3uGbD$TO-0`4$@35&!}&U_q#o^XY4qOD2Wx zn)AZjuxA290he)Y5IPN59y)KIb5={?_UnWf-f~0@3xWj@-JF6gZ75n-F;Qq0N;NLS zXBO*b5KQXcV1h_}t|9~#7T0RpXQzzo_ZwNLdrM<%?;UC2A|hyI&UDB4l$?cC2X_RabP%x}0LZ{(JQpY=j6b|C^s7Xx0B}-r955F= zU0gLGR`BOHg#Hjjm0%D8wDa*7?NkG5;0mKut31;>-;N+Eq;a!x67E`yF(wkqdgWM9 zM5L~!sh%%HUA6^aA~M|pDWQx|7^EnaQ5ls^zl@9`v~nd^P*+CLQJ|SA4)jM(^tule zp=MyVy(Iyg0i+-@O0_yyD_B^$poV(C=Y~Lc4Ho;*%%Iz^pWLt?o_GoF{RI5{`T8fr z2>r6|uB{59ZpN_51(t#&fnln3uyGxjf&YZK5a zU^oJg&@G|ahPid literal 0 Hc$@i8xs(Fd3fRC@0p}nH4#h9bNkD9oaqP?H1#EO=*e2A)mjIEodzl|`Tzg_`uhCd;p&o|x$*M#?C$fRsl$JZtf;Wa$=|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8wnRVf(hTfie*_UqIns?!!fz^*_&xKdfh+N&Ceb0tk(~M)>q=DeD zlGv7Q-k^Z+)WrPw^8f$;{rmXy;MCZdbNKS^`0eK3r-aalTj8&c?aR6C(ZSb|YSWEo z^xxImn045hc=qGm^WM+WieTo!s^hSg>BX?rjAG@upYF`M_UGZ=sf*s6c|85BVE_OC z19VbOQvgukR-snU00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0UAj} zK~yNueUs^Sf-n$;15vgaiBc9TNL!$`Xpy$>|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA85&o@_Z4z=KgV0o)q8gS3h4UuDq>Vk6AEa`!rs-HmQV#zEOPa3Qv7vj-TqtC{pUnYJKu zSE19W7kFVe&FrmN84Xp5DdSyvwt)qFNxL{~&TF$WPHam$n$ZCkg${g3y)tFaOCu6L z?e_!km=M~tW}`K7_r38DRj2)O955m4%!U^kB!Nq=1j%|!(g||w9Dfo&&8?^ zF<~@KVV(4vT?;}%!Bi{sE$d=oddRils-i(fLpbo;t{9<-exZgEagQHI!RQq%`3D}S z<(_q}p@uUFLRne)4qtyKC<@BW(qUW8a_PF1dK;EFL{N?_S*@$Mr7VSuYr#TLz6}$U cgN4PlKMBE*Bq%>al>h($07*qoM6N<$f)Nx07XSbN literal 0 Hc$@i8xs(Fd3fRC@0p}nH4#h9bNkD9oaqP?H1#EO=*e2A)mjIEodzl|`Tzg_`uhCd;p&o|x$*M#?C$fRsl$JZtf;Wa$=|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8;QohTg$>*}QJty?5ckfz`EW&#za}v0UB2eb292)3amT#DU<; zlGwX#-ob$I@^8f$;{r&j!>(to0bNKb{`19u9$Ar+ZTj9)(?cKTU;KA3oYSXi3 z^y<~xyLH&Rc=qhv^Xt#jvS8-cs^iO*>DjQ|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8%A_P)XZW z(9GLpScg|jdTCvZ?dYU|>2xc;sy`(8QnuB{jQH!N0eyH46e_}Pkud$`WHef-Wi8xs(Fd3fRC@0p}nH4#h9bNkD9oaqP?H1#EO=*e2A)mjIEodzl|`Tzg_`uhCd;p&o|x$*M#?C$fRsl$JZtf;Wa$=|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8o4$VCh=hng5)7?wo(-+&SO9l$w$d zIFmY)LZJk1OJt^#C+&62@FO#`_Io9TG6hTBp1Fm*DwL}K1iukNs+Hf-#_u3=YrBB= zzu6JnXRHHRpx>Rb799C&?ozLe(F@n$<07$OZ5GIQi zdEG)x=1%+T*CJguyWQ>tq%t+C!8C+Su5>zGqoXe+Af!Sy29rgt#>U3RdwcuzMzcf; z8BAuG48|~Ap+F=6h^SD#!32?WQCtjwdV>*!u@+l5~&oD3nfx=ZCbrSCVDI5Os*9?E)brSGEZ{gc5+H(;)@`Q2vI|dG{cj#2IuvJtc!eYrakM zHs5Zpqj||P@RNcHvNdI2asqQzmwnOK6w1`PZOph#2KH@*Z|B^!;Iqy5R~&?+;w%+b z+O3ieac@;Q6633>ereNo%t_muc_Ri{6g-C+UggpLHS1(fG3YI05Bi8vf-^ehn)6h4 z<@gsA_q@vW^-*R1bKlO&n3@#MnbFo}=!v{)v6O6IdM2%Q(Cuny_7qmLmilqC#UT%N zuAMmdd`H@{0_yy&=8N@fsO79CIHTq|_AK*qHcY=`?7PL~as#L5B}X6lh<3QP4KAR^ z)L8nD=dQj<0$jgtLC%b@8!3vC_~iKg{s(#LY1xN%vf1n_wV@;S4Mn2>pYbB;WGZ`C zqt3OF&R{U|PbH|HUTjpU=waT&@%8>=<51y}Zx{>n2K)o8yXh}gKi*y3W?xmYPO}gx z&jX9ndArb}`KP-Xmto{rPGj=yqj*u~Gj6XX)-fq5akeP<^Hcawe%N;BRJ$mi|< zvXpQWYp83>TBtZ@Wy0kb%Ws`O{V*nc^O^A=Vbl&^Z1Bw3tO%lb(rn{UR#sNMduK)l4-|pSL!)F2z?GBxM zGI#m;+Vuy+r=P1@2bB8p>dVR8rDuy*A4!_`=hvU-n=ilId%dZ{U@}lUb4ie2Fhjuo zu!Ujgfn3HUZ+91l4pvzYAcwQSBeIx*fm;}a85w5HkpK!7c)B=-Xq?}A`8HRB0#Cz3 z*(+!I-v8gCD>!L}cJn{&kIF2w89dgwaWvfFX}s}s!4p|wgVk!MbSl=hMQ;+-i8xs(Fd3fRC@0p}nH4#h9bNkD9oaqP?H1#EO=*e2A)mjIEodzl|`Tzg_`uhCd;p&o|x$*M#?C$fRsl$JZtf;Wa$=|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8o0+SAps|Lsz@59u zs;0M&v%#I*`~3Rl>fWijlJxcQ@bm1o!=Re1f4a!1)!oV2;LoMDjG?lK z=j-3ByOgZEmh0~1;^^9)uY=3fy}!$?&)CABv4zFZwbtIs?eOTh#ig*nn82wQRR910 z19VbOQvgukR-snU00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0UAj} zK~yNueUs^Sf-n$;15vgaiBc9TNL!$`Xpy$>|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8;QohTg$>*}QJty?5ckfz`EW&#za}v0UB2eb292)3amT#DU<; zlGwX#-ob$I@^8f$;{r&j!>(to0bNKb{`19u9$Ar+ZTj9)(?cKTU;KA3oYSXi3 z^y<~xyLH&Rc=qhv^Xt#jvS8-cs^iO*>DjQ|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8;QohTg$>*}QJty?5ckfz`EW&#za}v0UB2eb292)3amT#DU<; zlGwX#-ob$I@^8f$;{r&j!>(to0bNKb{`19u9$Ar+ZTj9)(?cKTU;KA3oYSXi3 z^y<~xyLH&Rc=qhv^Xt#jvS8-cs^iO*>DjQ|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8wnRVf(hTfie*_UqIns?!!fz^*_&xKdfh+N&Ceb0tk(~M)>q=DeD zlGv7Q-k^Z+)WrPw^8f$;{rmXy;MCZdbNKS^`0eK3r-aalTj8&c?aR6C(ZSb|YSWEo z^xxImn045hc=qGm^WM+WieTo!s^hSg>BX?rjAG@upYF`M_UGZ=sf*s6c|85BVE_OC z19VbOQvgukR-snU00001VoOIv0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA0UAj} zK~yNueUs^Sf-n$;15vgaiBc9TNL!$`Xpy$>|8SjzBSJ#GKR6t|WY!B~mdzJsTZ~!! zUs=rNtF^PB^@`h!^O3n8$#-v*bArQr0C>{|)R#L91>$xWMbVQyG>5T3NYYGd|Exp6 zLr$P53VP^JV3K9&vBVNyUUi5_SKh{wG+4@bTdK4IVctw+N{Wkh?89O~SwmR7J4P#~ z57o)%0kJZN(4JMJ=>^f9Lnmwz;Y;HE5xaR#I_puH(8$C?xsz%)RTvP4bR5wHN_Fkf zuK}+oN{Z!$!X_w;@#Rh5e-q0A!JO@|u>?>A8iZYR?)&Qx?OKHT%I<<-@Qs4XU@^I? r?z-CZgNebUHGdbRtohK?kA8Z7Avo>aBMz+Lcm4p)u-ppcS zS9Ie!mdKH}%CNaG%2Fm}LDA&{cV-4O%-x-Jb=BO>jVx2RS*+6!f1PJp%yxt|C_zu4 zVTVFhL9ydK$45)JZz2iAy{a}GJ*A1S81B+O#vz9P0==#pu@73(Z00N$_elF{r G5}E)2mS$%F literal 0 Hc$@;BalXZIA`yF3*rb@^!hkMFN;o$CX#y8@z9fx4JWg8YIR0`7+`3_B0xGA4Pu zyYPP7lK2(K;VkfoEM{Qf76xHPhFNnYfP%T6E{-7*my;7h%-Wch8QBt(RT54tcr%NQ zUD1u_SRzN>D#PZ&C`*}?1x1$++?g5BFn4#>)m3vhH?mCOX0c8`{B@pXG20Q=paeaE zh8+r31;vi{93M69VSd=u^p59*^@9b@TsL?U4lH7|&RW92pzAwpzgM#t0|Z7Avo>aBMz+Lcm4p)u-ppcS zS9Ie!mdKH}%CNaG%2Fm}LDA&{cV-4O%-x-Jb=BO>jVx2RS*+6!f1PJp%yxt|C_zu4 zVTVFhL9ydK$45)JZz2iAy{a}GJ*A1S81B+O#vz9P0==#pu@73(Z00N$_elF{r G5}E)2mS$%F literal 0 Hc$@;BalXZIA`yF3*rb@^!hkMFN;o$CX#y8@z9fx4JWg8YIR0`7+`3_B0xGA4Pu zyYPP7lK2(K;VkfoEM{Qf76xHPhFNnYfP%T6E{-7*my;7h%-Wch8QBt(RT54tcr%NQ zUD1u_SRzN>D#PZ&C`*}?1x1$++?g5BFn4#>)m3vhH?mCOX0c8`{B@pXG20Q=paeaE zh8+r31;vi{93M69VSd=u^p59*^@9b@TsL?U4lH7|&RW92pzAwpzgM#t0|Z7Avo>aBMz+Lcm4p)u-ppcS zS9Ie!mdKH}%CNaG%2Fm}LDA&{cV-4O%-x-Jb=BO>jVx2RS*+6!f1PJp%yxt|C_zu4 zVTVFhL9ydK$45)JZz2iAy{a}GJ*A1S81B+O#vz9P0==#pu@73(Z00N$_elF{r G5}E)2mS$%F literal 0 Hc$@;BalXZIA`yF3*rb@^!hkMFN;o$CX#y8@z9fx4JWg8YIR0`7+`3_B0xGA4Pu zyYPP7lK2(K;VkfoEM{Qf76xHPhFNnYfP%T6E{-7*my;7h%-Wch8QBt(RT54tcr%NQ zUD1u_SRzN>D#PZ&C`*}?1x1$++?g5BFn4#>)m3vhH?mCOX0c8`{B@pXG20Q=paeaE zh8+r31;vi{93M69VSd=u^p59*^@9b@TsL?U4lH7|&RW92pzAwpzgM#t0|=)Q}fQv zDZRP2>;BFeXQvn3+cp&_b$LnskB_fzZRi8C-H+|%1L|Tf3GxeO2)G}%Fzh^#%b4Wt z?!x7O1W@JlDR!KOq;LR*H zc11UyV~HGjs|=e9qby}o78G4RaA#&f!`$6jS69v5+{iM8o5ec)@Yi{k#cW4dgA()v z8g?jD6%;$(b9~gahxuVs(>tCM)(;jqbKT%cIIxJ-I%^37gRbwa{a(#p3?Sg?>gTe~ HDWM4f#oK2+ literal 0 Hc$@-EuXw;i8{P)Fh+T3Rd-=O5Z&bd zD?*NqP?e!s?z7@JVbpuR-}=LS!g<^wj5)sJ#IQ$zM_5RilTwYx1rHibw&qilr8N(m c`sh2n06ml%2ib`sgM}QCTi*P+d9| zT1vzcJw(na1@Qp5E&ey|`fl8mGsD4eVhiq?Q^J}TS`N(|1B*u|I(TW(wx+~A>lVa* z4DT4}x^H9f^~!Be+|)5KtYcN#hEeBD;u9vZm*sF~>>07|Gj5=XXm~~#aj2!|k$GP| zCGe1gAZ@#DS+-(HOT}0gt0aD^dG3QdsJI-c34*`yz=UN?9Xrnxtah-7kEn#RggZvM z)-<)0!UbvIC?0~d(iF6s;I++x=^WrLrd?9g)ytc4RxN+VDmq$9E~Hvb*|3UUYPWvI zn1eif_N$YkRoW?NNc-BbQi!g4`yW5#`QqG`y2c3?c2pe=^?2(Kw-;ufH;F2i00000 LNkvXXu0mjf(#_UB literal 0 Hc$@J;fTxajww{Qxn}xNVhop*g%)PJw|Nph0 zjjEA)>*e0!+0dbfaIBPmubG7W`}*9`$GWGK<=oZ!_x7ogdZUPO(#E;=^zo&QcCng+ zzN?zbx~uf??AFP>qlt3y?C0m;*~GJ=u$Y3YlYEB^lYIaH00VSVPE!C--&Ubk&j0`b z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_009+AL_t(2&wbPBc7iYv1z;dL zUS)HHs^Eg++Lpfm>vfUB~gIH)mUHI!L}$btH+M0znrmlxH1DPQSsYE=r__jx^|8`d)ORhYkEEN^w62Fq{mH{2jI71lXNm7isZj7DyeOmv!BfUJC)tb3cKY>u&g zn~zCxt9F&Dc$k+~e6D(#|NsA!PNkg|cE!<@59zbEnl000AYQchCtcd1426M88E{Z8 z1A{WaDTv$N|M`}-FdV4))#jroIc=~k+Yt}Lu`SCMPs6qxv2o>ta5uuSgmAq#6TZ#{ zX5+a+Fq9!oL>g|*Cfpeq14){u5dsA>yr&6}O9N$rJrO0?GV@R#=!i;AP$%dEnKU2P z1-w7VQVr3e)_x+#+G!PZTUt)U@Zt3{=F6de^*j6m1JWSC=gq@}00000NkvXXu0mjf1oivn literal 0 Hc$@J;fTxajww{Qxn}xNVhop*g%)PJw|Nph0 zjjEA)>*e0!+0dbfaIBPmubG7W`}*9`$GWGK<=oZ!_x7ogdZUPO(#E;=^zo&QcCng+ zzN?zbx~uf??AFP>qlt3y?C0m;*~GJ=u$Y3YlYEB^lYIaH00VSVPE!C--&Ubk&j0`b z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_009+AL_t(2&wbPBc7iYv1z;dL zUS)HHs^Eg++Lpfm>vfUB~gIH)mUHI!L}$btH+M0znrmlxH1DPQSsYE=r__jx^|8`d)ORhYkEEN^w62Fq{mH{2jI71#p48gqtcd1426M88E{Z8 z1A{WaDTv$N|M`}-FdV4))#jroIc=~k+Yt}Lu`SCMPs6qxv2o>ta5uuSgmAq#6TZ#{ zX5+a+Fq9!oL>g|*Cfpeq14){u5dsA>yr&6}O9N$rJrO0?GV@R#=!i;AP$%dEnKU2P z1-w7VQVr3e)_x+#+G!PZTUt)U@Zt3{=F6de^*j6m1JWSC=gq@}00000NkvXXu0mjf=Y;s# literal 0 Hc$@^a8(M;|2|W*g_kKwI^1N zd^%_0)>T&XoAblhAA9)RjjuiY#(%u@{MOrRpF`-Eg!suEbw3XP;@u?#M76HdUi!pG zKmX|`@BiGdEX^D|KxiDkao_{ENVE~*BKU~zCd3$tBI3pk_Tq?iBsp~K1FPwL_cJ@| zS4!9T7i1?A%!Krw0PztJ;o6G$SacVTtEV4%;&(oC!-IF@TL)4g6NOX?ftY}sfM_FT zCWAKMcPiY~igs;?y;>3#w3}mQdb4Wo@cctpw>Rc$Z@)$+{#OmC`{!36-zDa|pf>fw zQJ(&VCqDbg@-0X4)}k_l?j*>>Af*I<4PJTFHF(&m!AD|g@uL<$Zjl{LAkyrPNTOnT zW$r^e+gG~Ixo?uZTk}Y59NIXPY{{q8pZwe(e(I6gBg=#s(A^Z(Nm0EFJ(VFl25A&R z2+VR1B_v#JQBt9zL?Tf_A`!Tql60|$5CYdYI*sAv;Tt}$t^CdCGQlc}1?k4EW5K9Lxk}mcq zH>Gv-RL=7BfhXi{II0Gq1|UU<__|FF-mv;d#~!!?TQw+SP+5Y^3^G+rDl-yYKp2J0 z4dGIO3KA6+I!bhwpclF%*G-`pI_Qp}y*wa0JUcn-J-&6!<^B0w#>a)Nkgi55o1mlo z!r z3K1gd$`rn_1m}gP!YZ|FY zx;fqBi|Bqz@!d<4Kip{u+ZE{@i_o3HKEIBuTAX({9PO~cjcZ)h(2h##jR6Ngb{lKY zzDlui1%OoPlT{gSlRDM6sYKC?O6vVF)z&`6+AhV#9g6ciRO|bsD^rBl(QJ)yMMDq) zS2k4NUc(26e&8TZdR%ZcSI0D0hP1;H*H{99WUXsMA%gopAUw@L3_CVTiW+kf>E_2vMl z1I{~a+mbBxv30}lD;IH1OC%CPm^2|Fj%gvqw5@Bfk*GaUI-+s}EQv`-4)j?2hZiwv zLTFoD+aNs%2Uw4gisHF5BjTrXvQU@%R6M(&)|b6$Uul5B2jzi_v>*9KrZ0bz;Sbk&^UuFVwKpVc zD7JPCX)l_eO55r+Pu1qNp~DX#2cOoDYFb4}}7V$q^I zDRzATZjw)L9T-99#3HJf;>w14Fv8>U!O;#|+M+}x3Rjjm@4?5j+PnBlH7tm^$D0(@ zRV3XMnHzRrTO&3tp|%JY5nB?a6xKF0JLRM`3xTUz@|775|HjWSzPQ5+k3C8E_#*vN z2if}G8G-_5TdLg=!brSxIPaSvHoJDLYxg&{+pG}Ln=y|qQbS7{qvjHx+Xs~%hb^&n)Ly0ry%X+$OAL9 znTm9A`&H3 zRY5HTvxDuw`Q^d$e|uwd8Qo+ZKsf2`<7o`v@NFxa;K*f$Hj}sBNJDpVmUOm9XdI!m z2n)THdBl2!kcucE1dorNkSCY~TGtTE0=r+Ll8IXiqjt+xb6|#RC|VGJN3zexpD-C|_D9)hWqcN2%YqGU)|fMa(76Tk27P zt0U4&jL^8oQhCX#!Rx>Ov*nqK%{61F?r&D0vBb7TO7$&FdUGn(O}(r~uRlfB=_1Dg ze`!SAC<$u?{=$fOv49H&MkVA&7tl(hwSmGD#+K+OHXR@m7_7=$hsa|^>%Nld=Fwi|h*b?L!{`iIN7$&486+x0&lqG!V^AKvwd{?ra(eeI zUf+A`xotQ2r6|?E2%(9Mfp!{rZ!&q`*-!ux`E23rJwMvN{OJ04@3RNTYac!|cYV4v zzsyuSO`dmDUWE2P~-WU8%{10PV V))u&QBB=lX002ovPDHLkV1ii3RrUY? literal 0 Hc$@tTDc>FxE8p~<(y-0kr9`}_T^y4OZ{v1ER; z%hTq?&*N)_xTCVtbdJ7-nZ)w+`S$qxnX1lqkidPH#NXubp03cHt~6J)00009a7bBm000XU z000XU0RWnu7ytkPU`a$lR4C7VliPNJKnz8JQA8TG6;PvCu;SfnFKYk)&w7#>Tt29I zNyvueOeT1qADFuq_@3vRhvs{MX*9z-)@+!-Go}@(wcFN5v30E%14!07T^IDMdwo;b zPm(01Y(Oc3A!VBAlZ7K@9@B&j=vmFA1oV7S*TIhJ906<)98m$5*%eAawWXH-F;oeOtzQWjE2u`k}vn^{F z4(P;saveJ6e7S)acj3zkC3@k}4Ro79>&yjV4Y7Q2y<2X{4zhlL4_5O}SM%voKl>g2 Y0cCS7#XbA5Hvj+t07*qoM6N<$f + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include +#include +#include + +struct DocsetInformation +{ + QString path; + QString title; + QIcon icon; + bool isValid; +}; + +QString defaultDocsetsPath(); +QString docsetsPath(); +QStringList enabledDocsets(); + +QList availableDocsets(const QString& docsetsPath = docsetsPath()); diff --git a/documentation/zealdoc/util.cpp b/documentation/zealdoc/util.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/util.cpp @@ -0,0 +1,78 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "util.h" +#include "debug.h" + +#include "registry/docset.h" + +#include +#include +#include + +#include +#include +#include + +QString defaultDocsetsPath() +{ + QDir baseDataDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); + + if (!QCoreApplication::applicationName().isEmpty()) + { + baseDataDir.cdUp(); + } + + if (!QCoreApplication::organizationName().isEmpty()) + { + baseDataDir.cdUp(); + } + + QString docsetsPath = baseDataDir.canonicalPath() + "/Zeal/Zeal/docsets/"; + if (QDir(docsetsPath).exists()) + { + return QDir(docsetsPath).canonicalPath(); + } + + return QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +} + +QString docsetsPath() +{ + KConfigGroup config = KSharedConfig::openConfig()->group("Zealdoc"); + return config.readEntry(QStringLiteral("DocsetsPath"), defaultDocsetsPath()); +} + +QStringList enabledDocsets() +{ + KConfigGroup config = KSharedConfig::openConfig()->group("Zealdoc"); + return config.readEntry(QStringLiteral("EnabledDocsets"), QStringList()); +} + +QList availableDocsets(const QString& docsetsPath) +{ + QList docsets; + foreach( auto docsetFsName, QDir(docsetsPath).entryList( QDir::Dirs | QDir::NoDot | QDir::NoDotDot ) ) + { + Zeal::Registry::Docset ds(docsetsPath + QStringLiteral("/") + docsetFsName); + docsets += {ds.path(), ds.title(), ds.icon(), ds.isValid()}; + } + + return docsets; +} diff --git a/documentation/zealdoc/zeal/registry/cancellationtoken.h b/documentation/zealdoc/zeal/registry/cancellationtoken.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/registry/cancellationtoken.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Artur Spychaj +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#ifndef CANCELLATIONTOKEN_H +#define CANCELLATIONTOKEN_H + +#include + +namespace Zeal { +namespace Registry { + +/// Token that stores whether cancel was called on it. +/// In async code can be used to check if another thread called cancel. +struct CancellationToken +{ +public: + CancellationToken(); + bool isCanceled() const; + void cancel(); + +private: + QSharedPointer m_cancelled; +}; + +} // namespace Registry +} // namespace Zeal + +Q_DECLARE_METATYPE(Zeal::Registry::CancellationToken) + +#endif // CANCELLATIONTOKEN_H diff --git a/documentation/zealdoc/zeal/registry/cancellationtoken.cpp b/documentation/zealdoc/zeal/registry/cancellationtoken.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/registry/cancellationtoken.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Artur Spychaj +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#include "cancellationtoken.h" + +using namespace Zeal::Registry; + +CancellationToken::CancellationToken() +{ + m_cancelled = QSharedPointer(new bool(false)); +} + +void CancellationToken::cancel() +{ + *m_cancelled = true; +} + +bool CancellationToken::isCanceled() const +{ + return *m_cancelled; +} diff --git a/documentation/zealdoc/zeal/registry/docset.h b/documentation/zealdoc/zeal/registry/docset.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/registry/docset.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2015-2016 Oleg Shparber +** Copyright (C) 2013-2014 Jerzy Kozera +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#ifndef DOCSET_H +#define DOCSET_H + +#include +#include +#include +#include + +namespace Zeal { + +namespace Util { +class SQLiteDatabase; +} + +namespace Registry { + +struct CancellationToken; +struct SearchResult; + +class Docset +{ +public: + explicit Docset(const QString &path); + ~Docset(); + + bool isValid() const; + + QString name() const; + QString title() const; + QStringList keywords() const; + + QString version() const; + QString revision() const; + + QString path() const; + QString documentPath() const; + QIcon icon() const; + QIcon symbolTypeIcon(const QString &symbolType) const; + QUrl indexFileUrl() const; + + QMap symbolCounts() const; + int symbolCount(const QString &symbolType) const; + + const QMap &symbols(const QString &symbolType) const; + + QList search(const QString &query, const CancellationToken &token) const; + QList relatedLinks(const QUrl &url) const; + + // FIXME: This is an ugly workaround before we have a proper docset sources implementation + bool hasUpdate = false; + +private: + enum class Type { + Invalid, + Dash, + ZDash + }; + + void loadMetadata(); + void countSymbols(); + void loadSymbols(const QString &symbolType) const; + void loadSymbols(const QString &symbolType, const QString &symbolString) const; + void createIndex(); + QUrl createPageUrl(const QString &path, const QString &fragment = QString()) const; + + static QString parseSymbolType(const QString &str); + + QString m_name; + QString m_title; + QStringList m_keywords; + QString m_version; + QString m_revision; + Docset::Type m_type = Type::Invalid; + QString m_path; + QString m_documentPath; // FIXME add to github master + QIcon m_icon; + + QUrl m_indexFileUrl; + + QMap m_symbolStrings; + QMap m_symbolCounts; + mutable QMap> m_symbols; + Util::SQLiteDatabase *m_db = nullptr; +}; + +} // namespace Registry +} // namespace Zeal + +#endif // DOCSET_H diff --git a/documentation/zealdoc/zeal/registry/docset.cpp b/documentation/zealdoc/zeal/registry/docset.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/registry/docset.cpp @@ -0,0 +1,814 @@ +/**************************************************************************** +** +** Copyright (C) 2015-2016 Oleg Shparber +** Copyright (C) 2013-2014 Jerzy Kozera +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#include "docset.h" + +#include "cancellationtoken.h" +#include "searchresult.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static void scoreFunc(sqlite3_context *context, int, sqlite3_value **argv); +using namespace Zeal::Registry; + +namespace { +const char IndexNamePrefix[] = "__zi_name"; // zi - Zeal index +const char IndexNameVersion[] = "0001"; // Current index version + +namespace InfoPlist { +const char CFBundleName[] = "CFBundleName"; +//const char CFBundleIdentifier[] = "CFBundleIdentifier"; +const char DashDocSetFamily[] = "DashDocSetFamily"; +const char DashDocSetKeyword[] = "DashDocSetKeyword"; +const char DashDocSetPluginKeyword[] = "DashDocSetPluginKeyword"; +const char DashIndexFilePath[] = "dashIndexFilePath"; +const char DocSetPlatformFamily[] = "DocSetPlatformFamily"; +//const char IsDashDocset[] = "isDashDocset"; +//const char IsJavaScriptEnabled[] = "isJavaScriptEnabled"; +} +} + +Docset::Docset(const QString &path) : + m_path(path), + m_documentPath(QDir(m_path).absoluteFilePath(QStringLiteral("Contents/Resources/Documents"))) +{ + QDir dir(m_path); + if (!dir.exists()) + return; + + loadMetadata(); + + // Attempt to find the icon in any supported format + for (const QString &iconFile : dir.entryList({QStringLiteral("icon.*")}, QDir::Files)) { + m_icon = QIcon(dir.absoluteFilePath(iconFile)); + if (!m_icon.availableSizes().isEmpty()) + break; + } + + // TODO: Report errors here and below + if (!dir.cd(QStringLiteral("Contents"))) + return; + + // TODO: 'info.plist' is invalid according to Apple, and must alsways be 'Info.plist' + // https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPRuntimeConfig + // /Articles/ConfigFiles.html + Util::Plist plist; + if (dir.exists(QStringLiteral("Info.plist"))) + plist.read(dir.absoluteFilePath(QStringLiteral("Info.plist"))); + else if (dir.exists(QStringLiteral("info.plist"))) + plist.read(dir.absoluteFilePath(QStringLiteral("info.plist"))); + else + return; + + if (plist.hasError()) + return; + + if (m_name.isEmpty()) { + // Fallback if meta.json is absent + if (!plist.contains(InfoPlist::CFBundleName)) { + m_name = m_title = plist[InfoPlist::CFBundleName].toString(); + // TODO: Remove when MainWindow::docsetName() will not use directory name + m_name.replace(QLatin1Char(' '), QLatin1Char('_')); + } else { + m_name = QFileInfo(m_path).fileName().remove(QStringLiteral(".docset")); + } + } + + if (m_title.isEmpty()) { + m_title = m_name; + m_title.replace(QLatin1Char('_'), QLatin1Char(' ')); + } + + // TODO: Verify if this is needed + if (plist.contains(InfoPlist::DashDocSetFamily) + && plist[InfoPlist::DashDocSetFamily].toString() == QLatin1String("cheatsheet")) { + m_name = m_name + QLatin1String("cheats"); + } + + if (!dir.cd(QStringLiteral("Resources")) || !dir.exists(QStringLiteral("docSet.dsidx"))) + return; + + m_db = new Util::SQLiteDatabase(dir.absoluteFilePath(QStringLiteral("docSet.dsidx"))); + + if (!m_db->isOpen()) { + qWarning("SQL Error: %s", qPrintable(m_db->lastError())); + return; + } + + sqlite3_create_function(m_db->handle(), "zealScore", 2, SQLITE_UTF8, nullptr, scoreFunc, + nullptr, nullptr); + + m_type = m_db->tables().contains(QStringLiteral("searchIndex")) ? Type::Dash : Type::ZDash; + + createIndex(); + + if (!dir.cd(QStringLiteral("Documents"))) { + m_type = Type::Invalid; + return; + } + + // Setup keywords + if (plist.contains(InfoPlist::DocSetPlatformFamily)) + m_keywords << plist[InfoPlist::DocSetPlatformFamily].toString(); + + if (plist.contains(InfoPlist::DashDocSetPluginKeyword)) + m_keywords << plist[InfoPlist::DashDocSetPluginKeyword].toString(); + + if (plist.contains(InfoPlist::DashDocSetKeyword)) + m_keywords << plist[InfoPlist::DashDocSetKeyword].toString(); + + if (plist.contains(InfoPlist::DashDocSetFamily)) { + const QString kw = plist[InfoPlist::DashDocSetFamily].toString(); + if (kw != QLatin1String("dashtoc") && kw != QLatin1String("unsorteddashtoc")) + m_keywords << kw; + } + + m_keywords.removeDuplicates(); + + // Prefer index path provided by the docset over metadata. + if (plist.contains(InfoPlist::DashIndexFilePath)) { + m_indexFileUrl = createPageUrl(plist[InfoPlist::DashIndexFilePath].toString()); + } else if (m_indexFileUrl.isEmpty()) { + if (dir.exists(QStringLiteral("index.html"))) + m_indexFileUrl = createPageUrl(QStringLiteral("index.html")); + else + qWarning("Cannot determine index file for docset %s", qPrintable(m_name)); + } + + countSymbols(); +} + +Docset::~Docset() +{ + delete m_db; +} + +bool Docset::isValid() const +{ + return m_type != Type::Invalid; +} + +QString Docset::name() const +{ + return m_name; +} + +QString Docset::title() const +{ + return m_title; +} + +QStringList Docset::keywords() const +{ + return m_keywords; +} + +QString Docset::version() const +{ + return m_version; +} + +QString Docset::revision() const +{ + return m_revision; +} + +QString Docset::path() const +{ + return m_path; +} + +QString Docset::documentPath() const +{ + return m_documentPath; +} + +QIcon Docset::icon() const +{ + return m_icon; +} + +QIcon Docset::symbolTypeIcon(const QString &symbolType) const +{ + static const QIcon unknownIcon(QStringLiteral("typeIcon:Unknown.png")); + + const QIcon icon(QStringLiteral("typeIcon:%1.png").arg(symbolType)); + return icon.availableSizes().isEmpty() ? unknownIcon : icon; +} + +QUrl Docset::indexFileUrl() const +{ + return m_indexFileUrl; +} + +QMap Docset::symbolCounts() const +{ + return m_symbolCounts; +} + +int Docset::symbolCount(const QString &symbolType) const +{ + return m_symbolCounts.value(symbolType); +} + +const QMap &Docset::symbols(const QString &symbolType) const +{ + if (!m_symbols.contains(symbolType)) + loadSymbols(symbolType); + return m_symbols[symbolType]; +} + +QList Docset::search(const QString &query, const CancellationToken &token) const +{ + // Make it safe to use in a SQL query. + QString sanitizedQuery = query; + sanitizedQuery.replace(QLatin1String("\\"), QLatin1String("\\\\")); + sanitizedQuery.replace(QLatin1String("_"), QLatin1String("\\_")); + sanitizedQuery.replace(QLatin1String("%"), QLatin1String("\\%")); + sanitizedQuery.replace(QLatin1String("'"), QLatin1String("''")); + + QString queryStr; + if (m_type == Docset::Type::Dash) { + queryStr = QStringLiteral("SELECT name, type, path, '', zealScore('%1', name) as score " + " FROM searchIndex " + "WHERE score > 0 " + "ORDER BY score DESC").arg(sanitizedQuery); + } else { + queryStr = QStringLiteral("SELECT ztokenname, ztypename, zpath, zanchor, zealScore('%1', ztokenname) as score " + " FROM ztoken " + "LEFT JOIN ztokenmetainformation " + " ON ztoken.zmetainformation = ztokenmetainformation.z_pk " + "LEFT JOIN zfilepath " + " ON ztokenmetainformation.zfile = zfilepath.z_pk " + "LEFT JOIN ztokentype " + " ON ztoken.ztokentype = ztokentype.z_pk " + "WHERE score > 0 " + "ORDER BY score DESC").arg(sanitizedQuery); + } + + // Limit for very short queries. + // TODO: Show a notification about the reduced result set. + if (query.size() < 3) + queryStr += QLatin1String(" LIMIT 1000"); + + QList results; + + m_db->execute(queryStr); + while (m_db->next() && !token.isCanceled()) { + results.append({m_db->value(0).toString(), + parseSymbolType(m_db->value(1).toString()), + const_cast(this), + createPageUrl(m_db->value(2).toString(), m_db->value(3).toString()), + m_db->value(4).toInt()}); + } + + return results; +} + +QList Docset::relatedLinks(const QUrl &url) const +{ + QList results; + + // Strip docset path and anchor from url + const QString dir = documentPath(); + QString urlPath = url.path(); + int dirPosition = urlPath.indexOf(dir); + QString path = url.path().mid(dirPosition + dir.size() + 1); + + // Get the url without the #anchor. + QUrl cleanUrl(path); + cleanUrl.setFragment(QString()); + + // Prepare the query to look up all pages with the same url. + QString queryStr; + if (m_type == Docset::Type::Dash) { + queryStr = QStringLiteral("SELECT name, type, path FROM searchIndex " + "WHERE path LIKE \"%1%%\" AND path <> \"%1\""); + } else if (m_type == Docset::Type::ZDash) { + queryStr = QStringLiteral("SELECT ztoken.ztokenname, ztokentype.ztypename, zfilepath.zpath, ztokenmetainformation.zanchor " + "FROM ztoken " + "LEFT JOIN ztokenmetainformation ON ztoken.zmetainformation = ztokenmetainformation.z_pk " + "LEFT JOIN zfilepath ON ztokenmetainformation.zfile = zfilepath.z_pk " + "LEFT JOIN ztokentype ON ztoken.ztokentype = ztokentype.z_pk " + "WHERE zfilepath.zpath = \"%1\" AND ztokenmetainformation.zanchor IS NOT NULL"); + } + + m_db->execute(queryStr.arg(cleanUrl.toString())); + while (m_db->next()) { + results.append({m_db->value(0).toString(), + parseSymbolType(m_db->value(1).toString()), + const_cast(this), + createPageUrl(m_db->value(2).toString(), m_db->value(3).toString()), + 0}); + } + + if (results.size() == 1) + results.clear(); + + return results; +} + +void Docset::loadMetadata() +{ + const QDir dir(m_path); + + // Fallback if meta.json is absent + if (!dir.exists(QStringLiteral("meta.json"))) + return; + + QScopedPointer file(new QFile(dir.filePath(QStringLiteral("meta.json")))); + if (!file->open(QIODevice::ReadOnly)) + return; + + QJsonParseError jsonError; + const QJsonObject jsonObject = QJsonDocument::fromJson(file->readAll(), &jsonError).object(); + + if (jsonError.error != QJsonParseError::NoError) + return; + + m_name = jsonObject[QStringLiteral("name")].toString(); + m_title = jsonObject[QStringLiteral("title")].toString(); + m_version = jsonObject[QStringLiteral("version")].toString(); + m_revision = jsonObject[QStringLiteral("revision")].toString(); + + if (jsonObject.contains(QStringLiteral("extra"))) { + const QJsonObject extra = jsonObject[QStringLiteral("extra")].toObject(); + + if (extra.contains(QStringLiteral("indexFilePath"))) { + m_indexFileUrl = createPageUrl(extra[QStringLiteral("indexFilePath")].toString()); + } + + if (extra.contains(QStringLiteral("keywords"))) { + for (const QJsonValue &kw : extra[QStringLiteral("keywords")].toArray()) + m_keywords << kw.toString(); + } + } +} + +void Docset::countSymbols() +{ + QString queryStr; + if (m_type == Docset::Type::Dash) { + queryStr = QStringLiteral("SELECT type, COUNT(*) FROM searchIndex GROUP BY type"); + } else if (m_type == Docset::Type::ZDash) { + queryStr = QStringLiteral("SELECT ztypename, COUNT(*) FROM ztoken LEFT JOIN ztokentype" + " ON ztoken.ztokentype = ztokentype.z_pk GROUP BY ztypename"); + } + + if (!m_db->execute(queryStr)) { + qWarning("SQL Error: %s", qPrintable(m_db->lastError())); + return; + } + + while (m_db->next()) { + const QString symbolTypeStr = m_db->value(0).toString(); + const QString symbolType = parseSymbolType(symbolTypeStr); + m_symbolStrings.insertMulti(symbolType, symbolTypeStr); + m_symbolCounts[symbolType] += m_db->value(1).toInt(); + } +} + +// TODO: Fetch and cache only portions of symbols +void Docset::loadSymbols(const QString &symbolType) const +{ + for (const QString &symbol : m_symbolStrings.values(symbolType)) + loadSymbols(symbolType, symbol); +} + +void Docset::loadSymbols(const QString &symbolType, const QString &symbolString) const +{ + QString queryStr; + if (m_type == Docset::Type::Dash) { + queryStr = QStringLiteral("SELECT name, path FROM searchIndex WHERE type='%1' ORDER BY name ASC"); + } else { + queryStr = QStringLiteral("SELECT ztokenname, zpath, zanchor " + "FROM ztoken " + "LEFT JOIN ztokenmetainformation ON ztoken.zmetainformation = ztokenmetainformation.z_pk " + "LEFT JOIN zfilepath ON ztokenmetainformation.zfile = zfilepath.z_pk " + "LEFT JOIN ztokentype ON ztoken.ztokentype = ztokentype.z_pk WHERE ztypename='%1' " + "ORDER BY ztokenname ASC"); + } + + if (!m_db->execute(queryStr.arg(symbolString))) { + qWarning("SQL Error: %s", qPrintable(m_db->lastError())); + return; + } + + QMap &symbols = m_symbols[symbolType]; + while (m_db->next()) + symbols.insertMulti(m_db->value(0).toString(), + createPageUrl(m_db->value(1).toString(), m_db->value(2).toString())); +} + +void Docset::createIndex() +{ + static const QString indexListQuery = QStringLiteral("PRAGMA INDEX_LIST('%1')"); + static const QString indexDropQuery = QStringLiteral("DROP INDEX '%1'"); + static const QString indexCreateQuery = QStringLiteral("CREATE INDEX IF NOT EXISTS %1%2" + " ON %3 (%4 COLLATE NOCASE)"); + + const QString tableName = m_type == Type::Dash ? QStringLiteral("searchIndex") + : QStringLiteral("ztoken"); + const QString columnName = m_type == Type::Dash ? QStringLiteral("name") + : QStringLiteral("ztokenname"); + + m_db->execute(indexListQuery.arg(tableName)); + + QStringList oldIndexes; + + while (m_db->next()) { + const QString indexName = m_db->value(1).toString(); + if (!indexName.startsWith(IndexNamePrefix)) + continue; + + if (indexName.endsWith(IndexNameVersion)) + return; + + oldIndexes << indexName; + } + + // Drop old indexes + for (const QString &oldIndexName : oldIndexes) + m_db->execute(indexDropQuery.arg(oldIndexName)); + + m_db->execute(indexCreateQuery.arg(IndexNamePrefix, IndexNameVersion, tableName, columnName)); +} + +QUrl Docset::createPageUrl(const QString &path, const QString &fragment) const +{ + QString realPath; + QString realFragment; + + if (fragment.isEmpty()) { + const QStringList urlParts = path.split(QLatin1Char('#')); + realPath = urlParts[0]; + if (urlParts.size() > 1) + realFragment = urlParts[1]; + } else { + realPath = path; + realFragment = fragment; + } + + static const QRegularExpression dashEntryRegExp(QLatin1String("")); + realPath.remove(dashEntryRegExp); + realFragment.remove(dashEntryRegExp); + + QUrl url = QUrl::fromLocalFile(QDir(documentPath()).absoluteFilePath(realPath)); + if (!realFragment.isEmpty()) { + if (realFragment.startsWith(QLatin1String("//apple_ref")) + || realFragment.startsWith(QLatin1String("//dash_ref"))) { + url.setFragment(realFragment, QUrl::DecodedMode); + } else { + url.setFragment(realFragment); + } + } + + return url; +} + +QString Docset::parseSymbolType(const QString &str) +{ + // Dash symbol aliases + const static QHash aliases = { + // Attribute + {QStringLiteral("Package Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("Private Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("Protected Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("Public Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("Static Package Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("Static Private Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("Static Protected Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("Static Public Attributes"), QStringLiteral("Attribute")}, + {QStringLiteral("XML Attributes"), QStringLiteral("Attribute")}, + // Binding + {QStringLiteral("binding"), QStringLiteral("Binding")}, + // Category + {QStringLiteral("cat"), QStringLiteral("Category")}, + {QStringLiteral("Groups"), QStringLiteral("Category")}, + {QStringLiteral("Pages"), QStringLiteral("Category")}, + // Class + {QStringLiteral("cl"), QStringLiteral("Class")}, + {QStringLiteral("specialization"), QStringLiteral("Class")}, + {QStringLiteral("tmplt"), QStringLiteral("Class")}, + // Constant + {QStringLiteral("data"), QStringLiteral("Constant")}, + {QStringLiteral("econst"), QStringLiteral("Constant")}, + {QStringLiteral("enumdata"), QStringLiteral("Constant")}, + {QStringLiteral("enumelt"), QStringLiteral("Constant")}, + {QStringLiteral("clconst"), QStringLiteral("Constant")}, + {QStringLiteral("structdata"), QStringLiteral("Constant")}, + {QStringLiteral("writerid"), QStringLiteral("Constant")}, + {QStringLiteral("Notifications"), QStringLiteral("Constant")}, + // Constructor + {QStringLiteral("structctr"), QStringLiteral("Constructor")}, + {QStringLiteral("Public Constructors"), QStringLiteral("Constructor")}, + // Enumeration + {QStringLiteral("enum"), QStringLiteral("Enumeration")}, + {QStringLiteral("Enum"), QStringLiteral("Enumeration")}, + {QStringLiteral("Enumerations"), QStringLiteral("Enumeration")}, + // Event + {QStringLiteral("event"), QStringLiteral("Event")}, + {QStringLiteral("Public Events"), QStringLiteral("Event")}, + {QStringLiteral("Inherited Events"), QStringLiteral("Event")}, + {QStringLiteral("Private Events"), QStringLiteral("Event")}, + // Field + {QStringLiteral("Data Fields"), QStringLiteral("Field")}, + // Function + {QStringLiteral("dcop"), QStringLiteral("Function")}, + {QStringLiteral("func"), QStringLiteral("Function")}, + {QStringLiteral("ffunc"), QStringLiteral("Function")}, + {QStringLiteral("signal"), QStringLiteral("Function")}, + {QStringLiteral("slot"), QStringLiteral("Function")}, + {QStringLiteral("grammar"), QStringLiteral("Function")}, + {QStringLiteral("Function Prototypes"), QStringLiteral("Function")}, + {QStringLiteral("Functions/Subroutines"), QStringLiteral("Function")}, + {QStringLiteral("Members"), QStringLiteral("Function")}, + {QStringLiteral("Package Functions"), QStringLiteral("Function")}, + {QStringLiteral("Private Member Functions"), QStringLiteral("Function")}, + {QStringLiteral("Private Slots"), QStringLiteral("Function")}, + {QStringLiteral("Protected Member Functions"), QStringLiteral("Function")}, + {QStringLiteral("Protected Slots"), QStringLiteral("Function")}, + {QStringLiteral("Public Member Functions"), QStringLiteral("Function")}, + {QStringLiteral("Public Slots"), QStringLiteral("Function")}, + {QStringLiteral("Signals"), QStringLiteral("Function")}, + {QStringLiteral("Static Package Functions"), QStringLiteral("Function")}, + {QStringLiteral("Static Private Member Functions"), QStringLiteral("Function")}, + {QStringLiteral("Static Protected Member Functions"), QStringLiteral("Function")}, + {QStringLiteral("Static Public Member Functions"), QStringLiteral("Function")}, + // Guide + {QStringLiteral("doc"), QStringLiteral("Guide")}, + // Namespace + {QStringLiteral("ns"), QStringLiteral("Namespace")}, + // Macro + {QStringLiteral("macro"), QStringLiteral("Macro")}, + // Method + {QStringLiteral("clm"), QStringLiteral("Method")}, + {QStringLiteral("enumcm"), QStringLiteral("Method")}, + {QStringLiteral("enumctr"), QStringLiteral("Method")}, + {QStringLiteral("enumm"), QStringLiteral("Method")}, + {QStringLiteral("intfctr"), QStringLiteral("Method")}, + {QStringLiteral("intfcm"), QStringLiteral("Method")}, + {QStringLiteral("intfm"), QStringLiteral("Method")}, + {QStringLiteral("intfsub"), QStringLiteral("Method")}, + {QStringLiteral("instsub"), QStringLiteral("Method")}, + {QStringLiteral("instctr"), QStringLiteral("Method")}, + {QStringLiteral("instm"), QStringLiteral("Method")}, + {QStringLiteral("structcm"), QStringLiteral("Method")}, + {QStringLiteral("structm"), QStringLiteral("Method")}, + {QStringLiteral("structsub"), QStringLiteral("Method")}, + {QStringLiteral("Class Methods"), QStringLiteral("Method")}, + {QStringLiteral("Inherited Methods"), QStringLiteral("Method")}, + {QStringLiteral("Instance Methods"), QStringLiteral("Method")}, + {QStringLiteral("Private Methods"), QStringLiteral("Method")}, + {QStringLiteral("Protected Methods"), QStringLiteral("Method")}, + {QStringLiteral("Public Methods"), QStringLiteral("Method")}, + // Operator + {QStringLiteral("intfopfunc"), QStringLiteral("Operator")}, + {QStringLiteral("opfunc"), QStringLiteral("Operator")}, + // Property + {QStringLiteral("enump"), QStringLiteral("Property")}, + {QStringLiteral("intfdata"), QStringLiteral("Property")}, + {QStringLiteral("intfp"), QStringLiteral("Property")}, + {QStringLiteral("instp"), QStringLiteral("Property")}, + {QStringLiteral("structp"), QStringLiteral("Property")}, + {QStringLiteral("Inherited Properties"), QStringLiteral("Property")}, + {QStringLiteral("Private Properties"), QStringLiteral("Property")}, + {QStringLiteral("Protected Properties"), QStringLiteral("Property")}, + {QStringLiteral("Public Properties"), QStringLiteral("Property")}, + // Protocol + {QStringLiteral("intf"), QStringLiteral("Protocol")}, + // Structure + {QStringLiteral("struct"), QStringLiteral("Structure")}, + {QStringLiteral("Data Structures"), QStringLiteral("Structure")}, + {QStringLiteral("Struct"), QStringLiteral("Structure")}, + // Type + {QStringLiteral("tag"), QStringLiteral("Type")}, + {QStringLiteral("tdef"), QStringLiteral("Type")}, + {QStringLiteral("Data Types"), QStringLiteral("Type")}, + {QStringLiteral("Package Types"), QStringLiteral("Type")}, + {QStringLiteral("Private Types"), QStringLiteral("Type")}, + {QStringLiteral("Protected Types"), QStringLiteral("Type")}, + {QStringLiteral("Public Types"), QStringLiteral("Type")}, + {QStringLiteral("Typedefs"), QStringLiteral("Type")}, + // Variable + {QStringLiteral("var"), QStringLiteral("Variable")} + }; + + return aliases.value(str, str); +} + +// ported from DevDocs' searcher.coffee: +// (https://github.com/Thibaut/devdocs/blob/50f583246d5fbd92be7b71a50bfa56cf4e239c14/assets/javascripts/app/searcher.coffee#L91) +static void matchFuzzy(int nLen, const unsigned char *needle, int hLen, + const unsigned char *haystack, int *start, int *len) +{ + int j = 0; + int groups = 0; + for (int i = 0; i < nLen; ++i) { + bool found = false; + bool first = true; + int distance = 0; + while (j < hLen) { + if (needle[i] == haystack[j++]) { + if (*start == -1) + *start = j - 1; // first matched char + *len = j - *start; + found = true; + break; // continue the outer loop + } else { + // optimizations to reduce returned number of results + // (search was returning too many irrelevant results with large docsets) + if (first) { + groups += 1; + if (groups > 3) // optimization #1: too many mismatches + break; + first = false; + } + + if (i != 0) { + distance += 1; + if (distance > 8) { // optimization #2: too large distance between found chars + break; + } + } + } + } + + if (!found) { + // end of haystack, char not found + *start = -1; + return; + } + } +} + +static int scoreExact(int matchIndex, int matchLen, const unsigned char *value, int valueLen) +{ + int score = 100; + const unsigned char DOT = '.'; + // Remove one point for each unmatched character. + score -= (valueLen - matchLen); + if (matchIndex > 0) { + if (value[matchIndex - 1] == DOT) { + // If the character preceding the query is a dot, assign the same + // score as if the query was found at the beginning of the string, + // minus one. + score += (matchIndex - 1); + } else if (matchLen == 1) { + // Don't match a single-character query unless it's found at the + // beginning of the string or is preceded by a dot. + return 0; + } else { + // (1) Remove one point for each unmatched character up to + // the nearest preceding dot or the beginning of the + // string. + // (2) Remove one point for each unmatched character + // following the query. + int i = matchIndex - 2; + while (i >= 0 && value[i] != DOT) + --i; + score -= (matchIndex - i) // (1) + + (valueLen - matchLen - matchIndex); // (2) + } + + // Remove one point for each dot preceding the query, except for the + // one immediately before the query. + int separators = 0; + int i = matchIndex - 2; + + while (i >= 0) { + if (value[i] == DOT) + ++separators; + --i; + } + + score -= separators; + } + + // Remove five points for each dot following the query. + int separators = 0; + int i = valueLen - matchLen - matchIndex - 1; + while (i >= 0) { + if (value[matchIndex + matchLen + i] == DOT) { + ++separators; + } + --i; + } + + score -= separators * 5; + + return qMax(1, score); +} + +static int scoreFuzzy(int matchIndex, int matchLen, const unsigned char *value) +{ + if (matchIndex == 0 || value[matchIndex - 1] == '.') { + // score between 66..99, if the match follows a dot, or starts the string + return qMax(66, 100 - matchLen); + } else { + if (value[matchIndex + matchLen] == 0) { + // score between 33..66, if the match is at the end of the string + return qMax(33, 67 - matchLen); + } else { + // score between 1..33 otherwise (match in the middle of the string) + return qMax(1, 34 - matchLen); + } + } +} + +static void scoreFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { + Q_UNUSED(argc); + const unsigned char *needleOrig = sqlite3_value_text(argv[0]); + const unsigned char *haystackOrig = sqlite3_value_text(argv[1]); + + int haystackLen = 0, needleLen = 0; + while (haystackOrig[haystackLen] != 0) + ++haystackLen; + while (needleOrig[needleLen] != 0) + ++needleLen; + QScopedArrayPointer needle(new unsigned char[needleLen + 1]); + QScopedArrayPointer haystack(new unsigned char[haystackLen + 1]); + for (int i = 0; i < needleLen + 1; ++i) { + unsigned char c = needleOrig[i]; + if (c >= 'A' && c <= 'Z') + c += 32; + needle[i] = c; + } + + for (int i = 0, j = 0; i < haystackLen + 1; ++i, ++j) { + unsigned char c = haystackOrig[i]; + if ((i > 0 && haystackOrig[i - 1] == ':' && c == ':') // C++ (::) + || c == '/' || c == '_' || c == ' ') { // Go, some Guides + haystack[j] = '.'; + } else { + if (c >= 'A' && c <= 'Z') + c += 32; + haystack[j] = c; + } + } + + int best = 0; + int match1 = -1; + int match1Len; + + matchFuzzy(needleLen, needle.data(), haystackLen, haystack.data(), &match1, &match1Len); + + if (match1 == -1) { // no match + // simply return 0 + sqlite3_result_int(context, 0); + return; + } else if (needleLen == match1Len) { // exact match + best = scoreExact(match1, match1Len, haystack.data(), haystackLen); + } else { + best = scoreFuzzy(match1, match1Len, haystack.data()); + + int indexOfLastDot = -1; + for (int i = 0; haystack[i] != 0; ++i) { + if (haystack[i] == '.') + indexOfLastDot = i; + } + + if (indexOfLastDot != -1) { + int match2 = -1, match2Len; + matchFuzzy(needleLen, needle.data(), haystackLen - (indexOfLastDot + 1), + haystack.data() + indexOfLastDot + 1, &match2, &match2Len); + if (match2 != -1) { + best = qMax(best, scoreFuzzy(match2, match2Len, + haystack.data() + indexOfLastDot + 1)); + } + } + } + + sqlite3_result_int(context, best); +} diff --git a/documentation/zealdoc/zeal/registry/searchresult.h b/documentation/zealdoc/zeal/registry/searchresult.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/registry/searchresult.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015-2016 Oleg Shparber +** Copyright (C) 2013-2014 Jerzy Kozera +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#ifndef SEARCHRESULT_H +#define SEARCHRESULT_H + +#include +#include + +namespace Zeal { +namespace Registry { + +class Docset; + +struct SearchResult +{ + QString name; + QString type; + + Docset *docset; + + QUrl url; + + int score; + + inline bool operator<(const SearchResult &other) const + { + if (score == other.score) + return QString::compare(name, other.name, Qt::CaseInsensitive) < 0; + return score > other.score; + } +}; + +} // namespace Registry +} // namespace Zeal + +#endif // SEARCHRESULT_H diff --git a/documentation/zealdoc/zeal/util/plist.h b/documentation/zealdoc/zeal/util/plist.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/util/plist.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2015-2016 Oleg Shparber +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#ifndef PLIST_H +#define PLIST_H + +#include +#include + +namespace Zeal { +namespace Util { + +class Plist : public QHash +{ +public: + Plist(); + + bool read(const QString &fileName); + bool hasError() const; + +private: + bool m_hasError = false; +}; + +} // namespace Util +} // namespace Zeal + +#endif // PLIST_H diff --git a/documentation/zealdoc/zeal/util/plist.cpp b/documentation/zealdoc/zeal/util/plist.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/util/plist.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015-2016 Oleg Shparber +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#include "plist.h" + +#include +#include + +using namespace Zeal::Util; + +Plist::Plist() : + QHash() +{ +} + +bool Plist::read(const QString &fileName) +{ + QScopedPointer file(new QFile(fileName)); + if (!file->open(QIODevice::ReadOnly)) { + // TODO: Report/log error + m_hasError = true; + return false; + } + + QXmlStreamReader xml(file.data()); + + while (!xml.atEnd()) { + const QXmlStreamReader::TokenType token = xml.readNext(); + if (token == QXmlStreamReader::StartDocument || token != QXmlStreamReader::StartElement) + continue; + + if (xml.name() != QLatin1String("key")) + continue; // TODO: Should it fail here? + + const QString key = xml.readElementText(); + + // Skip whitespaces between tags + while (xml.readNext() == QXmlStreamReader::Characters); + + if (xml.tokenType() != QXmlStreamReader::StartElement) + continue; + + QVariant value; + if (xml.name() == QLatin1String("string")) + value = xml.readElementText(); + else if (xml.name() == QLatin1String("true")) + value = true; + else if (xml.name() == QLatin1String("false")) + value = false; + else + continue; // Skip unknown types + + insert(key, value); + } + + return m_hasError; +} + +bool Plist::hasError() const +{ + return m_hasError; +} diff --git a/documentation/zealdoc/zeal/util/sqlitedatabase.h b/documentation/zealdoc/zeal/util/sqlitedatabase.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/util/sqlitedatabase.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jerzy Kozera +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#ifndef ZEAL_UTIL_SQLITEDATABASE_H +#define ZEAL_UTIL_SQLITEDATABASE_H + +#include +#include + +struct sqlite3; +struct sqlite3_stmt; + +namespace Zeal { +namespace Util { + +class SQLiteDatabase +{ +public: + explicit SQLiteDatabase(const QString &path); + virtual ~SQLiteDatabase(); + + bool isOpen() const; + QStringList tables(); + + bool execute(const QString &queryStr); + bool next(); + + QVariant value(int index) const; + + QString lastError() const; + + sqlite3 *handle() const; + +private: + void close(); + void finalize(); + void updateLastError(); + + sqlite3 *m_db = nullptr; + sqlite3_stmt *m_stmt = nullptr; + QString m_lastError; +}; + +} // namespace Util +} // namespace Zeal + +#endif // ZEAL_UTIL_SQLITEDATABASE_H diff --git a/documentation/zealdoc/zeal/util/sqlitedatabase.cpp b/documentation/zealdoc/zeal/util/sqlitedatabase.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zeal/util/sqlitedatabase.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jerzy Kozera +** Contact: https://go.zealdocs.org/l/contact +** +** This file is part of Zeal. +** +** Zeal is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** Zeal is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with Zeal. If not, see . +** +****************************************************************************/ + +#include "sqlitedatabase.h" + +#include + +using namespace Zeal::Util; + +SQLiteDatabase::SQLiteDatabase(const QString &path) +{ + if (sqlite3_initialize() != SQLITE_OK) + return; + + if (sqlite3_open16(path.constData(), &m_db) != SQLITE_OK) { + updateLastError(); + close(); + } +} + +SQLiteDatabase::~SQLiteDatabase() +{ + finalize(); + close(); +} + +bool SQLiteDatabase::isOpen() const +{ + return m_db != nullptr; +} + +QStringList SQLiteDatabase::tables() +{ + Q_ASSERT(!m_stmt); // FIXME: usage between other execute/next calls (Zeal doesn't need it now) + + QStringList res; + if (!isOpen()) + return res; + + const QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE type='table' UNION ALL " + "SELECT name FROM sqlite_temp_master WHERE type='table'"); + + if (!sql.isEmpty() && execute(sql)) { + while(next()) + res.append(value(0).toString()); + } + + return res; +} + +bool SQLiteDatabase::execute(const QString &queryStr) +{ + if (m_db == nullptr) { + return false; + } + + if (m_stmt != nullptr) { + finalize(); + } + + m_lastError.clear(); + + sqlite3_mutex_enter(sqlite3_db_mutex(m_db)); + const void *pzTail = nullptr; + const int res = sqlite3_prepare16_v2(m_db, queryStr.constData(), + (queryStr.size() + 1) * sizeof(QChar), &m_stmt, &pzTail); + sqlite3_mutex_leave(sqlite3_db_mutex(m_db)); + + if (res != SQLITE_OK) { + // "Unable to execute statement" + updateLastError(); + finalize(); + return false; + } else if (pzTail && !QString(reinterpret_cast(pzTail)).trimmed().isEmpty()) { + // Unable to execute multiple statements at a time + updateLastError(); + finalize(); + return false; + } + + return true; +} + +bool SQLiteDatabase::next() +{ + if (m_stmt == nullptr) + return false; + + sqlite3_mutex_enter(sqlite3_db_mutex(m_db)); + const int res = sqlite3_step(m_stmt); + sqlite3_mutex_leave(sqlite3_db_mutex(m_db)); + + switch(res) { + case SQLITE_ROW: + return true; + case SQLITE_DONE: + case SQLITE_CONSTRAINT: + case SQLITE_ERROR: + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + updateLastError(); + } + + return false; +} + +QVariant SQLiteDatabase::value(int index) const +{ + Q_ASSERT(index >= 0); + + // sqlite3_data_count() returns 0 if m_stmt is nullptr. + if (index >= sqlite3_data_count(m_stmt)) + return QVariant(); + + sqlite3_mutex_enter(sqlite3_db_mutex(m_db)); + const int type = sqlite3_column_type(m_stmt, index); + + QVariant ret; + + switch (type) { + case SQLITE_INTEGER: + ret = sqlite3_column_int64(m_stmt, index); + case SQLITE_NULL: + ret = QVariant(QVariant::String); + default: + ret = QString(reinterpret_cast(sqlite3_column_text16(m_stmt, index)), + sqlite3_column_bytes16(m_stmt, index) / sizeof(QChar)); + } + sqlite3_mutex_leave(sqlite3_db_mutex(m_db)); + return ret; +} + +QString SQLiteDatabase::lastError() const +{ + return m_lastError; +} + +void SQLiteDatabase::close() +{ + sqlite3_close(m_db); + m_db = nullptr; +} + +void SQLiteDatabase::finalize() +{ + sqlite3_finalize(m_stmt); + m_stmt = nullptr; +} + +void SQLiteDatabase::updateLastError() +{ + if (!m_db) + return; + + m_lastError = QString(reinterpret_cast(sqlite3_errmsg16(m_db))); +} + +sqlite3 *SQLiteDatabase::handle() const +{ + return m_db; +} diff --git a/documentation/zealdoc/zealdocconfigpage.h b/documentation/zealdoc/zealdocconfigpage.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocconfigpage.h @@ -0,0 +1,56 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include + +namespace Ui +{ + +class ZealdocConfigPage; + +} + +class ZealdocPlugin; + +class ZealdocConfigPage: public KDevelop::ConfigPage +{ + Q_OBJECT + +public: + ZealdocConfigPage(KDevelop::IPlugin* plugin, QWidget* parent); + ~ZealdocConfigPage() override; + + KDevelop::ConfigPage::ConfigPageType configPageType() const override; + + QString name() const override; + QString fullName() const override; + QIcon icon() const override; + + void apply() override; + void defaults() override; + void reset() override; + +private: + void reloadDocsets(const QString& path); + + QScopedPointer ui; + ZealdocPlugin* m_plugin; +}; diff --git a/documentation/zealdoc/zealdocconfigpage.cpp b/documentation/zealdoc/zealdocconfigpage.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocconfigpage.cpp @@ -0,0 +1,132 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "zealdocconfigpage.h" +#include "ui_zealdocconfigpage.h" + +#include "zealdocplugin.h" +#include "util.h" +#include "debug.h" + +#include +#include +#include + +ZealdocConfigPage::ZealdocConfigPage(KDevelop::IPlugin* plugin, QWidget* parent) + : ConfigPage(plugin, nullptr, parent) + , ui(new Ui::ZealdocConfigPage) + , m_plugin(dynamic_cast(plugin)) +{ + ui->setupUi(this); + + ui->kcfg_docsetsPath->setText(docsetsPath()); + ui->kcfg_docsetsPath->lineEdit()->setReadOnly(true); + + reloadDocsets(docsetsPath()); + + connect(ui->docsetsList, &QListWidget::itemChanged, this, [this](QListWidgetItem*) { emit changed(); }); + + connect(ui->kcfg_docsetsPath, &KUrlRequester::textChanged, + this, [this](const QString& path) { reloadDocsets(path); }); +} + +ZealdocConfigPage::~ZealdocConfigPage() +{ +} + +void ZealdocConfigPage::reloadDocsets(const QString& path) +{ + auto enabled = enabledDocsets(); + + ui->docsetsList->clear(); + foreach(const auto& docsetInformation, availableDocsets(path)) + { + if (!docsetInformation.isValid) + { + continue; + } + + auto item = new QListWidgetItem(ui->docsetsList); + item->setText(docsetInformation.title); + item->setIcon(docsetInformation.icon); + + if (enabled.contains(docsetInformation.title)) + { + item->setCheckState(Qt::Checked); + } + else + { + item->setCheckState(Qt::Unchecked); + } + } +} + +KDevelop::ConfigPage::ConfigPageType ZealdocConfigPage::configPageType() const +{ + return KDevelop::ConfigPage::DefaultConfigPage; +} + +QString ZealdocConfigPage::name() const +{ + return i18n("Zeal"); +} + +QString ZealdocConfigPage::fullName() const +{ + return i18n("Configure Zeal documentation settings"); +} + +QIcon ZealdocConfigPage::icon() const +{ + return QIcon::fromTheme(QStringLiteral("zeal")); +} + +void ZealdocConfigPage::apply() +{ + QStringList enabled; + for (int i = 0; i < ui->docsetsList->count(); ++i) + { + auto item = ui->docsetsList->item(i); + if (item->checkState() == Qt::Checked) + { + enabled += item->text(); + } + } + + KConfigGroup config = KSharedConfig::openConfig()->group("Zealdoc"); + config.writeEntry(QStringLiteral("DocsetsPath"), ui->kcfg_docsetsPath->text()); + config.writeEntry(QStringLiteral("EnabledDocsets"), enabled); + + m_plugin->reloadDocsets(); +} + +void ZealdocConfigPage::defaults() +{ + QSignalBlocker blocker(this); + ui->kcfg_docsetsPath->setText(defaultDocsetsPath()); + + for (int i = 0; i < ui->docsetsList->count(); ++i) + { + ui->docsetsList->item(i)->setCheckState(Qt::Unchecked); + } +} + +void ZealdocConfigPage::reset() +{ +} diff --git a/documentation/zealdoc/zealdocconfigpage.ui b/documentation/zealdoc/zealdocconfigpage.ui new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocconfigpage.ui @@ -0,0 +1,63 @@ + + + ZealdocConfigPage + + + + 0 + 0 + 485 + 568 + + + + Cppcheck Settings + + + + + + + + &Docsets path + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + kcfg_docsetsPath + + + + + + + KFile::Directory|KFile::ExistingOnly + + + + + + + + + Available Docsets: + + + + + + + + + + + KUrlRequester + QWidget +
kurlrequester.h
+ 1 +
+
+ + +
diff --git a/documentation/zealdoc/zealdocplugin.h b/documentation/zealdoc/zealdocplugin.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocplugin.h @@ -0,0 +1,48 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include +#include + +class ZealdocProvider; + +class ZealdocPlugin : public KDevelop::IPlugin, public KDevelop::IDocumentationProviderProvider +{ + Q_OBJECT + Q_INTERFACES(KDevelop::IDocumentationProviderProvider) + +public: + ZealdocPlugin(QObject* parent, const QVariantList& args); + ~ZealdocPlugin() override; + + QList providers() override; + + int configPages() const override; + KDevelop::ConfigPage* configPage(int number, QWidget* parent) override; + + void reloadDocsets(); + +signals: + void changedProvidersList() const override; + +private: + QList m_providers; +}; diff --git a/documentation/zealdoc/zealdocplugin.cpp b/documentation/zealdoc/zealdocplugin.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocplugin.cpp @@ -0,0 +1,134 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "zealdocplugin.h" +#include "zealdocprovider.h" +#include "zealdocconfigpage.h" +#include "util.h" +#include "debug.h" + +#include +#include + +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(ZealdocFactory, "kdevzealdoc.json", registerPlugin();) + +using namespace KDevelop; + +ZealdocPlugin::ZealdocPlugin(QObject* parent, const QVariantList&) + : KDevelop::IPlugin("kdevzealdoc", parent) +{ + KDEV_USE_EXTENSION_INTERFACE( KDevelop::IDocumentationProviderProvider ); + + connect(this, + &ZealdocPlugin::changedProvidersList, + + KDevelop::ICore::self()->documentationController(), + &KDevelop::IDocumentationController::changedDocumentationProviders); + + reloadDocsets(); +} + +ZealdocPlugin::~ZealdocPlugin() +{ +} + +void ZealdocPlugin::reloadDocsets() +{ + QStringList enabled = enabledDocsets(); + QStringList loaded; + bool hasChanges = false; + + // First, unload disabled docsets + QMutableListIterator i(m_providers); + while (i.hasNext()) + { + auto provider = i.next(); + + if (!enabled.contains(provider->name())) + { + i.remove(); + hasChanges = true; + } + else + { + loaded += provider->name(); + } + } + + // Second, load new enabled docsets + foreach (const auto& docsetInformation, availableDocsets()) + { + if (!enabled.contains(docsetInformation.title)) + { + continue; + } + + if (loaded.contains(docsetInformation.title)) + { + continue; + } + + auto docset = new ZealdocProvider(docsetInformation.path, this); + if (!docset->isValid()) + { + delete docset; + continue; + } + + m_providers += docset; + hasChanges = true; + } + + if (!hasChanges) + { + emit changedProvidersList(); + } +} + +QList ZealdocPlugin::providers() +{ + QList result; + + foreach (const auto p, m_providers) + { + result += p; + } + + return result; +} + +int ZealdocPlugin::configPages() const +{ + return 1; +} + +KDevelop::ConfigPage* ZealdocPlugin::configPage(int number, QWidget* parent) +{ + if (number) + { + return nullptr; + } + + return new ZealdocConfigPage(this, parent); +} + +#include "zealdocplugin.moc" diff --git a/documentation/zealdoc/zealdocprovider.h b/documentation/zealdoc/zealdocprovider.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocprovider.h @@ -0,0 +1,69 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include +#include + +#include +#include +#include + +class ZealdocProvider : public QObject, public KDevelop::IDocumentationProvider +{ + Q_OBJECT + Q_INTERFACES(KDevelop::IDocumentationProvider) + +public: + ZealdocProvider(const QString& docsetPath, QObject* parent); + ~ZealdocProvider() override; + + bool isValid(); + + QIcon icon() const override; + QString name() const override; + + KDevelop::IDocumentation::Ptr homePage() const override; + + KDevelop::IDocumentation::Ptr documentationForDeclaration(KDevelop::Declaration* dec) const override; + KDevelop::IDocumentation::Ptr documentationForIndex(const QModelIndex& index) const override; + KDevelop::IDocumentation::Ptr documentationForToken(const QString& token) const; + + QAbstractListModel* indexModel() const override; + + QStringList tokenGroups() const; + QIcon groupIcon(const QString& group); + QStringList groupTokens(const QString& group) const; + +signals: + void addHistory(const KDevelop::IDocumentation::Ptr& doc) const override; + +private: + bool m_isValid; + QString m_name; + QIcon m_icon; + + QStringListModel* m_model; + + QStringList m_tokenGroups; + QMap< QString, QStringList > m_groupTokens; + + QMap< QString, QUrl > m_tokenUrls; +}; diff --git a/documentation/zealdoc/zealdocprovider.cpp b/documentation/zealdoc/zealdocprovider.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocprovider.cpp @@ -0,0 +1,176 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "zealdocprovider.h" +#include "zealdocumentation.h" +#include "debug.h" + +#include "registry/docset.h" + +#include +#include +#include + +#include + +#include + +using namespace KDevelop; + +ZealdocProvider::ZealdocProvider(const QString& docsetPath, QObject* parent) + : QObject(parent) +{ + Zeal::Registry::Docset ds(docsetPath); + + m_isValid = ds.isValid(); + if (!isValid()) + { + return; + } + + m_name = ds.title(); + m_icon = ds.icon(); + + QStringList allTokens; + + auto tokenGroups = ds.symbolCounts(); + QMapIterator i(tokenGroups); + while (i.hasNext()) + { + i.next(); + + QString groupName = i.key(); + auto groupTokens = ds.symbols(groupName); + + if (!groupTokens.isEmpty()) + { + m_tokenGroups += groupName; + } + + QMapIterator j(groupTokens); + while (j.hasNext()) + { + j.next(); + + QString token = j.key(); + QUrl url = j.value(); + + m_tokenUrls[token] = url; + m_groupTokens[groupName] += token; + } + } + + QMapIterator k(m_tokenUrls); + while (k.hasNext()) + { + k.next(); + allTokens += k.key(); + } + + m_model = new QStringListModel(this); + m_model->setStringList(allTokens); +} + +ZealdocProvider::~ZealdocProvider() +{ +} + +bool ZealdocProvider::isValid() +{ + return m_isValid; +} + +QIcon ZealdocProvider::icon() const +{ + return m_icon; +} + +QString ZealdocProvider::name() const +{ + return m_name; +} + +IDocumentation::Ptr ZealdocProvider::homePage() const +{ + ZealDocumentation::m_provider = const_cast(this); + return IDocumentation::Ptr(new ZealDocumentationHome); +} + +IDocumentation::Ptr ZealdocProvider::documentationForDeclaration(Declaration* dec) const +{ + if (dec) + { + static const IndexedString qmlJs("QML/JS"); + QString token; + + { + DUChainReadLocker lock; + token = dec->qualifiedIdentifier().toString(RemoveTemplateInformation); + if (dec->topContext()->parsingEnvironmentFile()->language() == qmlJs && !token.isEmpty()) + { + token = QLatin1String("QML.") + token; + } + } + + return documentationForToken(token); + } + + return {}; +} + +IDocumentation::Ptr ZealdocProvider::documentationForIndex(const QModelIndex& index) const +{ + return documentationForToken(index.data(Qt::DisplayRole).toString()); +} + +KDevelop::IDocumentation::Ptr ZealdocProvider::documentationForToken(const QString& token) const +{ + if (!token.isEmpty()) + { + QUrl url = m_tokenUrls[token]; + + if (url.isValid()) + { + ZealDocumentation::m_provider = const_cast(this); + return IDocumentation::Ptr(new ZealDocumentation(token, url)); + } + } + + return {}; +} + +QAbstractListModel* ZealdocProvider::indexModel() const +{ + return m_model; +} + +QStringList ZealdocProvider::tokenGroups() const +{ + return m_tokenGroups; +} + +QIcon ZealdocProvider::groupIcon(const QString& group) +{ + return QIcon::fromTheme(QStringLiteral("zealdoc_%1").arg(group.toLower())); +} + +QStringList ZealdocProvider::groupTokens(const QString& group) const +{ + return m_groupTokens[group]; +} diff --git a/documentation/zealdoc/zealdocumentation.h b/documentation/zealdoc/zealdocumentation.h new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocumentation.h @@ -0,0 +1,81 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#pragma once + +#include + +#include +#include + +class ZealdocProvider; + +class ZealDocumentation : public KDevelop::IDocumentation +{ + Q_OBJECT + +public: + ZealDocumentation(const QString& name, const QUrl& url); + + QString name() const override; + QString description() const override; + + QWidget* documentationWidget(KDevelop::DocumentationFindWidget* findWidget, QWidget* parent = nullptr) override; + + KDevelop::IDocumentationProvider* provider() const override; + + static ZealdocProvider* m_provider; + +private: + QString m_name; + QUrl m_url; +}; + +class ZealDocumentationHome : public KDevelop::IDocumentation +{ + Q_OBJECT + +public: + ZealDocumentationHome(); + + QString name() const override; + QString description() const override; + + QWidget* documentationWidget(KDevelop::DocumentationFindWidget* findWidget, QWidget* parent = nullptr) override; + + KDevelop::IDocumentationProvider* provider() const override; +}; + +class ZealContentsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + ZealContentsModel(QObject* parent); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex&) const override; + + QModelIndex parent(const QModelIndex& child) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + +public slots: + void showItem(const QModelIndex& idx); +}; diff --git a/documentation/zealdoc/zealdocumentation.cpp b/documentation/zealdoc/zealdocumentation.cpp new file mode 100644 --- /dev/null +++ b/documentation/zealdoc/zealdocumentation.cpp @@ -0,0 +1,197 @@ +/* This file is part of KDevelop + Copyright 2016 Anton Anikin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "zealdocumentation.h" +#include "zealdocprovider.h" + +#include +#include +#include + +#include + +#include + +using namespace KDevelop; + +ZealdocProvider* ZealDocumentation::m_provider = nullptr; + +ZealDocumentation::ZealDocumentation(const QString& name, const QUrl& url) + : m_name(name) + , m_url(url) +{ +} + +QWidget* ZealDocumentation::documentationWidget(DocumentationFindWidget* findWidget, QWidget* parent) +{ + auto view = new StandardDocumentationView(findWidget, parent); + view->setUrl(m_url); + + return view; +} + +QString ZealDocumentation::name() const +{ + return m_name; +} + +QString ZealDocumentation::description() const +{ + return m_name; +} + +IDocumentationProvider* ZealDocumentation::provider() const +{ + return m_provider; +} + +// ================================================================================================= + +ZealDocumentationHome::ZealDocumentationHome() +{ +} + +QString ZealDocumentationHome::name() const +{ + return i18n("%1 Content Page", ZealDocumentation::m_provider->name()); +} + +QString ZealDocumentationHome::description() const +{ + return name(); +} + +QWidget* ZealDocumentationHome::documentationWidget(DocumentationFindWidget* findWidget, QWidget* parent) +{ + Q_UNUSED(findWidget); + + QTreeView* contents = new QTreeView(parent); + auto model = new ZealContentsModel(contents); + + QObject::connect(contents, &QTreeView::clicked, model, &ZealContentsModel::showItem); + + contents->setHeaderHidden(true); + contents->setModel(model); + + return contents; +} + +IDocumentationProvider* ZealDocumentationHome::provider() const +{ + return ZealDocumentation::m_provider; +} + +// ================================================================================================= + +ZealContentsModel::ZealContentsModel(QObject* parent) + : QAbstractItemModel(parent) +{ +} + +int ZealContentsModel::rowCount(const QModelIndex& parent) const +{ + if (!parent.isValid()) + { + return ZealDocumentation::m_provider->tokenGroups().size(); + } + + if (int(parent.internalId()) < 0) + { + QString groupName = parent.data(Qt::DisplayRole).toString(); + int groupSize = ZealDocumentation::m_provider->groupTokens(groupName).size(); + + return groupSize; + } + + return 0; +} + +int ZealContentsModel::columnCount(const QModelIndex&) const +{ + return 1; +} + +QModelIndex ZealContentsModel::parent(const QModelIndex& child) const +{ + if (child.isValid() && child.column() == 0 && int(child.internalId()) >= 0) + { + return createIndex(child.internalId(), 0, -1); + } + + return QModelIndex(); +} + +QModelIndex ZealContentsModel::index(int row, int column, const QModelIndex& parent) const +{ + if (row < 0 || column != 0) + { + return QModelIndex(); + } + + if (!parent.isValid() && row >= rowCount()) + { + return QModelIndex(); + } + + return createIndex(row, column, int(parent.isValid() ? parent.row() : -1)); +} + +QVariant ZealContentsModel::data(const QModelIndex& index, int role) const +{ + if (index.isValid()) + { + int internal(index.internalId()); + QString groupName; + + if (internal < 0) + { + groupName = ZealDocumentation::m_provider->tokenGroups().at(index.row()); + } + else + { + groupName = index.parent().data(Qt::DisplayRole).toString(); + } + + if (role == Qt::DisplayRole) + { + if (internal < 0) + { + return groupName; + } + + return ZealDocumentation::m_provider->groupTokens(groupName).at(index.row()); + } + + if (role == Qt::DecorationRole) + { + return ZealDocumentation::m_provider->groupIcon(groupName); + } + } + + return QVariant(); +} + +void ZealContentsModel::showItem(const QModelIndex& idx) +{ + if (idx.isValid() && int(idx.internalId()) >=0) + { + auto doc = ZealDocumentation::m_provider->documentationForToken(idx.data(Qt::DisplayRole).toString()); + ICore::self()->documentationController()->showDocumentation(doc); + } +}