T3-image Sat May 02 12:25:40 2020ENTP SYMD&@ objToStringString.specialsToHtml.flagsfString.specialsToHtml.taglength 1 mainRestore RuntimeErrorMexceptionMessage$CharacterSet.UnknownCharSetExceptionStorageServerError T3StackInfoq FileSpec.getFilenameV FileSpec.closeFile0 File.FileInfo*File.FileNotFoundException+File.FileCreationException-File.FileOpenException6File.FileIOException.File.FileSyncException4File.FileClosedException,File.FileModeException5File.FileSafetyExceptionIfcComparator.calcHash IfcComparator.matchValuesGrammarProd.firstTokenIndexGrammarProd.lastTokenIndex GrammarProd.tokenListGrammarProd.tokenMatchListGrammarProd.altPropsDGrammarProd.GrammarAltInfoCGrammarProd.GrammarAltTokInfoxTadsNet.NetEventuTadsNet.NetRequestEventtTadsNet.NetTimeoutEventyTadsNet.NetReplyEventvTadsNet.NetReplyDoneEventwTadsNet.NetExceptionN!TadsNet.SocketDisconnectExceptionzTadsNet.NetSafetyException{TadsNet.FileUploadTadsNet.FileUpload.file< TadsNet.FileUpload.contentType?TadsNet.FileUpload.filename}propNotDefinedLastProp Constructor DestructorGrammarProd.grammarInfoGrammarProd.grammarTagGrammarProd.miscVocab  operator +  operator -  operator * operator / operator % operator ^ operator << operator >> operator >>> operator ~ operator | operator &operator negate operator [] operator []=FNSD= t3vm/010006tads-gen/030008tads-io/030007tads-net/030001CPDF CPPG|XlQ 0}|XlQ" 60XXQ}"f)| }|f)|Y1}| }QXjA)A6Q,Q \ j{ Qf(QUV r s r"} ^"2B 60XgCuwIIZ |j#"""k|vYQ5 ZN0nTf)A.AP \ f)X )XQQZcQc   clcjjcv Q5&N_i5Hdcm ,X m X XQ|bP iQ()|fgQ (nf'gQ(4|}Y|QQ $%%J 0 (vN "i''{(|}YP|Q6 (vN 'i Q,||a@ PP! ,tiP ciQ X T |`Tj  f)g|`u| 19f)g f)gm6jj>jj GWQg"2K  *d9_iFdA|, mclc`cmP A|}MP+ e\cMcdQc,W X PRPCPPG|`MP cMQ|s}a/}a.}acE" !c"1"}PJ2 sp?JccdQ|Q O[ |YP QQ(=>, ONù 5|K}b~B%-[>Ph 5ȹι׹NicK@ cb@'cK@cbAcb a+TPmfQc>%Q|cmQ N5AclTYdj{Q"+ 4f!1I ccl~cmjclccl.8JVrVEc La,c.c.ccc.$*.cc#.c u ac2.ccccc1c)c/"ccccc*cmcc.Pf,;J -iFiQ{*|`lT`|"Q, *i )||P i|`T ic{T|aT, ù  |P i |P iCPPG>|}~|%A|%PT >)18?FKRQQQQQQ>QNQaQQ||aP cQK} wc9mJ c9l~|dQ6 KW\el|}c9m9Q" Wl3| c9mJdQ" 3rW|c9m[9Q W|}|`~aQ, x}R Q, x}R N |`-c9l c9| |a C   Y  e   X$) ?I kF e:   k Y k|a:jT 8Qu N}5_i-/dJ/ x R\ |@P+ \ x|@P+ r x}~P6 }|}~P, ȹRc cmI"P|eT pOfE|}|J}F"X}|J}vYT6 OǺN0CPPG] fEfE||HF"|X|HvYT6 ]Ѻ0ۺ. fE|QF"XT, .Ǻ0 }   K}a|kB |bQ}`  Q P}XP|kGQ|bQT< ȹ0ik~}|GXCT, Ѻȹ0    Ka~kB ~bQ`  Q `Q}|XC|`P} |~kGQ~bQhP Ѻȹ0ik!|K}RfE|P" !N0Fc^cRlcQtc^cRlQVfE|  |~}|a|fE}E~fE}EQ6 VǺNb )m} fEm}EEQ" b$} |}Q6 $ $ % |fE|E}`fE|E}}j F   / k B< b 0 |" k B b    I g&Q &^m %   %rǺ#F$iMȹN-j 7  R|dQ @cdTdTCPPG`||d[P" Wc cdQ R^   |dQgfjQ1;N<T2 RELV_>Qej(cl5""cl{"|"P (r!|go|!Q !{X~#PX~#PN}|a |aQcc |aQR, Nfo""c"gOT |c0m! Q s|c0mx!Q s}ccT RTc0mzc0l3c _c!"` !:"P2`T6}/|fO@(gOclP" 6c( fO TfO0m"QPPX~#P Q,| TfO0mzQ ,|cm'cQ |gcm'cQ FRclcl,clj mcQ#?"@,BBBI]iclBc`4cl>j mccQ2S"T?% ]t*YVYI,| TfO0mzQ ,|c,|`,Q 4|`c,mx |`l""goQ 4Pc,lzTCPPG bxclNclCclc/j mclccQ0SNT0S"\J 0 ^VYe^^I'| DUX3Q 'cl"c `"gOQ fUfO0mzfO0lQo|9a@*kkQ UfO0mT, o1Y UfO0mT| _"P |aQ" h|}`l Qc c@Pccm@c APQ6 h` c&clc  |dQ, `ƻ̻7}""~""}""|aQ, 7ƻ̻"}|s|d -P" "t}d   d3ap3a|aQc|ajVQ-R1S^F trѻۻfpU`I|aT n0|<  `cm@PQ, 0/ۻ(c""fO"|Q ({}"K "|a|agf}}V }b fO fO/f j4j#"K "|aj(jl go}|sQ^2 Kj3|`c@c @ cmAP" 3{CPPGd|`gOQ Vcmk;QaQ, d R  dVTX VWX LW fWmWQ" fO|aQ" "-|gf |kQ|aQ+ -c# |" "|gf%Q #  |" "|}a$Q" j#|gfj|dQ+ #cI} go|gfk[ |dQ|aQ5 Iwj Pf  "||YfOA B#jj)m@j) WfWmgf j 5k;P  j l"  a(afO Pff` Q";INJ .\ P;Gdttf j I LL cqT n|cPQP Q Q xQP j%4 c"> "|"gj$Q %&}4 c"> "|"gjQ" &jP lI |a |`./|a.|acQPclTaPQ]4C lW!'IkwXhCPPGo}|cI "}"|~|c}L V }"^ ""g ~"n ""v |`l"} "" "dQ, or-2 WcmzQ/ Xcm}P! /sj P|cmQ xAccmcm j" "dcmQ Axc*& "  d XX3c*d+ "c*!clcoc*Tfo. foT P <c- |   " "dclQ <7A c""& "cl"; cl"K ""] "P Aj}l *|gf &c clq c* c*l" "" j" "" "dclQ, >ccmcl dclQl cl" "| |`" " " "cmclcclcmQ! ltDc d "  XX3 "QQPPP9} go}|k|dQ|aQ+ 9w6} goj|dQ|aQ+ 6wg|` g |dQ  |dQ|gf  |dQjmjljV|dQ5 gfI& }`" "|dQ" &|P id}c|Q" id|cm}Q N|cP cCPPG! dQdjVQ f |`Qc   c    Zk!Qf. NflITY}cQdQA c"S "a NYcm}`VQV j" " goQ["\*Yf^|e(4cQ |3aTQ$"%6 (r`'(IPcl/ cl/" "PQUcP c" "a 8Zcm}T Us   |""  """""=dQ@ rtho.clcmc|acm Q . c|acm Q dQc! cl"_"dQqdQ" cl" "P "jP|P n$||`""dQ $ux|s|`l""cl""ccc|c c3 Z|`mIlP! xsj^ccd| [X3ccTdP ^R}} QQ Q6 T|RCPPG @'    l * /cl/J4~"J} U[""a|`""krgO"" ~}|dT|`"""} U["""~"""j""""3cc3d ![X3cTT @}J|`""""3+cc3d K[X3cT}c~gnT2P6 'c9|`l"J"dQ '}\}b}hA}pQ  wQ~  cl/""" c"QI fɼ3Q |` fU2th\\3a\jk0kjV,jjVjg 3QCs"t|6P` μ<ּݼCqvI n~#5GcfURfU5fU9fUfU fU5fUfUfUfUfUfUfU ||""""""}`"""" u[~aIm"""""""" "cd [X3cTJ nu -}cQ" - |"cmQ" idclDPclT vtqvtMvtbPCPPG   }a|gfj,}h|dkQ |aQj3a\|aQ.j"j |ajVkQTg"h"|Rb idfpVjjuIIZal w|    |c.k_R}aP@ ѻj.|!|"} P" .ѻcT go Q}|go|dQ" }|go|dQ" }|go+|aQ" fo|dQ" }|go|aQ" *|gf }X|dQ, *I|`|`Q s|`(,P x|a$T+  c|aT+  cfO0l@P|`lT s|`#@P+  x|`(T x|aT6  .|`j ^`|"Q! .gi|`}`#P  ab9 V |`m|fW|"WQ 9i|`}`#P  ab|aT+  g|`T wCPPG2 R`|"^ "g }"n ""v }`l"} "" ""Q5 Rs Rrc& fo.-Pc,P="I "|"p"}"S "aQ? =fid =-| X}azQ5 -f -idTGs{|`"""}`""""aQ5 Gs GidH|""a Y}azaQ5 Hs HidT.|`9|`l"J"PP .xcAPc3@ cl,Pc3@ cl,P|`""P bcAP |:P! 0t}|s})P! t }|s~8}@B7;=a`Ca`T\ ^kmIj`kw"""PIYh "id*tV0;htnhr ~}|s}9~AP5 ɼtc@P|}<~?P, 5?N c "c"""P" NcQ#| T #sCPPGr  c c& cJJ cJ&J c 0c& c;; c;&; fc ic& c c& c c& c$$ c$&$Qr~f\ff#|\}~#YP Q\#Q=LMT rNLV]hp{|)}P" |cc)mT |d@T vQ~}|XPQ6 ýԽ QG+j= j=k@jT, G Bd=cF|`|`|`|`U |`kP" ={ U c*|Q f#Q'c|Rc|cmHRQ '{|/Q {c/Pc/lT$|Rc/|c/mRQ ${f#Q cAPcTP|~bQd@Q kQ/dw}QP@ P i=|  fITfTc c|PQ =+c }c|`Q" +i. j..dEQ5 .(-i&c."|dEP" &nmi Q -CPPG  c clKTclj c^lThej % 4 ; % Q   j  ijT{ $=M] x!i5;DDScT Q W Q  Q  Q  Q  Q  Q |T" LiQQR Q R'cp  p}cp|pQ" 'RVcpcp|Pc|cmPc/c/c|c/mPQ" VTR/||a@|P|T /j||a@ |`| |P|  V|aIT|dHT j6c'c |`~`}~|gQ6 6Ub- ff\f#f}|\#~}|dc f lfIj[csccGcfIj: Q\#QNrZ UbuL]-pNpcQcclT jGcU0f\dz f\ fcZkuf lcf Q,'d-[V/c |cm |cmQ /iZccl(QCPPGccU#cjj cEc)j"j.f ljB@dffmcflcvfmcc* d fmRcQQ ^"2B 9Ic fmR dQfQc clTcUccf mTQfl~}|d!Q|`@%|` %&f~|`bf mIQ|`%3|`%% ~|`bf m|`&Q, 3}|`%~|`b|`f mRQ, 3/|`T}jbgUQ6 /( .d Q `|S|`J|`%9|`%)~}|`bf m|`&Q, `(1cGQ}`}`% ~|dJRQ, 1;QQ Pf mQQdT Q, U-QIU| f|~Y |P*236 IN׾uiGclT( aB |d|P, GE|b~}|a|~bRQ6 i~}|bSXaBaB(XR dPff\d dThP ( i21CPPGb~|}bTf~fJ~}|a |b|}b|aB|b"P^F b;GLiM`Xc|`*}"|aB~P~  P~a"~Pe*L c`i Aij8k+|c k}Q" +ȹ( f`V`lT  vaRcejT$j%lT   a}cEcElT   P| .>N^ sV'Ed[dh| d#cE E|cEmQ #iP Q (|cdT  dZT=~}|dP =kIT@ =(i dp |az |a|`T}k f lP5ST]3C di(5QdVbes A|`T~}|jbm|`T ~A|P^4D A(np *d*?d r||P|d|`|`T+~}jbd-b'k ` jmTfv rW(( np/cd<`a}|`:|`$ |aJ|#|Pg";K a(_i'Yd7YCPPG J||P|d}~|da``0 9aJ .} k j  # aIzjbaF`l_jl_1@  kI"gl   `T  ` &P JÿϿ(ٿ( npM_ia F &F dI||P|d~}|d` a !kITT I(( np f||`|`TojblSjb"j kX(`T (jbjbkax`T .>Nh (V(d5 N&jr |`< k|aQ~}ddjQjkQjb`%jb`&jbTh ÿ(,( npcc clTPsT}|T" U- Q , |aR|`}Q o|`K |aRQ o~aQ, U-cQtPsQ!|dyj*P" !{}|dFXrQ" 6^~|dF ff\kO 6}a~}%XrQJ ^=,6{CCPPGG|dFff\kOj%k A j@P, GN{4|d+%}jj%m?|kP, 46{ Q P!|`cT|sT !,!cc|c%mT !i%~a~ }|dOc%mQ, %U-| P|sTP W!| cT|sTP !!| c%T|sTP !!| cT|sTP !!| cGT|sTP !!| c$T|sTP !v|c cm@cl&c`Ac`P, vLi!| cT|sTP !cP cmITP|dNQ SU||%|d clc`bcl cQ US$|a@ |dNQ|%Q $Sc`b ff\dOmKT!| cT|sT !ccfmecd1clGcAc`bcc cdJcc f lQ!1isccGcdZ"PcT|Q (Kdc$cGccd-cctccdAT K Q, U-'cc$bXQ 'CPPG;G  fccddccbQf lQ +,.:es  ctcdd cctbQ)c }|d~clcT, )U7-}|dP" U-fqT }P" YV#~c$cGc}|dgT, #(np"}c$cGc|dT" "(np"|c$cGc}dST" ",npcTcTc clTPc clTPcAclAAclAlTQP#c kITQP #l|}+cQ" U-}|d7P" )h Q" x{ Q" x{ Q" x{QQQQ Q" xR Q" xR Q" xR Q" xd Q" x0Q Q 0P" , Q xP , Q x Q, xk Q" xkQQ Q xQ Q kQQ Q _ Q v P^ xk,Y6}CPPG 8Q, ,6P" {QsQ}|dLXrQ" 6^~|dL ff\kT "}a~};XrQJ ^=,6{CG|dLff\kTj;kA j@P, GN{4|d+;}jj;m?|kP, 46{*|`|`|;Q" **|`|`|%Q" *P!|`&cT|sT !,cT!&cc|c;mT !i A ~a lc%lc;l c  c  0c%c;   O T  c$ -c;c%   T O  c8  }|d~ }|ekcl eel,e`b ~` l}|X a~ }|ek e~ }|ekQ AU    - | &P|sTP W!|& cT|sTP !|c cmc -cmN} ccj `A`P@( LiR(!|& P&P !!|& c;T|sTP !!|& cT|sTP !!|& cUT|sTP !CPPG!|& c8T|sTP !!|& cT|sTP !cP xcmITP|s}dQQ" SU||;|d clc`bcl cQ U)|s}a@ }dQQ};Q" )SccT l$P Rs&c`b ff\dTmK-P)c }|d~clcT, )U7-}|T" U- }P" YV#~&c8cUc}|dgT, #(np"}&c8cUc|dT" "(np"|&c8cUc}dST" ",np cl ccc%ccfmjcdcdc)&ccdcd1&ccd&cd1jccccjbjbcGNd4 d4f lA[cld!B&cld!-cldJ&cldJcc f lQg*C *(i<F| a aIl IkI`;` afm#Q@ =(UdR5|`%A|`%@ |`@%AP 5wdc$cGccd-&c8cUccd-cccctccccdAT wCPPG c.c6&.(6|}~>:|} }|dTc;m*Uj8N,D~&;7|} }|dOc%m3j8 5 Q.6Q,J Utd-tiPpcc'cc$b:cc8b&$cc8bcc$b XQ" p`l  iccdd  fccdd ccbccbQf lQ PQ6 NV]S_esz  ccdd  ctcddc)! cctb+ccbQ ccb cctbQ" zV]s&ccUcdZ"P!|<cT|sT !cTcTc clTPc clTPcAclAAclAlTQP#c kITQP #lccT||Q (c. c6.Qc.6Qc.Tc6T!|DcT|sT !|Q x,|a@|||Q ,dcT+~a~cm>~cmQ, +U- vtvtPj~ac~cm cl~ }|dOc%m~cm>c~cmQ, jU-| &P|sTP W!| &PP !!|& cT|sTP !!|& cT|sTP !|u}uQ" xSCPPG|u}uQ" dScT|Q (,|d+|kXrQ" ,{|`LQ *|`|`|%Q" *c &PP vtvtP2 }|d~cQmXcQcXkzXQ6 2U,-5|a@|gP|`3Q|d XQ 5ir|a@||I|a@<c c fIc c fI|d|QQ6 ri3m)hf|`SjJgA%j ~}dg`\TjP|P^4D fU}-id9S,)c@ }|d<@~c@lc@T, )U7-&}|dcccQ}|T" &U-!|`cT|sT !cX`bT)c }|d~clcT, )U7-~a~}|dQ, U-|dQ i|dQ i vtvtPs@Qd~ac%Flc%l ~}|d }|dO~c%mclcQ~cQm~}|dQ6 dUp-"cQnp|T, ",(np,|d+~Q|k2XrQ" ,{|`|QQ *|`|`|%Q" *CPPG&}|dcccQ}|}T" &U-2cr cT ltP 2R| &P|sTP W!| &PP !!|& cT|sTP !!|& cQT|sTP !|s}dQ" iS|s}dQ" iScT|Q (c &PP vtvtP.}`ylT}|sT, .,inp&}|dcccQ}|T" &U- R h| |aI| J}aI] v~aI93 ~az }az P|azDP@ |rtyJ|f#mC`||f#mn"|P" Jrt;||`||"|P" ;rtic|l c|Pc]l c]Pc9l c9PQ2|c|mA|c]mA |c9mAP 2ic3lTc3lT c3lmITcD|P iPPP%~}|s 3 PJ %U-{sc lQ>|0|`|`|`bm@ }|d|P" >(c Tc T R i|cmAP i |P" (CPPG#~}|d~T6 #rt(np"c3|T, "(np@c3|T" @rt}|dT" (np Q np Q" x{ Q" x{ Q" x{P npP~|c+m*T, (npQ |a|# |a|#c3|T@ Q(np|d |cmb-P iflfl f mf lc5f lQQ~}|gT6 |@P x|dHT xaT bQ" (imf;fdd bdjTkQ^4D m(i m(Tld|bT6 ȹ i |P,  i+ )|`rmzT, + +0$|aC |axQ@ $V $i!|T+ ! !x.|P* i xCPPG0|`)l}`)l#P|`}`#P* 0abD}|a}`blbl|`blbl#}`'|`'#P) Dcxy+|A|a-P5 +d +x*`blb|`blbmT4 *iٿ *x|`T x|`bT x|`bT x|`bT x|`b@P* f r1| MaJAP+ 1 1f |`bm`,P x|aKT+ , x|`b@P+ i x|`bT x|P x|aKT+ , x0eb  XQ@( 0  0|`b@P+ i x|`b@P+ i x|`bT x|`AP x|`bT x|`b@P* o l.| aT+ .( .o|`@P+ R x|P x|`bT x|`bT x|`bT x||`bP+ rt xCPPG'||`bP+ rt x||`bP+ rt x|P x|`bdT x!|`bcmT !x QQflO  fmQ f mQ|}[~`XQ6 Ub-{XQcX flQ}` flQcc[$c[cm c[cfm" Qc[c cXlccc[cXm2c[c c[cmRQ6 Ub-flc TflQYo f+gg||#fm1 Qfm QflX Q,-,?T*: Y/<ANɼ clh dI QcF f fldg )IQd  ?g 3jfm fl flY f P, *|d RX+]RQ`, qe|ifmQWj,)flb Qfl QflpQfm_ Qfl}"f"g fImR  #J 0  %zɼclh d fl Xc> Qflz Qf fg)f$ }` flQ dQ6 )Ub-CPPG fzgff\f#fIfI\#f< ffma fIm &\#|fIfIgRfl QX@ L]pfcQf QflXcY Qfl Qfl `f fl3@@flg f5fl(fl g Q  f_ fmQf_fmQfG 'fG lce !fl e QflfQc e QfG fG lQflfQfG fG lPl_fmQfl QfG fG lP_ fm Qfl QfG fG lP_fm Qfl Q%f&U lT j}Q $d%f&U lT jQ $d; f]wm flQ ; Q,  d~Qf c ckc` d2RSj|dI VD7|dI ,jfm flc9 g QW desc, fl| TflQ|d~Q |X7|2\2l}L|`fl`Qflq Q2\}fm Qfl_Q"I2 Iok ec clh dI QsQ dQ?f2\ flQX2\| fl[ Q ?flj TflQCPPG|d~Q jvc|X|2 }8fld Q2 }fm QflOQ"I2 jIok #e clh dI Q dQ@f2 flQX2 | fl8 Q @flgTfl Qgs}#2|a@ |`q|fmnc|fm Qfl Q3?"@I2 gIokBEec clh dI QsQfn cL lfn mQfl Qfn &cfn lcfmQfl Qfn fn llfm Qfl Qcx %@fm Q%x fvf*Q$x fvf*Q af mQ fmQfl Qg|`a aaj J}aTT< gYV%:Wd#c P}cm[TP" #YV&| }aT" &YV sc fm Xjfc mi Qc f mQ" s.<a|`c$clc`b`8 V}aT@ aYFV04|` }aT, 4YV0cj@|@P" hSff\mS Qff\m]Qff\mQff\mSQf\lUQCPPGAc gm ~}|sQ~fmQ@ ADýԽcLd T|a_T x#/|a_T xc Tc;Tfl Q f mQ fm{ Qfl Q.}`yl|T}|sT, .,inpa~a@K|~atBfF~k}k}|||~`%kk{|TQT( aU-{ `QS.}`yl|T}|sT, .,inp!}|s}` cmQ" !U-&| }aT" &YVc7 lNT&| }aT" &YV f mQ 3f mQ&| }aT" &YV&| f}aT" &YV>}`yjp k,|T}|sT6 >,VinpGc0cl%c`ba@~cQ|k T~}|sT6 G,(npS&| }aT" &YV&| }aT" &YV&| D}aT" &YV&| }aT" &YVfl ) ] f mQf#g`Qfl  P f mQf#g`QflQCPPG&| }aT" &YV&| "}aT" &YVfl Q&| l}aT" &YV&| }aT" &YVAc fXQ f mQ A\c7 c7 lNTQPflPfc;flmg TP0|a@|`; c;|`;@P|sT 0v  f mQ|sP bcsTfl Qc7 lNT<fc;flmg fXQ <\$|}f&XQ" $i{ vtvtPcfXQcfXQc\fXQc*fXQcfXQf] f] l<Qfl QRcaAcK AcZAcK Af] f] mQfl Q" Rg_flQflQf f lQfllf mQf f lQfllf mQ/f&U lT jQ.d'|a |ao-P+ 'x '0@| aJAaC,P5 @V% @x|aT+ V x|c l bT" iO|a|ao|a|A|A |a,PI2 OFV0 OxCPPGnM|a|ao|a|cA c|a,P5 MV0 Mx|aCT+ V x|aCT+ V x|aCT+ V x|aCT+ V x|aCT+ V x|aCT+ V x|aCT+ V x|aCT+ V x|aT+ V x|aT+ V x|aCT+ V x|aCT+ V x Q, mv} Q 0 Q" V0*|/ mff\XT *0}/|XQ" V0*| mff\XT *0}|XQ" V0*| mff\XT *0}|XQ" V0 Q" CPPG  z}c7  C"   `'*=dH c   ac /a.aj k fIm Yc R zg /}a"}a""c  c c d? }c7  T*l zxhspVA z Q; !{f m c Q}|ak Q" ;V?|` |c mc l| |` If mQ ?V} }a@ }`f l 'Ik@jm |` f lW Q}|`= j j}|` f mQ6 V c8 |8 Q  ]{ X % Qcz fIm{ z Q vtvtP!~}|d }|d RQ@ !i}|~aA Q, i~` eQjTA}|k k[ j  kjj  {}`3lmIjT |}k  jT y j  gk @/j T!}a&d  }a& d  LH kB=`..a~}|a  k QPJ dt &iid,fX}tI I tF mrimm ki z CPPG!q|}P|P}P|`}`% %P% %P% %P% %PP4 qabfafb}`|`a|.M|j3 j3 5j j (c}k c}k |a[|}~}mi Q 8Q`o (6~ixa$xj-rb$|`)lT |d Q, $i #d$|`)lT |d$ Q, $i #d # |d Q i # |d$ Q i#}e }|}emQ" ##i}e |}emxQ" #iQ1|fIdA fIc my RQ 1i?c ~`zg6 }|~aA j= g61 QJ ?-6Vi d T d TfIT,ck@PjQ +0.k@jP, .> ,0c |bT}P, ȹEQ*|c' @c }|d c ,P" *YVc) A c c) EPc cm d@ Qc l Tc l T}|c m T" YVQc l Tc l Tc c l Qc A {c mJAPLc g6 fIc my c fIdA g61 Q LVYc g6 {f]wm g61 fIc my P" YVVMcm@ cl TcTCPPG"c T~}|dA T6 i R" \V Q" \Vd c |Pc lTj }|k |g6 ""P|PJ2 d_dBdDc *c g6 |c mA g61  Qfl Q, D6Vc Q \c/ f6 QQ<} |PfIl j= |P|d|k T6 <V |P  Q, dbi Q" V}|a Q, dbi}|g6 Q" V}|a Q, dbi}|a Q, dbi}|a Q, dbi~}|a Q, dbi~}|aO Q, dbi~}|a( Q, dbi~}|a% Q, dbi~}|a4 Q, dbi~}|a Q, dbiczT%cl Kk@PQ %dc/Q |c mJQcl] Tcl] Qc/ J|c mJRQ||P|cl* "P c cl TQc cm v|c m Qc |cm Q mc |" Q mcl* Tcl] TCPPG# Q6 }ki Q" i+|d2 }|dd@c QcQ" +i|d c . Q %cl }|k9 c . Q, %td(|`| c |cm Q (tcl Tc/ cl] -Pccl* "P|cm Q i|cm Q i}|cm T" i}|cm T" YV}|cmT" y}|cmQ" icl)Tc cm+ Qc/Qc |cl mQcl] Tcl TclzTcT#c . c |cm Q #c cl Tc TP}c?cm@ |cmJc@Tcdp c@Tc^/`&`c jc^ c@TQ6 inx%|}`|mR|}`]mAP" %rtie}|cRcm@ ,}cmJAPca caA-P" eYV}cQcm@E }cmj }cmj%|a QLcm@ c|a Q6 i3s(9cm@}cmc@TQc}c@TQ" 9iF}cm@ T~cmJAPcaT" FYVL}a@}|a Q" iCPPG$-}a@ }|u?T}|u<T" -i%}|u? }|u<-P" %YV-}a@ }|u?T}|u<T" -i vt<vt?PT}cm@ ~cmJc@TQca@c@TQ" Ti R" YV Q" i}cm c@TQP" i R" YV|sLQ |sLQ |sLQ T}a@.c }`|mmlc }`]mmlQ}c mQc@T" Ti R" YV}gL}a@ }|a Q}a@D ~}`|mj $}`]mj |a Q6 i<~(.ctlT gQ -dcTg5d Qc5 l c@TQ" i R" YVfcGctl)ct  }c }`@PS 0 fdVNi,c k@PjQ +0 vtvtP8|ct mxc ct lc ct m[|ct mQ 8t w|g gc *ct j?aRQkRQe*L w}d'LilOvoc PCPPG% Q" i|P V Q" V Q" VMcm@ cl TcTc TcF c mK Qc l Qc l Qc l Q V lMXR c l TP|c mn Q" V}|d Q" -{=} cj Qf m}c |cY d Q" =V]c l= j# kj Pc$ kc Pc l# kP+ ](vc# TP pc ~`3 dBj= ~}|k -k~}|dA  }|~a ~}|k |~a QT pVi Q" mQQBc l}-)|a  !fd Q, B Q" ac j= ?g6 }|k  |k j= g61 Q}|k k RJ2 a\VUEc j= j j= l Qj Qd@c Q EV Q clQQc TcD T/} |dA Q" /V>}k.~|~a |dA Q@ >\Vi4~|d |c g6 Q~}|cD m Q, 4i|cD m T i}`z}|d Q" mCPPG&Zc }@|a $  f m }` |d cD kK Q6 ZmVcD l# Ts/cm@ cD Qc TJc j= }|sQc& j k@ }|kr kK R@ J\V~|}J `g *d X/QsQ@( i-rx|c fmE Q V&c j ` . sQ &V>c }a@} | |`} |a f | Q" >V|`} |a: }|sQ" V|P VAc j s%/k@j A j j EP" AV%c j 'jk Q" %Vc Nf mQc Q, i R" \&c l fc QT" &F}|d.c fc fm? }|d4c mK }|sQ" F|s} ~& P, Vc& lT Qc c l mxc& c mK c& l Q|c& m T ic c fmZ Q \}d[Q" \b|d[Q \|d[Q \|d[Q \|d[Q \}d[Q" \}d[Q" \CPPG'!|} f ~" P, !hcTQ 9 dC 9 Q 2XQc l ,c c m s-PfITf c Es-Pf |" P h6|c Qc |c m | c |a | Q 6  P|d Q Ac= | c1 ||= |jQ |}|aN Q, A\\| c l c mA ||c |dK fIdy }d }c= l Q" \|c m R|sT i~}|>cP mQ, hc m Q R { Q {|@|s-P Vcc3QcQccf c l sQ d4 Qfm3Q 6$f mQcc< Qc llQc l*Q|c mQ Vc lZT|dn Q" VA lKXR3Pc cm 5PTP A d4 Q d4 Q| d4 Q o]c cm XK fI } k, |bQf|bQ6 ]ȹ cl d Qc cU TPfI@Pc 0P/PWf"| mff\XT| mff\XT Wi$|  f mQ $i R i|a T \CPPG((c c Tc |cmTP (\(c c Tc |cm\TP (i|@P V|YQ NTTc | P" }|a T" o}|aq T" |cQ|dX mQ qc }k $} j}` cVm}}df# f#l} }}a Q, qV.cO Q}dX l}a Q|}a Q, .~}|}dX mi Q, '(|cm XQ" (\| Q i} ~ Q, 'c c c dC#  f mc fXQ>|aC|  f m|s >|c m>Q \|c m[Q \}|c mQ" \}|c mQ" \}|c mQ" \}|c mQ" \b}|c mCQ" \b|c mQ \|c mDQ \|c m>Q \|c m[Q \|c mQ \|c miQ \|c mQ \|c mQ \c c d c TQCPPG)_c d Dff\k  `bP6 _V]ttc f @P|d |a) Q |d Q #| |d  f Q #|p Q i}|c m9 Q" mP4cA  A |cA m DcA m|` Q 46cA |cA mxQ 6R^cA Q cA m cA mx cA mj R 9 RQ@FG, R6IPɼ*clTkBP, *< (d*clTkP, *< (d |ac $| U f m ؀|abc $| ~ f m|aBc& c#$|  f m |abc#Q}$|  f m cmd- j$|  f mj$g3g g3g kkjTMjbj mCjb|aCjbj m N|abc#R|  f mp>V CNWiGiabidr|aBc& c#$|  f mc, c#$|  f mQ" rNicR .R | |sQ i f mQOc cm fIm, k^ Qc tf mQ O+c cm |a,k( Q" +\c   Y f mQcl Qcl QCPPG*| Q f*f" c |cm fQ *r fg lT*c lT |ax~}|sQ@( *V "d |c mR|c mQ i|c mxQ iP c}|cmTQP" {H|`: c: mmdQjT|a |a RQJ 0 HV"G/D(~` PRP, (}c PP U/c Q| = c |` mQ /U#c  |c mQ #V c mITcc- |@-P in|d d,Q c mP9c " }~+ Q6 n\i(| c mT (iR|`Vcl|`lDcf|d, 0fm"Q 0fm"Q|d,"|d  0fm"Q iY|d,cb^c- | f m|- %f\ !f mcl|`lQA|`x dC5 |`lm ccl -f mQ|d j+ lj=d,  f mQ # f mQcl)d, # f mQ k jfXQ@ YiX|c bT ic |aQ i}|sc|a Q" ȹVCPPG+&|d,|d |c0 b-P &i |c0 Q i|d T i|dbT iP+c |kk Q| fl Q" +V}|c mk Q" Vfgf#f#m f#>af#m+f#lmCf#lfd c lQQc lQ[|}c m: Ia]a `d Q" [}|c mQ" _c Oc lDc lT(kCf#lXc lc mQ6 _Od-} Df m" -Vic Tc |" Q ic |# Q i6f  Pc d P" 6c m*c#clcm RcI DP2c lT|d g& P6 2&i 0}  kc` lTd kckclT k]ccmPjkj#aPj kjT{ ,<L[ (4}=Ud\uui0c lT|d l RQ, 0i /}0c lT|d l RQ, 0i /}0c lT|d l RQ, 0i /}CPPG,3c lTcVd |d& PQ@& 3i 2}/d T0c lT|a d PJ 0 0 .}+d zc;  j  clTkk]jT(d &P^4D zm)GdGxdc8 |d Q  Fc |&c}ai d; d; Q, F \|d Xf m}ai Q6 \.} c8 8  J c MdK P Qc cO Qcc )cO c j?XƹjQc cdsc ,scsTc= QcR c mJRQYc Qc c l m <c l mJ Rc l$c$R  Q" YW|V c$}"$Q" {pf c$c / hcP mcP mxj jd0 c l c$ c$.$Q6 p ,Y- Q- Qj ˃XT Wc Wc ld Ic ll c lRcv c lvbc lRc lc lm5  c= c Rc l0c*X%f lR ds c l4c c m[j@ cG k! c mJQRc- hcac- llclMc- d <c- Xƹclc- ll@c m c Rc c RT(8}DSPCPPG-_cR|$|a   f mQc$f | `|d Q|d QR _-Nc l:c lTj|| `|d Qc lc mR, N->|ay }|c mC T" -{|d Q V|d Q V|d Q VJ|d Q Vp|c |?|d $|  f m|dy ~}|` m Q~` f mQ, pVi6c j= fl Qdy j= mUQ 6V~}|c mQ, }#h~}|c mQ, }#h, ~}|9.vc mQ6 ,=#h{- ~}|9.vc mQ6 -=#h{c 1PP*Pc PP5}~| 7XIQ, 540-~}|. Q, 40- Q, U40|)d! Q i|d! Q i|d! Q i|d! Q (c |P R}c | }cJ |J Q" R}cJ |J Q" RcJ |P R ̄c m}Q( |` m}Q (hCPPG.,f|fm"Q , d sQ wd sQ d sQ d sQ d sQ d sQ d sQ d sQ afm"Qffm"Qf f#lfXsQ d Q Df mQ d Qfmy ,f#lfc m Q d Qfmy fc m Q d f#lf#l ifm"Qf#ld fmy f#lfc m Q \d f#lf#l fm"Qf#ld fmy f#lfc m Q"c; ||d> |a Q" "i 5d Qfmy f#lfc m Q d Qf#lf\(f#lfbf\XQfmy f#lfc m Q~}|dA ~}|d. Q, Vi}|~a Q, Vi# Nf mQ #V# Nf mQ #V# Nf mQ" #i# Nf mQ" #i# ?f mQ" #Ai# ?f mQ" #Ai# Nf mQ" #Ai# Nf mQ ## Nf mQ #$| tf mQ" $iCPPG/ vtLvtvtvtP f mQ Q 0 vtKvt+P|}~RP, ȹ0h|P h Q U}s|M ~ P, }#hcM c c|XQ U! }s|M ~+XP6 !=#h{0<cXc+|cvX>QQ ,-, 0U//J|I||XKQ V$c m T $|a@T x$||` `1 |`1 P $i|P x|` T xc c l Qc Q|`/T x|`/T x|` }` #P  ab|`/T x|dp T+ i x'|a |a-P5 'YV 'x2L|a@|`|mA-P+ 2i 2x2L|a@|`]mA-P+ 2i 2x|aT+ Y x|a@T+ i xL|a@T xL|a@T x"sT, "i|` ||` m Q 6CPPG0|a:T" i|`6 }`6 #P  ab|`9 T `x|`T xc Tc, T)|aBA |a-P )xc, TcI cm T|`9T x|`9@P+ i x|`9@P+ i x$`|d T6 $ $V+|aC |` b-P6 +} +i|`@ T x|`@ T x|` EP+  xch T cXIT|`@ T xc`9d d Qc vbQa5 Q, c40- }cJ |J P" }|d! T" +ZB gOQ +{SgoVgo g   fVN 1TO go gC   O"gC   O/0gC   OQ" ufZB Zfo<gSXXgfOgj$gf fZB mQR,|gSfZ|fZE mJ Q ,xpAP!pA%AP !sCPPG1`p9|+%`\"X&XT|gSXT5 `QOe|gS6Q 7:X| XT|goXT+ :)efS1QfSQ ,~}|jfUP gUH go|S_P ~}|go spfPc~}|go  +@/a@, mpXPz?O^ ,f0]6d}cMX~}|~}|go|xXPI Md   fZE (fZ jG jV |v<P|[?|3a*k g ZE ZPDJ"K"^4D iokRMMIIc|!fZfZE ||`|(@| |`\"kJ ||"\"k5DLTZ`h'p{X"kw"kh"kYkLk?"k0"k!k "k\k|P@ din|t3|a |a@"""P|P 3cCPPG2     %  "" |a_" "!#- k_" " "4"b|a_"" " "P@ xu}! }|XT6 !u}Q| PPP P P QcK QXTcQ|cL mJ Q x Q, FQs|M P -Q|cM mQ x vt_vtUPfF T R  ab Q ()| ~aJAP, )ki}~}|aW T@ ki}'|`}` |`` }`` @,P  'ab.|`S }`S |`}`APAP4 .ab4| }aJR~}sT, 4ki}T|~}a,'  haImjP~}sTT< Tki}"G|`}`AP  ab4| }aJR~}sT, 4ki}T|~}a,' n BaImjP~}sTT< Tki}"G|`0}`0AP  abcZ .Z Q cR .R QCPPG3's|^ }V ~=\ PJ 'k,}c=c~}|c\ m,T, picY Tc=c|c\ mKT ic=T np |P, (np |P" (np ~}}},|aT,  (|Q }_ P" k|T P |P |`[ |`X d7P % B`~}|sTa~`li^ Bxk,Y6}|j }`l^" x{}`l^" x{}`l^" x{}`l^" xd|`b@P+ i x-|`b|`S |`@P4 -i -lm|`bT m&|`S |`@P4 &i &mo|`bl@P+ i m|`bT m|`@P+ i mCPPG44 fa lT7ju  jkk6Qfe f #"e  kz kjT4ju jfPju kt 0Q* Q$  j j tj {x |j{xQ " ( `      " 4DTdt -6Yd&V;dID L U  \ c esh 0Ifa "a Pc$Tc$ c$|"$Q mRcsT?  TX`a Q ?6|cl mQ r|cl mxQ r2cl lT}|kk dh P@( 2ȹi 0dcv dh Q6cl lTju P@& 6- 4d1;. cl mdg R .( ޮcl mdg Q |`m|`T\cv v fIj[fjm fIj:cr v Q1^"_1klh *:J (sud(}y1YpcafI vtqvtP|b}P" ȹiQ|cb@}c@P" ȹi efIXQc$Tc$|"$Q hgth Q}|sgto P" ȹiCPPG5}|sf ~"$P, ȹihc{ gth Q~}|slP@ ȹih},}|s~~y f ~"/$P, ,ȹic{ c$cy "$Q~}|slP@ ȹi}c{ Qsch Q6cl lTjj  P@& 6 4d1_cl lT:jj c fjm  PT(8 _ ZSdPcn #P|#n Q tn Qc i Qci #n Q vtqvt'Pfz Qfp Qcw T}|sgo P" ȹigh Q}|sf ~"w P, ȹic{ gh Q~}|slP@ ȹi}#}|s~y f ~"w P, #ȹic{ cw cy "w f cw f cy "w Q~}|slP@ ȹi}|`T x|`s}`s#P  ab|aT+ 6 s#|`u f @P #x|`x T x|`c }`c #P  abcbcbT FcclXITCPPG6O | } f"   *  ! `u    g ~ aA [kjU~gA kI kj @kJ  `b ~  ~X~g5  s  a Q  a Q  g `\j ~ajjl.~aj 9  0 ` g ` l Q~  a| Q  k 7  kjy j   aw k  aQ j 7  k  `     k "  az g !    g wXz j! a|     aRQ  k Q ` l QQj~ "Gj Qg j[ jX ~  as Q QUUUUUC V O U}  - UK( {U  i    'J/K7 Q, U-{ tf jf  k 4 ~}|aC~}Tz?O^ tU_v-{6n]?kiFermc Qc Pf m  Qf mxQ |~g ~|k7|~aj+mj+`A`8|` ~`*&`< }}` "~az|~ |~a ~au|`G ~|=~az3~|aC (|~a  ~au}|~a}|~aQ3>?hP Ub-{rmAU{CPPG@7 `F~kvajQ}` / f l}|k}` /f m Qf lRQ}` /Q,st,swrHX U=?-{vw{yI) }| vX, )=?V6| X|f\fXP 6Q( `F~}|vX>TJ (UL=?-{5~}  ||XzT@ 5UL=]-) ~`Fva}|X(Q@ )h=?V{K~}|X (`XIQ6 KhV{wrf#lU~a~`*f#~a}f#~a|+f#lUf#lZf#l(Q~ZQ, rh{w>|`|`*f#|a|Z|f\fXPQ >{frf#f|`#}~XzP Q#QHI@ fN?LVp|+P {~a~| X ukJkw} i} }}| f##}kkhkMk-P Q#Q}PQghP WQ=LEp?~}|X| f X(6 ?QLCPPG8 ~~af#kyf#kj|ljTP. }f#mf#mCk}f#m&kjva{`jTG k }f#mf#mnkjvaP|Rb Q=LBdd\ |!|a@|`VV|`HHP||H|f#led VP" \ 7|`A j/`B|a|#"}fl |`T}kB}kC !\]}b\}b^}kE }b~}vb}j`~}vb}gf}g }P2B[t 7RtrRi%Ls\hd  |vdQ" cVX/Q|a AP5 U- x%|aT+ %- %x|A |`AP xK|`bm@T x1 A-XPQ@ 1U-{|` }` #P  ab aT, U]-pva f\ *`XIf T6 pU=]-%f\aT" %V{|@P x] Qc c f ,-Pc PPCPPG9fd c fl Q,| } }|fm Q" ,g_|`|d Q Vf 5fIfIlm fIlfId Qf PP f ~c }|d Q, V0#c |}a }|d Q" #V0|}a }|d Q" V0   ~` f lf lT|}ag |}k j  |}k |}k |` @ c_}| a/Qf &f & k  `x"x\ S k:j~a Q Vfv V0*b=\h  |s} ~3P6 bc c fm Tc "c "P=|` # @  @ Q =|` @P+  xc c c c Tc c lc c lc c lc c lQc c lTsTPc c lTsTP"c |c m Q|sQ "_"c |c mSQ|sQ "_c c TsTPc c TsTPc c TsTPc c TsTPc c TsTPc &c kTsTPc c TsTPc &c TsTPc c TsTPc c |TsTPc c TsTPCPPG:c c TsTPc c TsTP/d /d /TsTPd d TsTPd d TsTPd d TsTPd d TsTPfd fd \TsTPfd fd RTsTP?c c m@c Tc c m@c TQ ?{,c lT|aCPQ, ,V +c c "c c "&c c "c c " |aT" Y1|` |em5e|| Q|sQ" 1$i c d c d |sQ )*~cc d c d ~}|sQ, * ||a@|` Q $c |c mTQP )c |c m'TQP icsQc cl8TP R {clT vtvtPcl Tc lTcfl fl X/sQc Tc Tc Tc Tc T&c kTc T&c Tc |sQ 2 ~c8lc ~}|sQ, c8lTc Qc c8l Gc Af l  ~fXf l clT9c9m@c9mQ6 3r@ICPPG;c c3TsTc d Q.| } fl f#mP" .m1m2flc c c f m QfXQ+|| mff\XT +ic Qc # 9 f mQf#lm nf mQQc Q P i 9d Q vtvtPfXQ+| mff\XT +ic Qc #  f mQf#lm bf mQQc Q cP i d Q vtvtP cP i d Q vtvtP'cc c Pc TP 'R(clTjP, (R &d|sc Q W|sc Q d|a|sQ 6|PdP i|a@T |d PP i+| ff\XT +)qsfd kj1jT k f mQ, q(AWd|`T Vf#l8 f#lmJE Dfm"Qf#ld  Dfm"Qf#lm f mQTaTf#lU,#f#lAf#lm, QCPPG<c f#f#mf#ld fdCfm uff#lm\f#lm f m  8f mQ 1f mQYclT+|a $f mP  f mQ, Yq @c|aQ 62k}a@}a@ ~}|sTP, 2w{cl fm"f#lrclTf#lm %dfm"Qd2fm"Q,:<fd>f#ld !f#lkfXQ >cl fm"QcMQ>f#ld !f#lfXQ > vtvtLP/ccm@ |cmT|sT /Vccm@ clTsTccm@ fm"Qccm@ TsTf#ltf#lm@^f#lm "d)fm"f#lm c fm"QcMQc |a@T if#lZc +f#ld  `fm"f#ld  kfm"sQs dC c Qc dCaf#Af#m@f#Aaf#md4<fm"Q|aCT ic c|aQ 6 vtvtvtPf#l X0fm"c0%dNfm"Qfm"Qc0fm Qfm Qc0 PPCPPG=c0 .fm"QO hf m dc f#lU yQ OtdNc0 fm"Qc0 +fm"Q kf mddQc0QnfmdQ vtvtPPc Tc |# Q P|c l Q|sc0 o Qc gth  Q Pd4c l nc dQc m Q 4vfm Qfm Q R ic0fm QsQc0 .fm"Qc9d # X f mc l $c  f mQ dc f mQc0 :fm"Q vf mdQfm, c QsQ;fm,c fXQsQ ;c m@ c l TQPfITc |a }c @-P" \"c fcc mT" "\c cfm Q \ c |" |` " Q i c |# |` # Q i Rc mT|c mAP i Q i|d , |d ,P i|d |a -P i$ |XR |a TQ $i"  f mQ "i+ |XR |a T|d ,P +iCPPG>$d@ |a@-P $i| P ^P i Q  Q" ) Q,  Q PP Ksfl; c fc mi c fc mi Q KF|c ld c mzsQ F)Q|}c l)a d c mzQ" Q:sc lT j m` " Q9df#lTf#ld  fm"Qf#l $fm"Qf#ld f#ld Qc f#l" _f mf#ld Qf#lTf#ld  fm"Qf#l $fm"Qf#ld f#ld Qc f#l" f#ld Q/|c m|` m |d |a Q /c ld fm"Qnc jd  f mQjfXQf#gaQ n(f#l+f#ld  Kfm"Qf#ld 0f#ld  f mQc f#l# f mf#ld Qf#l8 6f#lmJS Kfm"Qf#l+f#ld  Kfm"Qf#ld 0f#ld  f mQc f#l# f#ld Q/|c m|` m |d |a Q /CPPG?f#lf#ld sQf#ld sQf#ld $f#lfXQsQf#lf#ld sQf#ld sQf#ld sQ| q` mJQ}I mff\X% ` mJ R 0 f m" is"Ps"P3|a@|`|`TccT 3@|-c lTkCd d Q|d Q6 @)7dA| mff\X|c m Q Ai f#lm@f#lf#lm f#lm@f#lf#lm  }f#ld }f#ld -P^ iE|`}Q~|}a'|`} R|}ax, Ei0Qf#gvaQ&`T`TT&TT8c%cm@cd |cm Tc T 8icPcy!cy c yQsQc c QsQjc %c m@ c mz c dfIk,k> j; d Q jpc|c QdQ ,| AXQ ,4}| XQ" 4(|a |a_,-P* (x (cCPPG@M|A&a@ aJAP+ M Mxc T|d T x|`bd T x-|a@ |`H kA-P -x|d ,P x|a T+ ) x!|a T5 ! !x|`bd T x|a ,P+ i x|a ,P+ i x|dOT 0 |` |` Q,  iB|` $X |Y|a QJ 0 B BiAf/Pc *f .  f m  cl"37c fm Tc Pc fm TPPP8||c c |jw  Q|fm Q, 8(fnc fl ftr Qcfm Tc " PPP^|`  | QQ"Q `n GQ@c |` c |` #Pc|`Pc|`PP @c$ Ac$ fIm ec# A c# l2 Rc Ac lA=c- Ac- fIm !c Af68 c Ac& -Pc) Ac) fIm ec( A c( l2 RcAclA=c7 Ac7 fIm !c% Af68 c% Ac+ -PCPPGAc1 2c* 31 c1 23 c3 41 Q!~}|s|cl , Q, !Wc1 3@P vtvtP6c?lTj1 231 cT 2dKc0lT j c0lTj/ kQ@& K6'd'FdclAPc0|"0Q i c0m0Q vtvtP vtvtP0 Q vtvtP Q LflLf mQc0 "fllf mQfL"fl. f mQc0 l c0 ll"fl" f mQcQc0 l' fl4 f mQf! 'f! fl ! ! Q ! R}|a6 T  abQQ|M }< P" $}|d9 T" $f  f lR dB f |`M dC |dT X|`M g .001'9D)P\ Z cP (W ? fTY ^ gU8 K3:Q@ 3$t0[f f lR  dB f X8 f l|g Q dC Q, [t0;P*f ~}|X\g PJ *,f ~}|Xg PT ,  }|dL P, $CPPGB}|dL T" $f  f lR dB f |dC }d; X͈| g  "&$. Z (cO P~cO P}QT( `$$t0$|XcW cc> cO Q $71|!fq cZ f #P6 1$/ic> |d: > Q c> > Q+CcW |`< d: |`V W |? Q +3cW W c? lG Uf? Q'f  f lR ||YQ ' XW > Z Q4|fts  gP" 4 |P" gI PgI Q#|cF m|cF mP Q #ppDcP  cF m P cF lT}|k|Q|P6 D!Bdh|cD  cS l.|acS >f2\flX QcJ .J fllQflK Q|P, hspgs|X|gA QgjlfmU jTfl= Q$1:2@( g}4JpfmH T |`Q }`Q #P  xy L |`T&~}|XKdi QJ2 Lx(:dj~~d[   ~d_ ~}|d h %%&|de Q jx%(CPPGCV @|a}+|`|`T}k|PJ 0 V(+T5Qd `dXid[k .kj5jT*| |}ak}`'|}`}`p }a[}`q}| . V} | = d k  k  }a[ }a[ }`\}|B2}|X)a}a[}`T|jk`fdXPd sjT}a|a~a}`1|}j}`p ~a}a[|}~dg T"w,;Udt _id([gh< g1< j6  g26 Bi Pg1 \j dg2g 6gIr   }|dQ``f  %7d % }|d| }|du.~}|dV2&``"~`T  a%C%; d1 dL' dux& |dd | d_%n%f d\ dLR.& |d> 7@a &d   |k  ax d % }|dQW lstz x  %( &#_i d.9GQ^ k CPPGDD|`Td}`T~kl P\4C D_ %s%Bg/|`}`T~ka PI2 /_-gI}`T5ddX dL ~|d~d=|de QJ2 I( Hd$|%} zQ5 $x#i|%uQ  }`"}`}~%;~%3d+dL#du}.~&|d>~d |~d_~%(~% ddL.~&|d~dlQm yx_i(.%d~}`*|}a| Q@ xi|`LT i~}`|}a| Q@ xi Q6 %( Q, % Q, %~}|dQ, % Q" %|`XT i|aT i|`T i|`T i}|auT" i|`T i~}|a_Q6 i~}||aQ6 (CPPGE_|%A|%uQ}~Q}.~(~cQcQcQc}QcQcQ6 _flQflQflQflQfl}QflQP|d Q ( R i-| @g T -(|X/Q6 |`T,  Q, % Q, % Q, %|u }k P"  Q, % Q, % Q, % Q" %~}|cu ck mQ6 i~}|cu ck m` Q6 ( co |vxT" ȹ0cu lo TQ, %}~Q,  R i~||aw Q6 i|`ZT i|rP  R icr~||ax Q6 i|`ZT i R i|} P" CPPGF cQc cm |a,|cm~  |aC}dc ~c |k{+~c |fmi ~c fm] ~c|gi ~c |kQ~c |fmj Q~c fm^ Q^ x>(|`T i~}|av Q6 i~}||a Q6 (~}`*|}an Q@ xi~}`|}an Q@ xi~}|as Q6 i~}||a Q6 ( }| %~sQ   ~`T ky  a @f]wm  WAf]wm d#%$%&~de QR x%(U gd Q, %  Q, % Q, % Q" % Q6 ( Q, % Q, % Q" % Q6 (|`dT i R i~}|d_Q6 (CPPGG|` T i|`Q6 i R i" pf mQ" "%|` T i|`Q6 i R i" /f mQ" "%|`Q6 i|`Q6 i QJ x(~}|a_Q@ i~}|aQ@ (}`T" (}`T" (| T ~dr QJ x( Q (,~d} }dc %~|}ai QJ ,x(3 t dB A|aT|P 3([~d} }dc ~dz %u%~|}ai Q%~|}ai QJ [x(|`|aX/Q (?~d} }dc ~|db }a .%~|}ai ~|dd QJ ?x(ccQ" (cm Q" (|~}aQJ x( P" (CPPGH|dXT x |aAP5 d gd T ( 1c mi TT 1x% 1 c{ mi TT 1 x%}|dt T  abA|cim Fg] ciT A|cimQ xsid  d d Qc cm5Qh|` N |XR<clT"j j j | c|"Q@& hi$^ &SdcT:c |` Pc P|` Pc |` #P : !GcmQ XGX GXQcTc cl TPc cl TPc cl TPc cl TPc cl TPc cl TRPc clFT fl TPcfl5TQPQ2|cmAclB c.PQP" 2W(,|cmAD c/PQP" ,W(|cmT .cTfl7TflTPcTflTT= Y dJ Q =}|a T  ab|`T (|`T (cF   d  NX3QCPPGI0|d""gooQ" 0̻1|#|clc| k Q+ 1W0mc cl""kcf kclT  jl" "k|cm "& "kd T?' WsGr6w4 |ac lTM@ |ajT)K N V "] "|ae |aq |aQ?& wùlkl-_k|g jlT" xN O }aw ) }aw^f -f !jj k  go o }aw} f m  }aw:f l |df f m""oQ }awH} f j k k  k |dQ{"2B O/dPd5I5mbc cl"" cl" ""kcf kc d kd T4 s[xic cl c . c d TOc| Nd  "!"P! Oi6c cAc A ccj""k! /Od "!"kc Ncm +!jl":!"kcm J!jl"Y!"kd T=& st|mf,P`T 6cTQQfTY c X2Qc6Q3fl |gH i! X2Qc6Q" 35o!fx!QQCPPGJ| |!fIl !"wfG cwfG l!""wcwfe !""wcw!P! t:,fG fG l P ,mc_fm Tf  f lR XJQ-| afIXXQ -g| XQQ 0| } XQQ" 60Si f f)AXW g(8X|vbQQ"O`PI 0 Sȹ0IRRq)}|a@~|aB~}aBA-P, )ȹDi aXf I aX bLX 9bX fb-X b?X bMX cKX 2cX _cX cXf  pc  W'p Tc g =g g g g 6g g g  g  g) g) g3 g3 Q! Jd /Qc TQc sQdc Qc c / c  c  R|P (clTc . Q4c cj c d c Q" 4W(Q|X |X/Q|` Q|YQ|eQQ Sc QQc . c c  Qc c c . QcDl TcDl QcDl Q8c Qc cj c d Q" 8W(c .P vt+vtPCPPGKucljcj c Qc clc . .c c c "c . # c d Q6 uO(Yc c 2 c c c l T vt+vtP|P (c ccl c l c c c .$c c c .c c c c c c c / P+ iccl Q| } P" syFc*c c c #. casTA@@  @  @  @  Q    Qc Acc" Q Vc j9!k !Q|k|`.kTQG. V0sUiOafIl$f#lZ"e fG fG lH|!|` |f|b||fm }$ d}aJi!fG li!g fI!}}XA]Q, (|X[Q !XA]Qf|g: "fTY g"|`Tk j Qfm Q@& f( b2Vd0|`|c8l@ccA |ccl@P 0RfY QfHF RfJ> Qg*fIfIgi!R'"fi!RQ'"f>5i!R0  /dXQ 0'3cfl Qfl Q "&ICPPGL8Nc fl Qfl Qfm Q > "),&+7I||P*"P 7}|a d|aT6 7(fn4|`}`Q|`T}aQR4 4ab3d fImT|`| P i|` T i|`T i|` T i|`T i'f |" P 'i|` T i|` T i|` T i|`' T i|`6 T i.P+ @ i|` T x4|`  fl |`Q, 4 46|Y,P+ fn xQccfm}."QQO  h&X` KhaP O(c QQ vt&vtP m"c 3ac l.Qc c .et"w"k_,"z""".""kjV,"c~" Q\. f i0yȹ^ H$,"" H$,""K$""K$""P, ^dddtmmccfmQsN$QY,"("$"(%"$"%"$"$"fm}."Q YCPPGM|aT, ( iD|`|}`||`|}`|#P|`}`P|`}`PP  DabZ|$P!!m"| |%01|("#kT, ZN$| |Q%| |.|#P5 NicT R { Q { ifm"Q ifm"Q3:|a@u|a@ |a@,P 3{sQsQsQ sf mQd%Ffm"QcUTcmQc(TcQcQcUTcmQc(TcQcQcUTcmQc(TcQcQcUTcmQc(TcQcQcc Qc f fId  b  - dCem@ el QeQ" cȹV Q cTcc A |a,-P" VcX c) ) Qc) .) QcX Qc[ Qc@ c* Qc Qc  c Qf c Qc@ c l c@ .@ c Qc @  ) Q7c@ @ c c@  Qf " Q 7f"  g dW  C dW fQq~}e|fIm  a} a} a}}Q@ q$#}sQTc fd c` Qc+ cc (fm. fm,d Q,"S-Sc TCPPG Nc[ ,c -Pc Q|aQ cTc QcTcsQcQc Tc[ ,c -Pc Q|a`Q cTc QcTcsQcQcwQcwQcwQcwQcwQcwQcwQcwQcwQcwQ/| Y`Vm}Q / Q, r Q, r Q, r"| c Q~}|sT, "mv}0 fIa,k> j; d Q 0pcY|`T9jbdW` |#| |a#|PT< Y(np{Wd Q ijd$ ~}d?  +|a- h|aJ |aJ|a[|P@ j(np{3} ܼLXP" 3(R:|a@RQ" {}@P" np2| 4a}`T 2>c\d@ }a}}|sQ" >}P|}c\d@ s .`Vm}Q" P6CPPGO $fl; d j k#jffllR  kfai jT d jj%jgfffl8 mi QflO  | f mQ  f mQg4N $Qx%%|:fXQ %xfm1QfmQfdCd%Pfm"QQ 0 dC9fm j c1Qj cQc0 QcQJd%2fm"Qf#gGQQfm~ f#lfdA cA Q f mQ}%%|T, ,(np vtLvtP Q" i Q" icD T|d|do-P 0 Q V|c@P VQA|a@)|a@|a@ |a@-P A{ Q {#cZfm"Q #c fm"Qc fm"QcBfm"QcBfm"QcBfm"QcBfm"QcZfm"QcZfm"QcZfm"QcZfm"QcZfm"QcZfm"QcZfm"QcZfm"Q0|sR|cRc|cmJRQ 0iCPPGP&c wfm"Q & :|a@|s-P {&|c@cA |cm_TP &i|d_T Vc >fm"Qc >fm"Qc Sfm"Qc Sfm"Qc Sfm"Qc Sfm"Q R {c Tc Tc dP QcBdP QcBdP QcBdP QcBdP QcZ f mQcZ f mQcZ f mQcZ f mQcZdP QcZdP QcZ f mQcZfm"QcZfm"Qd%2fm"QcZ f mQc Tc T R { Q {c fm"Qc fm"Qsd%Ffm"Qsd%Ffm"Qsd%Ffm"Qsd%Ffm"Qsd%Ffm"Qsd%Ffm"Q f mQ R {cf mQ(|cf mQ ((|cf mQ (cTcM Q xcM Q xcM Q/ g  /8|` -|aJ|a[|P@ 8(np{CPPGQ$cf mQ $- R { fm"Q fm"QsQsQsQ~ |dh m}}dq sTZ~ |dS I}}dq sT6~-} |d }|d, }}dQ sT|P@( iop[ok P  P  Q i|dS T i Q" i|d, T i Q" i}#cB |cB mTP #}c" P R ic  < \XR &Pc -c @d< kc "d< kjT@& (7_ibi6|dR fcymJAP 6i|cymAP i) & d=,Pc&,P )|@|dC-P i cymzQVc" cylTkkjTd,k5yQ@& V101Q0cyTScylT|ak|`Tcymk5|yQ@& S ,d,NdD cymz||y|a5Q&yQ D2cy|"y|a5Q 2"| ||a |dm Q "2|acy|#yQ CPPGR*|dQ c9|a |d3 Q *+ cylT|vYQ6 +N0*d Q N Q 0|dm Q 0 |c lT |clT||k7fzfzmC cylT||k7QT(8 (d(Fdad&cylT~}|k Q@( &m} %d'cylT~}|kd QJ2 'v} &d|}fmV }@}a5/}/  }|}#aTcyl}|cyaTQP, |Wi 9cymzQ@c% lT|kk|cyl|d Q, @0 +d|cym|d Q 0|d5 kQ" 0(| qc% mT (05ca c9 &adc% m|kQ" 50|ca m5Q i|ca mQ iP|: coP %Wcc: `yc#y|s|"|c: lymc: `y|"yQc: l% mxQ W)c% Tc: lSTdGsTPscDcDlDDQcDcTcDlT'cD | |cDm |sQ '_cfm Tfm^ TPscD cQ5cP mff\XTP" 5i}fmG TCPPGSc,Pc dfm"Q d d  f mfmCdXəf#lUCf#lm@f#lm@f#ll@7fl; cfcL mi fd c{Q, vc fm"Qd  f mQBsX`dfmC fP B(8sX`fmC fP 8(+fmC|X`f"|P +(sdO TsdO TsX`"PsX`"PsX`fq"PsX`fq"PcDcU TcDlT'cD |U |cDmS|sQ '_sc/ -c 'cfl fl2 X/QcfmY Tfm TPdG sTRPscD c U QsX`&"Pc Sfm"Q dS f mQsX` &"Pc Kfm"QdS if mQsX`'&"P fm"QsX`.&"P fm"Qc/ T'sX`c TP 'c/ ) f mfm sQc f mcTcqQcTcQcQc f mcTcQcTc=QcQ|cFmAP  R c l& Tc& l cDl& PcDT|c mAP CPPG]Ts c mJA-Ps#Pc Sfm"Qf#gkaQc Kfm"Qf#lU-f#gY  f mQf#gaQf#lm@Qf#ld dc j( /f#lj& m`& f#l"& |dS|  if mQ  f mQ, q5koc Sfm"Q d8 Qc Kfm"Qd8 Q=|}  a }sQ" =i)s|`c& c#%|  f mc; c#&|cM f mQ" sNWt|`c& c#&|cT  f mc; c#&|c6  f mQ" tNig|`  4|a c#c# c&c}|cm }|kTP@ g )c1d f li Q gc %clTQk@jI fl; cf|ai fd c{Q6 g3df#lt Lf#lmJ vfm" f#lmJd9&2fm"cf#lf#ld_ fm"f#lm vfm"fdC"dO&<fm"Qfdd%Ffm"Qg~  <c|csT|dn |~  ( sT|P, giopCPPGU{~ }|P~  <c|c sT|d |~  o sT|P, {iop R i R icP|cB mT }cc cl:TQ1|aC|fme Q|fm> Q 1i1|aC|fm# Q|fmN Q 1i1|aC|fm! Q|fmJ Q 1ic. sQQZ|`c&Q  |a Q| ff\XT Z)+| mff\XT +iQcDd2fmC# - f mQcCc QsX`d&"PcMNd;fmC,f   f mQc Qf#lm f mQ|cH mAP iJf#ld4 /f#l|e f mQ J P i Z d Q vtvtPsx"P vtvtP vtnvtcP vtvtcP+| mff\XT +ic Qf#lm f mQQcCc Q P i ] d Q vtvtdPQQ jf mdQc_fm Tfm TP|_Q c_ fm"Q d Vf mQc_ gfm"QCPPGVd uf mQQc_,d c_ V uf mQcTc/QcQTc QcQ |P | Q  R dp&fm"Qf#g QDf#lL,dl d_ d&2fm"Q Dxf#lLdl d_ $c\  f mQf#lLdl d c c f mQ fm"QcTc(Qc TcQcJQ|XNT|P" 3|& |`Q|c Ec CP" 38|` cK mP|P" 8x|cK mAP Q f mQ|= Q 4c=  fm"Qd f mQc=  fm"Q d f mQQc= c% Qc# Qc= cpQcaQ Uf mQc AP| Q V|dy Q|sT V*c}c   |d T}|sT" *Ncf1fdy " mff\XTsTc |@P V|dy PcTP VcT Vc P^Pfdy  fm"Qfdr f mQfdy  `fm"Qd&fm"Qdr Pf mQcTCPPGWcEQc Tc QcQ)sfdy P )(|0Q dc0 c}TcF TPc vPP|s|dQ _sc_0QcTc Qc Tc QcQcTcQcTcrQcQsc_c0 f mQ vtvtVP+|a@ |` |axQ@ +$ +i3& | |`k Q|`* Q6 3 3i&| &|` Q6 & &iIc |aB)c |a j |c bQJ0 I Id!H|`b@P x |`bmE ,P x|`bmE T x|`b@P x&|aE |"Q, &( &i&|aE |axQ6 & &]|aE Acf |`f Xə&|` c |`f f |`  |`o o Q5 ]d\t.|aE |a1 aQ@ .6 .d"f|aCT "x|aX,P+  x|`b@P x|aCT, i 0|a5T 0CPPG9X|aT 0|d T 0|aT+ 0 x"f|aCT "xdT ic; T |`bm,P x |`bmC,P xc; c#DP|`@P+ x x|f]wmJ Q &|"&"P xcw|wP" <,8|d| }Y Qd| Q, ,N<X Pa||&Q Q||d | |&Q|dQ a>Jd |Yjy P Qd Q+," >DNHT d |vYjP Qd Q56, HIN0  PcQ Q x|c mQ I-}c m|c mQ|c mQ, -WQ\/c l c |c m[Q" /I2c l||c a|P+ 2 0iEc l.|*c k@ |k|P>% E Ci=f~ QfO |X&Q xCPPGY =&}&#|`~ 5}ct  .}a}c  }a|~ |` !}ct  .}a |~ }c  }&|~ a/}a"}aM'T' ['c' ,|~ jkc  A-| |`~ |` cx P^( =x8p0l }P" x}}& y }P"  Q" xc/P}P" x}d Q" xc|"Q xf] c Tc TPf] c Tc TP}}cw  e d ""}a/}a""j"cw  /"{}PT( }Wxr|F`(|a |ac | jTjTQP, F XQ vtvtPCPPGZ &(|a|du do/|adu d.|a|`(d6|a((d|a~(|a(d^/|a.|ac  Ac  A jdc  !  " )c    c} +c  c}  `(" "Tf#f#mI j dZ( "("dA Mf#f#mIc X Y c c ("("ddr  b  j  k lk " dtP   ȹW  ~P,  |P x((|a_T ((|a_T Y} cLlTc} }  Dc ) Q:d}dfT" x |P  vtvtP9Q5Ac6|X/ QQ#$" 5xc6}P&}&}cs  }&&/}a"}a &c:.6'9 ;78'5BVSflz 9:9gB fl fl 9S)'Y)+^)/c)bh)fp);9c: 756 9 58c759&fl d fl{ d :Ec:97.PT( x(dCPPG[&~}|[g] jG Q@ &5NBs~}| d d d d P, B5c  Q|c  Q x7CcL g]| |Y Qg]| Q !" 7N<cL g]| Tc;L cL m cL m cL m cL m QWT$|c |` P $f fT fITP~}||` meQT U} ~}||` mQ, U{* |b}AP, *inp= =|aJA =|aJAP =(B} >|aJA Z>|aJAP" B( sw)|`TO >}a`AjA`j&`bPh,<L sqd'n;nnp ?|aIT (cy)Pcg Tcc#.ccX`mT||`Q } ]~}a~}~dkzAjD|`bm -P^ ]U,-i{)|`T`%|P, )( 'dQ Q -|cBm Q fITcBlcmT}|cBm T" U-CPPG\~}|cBm~d Q, U-cBl!TcBl TcBl TcB{)TRcTcT Q -=~|d cm~aj`b~`:~d Q6 =U(-"c|cT "- vtvtP~}|cBm~d Q, U-c . |`&Q }|cBm T" U-c @ c l!-Pc c lTcBl.Pc cBc `.P~}|cm~d Q, U-#c . |`&|cm Q #}|cm T" U-c l!Tc lTclTc cB)Tcc c)T(| -?|aT" (&.}`|P z?|ajP|P, .-,(}|cAmT" ,2}` }|cAm}` j c}aP, 2,("c}a}|cAm*P" ",cAlTcAlT}` }|s}` P, ,}|d uc }c |aH*clc }a@ ?kzc~6j-|ajk`@&}`~P, ,( Q" ,c |a*P dR$|`|`|`clc }a*PdRclc }aP, ,(CPPG] R (O|`- }` *P|aMjT`&j}`P@( O,(#@d >}|d |d }|cc m *}`o|`r|`a @kIjr`ba h@kJ* @kJ @kJk[ *}|d d jT  `c&}a P^w >;7, @{idN6 d*P" , Q" , |P ("|`- }`*P|aMT" ",}`Q" ,}|cAmT" ,}`Q" ,?|a@$|a@|||bP ?icbT" ,2 EA|aIX} ~ P, 2X=6}|cXc c d T" ,$|a@ |`b|P $xcT, {cT||`P }c |" Q c T(}|}|cAmcAld T" (, ~d ~~a~a~`/ ~a~d ~`}`ra~`~}|a~a ~P^ `p,(Y6 |P, ,(CPPG^ vtvtP]}|cAmjcAl|`r}a|d |a Ak}a }` P, ],(1f  A|ajP|P|P, 1/$}|d T" , |P }|d T" ,}|cAmT" ,c2lT }|d *P|d  Akc cAl}a]j,|a|aj }|d  4BkzcAl}a}a P^4D ,(Uz^Y|`} }|aT|P@ ,(Y"c |` |aT|P" " I|`/ |a|` }kT}~|cAlaTJ Ip,(Y |P  vtvtP}|cAmT" ,}|d *P|d jcAl|`r}a.j%d |a|a}|d j`@&}a }`P6 ,(8|`b|`|`bkQR? 8(7_i~}|aT, ,(~aT, ,(}`}|cAmT" ,0}|d *P|a|aP, 0,(}`Q" ,$|` *P|aTP $(PCPPG_c Qc |a*NdfFc |`m 4k@ BkI* dfPQ@( ,(<i R (cT PC}|cAmjcAl|`r}a Q~cAl}|d P6 C,( C|`}` }c m|`|`rm}` !*~Pc l  BkIN}c c l{d jjc l{}a#*P`b 8C~a%* C~a~`TD`bl a  CaJ ~`}a'*P~P|d Cd,()d1}|d )*P}|cAld T, 1,(F}|d +*P|d |a 8Dk}a P, F,(c }|d T" , vtvtP|` |` }|d T" , [|` |`^ |`}|d &j||`=|`^ }a -*PP, [,(4 D|aj|P6 4,( vtvtP }|cAm}` |`cAl}}crmcrld }` jjcrl}a1*P`b Ek iEkJ EkjcAl}a3*PPJ ,(cY TE~}|sclx  F|aj|P@ E>(npCPPG`~s| } P, ;Z|`|`T% PFc mJkjjT|P@( Z(6npJd |}` `}}crmcrld }` jjcrl}a5*P`b`ll `l` k[ Fk FkIj}aO7*P}d }a PJ ,( ~P6 ,(V| +G~a~`T`&~`}aO~PJ2 V,((Ed P d~`Q~~|a~|a~}`/ ~|a~`|~c c }a~PT2 d,(bpY~` ~a~P6 ,(I|`|`TjbllkjjT|P@( I(6np9d,}|gFd ~&d PJ ,,(h||`}`T? ~G|aJ |"|j|d |P\"1A hfb,ciaKca=}`%|`%}`%|`%Q  =ab }|`|`\||.d 7",||.!d kkP $=Wf }({i!u 4ij?c"|%@P CPPGa|%@}%AP" *2#~}|d XWPT #,( l|`|`H|aK4|.%~}kjT@Yi l,:g_i!ad/aM=}|d| 9*gFd d |}d T6 =k,Y ccP~`~`Tijb}a,k@+jTjkjkjj. pk }|ajc}`r|aPTdt k,cd+kNt Q Q, s,|} Q" 6pc }|c mT}|d Pc|`r ||`rmcv|c }a<*P c l T6 prp,|}a T" ,c c lT>*Pc c lTy)Pc @P3|`l T|}a T, 3,Vc c T~}|aJ c \c g$jlfmU gSAj~}|a c T'7:8T< }{:ZIc Tc @P@c }|c mTc|c }a@*P c l T, @, Pc c lTsTPc c lTsTPPCPPGbC~|} HXIQJ CU-  ~ } |`3||,gQEgA yHk  j g |` `   ` mX%f  f lgAju` fl "/ ` X(g gA g }  U}w -[i*UdRj (1 }/|a~"}.|a"P, W( 0<X%f  f laAB*~lg`Z 6` +}|Xj~ g `Z ` `\gAD*jk PQR 0U},-F,k`I|X }[ Q" )h }`l| " x{ }`l| " x{}|dQ" x{  Q   }`l| @" xR }`l| " xR }`l| " xRCPPGc1|`} O}`l|aQ" 1xd}`l|aJQ" x0|`Q 0 I||`UcX l /Qc[ lcX c[ @c[ cX m -P I{&.9 Ha`A jF*~`l|` \H*J*`rd~`  L* `blklT d j j  aT j g dj `bl  k - cX }aeX% f  f l     N*k b*k gAv*  ~_ ~_gm`Z ?` 4 cX c[ Xj~ g [`Z `  `\l jTVkjTjAjAm kjQ l`T jQ k kjx*` `T""! k! !k j  j_ !#~`j#cX cX l m $ gA%j% j$T ~`cX cX l m   |a j)``=` kPq^qiqjKq^s)<L\l| xk,Y6 &} np2 :  }CU_  l ov 5Idq{nH  d  -!dMp#u%$CPPGd9|`|`T}k kjT@( 9(4dA|`}afP|`|`cX c[ Xz*P6 A,|}a[T" ,np[}`rd}` }`r }`rm }` }`rcX |a~}cX c[ XyT, [,6 Q xT|d}| }|cX cX l mJ Xf  f lP, T{  ,|`l  x!~}|`l , !xk }|`l " xk|`l  xQQ Q xQ Q kQQQQQQQ Q _ Q v|}=~ P, xp(sfI[ X P t" ,np " xk  x Q" x{ Q" x{ Q" x{QQQQQQQ Q" xR Q" xd Q" x0Q Q 0CPPGe Q^ x,Y Q" ,*P" ,np*P, ,6 Q x Q" { Q , Q x Q, xk Q" xk Q xQQ Q k Q xQQQQQQQQ Q _Q P  ab P  ab7|cb}cb P PP4 7ab}cb|cb#P  ab P  ab}cb|cb#P  ab P  ab%|`Z }`Z P|` }` #P  %ab}` |` #P  ab}` |` #P  ab|` }` #P  ab|`}`#P  ab}` |` #P  abG |`|`Tdk k I kTT*: G(;d%8|\|`|`#. P |c\vaQ CPPGfNclT|k PclT|k PPJ 0 N +d+Ldc . Q" x{cZ .Z Q" x{cZ .Z c . Q" x{cZ .Z QcZ .Z Qc . Qc . Qc . QcZ .Z Q" xRcZ .Z Q" xRcZ .Z Q" xRcZ .Z Q" xdcZ .Z Q" x0QcZ .Z Q x@c . a`j.a Ph @xk,(Y6}4|`}afcZ .Z c . *P6 4,c . Q" ,np Q, ,6c |`" Q xcZ .Z *P ,cZ .Z Q xc . Q, xkc . Q x Q" xkc . Qc . Qc . |d&Q xc . QC|`T/j% c . j%c . Q, Ck Bdc . Qc / Qc . Qc / Qc c c.Qc . Qc . Qc|Q _| Q vCPPGg|[ }X P" U-B|`}afP|`|`cX c[ X*P6 B,~s| } P, ;,~}|aW T, icY T+| LIc mJAP +ic T np |P" (np |P, (np |P" (np| P  Q" x{ Q" x{ Q" x{ Q" xd Q" x0 Q 0 P^ xk,Y6} c |vbT" ȹ0 | RP" 40cRc }|a Q" U-*Q|~ P (6| } fI}[ ~ fI~X P, 6)h|`bcb@P x!cb|`bmT !x|`bcb@P x'cb|`ba,P+ ' 'x|`b`b@P+ d x|`bT x|a@P+ & x|`%@P xc |P xCPPGh#|`baT?( # { #x|`b@P+ d x|@P+ d x|`b@P+ N x |T x|`bl}`bl#P  ab|` ,P x|`bl}`bl#P  ab|`2"P+ z x |T x|`bT x|`bmJT* x y1| BaJAP+ 1d 1x |`baAP+ d x|`bmT+ d x|`bl}`bl#P  ab `b|`bmJT* x y5| zDc mJAP 5x|`bm T+  x|`bmT+  x|`bmT+  x|`bmCT+ > x `b|`bm T+ d x|`m,T+ , x |T x"`r|`bm,P+ ", "x|`b`b@P+ b CPPG0i)XQJ )U-|a AP5 U- x|`bl}`bl#P  ab}|a T  xy|`b@P+ i x~}|gT, N}Qc c  c TclQclcfIm l @P=Iclc}|d ~YP Qd Q)*@ =N }|l}cQ" }f TfTf P|P" "fP|P" "| }Q" V8f j| }Q}|Q, 8(V9ff mf f m}|XKQ" 9VOf j" //kQ Q" O()f XKjkQ" )(7C }|XeL~vY QXLQ#$6 7-0V Q" i Q i|b})P" i}cbc)mT" icbc)mQ ic)lTCPPGj||fm,flO  | fm"Q|`,|fm|  fm"Q|`,|fm| i fm"Q|  fm"Q i6|)|fm|  fm"Q 6i6|)|fm|  fm"Q 6ifl  Q}A mff\X fl   R  f m" imfl|k+Q}(|flm|k+ Rj f m, mi0|}~P, \V0}cccmX mT" i}|fmT" i}|fm{ T" i@|3|fm $|  f mQ" @is|f|aQ}$|`f|a R| W f m|fm " si?|2f|a"+Pfm"Q ?i|P  |cm QcmV  acm Rfm,& 0 f mk}NaO4`` .vf m ;bf m kQ^( i9/_CPPGk+cQ|cm |fm,_|fm *j |  fm"Q#+Pfm"Q|`,|fm|  fm"Q|`,|fm| i fm"QflO  | fm"Q|  fm"Q, +i,fTf#lTf#lT|P iw|`clQ|a@c|` |cl}%k'|`cl Rkx6 wBMif#lTf#lT}|fm T" i|f|ay Q}?| mff\Xf|ay R|  f m|fm " i?|2f|ay "<+Pfm"Q ?i]| |`Q}6| mff\X|` R|d" ]i/|  f m|fm Q /i6|)|`"J+Pfm"Q 6iE|fm,)| } f m|fm Q|`Q Ei|| |`Q}6| mff\X|` R| X f m|fm " |i6|)|`"X+Pfm"Q 6i|| |`Q}6| mff\X|` R|  f m|fm " |i6|)|`"g+Pfm"Q 6i2|fmjx Q}jx gT, 2iCPPGl|| |`0Q}6| mff\X|`0 R|  f m|fm " |i6|)|`0"w+Pfm"Q 6i||`lQ|`}9|a mff\X|kC R|  f m|fm , i4}c|a`R|QJ( 4i 4@op|`T xc+ f#+Pc+T|+Q {c+A c+lU-P,c+A|`rA|`rc+mH-P ,4c+ |`+Qc|`Q|`+c+mT 4V|`r+|`P Vfl6X/Q|`r+|`P VflX/QQ% u2vuP %|m P id+cm "+"X/Q|s}$P" idcm 2+cm "+"c$ +c$"+"X/Q svd8VP # fcHvbT #cVX/Q~}s||VP6 ^i{ |b}5.~+sP6 i{c.c+c5cbmTf#sP}H6|sP" {+V HQ #-crd8VQ *crd8VQ+P+Q4P+ f mQ+| zc4mJAP +{CPPGm c4j`rf#Q|Yk 9%k@)3k@k@4k@ *j jT k k[  8% a@" a@4 a@ 3 a@ k[ jr lI a@\ /a@K `m /`m 7 /k}Ykj jkQmQ M]m _ijV6;okBdxod W /Q/Qc.Q dcP w?c/cc& 2{XI |cP" ?wj|`7 f#lU&fl /'c4mQf/Q|` c||c4mQ jV"c4lPc4P "_ c4l c4m[Q _,dP 0|c4m}c4m c4mQ6 0@IL|c4m}c4mQc4c4mc4mQI2 L@I'Bi|c4mzQ N9dQ1fl 0}|%dPQ6 1{0V#dQ#f#~}|3dQ, #^i"f#}|/dQ" "i~}|!dQ6 i{+dQc4lc4lc4mQ R VCPPGn5c/}Pf/Q}+}dQ" 5x'c+lTc4k9Q &d|d3AP N,| {c4mT ,N Q" v6}`j})k@Rj)JC})k@+j)j)k}a[}aQz $=M] v6 iyd1yW8sx}`}a@! {}aJ}a[$a@= Q|}aJ( |}aJ}a[yQ{ $=M] v6i#df{ }`G}%a@, }}aj `B`/}`}%k@}.d-t -}  } d, } d- d0 g1*}V +}V /%k@!k@jjV+]/ } `V+ 6)} a@&} k%$}aQ(A[t v6di^d$dird j - jV}~~.}`~2}~d-'jr}~`rmH~}|d,|a~PJ2 VWd6Ny|`c|!k@KjV+2.)|`rjrljr|a7|a[Q\1A y6 xird6fj6%}a@R!}a@}`rl|`r@-P  6abCPPGo |}`T`3a@ }}aJ}`PJ}j`3k@jVjVl<  W }}aJ$}a ~}a}`$.}aa@`(}a}`}.k@jr`ra%%k@8.}aQ &6Zjz |v6{d/_WV$xW(xzf f l/}YT|`f  g }YP Qg  kQFUV6 zLN#|a|`-P+ #{ #x3c4lTd2j.Q2d*|`c@ |Y-P+ *N *xB|A'|a)|a@ %|a@TP+ Bd Bx5|A|a )|a@-P+ 5d 5x5|A|a 9|a@-P+ 5d 5x6|` |`!|a |a%TP+ 6d 6x|@P+ d x|``@P+ d x|``@P+ d x|+}~cP, U-{<Qc+TcT~}|a,T, iP<TclQ|cmAP icT|crmT RCPPGp }|crm |d=+jT|kjjTNjSj, Œ"acX(dKk jmI + |d;cc+mPJZj (Rnp<d|&|( 1sclcl, f +PcTclcl, f +Pcl_ fITcT|cm T Rc+lAT%|c cc+md>T %np t~|`|`T!ccrkk LkyccrmjTJ2 t(np$Nd6c cc+m kIT 6(1}|~ ݍd:T, 1(np: 4|a c|aj}Pj}P|P6 :(|`P``blc<K`bc<m5 aa[/ac<  <`bc<m.4`b`bma[.2PR 9 (np%ij-|} d:T" -(np~}aT,  ()| ;d:T )np Q |c+ccX aT N|Y P  cY |vbT" ȹ0<T%|&c cc+m$d>T %npCPPGq6c cc+m$ ykIT 6(1}|~ d:T, 1(np-|} Wd:T" -(np)| d:T )np+P np+P |P, (np |P" (np Q np&|;+cc+cP &- N np+P]}~ |crm|`Tjbl|`} ߐ|a|PJ2 ](np;d}|dT" (np Q np Q l|( 'Gclcl, +PcTcl cl, +PcT|cm T lR,|`|cY mK|cY lym -P ,i|dKT x"|T+ "np "x#|T+ #np #x |T x%c+m,T, %(np|` EP x|` EP x$`b|`bmT4 $i( $x"c+mT" "(np c+m T np |T xCPPGr%c+m5T, %(np"c+mT" "(np c+mT np|`blT dckTcGfmOcwQc |XhRQ ckXhQckdMT|RP cRX/QflPQ, % R i P i+P i+P" i Q@ xi Q@ xi|`HQ6 iff@cNNf@Pl_6f@cN#cIfmBfmD If@NQ&Z GQ}|g@AQ" }* cLm?`.c|"cLm cLm`G|"GQ@( W&NfAGQf $fe f cfmEfe cfmCcdKQL|f Q nkfm?Q, LWccLlg= Q= >XPQP" =G Qc_fmJT-?|a@ |`R@-P+ - -x|EP+  xCPPGs&|`Q" Q? & &mi|`eeT }Jc\~Q}  #R|cdbAPQ, Jmvi}Q~}|sT, mvi>|}P}|P| }PP  >abm|# }# |}P|P}P| P} P|PP  mabE}  # |P||/PPPQ" Evbr|camT } e |c lT |clT||k7cylT||k7QT(8 e(d(FdFddW~c F~ k~}dT}dY~X\"cylT|}k Q^4D Wm}$V06Vd rcf ~XəY~f o Z~}dh}dY~Xe~$cylT|}kd Qh>N rv}$q;Oqd |vdRQ" N0cyT| sT" i} sT" i} nsT" i6~ }|dXTdG ~}|sT|P, 6iop6~ }|d^TdG ~}|sT|P, 6iop6~ }|d`TdG ~}|sT|P, 6iop~}|a[T, i}sfzVmQCPPGt5}fz |a}Q, 5=}P }} sT" i} gsT" i|P" i!}L|}|dmQ" !F vtMvtP}|s}}`_P" ),}`c_| Yf mQ" ,ic_|fl mfT" i;|dl|U|f |W|o Q@ ;} ;Q Q *c|at j{c dQ! *Rscc|a!Q! sc_ +P+P3|`+  |`+@_Q 3cwdmTU|, .,|E,I,M,Q,cx wQ|c|  wQ U6|cs !9,,,cwm_"9,"P|P 6=cpQ XdyQ =s/cp ߪXQ /s N,3a$Pj, >j+   kqk}jVP-*.-5e*:J f\s02e7PjPl63a|`lT knjVQ4 6fs16 zPcz|P idCPPGu7clT!Ok@j{ju|YQ+ 7N 6s)}|Ocmcz|zQ, )id6$|dt }{Q}|dqQ, $idR|QcmQ 'cz||czmxcmxQ" 'id6|u}{P" id%cu-"c{"?,"|aQ %f&?,|a |?,"|P &c|aQ f|avT+ s 6|aoT+ s 6|Q x|Q xci|"iQ xci -iQvcO fIQf" c~cO =<1 > cc QdfQ)\] v<cY cO <1 fVD QXmflXc-XmflXc-XmfIlfIllyQ-fIlfIll P -htc3g]| cf3Q3fI(-j jm7-Q 3V5fG G-j_-fe b-Q 5sfVQcO <17=C>VjfV cgVMfV| cEfVfV| 2 g]| s-c )g]| P <n|g]| cO <1)=,>57-gVI fVQfVQ-g J Q Q n< D!|a <O Qf] =O Q>O Q D-CPPGv/clc`cmQ &i }VXQ vt'vt"vtzPcQfRmc Q-cw-Qc, fT_-P fR"RQ Rc Qc_gUTcaT_cagUQE|`|cmc|"Q|cm |Q Eo,|9} ~}X\ P6 ,mvti|` c XəT %|}P}|P}|a|P}P  %ab|P 2}  8|a}P, 2N<6 |P" ȹ%|~}cmP, %sti|cmQ iclTcTcccbT" f mQ" "i|fl mT" i|_}P" )U}`c_!c_| f mQc_| f mQ, Uic_|fl mT" i|b}~ P6 Wi|f#*af#mf#lcbPc|` c|`#Pc|` c|`#P|`bl cbl #P |=||`Tjbl jbl |axP6 =&65d Q (|d@T (|d@T (CPPGw |P6 imcOfX }`6}}. p 9cmJ Q}|d)Tq"2K mfsc$ei1_R}|d)T" !c[c}|k,TP, !stcT |P@ (np{ }P" (R|  x|  x O-Pc Ac |@P ic Ta|}`Q}`}P y9}aIjTgcjbTJ2 a&(V@\d)}b}|a }N|a P" )ViK|}P}|P}`|`m|P}P}`|`m|P}P) Kabdc sc PLc&"f c&c&f mx|&|f | Q LrclTclTc*Q VkfX.Kcx jx } dQ|` #cQ|` cQ}|dcQ, k\cchQclQccQcQccQc*Qc |dQ|dQ V ldCAc-Pc&T oc&|dC-P ufX.Kcx jx } ~dQ|` # ~dQ|`  ~dQ}|d~d4Q6 u\CPPGxcQ" VcQ" VcQ" V|dQ" VchTchTcQ Vc ,Pc hdCA-Pc dCA-P*} 9|a}Q" *V&} 0:|a}Q" &V }Q" ȹV Q {|dT {cdTcd=,PR|d |a-P Qc|cmcQc c |c@Pc |c@Pcl|RQ Q2|dQc c|Rc& c|RQ 2cD cXc&-P @~}|dQ, E~ @}dX/}dbfmQT Est @~}|dQ6 ( ~|`dX/ |`TY~k|b :k kdkjP :kj-`|```fmQjg] QTdt (0d=st\| ~}dT,  ~}|dQ,  ~}|dQ6 ( h~}|dQ, CPPGNy h~}|dQ6 (}|dc[TQ"  $f mQ f mQfmQ|fmQ  $f mQfmQ|fmQ  $f mQfmQ|fmQ fmQfmQQQ#f |dTeP #(fldT|# ;|aleP X;|a|`P ;|acklT@ ;|a|`PPT*: (r_/| 7<ckmJ@P /ic cT-P|c mAP gcTfX.Kfmn Qc;Qc;Q VflTflQCXKcck, |cmQ|fmQ" CV}|d Q" rV-}|d~}| }|dQ~}|cm Q, -rVc@ c|a,,P" VCPPGz:|~  ~ a ~&}a } j }d~%.d .~%.d(.~%: <d* <X`}cvmi ~% d$~%}cvmi }d}dd a> Q|d :rV -3cc|a, |cmQ|`I |d Q 3V;}|ax~ } ~}|}a~} ~}|~aQ, ;V-}6.cG cX/|a# Q" -VG}6|XsK|aC| |dQ|a c;QcQcQ6 GV@ g|cx ~d})}# =~XcTcaCXsK@ cR d' g=~a} 9.~ki V}`lj =k   J>k  .~ a'mi V QW examineeW examinee^w g>(3V<Di>d  jr|~}a >XjTa }ai VQW examinee^F r.V}7Od$f] }|f] mQ" $VYP|[T ePc clx TP/c|cm~ P|a,PQP" /Q c}|cmT|P" \G c}|cm\T|P" iGCPPG {c clT?.Pc |cm+TR \c clTQPc |cmLQ VcclyQc cl TPL|`dB|`ePc}c}a, }|cmg T|aT, L\Vb=f lT#|dg }|kPQ@( =\V <b.f lT}dg |PQ6 .\V -b/f lTje|PQ, /\ .b}|cmT" SclTfmC|kT  f m, S 2dc clzTQPc |cmTQ cclhQccfIm, clTQ,A. ?dRP ,(c clTQP(clTjP, (< &d!c }|cm/TC.P" !Vc clTP c}|cmTP" i P" )|a|}aQ" iP~}|fmT, w{+c3clTjP, +<)dcTcT VcT VCPPG|  j*L.!cQk kCdek|Y  P Q3   d Qn~?Yh _iNMV$k_hj  _} |d T" NV f?dRQ Q d djE.P|`|`TWjT'k kB3k ? k j S L j   @kJ!  k[ k . P2B\l| px(7dDHxUuW i  W d6NdQclT |kQ, 665dM|`\}~X\Q} #  R~}|acTQ, Mmv}K}}` Q|`deR}`  PRP, K}7C }|XeL~ve QXLQ#$6 70V,| K@dRP" ,iD| @dRP AdRP" DiP Q icccc&f c& Qc cm5Qccc#cc` cclccc`  dC cc"Q lAcmT AcmT dP 6CPPG}% A|aT %Y6clT |a|a|kQ, 66 5d +}auI.~|}ai QJ +x~}|d| Q@ x-| DB}dmT" -(} BcmT" (+c |c6,PQ|R|kCT" +i0c|@P i*|dCRa|a@ |dRQ *icA |cm-P 0|@|dC-P i Q 0|dT V|dT V Q 6y|c c @Pd aTcdC BdRQaQaJT" yic c TcQcmcTclS T Q icQcl cTclTfdC&ca mff\XT mff\XT Q i$| ( f mQ $i |cm c|"Q ic|#Q icT|dQ |d|dQ 2|dQ 20 |d | ||a |d"|d Q 02-c cm|c cm5Q -2CPPG~\|dQdCj#k K. 9CdQ" \2# cc|vYQ" #N0 |vdRQ" N0 c W.PTP 0.cY.jT kQ, .-dc|kjoQ" cl0Q|aC|  f m u f m Q|fmQ -[.P VBf  Pz@ d7P" Bf |c lT |cj||k7c||k7QI fd(Qi Q" i}|fmQ i Q i Q iI|f jT %|d\P@ Ii |f # kBj Tj dV  Cd\ PJ i"NcVd |Nd& T "i |dT  (|dT |P, iop~}|dT, iop~}|dT, iop~}|dlT, iopCPPG~}|dlT, iop~}|dlT, iopI~}|dj0jj .vf mQ6 Ii_op }|d1 jQjj-  Cdkjj  # j j P B[u i;siHmoki  | |`R||}YQe| '+|/"|.|.|/a|/ }YQ|| |}YQR]4C )N.i9op2 |d|j |a#P" 2i6 Q" 6clKc~}~ "d|a}a~} "|kcTjH~}"d|a}a~}"|kQ]E idi6b|`Y| H"|`<|".|"|/a.|a".|a"|`I|8"|`,|" /|a""|a"|P>%  nini,ccTfXsKl; P ,JV }d||XeL~veP QXLdQ12@ J 0c|P" P }|dT }c j:TQP 0CPPG|d l AP i|d l AP i|d l AP i|a,T V|aT V|aT V|a T VfIdTfIdTfIdTfIdT Q, },|d }}&P, ,i} o}d1 QjF/jUjjf j |k #PQq*C oi}ni*hmvi |f PcVzd j|   gDk} PJ =F}NcZcf c co cUccW|d&}Q@ Nmv}1|d DXT" 1}>cV|jdc c PS( > =*<i(OfzVj jk}|d}d j}|aSQI( O2=}2Nip}`\J E|a}`-b}kQ E|a}Qq,<U p8=}b5biD\dI|`\ec  k|d\|dTcQ" Im}c ~}|cm Q, m}CPPG s|d }X\UcMc~|kX\%cj|amQq4D sm?}'rLOri ~c}kX\Q}dX\c  k}d\c2cj| }kmQRu m?XfL}divd"~c ~ k~}dTQ6 "v}$? Zf Uo W|d!|dhQ ?}c~}|cmd Q6 v}}|d}Xe}~c!|kXe'cj|aQRb }vqL|}3NW|i~c}kXe~Q}d Xecf Xəf o Z}d!cUXə<UWZcj| }kQf vfL|}id0cf ~Xə~f o Z~}dhQ@ 0v}$ Z kf Uo Wc:Q=|}P}P E}a}P  =abM|}P}|P|` }` |` }` |P}P}` |` Xə}P|P  MabQQ Q"  Q" cTc|"Q iCPPGc|#Q i" |d | ||a Q "-~}| (FdRQ, -ia~|}aC FdReQa eQ, ai) Q i.|dC|}|`0fm"Q" .i) Q i Q" i) Q )A|dB}dBQRk@Rk@QdT6 AcAjQj }|aTj4,|a@}a@BP}|aTS< s1s2(,ni4 | .vf m" 4fdCdf.Pfm"Q ffdQcd,dc{Qcj cx fXsKlx " dBfXsKdQ# dBjdQ dBcQfXsKdcccw 2 cQ#jdQcQ! tc Qcd Q ]fl; fd cD Q|df|ai VQW examinee, ]( c| `[ QfXsKm j fXsKd` ` )c##jdc|BfXsKm  GXjT jQg3M ti.<dCPPG c| `[ QfXsKm j fXsKd` ` )cj##jdc|BfXsKm  zGXjT jQg3M ti.<dcQcQ Nfl; Hd*fmi Q" N($|`}k 9HkT, $(dw.2fm"fdCdf.Pfm"QcQfdCdf.Pfm"QcCQcUTcmQc(TcQcQQ f mQQ f mQQ f mQQ ,ffdQQ ffdQd.2fm"Q ffdQQ ffdQfd fm"`fdCd.Ffm"c+fd.<fm"fdhQ3fd kf mQfd O0fm"Qf#gaaQf#l6f#ldC$f#ll5fm"c`QcQfXQf#lx Hf#lmJc5fm"Q Hf#lmJd/fm"Qf#lmCc5fm"Qf#lm"d/fm"QfdCfmdhQ fm"Q_fmmQCPPGf#l+f#ld vfm"f#l fm"f#ldhQ Vfm"Qf#l+f#ld fm"f#l fm"f#ldhQ fm"QQ 9fm"QQ fm"Q cfm"Q fm"Qda/2fm"Q ^f mQ fm"Q fm"Qf#l+f#ld fm"sQ qfm"QfdQfdC"df.Pfm"Qd/Ffm"Qf#lm,/f#l  f mff#lm,6ff#l  f mQ _fm"Q fm"Q fm"Q afm"Q fm"QceQff#l fm"fm Qfm Q fm"QQ bf mQQ bf mQ fm"QQf#g=aQf#l;U fm"Qf#l;U  b f mQfmdhf#l fm"f#l+f#lmC 0fm"Q f#ldQQG|d Id }bQ6 G)_}~|a(R|aT6 )iop!}K|}|dmQ" !FCPPG}|dQ" F}a}#}/|c}|cm>kC }|d*d}|k }|k}|kTT W)c,Q f#ldQ fm"Q3|a|a| Hf mQ" 3iQf#g,aQ fm"Q Nfm"Q =fm"Q nfm"Qd/2fm"Q W f mQd/2fm"Q N f mQd/2fm"Q 9 f mQd/2fm"Q 9 f mQ (fm"Qd/2fm"Q  f mQ fm"Q fm"Q fm"Q gfm"QQf#gaQ ^fm"Q Ifm"Q Ifm"Q <fm"Qf#gm Q <fm"Q fm"Q fm"Q "fm"Q efm"Q fm"Qc-TcQc6 Tc QcMQd02fm"Qf#gyaQ Tfm"Q )fm"Q fm"Q fm"Q fm"Q ^fm"Q fm"Q ^fm"Q fm"QCPPGd02fm"Q  f mQ fm"Q 8fm"Q 8fm"Q 8fm"Q <fm"Q xfm"Q fm"Q fm"Q fm"Q fm"Q fm"Q fm"Q fm"Q fm"Q fm"Q fm"Q Xfm"Q fm"Q jfm"Q 'fm"Q 'fm"Q fm"Q Wfm"Q fm"Q ofm"Q >fm"Q fm"Q _fm"Q Hfm"Q Hfm"Q fm"Q ffm"Q ffm"Q !fm"Q ufm"Q ufm"Q fm"Q fm"Q fm"Q fm"Q fm"Q fm"Q ufm"Q Gfm"Q Gfm"Q fm"Q fm"Q Ufm"Q fm"QcJTcQc Tc QcIQf#mdQcJTcQc Tc QcIQf#mdQcJTcQc Tc QcIQ\f#mdQcJTCPPGcQc Tc QcIQ*f#mdQcJTcQc Tc QcIQf#mdQ#}|Y |aQ@ #N6 #i|aAP5 sc x|cT+ V x-|aC|` |a> Q6 -V -i |` |a> Q6 V i|`@P+ st x|`l|}`l|#P  ab!|ge,P !i!|g,P !i|aT,  i|aT,  i|aT, i d*|a) |aC,-P, *V *i|` T i|aC,P6 V i=|'|aC|`x a aQT : =>< =i"<|aC,P+ > x|aC,P4 j< x*|` b |aC,-P@ *V} *i!|`raP, !( !|a T 0}|aT  abCPPG|`b@P+  x |a|Q6 i 0 |aC|Q6 i 0#|aQ@ #i #d0)|a@|`-P )i)|a@|`-P )i-|A|a |a_,P -x|aXT+  x|AP+  x'| |aC|Q5 'i 'x}|aR6 2 )op}|a%T6 i @op(}|bRQJ( (oki ()op|aQ@ } d|` bT6 } i(|`9|b |aQ@ (8 (d|`9T" d(}|d|P5 (a (i@|$|aC|b|a Q@( @i @0|a T@( i) 0,|`|aC|AP5 ,i ,x,|`|aC|AP5 ,i ,xCPPG|a=T i<|`Y}`Y|`Y}`Y#P}|aCP|}aCPP  <ab|`bmCT x|`bmT x!}|dT@ !) !iopdQ iP 0f mQ "X Oc mQ|` PP 0iP 0{P 0Pcfm>Q \cfm[Q \|cfmQ" \|cfmQ" \|cfmQ" \c|a>Q" \bc|a[Q" \b|cfmQ \|cfmDQ \|cfm>Q \|cfm[Q \|cfmQ \|cfmiQ \|cfmQ \|cfmQ \}|aT"  Q" o12P \ R"  Q" D}|fId  XIQ" DCPPGE|fIm,"cx fImC|c}aRQ|c}a[Q" E}|}fId ,flDX/fl_fImQfIdx ҥXIQ" }'u}\|fIm,?cx fImCc|}aQ|fImC*c|}a2Qc|}a0Qfm>Q" u' }c|A `P- cm}ffm\  |k6apcp|d| `k0jT%~|k|a~|~af# f#lc~cmI scm}Q^F 'd}||aQ" VcVT|a,T V|cpmAP V1|aCRcplT|aCRQ, 1i0d|cpmzQ Nm72ccfLd@.c'fmX qP, m(FkcTqT VfmX |kN|khd/d4clk@jT k/k4Q@( (bd R  Q T R" V R" K}|aK}aX |dPc`c`|} }a PQ, KV Q" }|d~PQP,  Q CPPG.fr`~fr`|}r`Q, .VcPT.fI}a |aCQ|a[Q6 .b.fI~a |aQ|a>Q6 .b~|aQ, ~|aQ, ~|aQ,  j}Q|`f lT}ag Pf lT!}ag |}k@PQh,<L j9b9ibLf\ Q" #}aX |dQ}k T, #V||`m{Q" VfmX mgfll"cU"PQ7fmX jdfm< duQ! 7t9fmX jddifm Q! 9t R  Q |EP &PcET cE|vbT" ȹ0|s |cEm@-P v Q" V Q" fllQco~}|sQT U} co~}|sQ, U{Qc3cc3f#cZmQf#cZm`QBc3Qc3lT$rk@~}|kPQT*: BAd.>\CPPG vtrvtP Q" V Q"  R" VflwQd@c QflcVQcdQc||sQ cuT" ctcuT"  vtvtrPc|QcVT Q" V Q"  R" V Q" #cWPQ"/cW}|aKc`|}cWTQ" /V Q"  |P" |P \clT" V#|ccT|ccTQ" #}|dT" ^fmX l;fm,,kfTjC2P X0cuT"  P" cvcvllzTQPcuT" cD cT cDlTPc }|s-P" VcT" scDcDvcDm?Q|vQ cv cvTsTPcuT" cvcvT}|sT" cT VfllQfdtcsQCPPGc f mQsQ vtvtvtrP|aDQ, |aQ, Tc.TcQcTc Qc Qf#mdQf#mdQ|a[Q, |a>Q,  E f mQT|aiQ, |aQ, cTc~QcTco Qc Q fm"QcTc~QcTco Qc Q|aQ, |aQ,  Ffm"Q'|sc|cv |cvm~Q '_;|f/ ͦXIQ ;_|fmQ _sfmL Qfl cv1fl cvd]2xfm"Q vtvtPfm,#c  f mQsQqTscbP" !scmQscmQsf"Pc4 f m  qf mQsQsX`f"P vtvtPc }|sTQ" VcTsQfl m@ d c5Q (f mQQcfXQTf#mdQCPPG vtvtPTf#mdQ vtvtPTT R"  Q" |`d}|d-Q" 0fl Sf mQ" 0Qvfm c^mfm fmS f#ls Qfm fm Q=LM, v+4A|b}P" iVcl|}ccbm c|cbm~}|cmi cbmcl}ccbmXQ@ V'/cb + f m" /V#}|cmN }|cbm<-P" #,}|cmN}|cmhQ}|cbmSQ" , c|vbT" ȹ0 Q i'|`b  f mQ '"|a@R|`bdrT "@c c||c|c|P @b|` mcd R  Pgn DP bVcwQ|fm>Q VNXKd~   |fmgQ|fmQ, N>V|fm]Q V|fmnQ V(|` f mQ (V|fm>Q VCPPG0~}fm{Q~}|fmQ6 0J(0~}fmQ~}|fmQ6 0J(#f] |f] mLQ #Vf] f] lyQf] f] l TP fme   tf mQcmo T Q fmC#  f mfm^fl cQ|%cqfl c Rcc f mclTfmC|kT  f m, d Q |aQ}|aT"  Q fl  @P \) mff\XT )\fXQ P" {P P y2P VQQ+c dRP +(c|"Q ic|#Q iP Q  Q  }|` ~}|a[|dAQ, ?|`lTj ccke |d}|a0QJ2 ?' 2V Q" |dRQ" \V Q VflO c9QcQc9 Vc f mfdLQ  f mQCPPGPfIlfIllhQsc cm"Q R {8/|a@|a@|a@Q|sT 8{cT" Vc 2cl QcPP RcT 4c#| mff\XTQ" 4Ri4c| ( f mQ|sQ" 4Ri6|cmP||` kCPQ" 60 /cmT&c|aT2P" &RVfmC"d2Pfm"Qd2Ffm"QfmC!fm,fmQsQ:T ?fm"Q fm"Qcc/TsTPccTsTPccTsTPf#l(cfcm 2P2PsTcTcoQc@TcQcQfm %fm"QfmC4fflm  0fm"QfmC#fflm 2PfmCfm2PsTfmC?fm0 kcmJ\fX3fflm fX  f mQ,3^a\ vtvt'vtP vtvtP smPAc7|a|}a|fl mF"f mQ" Ai vtvtP|a,P iCPPG#|`"|`"Q #o#|`#|`#Q #o R { Q { tP |a}`lx  &kjj |#~}|sTh< t(np{iY><sd2Ffm"Q dQ `flmQ|el1|el||e1fl; jf|emi Q" `b0 Hfl; ud*fcvmi Q" H(Cc"flmfl}|k}|kT6 C)0.||a| |kCR|sT" .n0Q 1dc{Qscclmc`"QcTc=TcTcTc/T8/|a@|a@|a@Q|sT 8{|`XT if#lqTQf#lfXQfd# f mQ|`m|aT Vfl |<fds-f#lm}0fm"Qd22fm"f#`"Q" sf0|fme | f mQ 0ffllx qT  dJQ daQfllx qT )dJQ)daQfllx qT dJQdaQCPPG4|`  |fmQ|fm>Q 4V||`mQ V%|`  |fm]Q %V%|`  |fmnQ %VT|`  !  tf mQ|` f mQ TV%|`  |fm>Q %VH~~}|fmi Q} }fm] Q}fm{Q6 HJ(H~~}|fmj Q} }fm^ Q}fmQ6 HJ( vtgvtaPD|dQc hdC c|R ldC c|RQ D Q o Q cT!fX.Kkn Q !*c |aC |aR|sT *i|aT2P VcBQfl c:#  f mjfme fm )fmCfme fl QPab" QQQfmC|sT|"cfm Rfm,cG f m  f m S|%cHc:fm R V f m S5fmQfmC|dLT|d T 5gc\jcTjTfmQcCk,|k TcT@( g)(%Ed1c\lTfmCPcYT -dCPPG*c\jTd\PQ, *()d  f mQ  f mQcTcYT R 06ccfm, clsP 6fmC 0fm"sQqTfmC 0fm"Qc:c+Qc:fm fllfme f mQf#mdQ t f mQ < f mQclTcmo T c}|cmTP" ifl )! mff\XTR mff\XTfl )fXQ\fXQ)d,T I )dJsQ)daQ d,T `  dJsQ daQd,T  dJsQdaQQfcm QcTcQcsTc8QcQ3|c;msTqT 34|c;mR|c=m?2fm"fl |<fm-f#lm}0fm"f#`"fmC.c; fl"c#~0fm"fdC 0fm"fm.fl |d3xfm"Q, 4s<fm |fme | f mQ < vtvtdP vtvtdPP P" iR mff\XTRfXQCPPG c;m sQ fme   f mQ vtvtdP ofm"Q Wfm"Q fm"Q R { R {clT vtvtP mff\XT\ mff\XT\fXQcTcQcTcQc4Qf#mdQ vtvtP}La@ aC4 f mJ` kC|PccmTP, \G(}aC|Pc }cm\TP" (iG|aCT V(| c0mzQ (N Qc0mT`3P \5|dQ  f m" 5o vtvtP|a@,P #| Z f mQ #|`T b|`_}`_#P  abd.T" deT" '|a:T6  i|aT6  idjT _,|`+m@|`(-P ,x ` d T V!|`raP, !( !CPPG|`@P xk|a@T x!|fmCT !x!k|a@,P !x"a|`bm@T "x|`bmC,P+ > x|aT i!|`e@P, !\ !b|` |YQ, N i|` T xcTc_|`_BP c_|`_#P |dBP |d@P |}~P, 'c_|`_ c|`BP|sT ''c_|`_|sTc|`#P '9c_|`_|sTc|` c|`BP|sT 9 |d@ c|`@P f_TQPGcl(c|aQc|a|cmQ! Gi cmJ@P .cmJAP ZcmJ@P cmj.Q clPcP cl|`lj jqjjkPj%acblbl|`blbl#Po4V W/abijo|dQ|`)c|`blb@c lQ}`Q@c| ~@Pc|`@c lQ|` lQ@ c| |`| @P, o{CPPG|`,P x|` T x|`,P x|`,P x-v3g33 AP -3fgXwQ| |gQ scY~`:~ dQcmJ QaJQ|Pg4M YiWiQdL|`$ QcmJR"Q5 L}KichT (c d d Bd d dP |e||egQ ȹ d QJ}aQ}a dB c|apcAlT}|k Q6 J])0Isc (3 |33|a3|a |3 |`L|a !|`.|a3 33k83k+j#kc 3c"3k43kj#k k $3k j#ke b"{殭g3k(j/kb"{殯gk.|`b.|a333 B "|a|3  .|a jTelQ .HXh (dm 'pcc,PcTcTcWTc~T=|)cTcTcxTcTQ =Rc~PP cTcTcTcc cT cdTPcc c{T cdLTPCPPG:cc@cc,@ |dTc,c|d4TP :_cc cT cdcTPcTc{Tcc|d4T ,| 3}"Pd|X3"~"P, ,c~PcPcPP3cP4cPD4cPk4cP4cP4cPc f3c "c~ 44"P4cPc3"c "Pcf3c "c~ 54"Pc3"|d"P f>5|a |"5"P|c |`/|a%5"P|*5"Pc~|P|c |`/|a.5"P|c |35"P|75"P cdTcT c 3V5|"P cc~75cm \54"PcKT'cS jK3"c"PcT 'dcOTce5q5 cu5"PcTc3"cy"Pc3"c"Pc\3"cy"Pc:3"cy"PcT fcbTc|d?T c|d?T }d|d?T" |3"cO"3"}"P" ScS 7| d+jK3"c"cc~ y5"Pc{cm}T6 SdG;cS | djK3"c"Pccm}T" ;dEcS !} djK3"|d"PV5|d"cm}T, E_d#cS | djdQ" #dCPPGcfIm@ cfImB QcdLTc{T >|566c|Pc~c  7|"P37Pkc $c~ .kkjc c" 77|"Pk k 7|"PkLk3(c jk k 7|"P7|"Pc 7|"P7|"P@Zj >ys)>>0;b"KcdcTc~|P|c   |dc"P|`3P|c |4"P|75"P|a /|ac |75"P3 |4"Pch /|a.5"P7a |35"P|75"PJ2 Wa:kc3"c "Pc7"Pc3"|d"P fc~ 7P8Pc~ 8P 8Pf 8Pc~ 7P8Pf 8Pc~ 8P%8Pf *8P/8c"Pc3"c#"Pf 38P98c"Pf =8PC8c"Pf I8PO8c"Pf V8P[8c"Pc3"cR"Pf `8Pf8c"Pc3"c^"Pf k8Ps8Pf y8P8Pf 8P8Pf 8P8Pf 8P8Pf 8P8Pc~ 3P75Pf "5PcTPf *5PcTPf fTcTPf *5Pc~ 3P35Pf %5Pc~ 8P.5P1|  ORXT" 1ȹ0fdTcwTcQc Tc QCPPGc;Qcal~TcalTcalTcalTcalWTcalTcalTcalTcalTcalzTcalT|camT _cal Tcal@T|camT _calTcalTcalSTcalTcalTcal TcalTcalTcalXTcalT|camT |camT calTcalyTcalKTcalTcalT|camT u|camT u}|camT" _u|camQ ucaldQcal{TcalTcalTcalTcal6T|cam*T cal Tcal TcalTcal#Tcal'Tcal%TcalTcalTcalRTcal-Tcal^Tcal7TcalATcalTcalTcal2Tcal9Tcal@TcalTcalTcalTcalTcal$TcTcalOTcalTcal\Tcal:T|camT |cam}T }|cam?T" calTcalNTcal0TcalTcTc, /$c~cc"Pc, c~"P9cP\9cP9cP9cP@:cP:cP:cPc f3c " #;c|"PCPPGcf3c "\5M;c|""P%c, f|P|sT %c,  sTcTPc,  sTcTPc,  sTcSTPc,  sTcTPc,  sTcTPc,  sTcTPf t;;c|Pf 8P;c|Pf 8P;c|P;c|Pf *5P<c|Pf %5P8<c|Pc|@f, X<PsTP+fIm cc{X/Q +sc-Psc-Psc-Psc-Pc, c, @PK|g<Q|` R|aId d Q|` |`bd Q, K(3= Rk de  Rk "SkI PSk`d |Sk`d@ Sk`dB  Sk`d Q6 0(3| T}aI|d! Q}`b|d! Q" 3(RF|`8|`|d@ |`|dB |`x|d |` |d Q Fic|`|cJ J |`|cJ J |`x|cJ )J |`|cJ J Q cif cTcTPf cTcTP<c"P=c"P6=c"PU=c"Pq=c"P23|c; |c  |P 2|c Q}|sT" d4fIlfIcm/P>P 4nmfIlx dC3PcTB| c{cX/fIl; c0fIcMmi Q BCPPG8fIm cc{ >cbly3Q 8f#lK>TsTPd$>2fm"Qd$>2fm"Qc-TcQc6 Tc QcMQc-TcQc6 Tc QcMQcGlTcT0fImCfI|dT|cmT 0(fImC|sT|cm}T (|O>"}"P" |fm>Q |fm[Q c|fmQ c|fmCQ @|fm8Q G@|fm]Q G@|fm8Q G@|fm]Q G@|fm8Q G|fmQ G&A|fm8Q G|fmQ G |`|`b /A 6A}`}`ba'~aX/a{X/aX/yQ6Oi k} ii6^j@f#l0% #f#lf}P|P" @bf#l0% f#l0% 2f#l#f#lf}P|P" bo:A|k .ZAP ,Tc<mj`@&P\APJ oW(RnpCPPG|dP~}|sT@ (Rnp|dP|sT" (R2|cmnclcTQ 2R s| V|   xTcmjPcc0>~a^AP~}|sTT2 Rnp3ȹ$|`}`AP  ab|`T i|`{T i|`T i}|aT" _i|`T i|`T i|`zT i}|aT" _i |aT i |aT i |aT i }|aT" _i$|`T jbmQ, $( #d|aT i|aT i|aT i}|aT" _i$|`TjbmQ, $( #d|`T i|`T i|`T i|`3T" _ic0 `AfAc"Pc~cc0 `AfAc"Pc0 nAvAc"Pc0 AAc"Pc0 `AfAc"P Q (@A}c A}cV AA}a_}P" @CPPG|dsT |c ~|c f OOk_OOk_/|a""|a"t|P@ WP|c}c~ ~ ~}|s~ |}c  P6 P$|c |sT $x|c(  /|a.|a"|c & 3" " "&|cF  3" "|c  G|P6 9<Hf|}Y P%&6 < |X T }|XT" *.|| |} ~|X T|X0T@ .5*AI|}% 3)}% 3}%fXs6j'#k"#.k"jPS2 Mi {P3|$QP| *Q| |dj}%Y|LP|'G}%j/k6A"}|d*X Q"|d+  R|E}%j/k6A"}|*X ."|+  E}%| 5Q |(;Q| */| +| 3| Q| #|1R|j/kP]F {U]l7ib|| | R"P| +R"PR"PR"PR"P  bns|XT nCPPG}|X $QRP0>kj#kR"P`Rkj#kR"PeRkj#kR"PrRkj#kR"PRkj#kR"PRkj#kR"P8kj/kR"PR"P* ns ,|gXTQ "@&  } IUa|`|R |T|g%Aj `TQPQ Q"R@& U} R(TUI@|`#|a|a}~aA~aQ> @x}swR>|`/|a|a}~aB~aQ> >x}swRY|c  } ~a3}3~a } ~aQ, Yx}R T|gT x/|`/|a}~aV~aQ5 /x}wR3|`|"K|cf ;|.3-|"c |.|""C.=|,0|.AB |.(|.c< |3 32P? }ic_ Q -cBcWT/cccmc_ c|a Q /-cBcWT~|d cmT, U-cWTRQQQ}|cAmT" ,cAXT}|cAmT" ,cAXT}|cmT" ,CPPGcYTP }|cAm}|cm"P" ,cAcMYT}|cmT" ,cjYT }|cHm}|cIm"P" ,cIcHYT }|cm}|cAm"P" ,cAcYT}|cAmT" ,cAYT }|cAm}|cm"P" ,ccAYT}|cAmT" ,cAZTc :ZTcApZTPcAZTcAZT}|cAmT" ,cAA cAlDP}|cAmT" ,cAZTcAZTcA[TcA[TcA2[TcAH[TcA\[TcAr[TcA[TcA[TcA[T&}`9}|cHm}|cIm[P" &,cIcH[TcA[T |`@ |`blx-P ( R ( |`@ |`bl-P ( |`@ |`bl-P (c ^Tc cA3^Tc cAc^TcA^TcA^TcA^TcA_TcAc ;_TcAp_Tcr_Tcr_TcA `TcrcA1`TcAb`TcAc2`TcAc2`TcA`TcAaTcr7aTcAhaTCPPGcAaTcAaTcAaTcAc2aTcAc -bTcAc2c VbTcAbTcAc2bTcAc bTcAc2c  cTcAScTcrcAqcTcAcTcrcAcTcAcTcAcT~}|cAmT, s,cAlPTcAdT+~}|cHm~}|cImXWT@ +s,Ec_ pc1 pc_ pc1cHlP"cIlP"P Euc_cIc1cH'dT~}|cAmT, s,cAlPTcACdT+~}|cHm~}|cImXWT@ +s,Ec_ pc1 pc_ pc1cHlP"cIlP"P Euc_cIc1cHadT~}|cmT, s,clPTc{dT'~}|cAm~}|cQmXWT, 's,cQlPcAlP"PcAcQdT2}`~&}|cQm|`~}|cQmP6 2s,(cQlPTcQdT+~}|cm~}|cQmXWT@ +|s,cQlPclP"PccQdT+~}|cm~}|cQmXWT@ +|s,clPcQlP"PcQc eT2}`~&}|cQm|`~}|cQmP6 2s,(cQlPTcQ;eTCPPG }`~&| cQd T, s, cQTcQWeT1~}|cm jcl}a P6 1s,(clPTceT|}aT, s,eP%} ced |d T@ %s,(cllZT4~| cllZd ~| ed P6 4s, cllZTcleTclh lT=~| clh ld ~| ed d P@ =s, clh lTceT=~| cQd |`~| cQd d PJ2 =s,("; cQTcQfT<c~| d |`~| d d PS; <s,w$: cTcTc+fTcc"Pcc;fT~| cQd T, s, cQTcQYfT~| cQd T, s, cQTccQgfT~| cQc"d T, s, cQc"TccQfT |`@ |`blx-P ( |`@ |`bl-P ( |`@ |`bl-P ( |`@ |`bl~-P ( Q ( Q (cAlTccAigTCPPGcAlT}` }|sT" ,ccAgTcAlTc ccAhTZ~| Bcd ~| cd d |`jj TkzPJ Zs,( BcTchT'~}|cAm~}|cQmXWT, 's,cQlPcAlP"PcAcQhTF~}|cAm~| cllZd ~| ed "XWT@ Fs, cllZcAlP"PcAcliT~&| cQd T, s, cQTcQ,iT|}aT, s,XiP1~}|cm jcl}a P6 1s,(clPTc|iT4}`~&| cQd |` ~}|dP6 4s,( cQTcQiT%~}|c+m~| cQd T, %s, cQc+lP"Pc+cQiTcAiTcAl}a }|sT" ,cAiTcXTciTj|a3|a$ |`D|a|a-vj|a. |`Dj|a j|a=j|a  j|aj|a j|a%|`#|a |a|P, clh T, {clh Tcl|a&Q cjTCPPG+clcll XP6 +x{clTcl|a&Q c2jT Q rjP, s, pcTctjTjP, s, pcclP"PccjT}|c/mT" ,c/l Tc/jT|aMd T" ,T|aMd T" ,T0|`mj`@&P, 0,(T}|cmd T" ,cl Tc(kT}|cmd T" ,cTc>kT}|cmT" ,cTchkT }|cm}|cm"P" ,ccl "Pcc~kT}`] }|cdT" ,M}`V  )|gF`jj|~aU kPkT@ MWk,ckT}|cAm}a P, ,(cAkT:}|cAmjcAl|`r}a}a P, :,(cAkTc kT*}`] }`] }|ctd}|cud"P" *,cuctlT%}`] }|cd}|cm"P" %,cc)lTPcTclTclTCPPGclJlTcllTclhlTclmTclmTcllT clgF`TcllT oclgF`TcllT clgF`TcllT cgF` cgF`"PccecmTPcllTclTmTcgld$PcgrmTcgld$cll"PcUclcgmTdPdcll"PclmTcl$PcnTcl$cll"Pclc@nTcl$cll"PclconTPcll"PclnTc`l@B$Pc` oTc`l@B$cql"Pcqc`/oT@BP@Bcll"PcloTc`l@B$cll"Pclc`oTcllTcloTcl TcpTclTcpTQMp^pkpqpXQzppppXpQppqqXqQ#q7qDqJqXPqQcqmq{qqqXQqqqqqXQqqr&r/rX8rQHrbrprwrX~rQzppppXrQ|c|`T|`yT i|c@P i|Q i Q i!c)r|"r"|P !xc r|"|sT x7|`# -Q|`  *Q6Q 7PcxTCPPG|cxT x vtvtP P" U- Q #|rflT|P #x %| |r 4P|`|`Tj |j "|` >4P|`T Mjbljbljbl~jbl, jbl, |`>4P|`~:P|`:P|` 44P|` 94P  4P>4P|Rb % `#Dddd3P, wi Q |`|`d.T  d.TcT d.T=cr |  "P  "P" = ~}|aQ, xV|3|P3|"P |3|P|3"P "cs  dCT "cc2s  3   |d5|fl|am3P"P@ cw&nmi |P" &i|cdxQ :cYs   d>" d>"P :?} c}a}avc|dD}a PJ ?,6 S}s |      cd5 d>d>3~" d> PT ,Evp6L CPPG2cMc%ls d~a ~}|sQ, 2U-E| &cs ct  dCT E33}!~c8t  3"} &/cPt  cwt  |fl|amdBdB""PJ w&nmi'|cdx|&cdxQ 'ct   3    |d=|0d>d>"cP?;d>3""'d>3""d>d>"PJ SETLW| cc 6u| c6c.8udrTQ" W( _} c}av}ac }avc|dE}`}a P^ _,Z6 S}:u |    3     d>d>3"~" d>d>3""Ph S,EvpZ6L |xT $f#lLc|gDT" $ |xT ~c |xT, wi|xT |xT '|c&xT4P ' q} c}av}af#lLcc|gEc|gE}a PT q,6d S|xT CPPG#cllc|gDT" # |xT ~c |xT, wi|xT |xT '|c&xT4P ' p} c}av}acllcc|gEc|gE}a PT pm,6 Sc%uTc;c%*vTc%avTc%vTc%vTc%vTc%$wTc%XwTc%wTc%wTc% xTc%GxTc%txT~}|aQ, xVc%xT~}|aQ, xVc%yTPc;c%yTPc;c%yTc;c% zTc;c%LzT;c;Pc%mzTc%zTc%zTc%zTPPcQc%3{T%c%PcQN{TPcQc%{T%c%PcQ{TPQCcQPc%{TPcQc%(|T%c%PcQI|TPQCcQPc%z|TcQc%|TcQc%|TPc%#}TP%c%PPc;c%~TPc%c;~TCPPG;c;Pc%~TPc;c%*TPc%c;ET;c;Pc%jTc%TPc;c%TPc;c%!TPc%c;>Tec7 lNl"Pc7 c%}TUPc%TPc%TPc%,TPPc;c%lTcL RTcTcnTcTcWTc̊TccJ&PcJcciTcTc7 lNl"Pc7 TPc%TPc%7TPc%TPPc;c%ЍTPc%jTPc%Tc%ՎTc%Tc%,TPc;c%aTPPc;c%Tc%ЏTPPc;c%TPcc%FTc%uTPcc%Tc%ؐTPc%cT%c%Pc:TPc%cT% c%m?PcT/ff\Α}`L", /U{Pc%TPcQc%7T%c%PcQTc%ȒTc%Tc%(Tc%\TCPPGc%Tc%Tc%ܓTPPc;c%Tc%VTc%T;c;Pc%ɔTPPc;c%Tc%Tc%JTc%xTPc;c%TPc;c%TPc%&TPc%YTP%c%PPc%TP%c%Pc%&TPc;c%bTPc;c%T;c;Pc%ŗTPc;c%Tc%MTc%}Tc%Tc%ؘTc%TPPc;c%DTPPc;c%TP}}`3"P|P" &ic%™TP}}`3"P|P" &ic%TP}}`3"P|P" &ic%}TP}}`\3"P|P" &ic%TP}}`\3"P|P" &ic%"TPc%Tc%TPc;c%+Tc%gTPc;c%TPc;c%T;c;Pc%Tc%TTPc;c%Tc%ҝTc%TCPPGPc;c%@Tc%|TPc;c%Tc7 lNl"Pc7 c%Tc;c%HTc;c%Tc;c%Tc;c%RTc;c%TcZcacYcK .TcءT|@P+ d t|`mAP+ d s|@P4 }i x!veT" !ȹ0|`bT x|`DP x|`@P x|`bT x|`xT x|`T x|`T x|`T x|`bT x|`bbT+  x|`bbT+ ȹ x|%@P |`@&{P x3/ ln rln lbQ f cg Q f2\TѱԱXٱXg" jg f2\Q 1O jdQpXdQcccccccccccc gQ, xtfmH g<clT+clT+c 0Q,)BdB[dCPPGcIcZ5cXN`c&aJc"&aPc&a_QfQQXcQc QQc 8V0c cQf] 5Q?QQAQh Q  |" "}" "P" nm, |" "}f#m" "P" ,i |`m" "P {}|a }aT" { P" {}|a  "}aT" {| }Q" r| }Q" r6 |: Q 46 |` = C  }: Q" 4(!|H f#mM Q !i'} |H f#m Q" 'i Q i'} |H f#m Q" 'i3 Q i'} |H f#mc Q" 'i!|H f#m Q !i Q i!|H f#m Q !i!|H f#mX Q !i |` Q i |` Q i |` Q i |`y"P i |`y"P i |`y"P iCPPG |`y"P i |`y"P i |`y"P iU ~~   |}0}  8Q, UhN }}   |0|  8Q" Nh<|8Q *|0|  fQ *|dBfDQ m|dQ cX| ||| XN0|   Q '|  Q '_"| "Q "_*'|/|"X":"P *f?QCQ |K" XjXQ:N]XljXQ X::N]X}Q _|d Q _8| "| Q 8_Q }X/Q" V |UQ chf ""P~Q~Q1 |`}` }`Q" 1U-1 }`}` |`KQ" 1Vf>Q| QGQ r"| xQ "_CPPG'|  Q '_"| "Q "_'|  Q '_+|}~a|am""P6 +nmi{/|fl|am" "P6 /nmi{ }|~a" "P6 wi{&Q\Q& |` |`Q" &)hQ hQkXQQQXQQC|MQ sf  y""Psf  y""PQQQ I |`dQ I" I| T "QCQQ b|`dQ IQQQnQ IQH|d[Q QKQwQ$|3alQ $!|`{[Q IbXQ X. Q!|`{Q IT!!!!X!Q!|`{[Q I""| | }"Q" "DV#Q####X#Q-$Q CPPG^|} U$Q|} $Q2%| O%U%_%} O%%%Q" ^g_fCQ=%|} Q| %Q} %Q"Q" ='((X())X!)Qf<Q)f A*Q*f Q,Q I&'.|Q.|{.Q &.Q/|/}/Q" dRk*/|B/|R/} }k/X u/|/|/|/Q" k8/}~X/|//XQ6 8u/|`y8Q i'}/|H f#m/P" 'i0'}/|H f#m80P" 'i01 |`w0|`0}`y8Q" 1i01 |`w0|`0}`y8Q" 1i01 |`w0|`0}`y8Q" 1i01 |`w0|`0}`y8Q" 1i0& |`0|`0Q &V+|`  6 |` l: Q +V)6 |` l }`: Q" )VoF |` |` l f 1 18Q FV) |` |` l1Q )V4 |` |` l }`8Q" 4V4 |`X |` l }`8Q" 4VoCPPG6|`   |`X |` l8Q 6V6 |` |` l ~}a8Q, 6VoA |` ~a |` l }`8Q6 A>V Q, (f ~`f 1!!1f %1+1 |` }`8Q, f( Q@ J(t `f 1!!1f %1+1 |a }` ~`8Q@ tJ( Q" (x }`f 1!!1f %1+1 |` f 1 18Q" x( Q6 J(h `f 1!!1f %1+1 }` |~a8Q6 hJ(3  |a01|` |`8Q 33 |a |` |`8Q 33  |a01|` |`8Q" 33  |a |` |`8Q" 33  |a |`%81|`8Q" 3:  |a01|` |`>1}8Q" :N|` |a |`J1}  T1" 8Q, Nnm:  |a01|` |`T1}8Q" :CPPGN|` |a |`81}  T1" 8Q, NnmL|` |a |`%\1  T1" 8Q" LnmL|` |a |`%b1  T1" 8Q" Lnm> |a |` |`1}`y8Q" >>  |a01|` |`1}`y8Q" >> |a |` |`1}`y8Q" >>  |a01|` |`1}`y8Q" >3 |a |`%1}`y8Q" 33 |a |`%1}`y8Q" 3R|`  |a |`1}`y  81" 8Q, RnmR|`  |a |`1}`y  81" 8Q, Rnm3 |a |`1}`y8Q" 3C }`1f  }` |`y1Q" Cb1}"1f "1f -" |`y1Q" b&_1P i2P i|` "|`7 "P iX2P i`2P iP iP i!|H f#m2Q !i!|H f#m2Q !iCPPG!|H f#m?3Q !i3|3Q r|a|3"P" 5,4Q V]4Q V~|}aQ, xV{4}4Q" xV4}4Q" xV4Q V!5Q Va5Q V|dQ V5~5Q, 5VR^5~}a"5"|`,  |` "81"~}ao" 6"&f mQ, ^V{G6~}aG6|`,  |` 81~}ao Q, GV{~}|dQ, V{7S6~}a81~}ao|6~}ao6Q, 7V{6~6Q, 5VR7~R7Q, 5VR5}`'n7Q, xdV-7f 777Q" -xV(5|`-7~}a Q, (xV0,5|`- 7}a Q" ,V07Q, BTV+8Q V&5|` |`2f8Q" &xV?5|`'8} f 1 1 Q6 ?xkV8Q, xkVCPPG8Q" xV:9Q, `Vm &f mg9>r9~d9q9}9~d9R)9XN9 ~d9&9XN9 ~d9[QT kVm} 9}9Q6 kVm}7:c;:f  F:Q 7V_:Q V:Q5};cQ" xV8;Q" xV |`~;Q" U-&5|` |`;Q &VQ |`';Q V" |`-7}8Q" "xV" |`;}8Q" "xV5|`';Q V5|`-<Q V"5|`'8} Q6 "xkV?5|`'!<}8<f C<  Q6 ?kVm}L5~}a"5"|`y"81"~}ao" 6"&f mQ, LV{5F<~}aG6|`y81~}ao Q, 5V{Mq<~}aG6|`y81~}ao<|` ~}ao6Q, MV{( <|a*<}<Q" (xV"<}<|`78Q" "xV |`7=Q VCPPG |`77=Q V |`7[=Q" xV" |`7=}=Q6 "xkV |`7=Q, xkV |`7=Q" xV |`74>Q, BTV |`7\>Q, `Vm &f m< >|a*>9}9~d>Q >|a*>9XN9 ~d>QT kVm} " |`7>}>Q6 "kVm}L |`7">"~}a">"~}ao">"&f mQ, LV{5 |`7?~}a?~}ao<Q, 5V{~}|dQ, V{" |`7-?}S?Q" "xV |`7_?Q" xV |`7?Q V" |`7?}<Q" "xV" |`7@}<Q" "xV |`7#@Q V |`7T@Q V |`7@Q V |`7@Q" xV" |`7@}<Q6 "xkV |`7@Q, xkV |`71AQ" xVCPPG" |`7fA}AQ6 "kVm}L |`7"A"~}a"A"~}ao"<"&f mQ, LV{ |`7AQ" xV!|H f#mBP !i!|H f#muBP !i!|H f#mBP !i!|H f#mBP !i!|H f#m4CP !i!|H f#m]CP !i!|H f#mCP !i!|H f#mCP !i!|H f#mCP !i!|H f#mYDP !i!|H f#mDP !i!|H f#mDP !i!|H f#mEP !i!|H f#mEP !i!|H f#mEP !i'}/|H f#mFP" 'i0'}ZF|H f#maFP" 'Vi!|H f#mFP !i!|H f#mFP !i!|H f#mGP !i!|H f#m8GP !i!|H f#mdGP !i!|H f#mGP !i!|H f#mGP !i'}/|H f#mGP" 'i0!|H f#mHP !iTHP iCPPGHP i!|Hf#mHP !{AK|`y"8"P nK|`y"8"P LLdiTL!MdiT+MQMdiT[MMdiTP|`y"8"P 0P|`y"8"P 0P|`y"8"P 0FSpSdiTySSdiT!|Sf#mSP !!|Sf#mSP !TJTdTRTJTdTTJTdTTJTdT'} |H f#mVP" 'i'}V|H f#mVP" 'i!}Vf#m9WP" !i'}V|H f#mwWP" 'i'}V|H f#mWP" 'i'}V|H f#m,XP" 'i'}V|H f#mjXP" 'i'}V|H f#mXP" 'i'}V|H f#mXP" 'i!|H f#m-YP !ibY|`y"8"P i!|H f#mYP !i!|H f#mYP !i!|H f#m]ZP !i!|H f#mZP !i'}V|H f#mZP" 'i'}V|H f#m[P" 'iCPPG'}V|H f#mX[P" 'i'}V|H f#m[P" 'i'}V|H f#m[P" 'i'}V|H f#m\P" 'i'}V|H f#m\\P" 'i'}V|H f#m\P" 'i'}V|H f#m]P" 'i!|Qaf#mZaP !!|Qaf#maP !>|Qaf#ma}`  ")b"P" >'}|Bbf#mEbP  'xy'}|Bbf#mbP  'xy'}|Bbf#mbP  'xy'}|Bbf#m cP  'xy'}H |Udf#m]dP" 'i!|ef#meP !&'}e|Vf#meP" ''}e|Vf#m fP" '!|ef#mlfP !!|H f#mfP !ifP i!|H f#mgP !i-|`/|H f#m0gP" -i0!|mgf#mwgP !!|H f#mgP !i!|H f#mgP !i!|H f#mhP !i!|H f#m:hP !iCPPG!|H f#mfhP !icTcTcTcT7ifl"`i"P'}j|jf#mjP" 'U-!|H f#m3kP !i!|_kf#mfkP ! |`'"n"P V |`'"o"P i( |`'"p"}`y"8"P" (Vi0Eqflf|a/"8"P 0q|`0"8"P 0t|`y"t"P iMu|"8"P Mu|"8"P w|`y"*w"P i!|H f#mI}P !i'}|f#mP" '!|f#mP !|`"8"P -}H f#m|`""P" -i'}/|{f#mP" ')0fl,  / "I"P'}{|rf#m~P" ')F'}{|rf#mˍP" ')F3}{|rf#m~`T"8"P, 3)F'}{|rf#mlP" ')F3}{|rf#m~`T"֎"P, 3)FL }`" "f  }`"" "|`y"8"P" L)i4 |`'"="|`"S"|`"Z"P 4CPPG;|`y"8"P 0'}V|H f#mXP" 'i'}V|H f#mP" 'i'}V|H f#mP" 'i'}V|H f#mSP" 'i'}V|H f#mP" 'i3|`!"8"P -}H f#m3|`&""P" -id|"8"P pXQ-@LXX@]hXQO XQʟX9ZfXrȠXҠ XQ:ǡ͡XӡQQ, %4Q, %0Q, %BQ, %8Q, %gQ, %|P o }cmQ, %8Q, % }cmQ, %|}P" c Q, %c Q, %cQ, %~XYQ, %CPPG8Q, %~XY͢Q, %~ޢXYQ, %}XYQ" %n} ~  |dQ}  ~|dQ~  }|dQ}d~d"cq ~}|dQ~}|dQ, n% <| P|c "PJ 0 <x_:7W|XYQ %%|XY-}8Q" %%%|XY}8Q" %% M|f#mXY} XXY^~8Q6 M%nm M|f#mXY}8oXYu~8Q6 M%nm ~XY͢Q, %~ޢXYQ, %}XYQ" % Q %*~|XY}8Q" *%*~|XY}8Q" *% R|f#m~XY} XXY^~8Q6 R%nm R|f#m~XY}8oXYu~8Q6 R%nm  Q %%|XY}8Q" %%%|XY}8Q" %% CPPGM|f#mXY} XXY^~8Q6 M%nm M|f#mXY}8oXYu~8Q6 M%nm ! ~a* Q, !%8Q, %! ~a*Q, !%!~`{~`Q, !% vt vt@P! ~a Q, !% vtvt@P }` 8Q" %& ~` ~` Q, &%A}f#mȣ}`O""$f mQ" A%5}f#m$f mQ" 5% vtBvt@P Q" %6~}.f#m}` 3Q[Q, 6%b ~`O ~`y |f 1!!1f %1+1 Q, b%8Q, %b ~`O ~`y |f 1!!1f %1+1Q, b%]~`{ ~`O|f 1!!1f %1+1Q, ]% vt vt5P vtBvt5P vtvt5Pc1|aT ic1|aT iCPPGc1|aT i vt vt=P vtBvt=P Q" %8~`yf  Q, 8%Q, %8~`yf Q, 8%W~`y |f 1!!1f %1+1 Q, W% vtvt=P vt vt:P vtBvt:P Q" %8~`yf  Q, 8%ä~`8Q, %8~`yf Q, 8%Wͤ~`y |f 1!!1f %1+1 Q, W% vtvt:P Q" %W6 ~`O|f 1!!1f %1+1 Q, W_%: Q, %֤~` Q, _%Q, _%Q, _% }`Q" %W~`y |f 1!!1f %1+1 Q, W_%8Q, %Q, %CPPG: Q, %|`T i|P % Q" %!~`(Q, _%8Q, _%|cm T i!Q, _%W |f 1!!1f %1+1(~`y8Q, W_%|cm T i7Q, _%OQ, _%|`XT i P i|`wQ6 i.}.~~ WQ]Q~}|sQ, .6 Q, _%: Q, _% R i P i%d|`X |`Q6 %i}~ Q, | dQ򰈥Q, _%8Q, _% R i P iq|`|`3|`sl "X/|d|`37|`xg6 }c %|`81|`3Q6 qiO|c %|&}*~$|c %|% Q Q~}|sQ, OCPPGT|`sl}klk"f] lCXX/QX/Q, TiQ, _%: Q, _% R i P i=f] lC|`|`XX/Q|`X/Q6 =i}~WQ, ~}|sQ, _%Q, _% |dQ6 i[Q, _%}~ Q, Q" %$Xm XmQ" $%Xm&Q, _%XmQ, _%26|`sl"|`sl|`slXQ6 2i}~;Q,   |`|kjrlk8j# |.`#j fa|.` W| cjrlk8  E"    Q8  Pg6T{,E_ x(i(dt |}~|`P, 6AcjcZf#mc  gjQ, A_%)c  :  Q, )_%CPPG`cQcjcZf#mc  gc  :  Q" `% }.~ ]Q~}|sQ, cc|a T i P i Q i|`3 T i Q" i|`7 |` X/Q6 icQcQc}Q}`X/Q@ iZ~`0 u~aJ ~aJ Q~}|sQJ Zx(|`T m|a@T x|a@T xQĨQũcfQIgMCQHQ vtvt+P6cQgMCQIdCT vtvtcPs<QsQ vtvtcPױ 0X=RgX QafJl"p"fJl""gHQQ vtavtUPVgofVN 1TO 3Sgo<\HgoafJl"p"fJl""gHUgoQsafJl"p"fJl""gHQ4f[TgVgVQ 44fZTgVgVQ 44f]TgVgVQ 4clTCPPG !fTQCPDF  CPPGAՄߊ߼񀺧߲߳ߊ䌦ߍߊߺߺߺ߶߱ߪ߰ߗ߱ߌ՚߬߬߭߭򱰭߭߬߬߹߹߼߼߹߰߹߶߾򬯺ߋߋ!վB @ٯߨ㌯ו߯߄ףJߨ㞳㞳ᣄg߬򮪰/߬򮪰ڟ߶߄߱򾳫ߊ򲺫ߑ򲺫߷ߋߗߧ򧧧򧧧򧧧򧧧ߘ߫𧲳৲㱰𱰐אAoߒߜ򼰱򬫰򼾼򭺩򼷺򼷺ߏ򼾼ߚ৲㺩㴺𺩺㼾𼾱ןAoߒߜ򼰱򬫰򼾼򭺩򼷺򼷺ߋ߫𷫲㷫㫶ዞ𫶫𷫲ߘߏ㰴߭৲߫𵾩ߵ߫𼬬߼je <72-("ߵߵ߲߽߯߸߲߲߲߰ߨ߯߻߻߬߻߽߬߸߫߫𨺽񬦬𜰩߬ߪ߱𨺽𸪺߫𯳾𨺽𸺫৲㺩𺩺𨺽𹳪𨺽𸺫ߨߪߊ㨶𨶱㬪㱾𱾲㯰𯰬㬭𬭼𬪽𳾦񷫲ߨ𳾦񷫲㬾𬾦߶㶱ߨ㼾ߦ߱㲰𲰻㬼㬽㫺𫺧𬽶𬼭߫㼳㫺𫺧㪬𪬺𼳰㼳㶱𶱯㬽𬽶㲰߲𼲻񷫲ߨ𼲻񷫲𨺽𶱯𨺽𲰭𨺽𬺫ߚ߰ߚߚ㺭𺭭߼򯭰ՂՂ߼򯭰ߞ߳߾ߡ𬯺㼪𼪭𬫾񷫲ߨ𬫾񷫲㻶𻶩㻶㻶𻶩𫺧㭺㭺ߨ𲾶񷫲߲ߋ㬺𬺫㯭𯭺㫶𫶫㸺㼾߫߯ߴ߷߼߳߹๶߰߬𨺽𶱯㹶㯭𯭰㻶𻶾㹶𹶳㪭𪭳𹶳㪯𪯳㰹𰹹߹߻߼ߺߡ㶱㶼𶼰㽪𽪫㻺𻺹㼾𼾱𶱯߽𼳶ߜ򛶬ߙߌߌߙ񽶱ߙ񫧫ߜ񫧫ߙ񻾫ߌ񫧫߾𰼫򬫭߾쩲򬫾߾쩲򶲾𨺽𶱯𨺽𶱯㷫㽰㬼𵾩ᨶ񯾭񻶬𬼭𽰻𷫲𨺽𶱯𨺽𪯳㷫㽰㬼𵾩ᨶ񯾭񻶬𬼭𽰻𷫲𨺽𬺫߱㬪㱾㶫㶻㩾𩾳𶫺㯭㶻𯭰㽪𽪫ߑߑߑ򬯺ߑ߁㬯㬯߯߾߬߇߻uu߶߳{{َ߫C߇َ߇ߛ߻񼰲߬ߌ߄ߧ߯ߌ׶߯ߚڋ߭߼߼߼߼߫㱰㳾㻰ߣ㬯ね㭾4ڗߧ㬯㯪?߿S?߿S߿SgGGG߿߿߿߿ߓ񼰱߷ G ߗ [  ߓ=ڕ=ڕ߯kߣߛߛS''''S''' =㱰㬯㬯㬯oߺ ߶񼰲3333񽰫߼񷺶߬㱰カ𫶫򸾲򬫰򪬺㬼㬷߫㹶㻶㺩߽㴺㫶㷭㱰㺰㳶㼰㺱߄߂߼孺ױ׭߃􌫭񹭰D񼰲㻶𻶩㻶𻶩㻶𻶩ԄԄ߂Ԅ߾񺧶߰߰㬯㾳㬯㬯㾳ߴ߱ߴ߶uG߹bFus_ݫ_ݫ____߾񹰰߲߹߰㬯㾳񶱯𶱯񼰲߱߳񼰲񼰲񼰲㬯߽߳߫߬߬㲺𲺱㫶𫶫㶬CPPG㲺𲺱㹭𹭰㴺㴺ߊ㴺𴺦𴺦𴺦𨺽𲺱߼߯㲺㼳𲺱߬߱߼߱㱪𱪲㫰𫰯㳰𳰱㱺𱺧㯭𯭺㫶𫶫ߊߞ߾񩺭N[񼰲񼰲ߙߑߛߪUO㬯ߘ񫧫׭ק^ס8WOZCvf?95ߖߑߗߝߗߞߊߛߗ߉ߍߙߌߌߘߙߓߓߜߏߋ߽ʍ߽岵񼰲ᒶʍߋ7׍[׊+׆O׃ػ׿؅׼׸׵ױ׮תקףߒߜߛߜߜ߇ߓ߇߇ߖ߉ߖߖ߳OQں߱ڕ߱2W2ڕ߫߻ߺ߻߱߬߶㻶VځuڰߨvڿڸھکZ񼰲߄Ղ㳾㻰ᄯ㭾㳾㻰㭾߭㽭߭߭߶㹰򶱯𹰱߾߬߬߯߱߾߾㱰㳾򥂄㭾㪯㪯㪯ぬ㬯ぬぬぬ㬯ぬߤߢ.*ߤߢ=߾ߜ㱰㳾㻰ἰ㭾߲߱߬߶߽߾߹߼߼߻߼߾׀׀׬׬)(8߫߫>8䭺ߺ߻߶ߺ߶ߺ߬߫߶߰߰߫߰羺~}|{zywvutsrqpmlkjigfedc^\ZLJHFDvtrpRPNvtrpnl~|oQOMKIGEC@~|trdb`^\ZXVTRPJHKDwV>g^>gJ>gH>gF>gD>gB>gr>gp>fR>fP>fN>fL>fl>fj>fh>ff>fd>eE>e~>e|>ez>ex>ev>et>er>ep>en>el>ej>eh>ef>ed>eb>e`>d^>d\>dZ>dX>dV>dT>dR>dP>dN>dL>dJ>dH>dF>dD>dB>d@>d~>d|>dz>dx>dv>dt>dr>dp>dn0b^0bP0bJo@ߦb`hk>eP>eF>dl>dj>dh>df=Mk0bF߬߾߾߾߄㳾ね㭾㾳ま㱰ᄾ㾳ほ߬㼾㳰㪯߄㱰㬯㬯߱ߨߨ߾߶߷߷߷߷߻߻ߨ߸߼߼߳߳߬߬߬߬߷߲߼߼߼߼߼߼ߨߨߨߨߦ߰ߪ߹߽߹׏׏׏׌׈׈׈׈ߖߨߦEEEA׈׈׈׈߲ߪ???;6666ׅ׀׻߲߰ߦׅ׀^X߲߰ߦסססYNNNDzri`߲߰ߦߦ׏׏׏׌׈׈׈׈>8EEEA׈׈׈׈>8ל+1ײׯׯׯׯ߲߭####k߾####kkkkkkkkkkߖ߬߬߬߬߬߬߬߬߬߬߳߳߳߳߳߾𷶲߫𷶲߾𷶲߸𷶲߬𷶲ߦ߬߱߬㱰㬯㬯ぬ߄㱰㬯㬯߫߫ߺ߰߳חߪ߹߸ߨ߼߱߬ߺߨ߬r߻߽߱߻߬߻ߺ߻ߨי߻߱׭߻߱A߻߬߻߬ߟCPPG ߻ߪ߽߻߻י߻׿߻߰_߻߽߯߫`߻߬4߻߾Ԟ߻߹߹٧߳ߪ߾߾߫߫߯ח߽=_]50d`=_T0d`=_Tڝ՝㼾㭯=_F=_B㳾ね㭾㱰㻰=_]߲՗խIa8՚ՍըChۙՓզKh ՖկTt1֗ՙյZd .Ӗ՗ն[y; ՃըMr5ڔ՗յSp+ɓՄա~7ҒՅդ@6ёՓը}b%ÐՁ׼$״ߦ߾R$״ߦ𬷺ng״ߦg״ߦ𬷺ݚg״ߦ٦״<ߦ𷶲ז٦״<ߦ𷺭ײٔ״ߦ𷺭Mٔ״ߦ𷶬6ٔ״ s״ߦ𷺭&s״ߦ𷶬s״<״ߦ𷶲׸<״ߦ𷺭N<״p$߫ $߫𬷺)٦e<߫𷶲Ř٦e<߫𷺭ٔZٌP߫𷺭׻s`:߫𷺭%%%y4߬ޕޕݕ߬8---߬ົ))ߺދދ݋׾ދދ݋߶𶺻ގގݎގގݎ#WWފފ݊߻ފފ݊׼މމ݉߸މމ݉@V߼לV """߳א """ŕٍRRRٍ߬ׄRRR׉فFFF߬׹فFFF׬ٞަަݦ_%Ok<޻޻ݻ߼c޲޲ݲٟީީݩߨם٤ccc߾׼٤ccc߾Z٤ccc߾𬷺x٤ccc߾𬷺ޖ<߾𷶲3ޖ<߾𷶲Гޖ<߾𷺭ޖ<߾𷺭׋t߶שt߶𬷺FT<߶𷶲cT<߶𷺭ٌPPP߶𷺭ܒ`:::߶𷺭g߶בg߶𬷺תg߶I߫e߫𬷺<߫𷶲ۑ<߫𷺭ޛޛݛ߫z<׸<߶𷶲M<߶𷺭׮***׺***,ّWWW߰ʐّWWW߰޼޼޼ׇ߰޼޼޼߶צٵxxx߬ߤ߄ߢ߂㳽㳽㳽㳽ね㭽㳽㳽ね㩽ね㭽߄まぬ㬯ぬぬぬ㬯ぬ㬯ぬ㬯߬㾳ẻ㾳ẻE(؟ݎ7ҎػƎߥ߱߾׺ײתף\ULD߫߫߹߹߬߬ߺ߱;3)ߍ֍͍߫ߺ߫߫߹߹߬߬ߺ߱׿׺׳׭קע[T߫߫߹߹߬߬ߺ߱߫߬߱߭ߥ߹߬߫߹ߺ߱߶㻶߶߄߶=_G=_C߬㲶=_C=_B߬򮪰㲶=_G=_F߬򮪰㲶㞳߬򮪰㲶㞳߬򮪰㛶㞳㞳㞳ߨ㛶㞳㞳㞳㯺߾㞳㞳ߨ㞳㞳㬮㞳߯㞳㞳㬮ᄬ߾㞳㞳򣬮㯺߾㾳㯺㾳㯺߫㾳㯺㾳㯺㾳㯺߫㑰㞳߬߄߯㌯ߨ㾳㾳pПp㱰᫨㱰ᰱ߄߹ߨ߹߾߾߲ˇ߼߬׌߼߱G߼`߱߱߫߱߱߱߳߱ߺ߲߱߱߱߱߯߱ߺ߬ߺߺ߬ߺ߬߬߱L߲߬߬߬߶߹߫߫߹߰ߨ߾߰߾߼߼߲߼߮߼ׂ߼W߼m߼߼Ԃ߼߼׶߼H߼߼1߼߫߾߫߯߫߾߮߮߻߮߶߮߾߮߯߮߾߮߫߮CPPG ߾߶߽߶߳߮߻߮߾߮߾߽߮߮߾߮߫߻߻߶ߺߺ߻ߺߺ߯ߺߺߺߺ߶߶߶߶߱߯߼߬߼߰߼߼߬߱߬߾߬߱߬߱߬߱߬߾߬߾߲߬y߬߳߳߬߳߳߱߱߱߾߾߾߾߾w߯߯5߯Ը߯߯׏߯߯߱߯߯ ߯'߯ŷ߯߯׽߯Y߯߯߬߯߬߬߯߬߾׾߬߬߾߾߾߫߫߮߿=_C=_B=_G=_F߲߳߳׃߲߳ߨ߲߲ ߻.߻Ѵ߻߾߻߻߰߻߻߻߰߻߻߻߻߫߻߷߱߻߱߬߯߬߻߬߫߬߫߬߫߬߬߬߬߷߬߷߬߾߬߾߬߫߬߫߬߫r߬߾߬߾߲߲߬߬׽߬߾߬߾߲߲߬߬߰߰߰ߓኖߎ߮߮ߍᚌߍ߭߭ߌߍዞߍ߭ߊᑛߊߪߪ߬ߌፚߜ߼߼߬ߌናߙ߹߹߬ߌᒊߞ߾ߌዞߌ߬񾬬Ṷ𾬬߫ߨ㾳򣬮㳯㾳㭯㳯㬯㾳㭯㾳򣬮㬯㳯㬯㾳㭯㾳򣬮㳯㬯㾳㭯㬯㭯㳯㾳㭯び㳯㳯㬯㾳㭯㭯㳯㬯㾳㭯S㾳򣬮㬯び㬯㳯㬯㾳㭯㬯㳯㬯㾳㭯Q㾳򣬮㬯び㬯㳯㬯㾳㭯㬯㳯㬯㾳㭯ߩ𩺭߫𫾴߯ߋ߫𫾴߯ߋ߭𭺲߯ߍ߻𻭰߯ߛߺ𺧾߯ߚ߭𭺾߯ߍ߳𳰰߯ߓ߬𬺾߯ߌ߳𳰰߯ߓ߳𳰰߯ߓ߳𳰰߯ߓ߫𫰪߯ߙ߫𫾬߯ߋ߬𬲺߯ߌ߬𬲺 ߯ߌ߳𳶬߯ߓ߳𳶬׎߯ߓ߯𯪫߯ߏ߯𯪫߯ߏ߯𯪫߯ߏ߯𯪫߯ߏ߯ߏߨ𨺾߯߈߫𫾴߯ߛߴ𴶬߯ߔ߾𾬴߯ߞ߯ߞ߾𾬴߯ߞ߯ߞ߯ߞ߫𫺳߯ߋ߯ߋ߯ߋ߾𾬴߯ߞ߫𫺳߯ߋ߫𫾳߯ߋך߯ߋ߬𬷰[߯ߋ߬𬾦g߯ߗ߬𬾦3߯ߘ߬𬾦߯߆߬𬾦ה߯ߑߦ𦺳׬߯߆߸𸶩߯ߘ߯ߘ߯ߘ߬𬷰߯ߌ߯ߌ߯ߌ߫𫷭߯ߋ߫𫷭߯ߋCPPG߫𫷭߯ߋ߯ߋ߫𫷭߯ߋ߫𫷭߯ߋ߹𹰳߯ߙ߾𾫫߯ߞ߾𾫫߯ߞ߫𫾴r^߯ߖ߫𫾴,^߯ߖ߫𫾴מ]߯ߖߨ𨾶נ]߯߈߳𳰰m]߯ߓ߮𮪶]߯ߎ߭𭺯\߯ߞ߬𬷰߯ߙߺ𺱾H\߯ߙߺ𺱾9\߯ߙ߷𷶻[߯ߙ߬𬷰ע[߯ߙ߫𫪭[߯ߋ߫𫪭Z߯ߋߺ𺱫מZ߯߉ߺ𺱫_Z߯ߋ߬𬷰kZ߯ߌ߬𬷰2Z߯ߙ߬𬷰Y߯ߑ߫𫪭עY߯ߑ߫𫪭Y߯ߑ߬𬾩"Y߯ߌ߯ߌ߭𭺬׎X߯ߍ߯ߍ߬𬾩kX߯ߌ߭𭺬!X߯ߍ߭𭺬יW߯ߍ߯𯾪צW߯ߏߪ𪱻xW߯ߊ߬𬷰W߯߉߬𬷰V߯ߜ߬𬷰׆V߯ߞ߬𬫾LV߯ߌ߯ߌߺ𺱻-V߯ߌ߬𬫾U߯ߍ߯ߍ߬𬫾zU߯ߍ߯ߍߺ𺱻T߯ߍ߭𭺯߯ߍ߯ߍ߸𸰶T߯߉߸𸰶߯ߋߪ𪬶߸𸰶S߯ߏ߸𸰶׮S߯ߌߺ𺱫vS߯ߖߺ𺧶S߯ߐ߸𸰶߯ߘߺ𺱫߯ߚ߸𸰶׹R߯ߘ߻𻶸߯ߛ߻𻶸߯ߛߵ𵪲Q߯ߕߵ𵪲Q߯ߕߵ𵪲߯ߕߵ𵪲߯ߕ߯𯪬߯ߏ߯𯪳߯ߏ߲𲰩߯ߒ߲𲰩߯ߒ߲𲰩߯ߒ߫𫪭߯ߋ߫𫪭߯ߋ߫𫪭߯ߋ߬𬺫߯ߌ߬𬺫߯ߌ߫𫦯߯ߋ߫𫦯߯ߋ߯ߋߺ𺱫߯ߚ߯ߚ߸߼𼰱߯ߜ߼𼰱߯ߜ߳𳰰߯ߜ߬𬨶߯ߌ߹𹳶߯ߙ߫𫪭߯ߋ߫𫪭߯ߋ߳𳶸߯ߓ߬𬫭߯ߌ߯ߝ߳𳶸߯ߝߺ𺧫߯ߚ߽𽭺߯ߝ߼𼪫߯ߜ߯ߜߺ𺾫߯ߚ߻𻭶߯ߛ߯𯰪߯ߏ߯𯰪߯ߏ߯𯰪߯ߏ߼𼳶߯ߜ߼𼳶߯ߜעI߯ߜ߼𼳶߯ߜ9I߯ߜ߼𼳺߯ߜ߼𼳺߯ߜ߾𾫫߯ߞ߯ߞ߻𻺫߯ߛ߻𻺫߯ߛ߰𰯺߯ߐ߼𼳰߯ߜ߳𳰼߯ߓߪ𪱳߯ߊ߳𳰼߯ߓߪ𪱳߯ߊ߬𬶫߯ߌ߬𬶫+F߯ߌ߳𳦶߯ߓ߳𳦶בE߯ߓ߬𬫾߯ߌ߬𬫾kE߯ߌ߸𸺫߯ߘ߸𸺫߯ߘ߸𸺫׆D߯ߘ߸𸺫߯ߝ߬𬳺dD߯ߌ߹𹾬߯ߙ߹𹾬߯ߙߪ𪱹߯ߊߪ𪱹߯ߊ߯𯳪߯ߏ߯ߏ߯𯳪߯ߏߪ𪱯߯ߊߪ𪱯߯ߊ߬𬼭߯ߌ߬𬼭߯ߌߪ𪱬߯ߊߪ𪱬߯ߊ߯𯪬߯ߏ߯𯪬߯ߏ߯𯪬߯ߏ߯𯪬CPPG߯ߏ߯𯪬߯ߏ߯𯪬߯ߏߺ𬷰*߯ߚ߫𫪭߯ߚ߻𻶬װ~߯ߗ߬𬷰v~߯ߗ߰𼰭߯ߐ߰𼰭}߯ߐח}׀}ש}K}t}}ߞߞߋߌߘ߆֞֋ۆᾬ䳳䩺ᬷԖ䭺䭺<ԋԋԖ䭺䞏䚇Ԍ䭺䭺䭺䭺Ԗ䭺䭺䭺ԆԖ䍐Ԕpߋ߆ߦߌ߬㱰㬯㬯㱰㬯ߜeᚱ䩺ᯭ Ԕ䭺ᘭᘭ䳳Ԗ䳳䳳ԙ䳳䭺Ԍ䳳24ԋ7Ԓ䳳䳳䳳CPPG\ԞួԆ䳳򪬺5Ԉ䭺䭺߆ߌߌይԞᳰ䭺ԚԋԖԒᒾԆԆԆ|ᖱԆዶԋ᭺䭺Ԗ䭺ԋጾԆԛݖ䳳䳳=ߖ߹߹᫷<ԋ䳳䳳ԆCPPG኱Ԛ}ጰԋԞԖԓVԚԚ𐙙𓐐ԐԎԍԍԌԌԌԊԌԍ኱ԋkឲԖ䋖Ԗ䳳䊑䔚䘐ԖbុԐ䩺ኬԖԆԞ䭺ᖫዷԆᒪԆCPPG߹ԑԚԒ䭺Ԗ䭺᫷Ԗ䭺ᱰ᫷ዷᦰʾԖ䭺ԙ孺񸾲񶱫򹶼᭺񸾲񶱫򹶼׭߯߶ߤߢߨ߳߹񾬬𾬬񯾭ዷ𯾭߽߾߰ߤ߰ߤ𷺢𷶲ߤ𷺢ߤ𷺢𷶲ߤ𷺢ߤ𷺢𷶲ߤ𷺢ߤ𷺢ߤ𷺢𷶲𷶲ߋ䬣𷶲߶߰ߪ߽߫ߖ߬ߋ߆񼰲񱰫𱰫߆ߌ߬߶߻߼񯾭ዶ߰߰𯾭ߋߐߐ㬪߹߂𬪯񯾭ዷ𯾭ߋߐߌߙ߹䩺ߌߙ߹ߒ䩺ߋߙ߹䩺ߙ䩺񯾭ዷߙߒߙ񯾭𯾭񯾭ዷ𯾭ߑߺ񯾭ᤆ𯾭X񯾭ᆰ𯾭񯾭ደ𯾭񯾭ቚ𯾭񯾭ዚ𯾭߉ߋ񯾭ጼߑ񯾭ጼ񾱱𾱱񾬬񯾭ዷ𯾭񯾭ደ᫪Ᾰ᫪𯾭ߋ񯾭ዷ𯾭ߛߜ߆ߦ񯾭ᜰ𯾭ߛߜ񯾭ᜰ𯾭ߤ߆ߗ߈ߙߘߌߍ񯾭ጾ𯾭񯾭᜾𯾭N񯾭ᙾ𯾭񯾭ᙾ𯾭0 ߁㬯℁㬯񯾭፺𯾭񯾭፺𯾭񯾭ᙾ񯾭ᙾ𯾭񯾭ᙾ򽶱𯾭񯾭ᙾ𯾭񯾭ᙾ𯾭H񯾭𯾭񯾭ᙾ𯾭ߋm񯾭ዷ𯾭񯾭ጾ𯾭񯾭፺𯾭񯾭፺𯾭񯾭ᙾ𯾭񯾭ᙾߏ񯾭ዷߋߌ߬𯾭CPPG 񯾭ዷ𯾭񯾭ᙾ𯾭񯾭ጼ𯾭񯾭ᑰ𯾭ߏ񯾭ᜰߋߍ߭񯾭񯾭ᙾ𯾭񯾭ᜰ𯾭񯾭ᑰ𯾭ߏ񯾭ዾ𯾭񯾭ᑰ𯾭񯾭ᜰ𯾭񯾭ᜰᱰ𯾭񯾭ᜰᱰߝߌ߬𯾭ԋ񯾭ዷ𯾭񯾭ዷ𯾭񯾭ዷߨߨߨ𯾭ߚߌߓ񯾭ᗶ𯾭/񯾭ጰ䩺䳳𯾭񯾭ጰ𯾭񯾭ጰ𯾭񱰫ሾ䭺ߗ߷ߗ߷𱰫񯾭ᛰ𯾭񯾭ዷ𯾭񯾭ዷ𯾭񯾭ᆰK𯾭񯾭ዷ񯾭ጰ䭺𯾭񯾭ឱ𯾭񯾭ឱ𯾭ՁշGr׺߮ס^VN߯߄߄߄{xߪ߄fc߻߄>;Cߚ߄㹰Ꮽ𹰱㹰ᑺ𹰱ԌZԏ߂߄㫾⭶⎪⏭㽭߯㫾⭶⊯⛰⌺㽭ߑߒߤ𷺢߳ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߖߖ䬣߫߷ߨ߶ߨ߾ߋߌ߰߼߼ߤߤ߳ߪߤߤߤ𷺢𺻢𺻢ߤ𷺢𺻢ߤ𷺢ߑ߈ߎߚߐߜ߆ߑԄ0񯾭ዷ𯾭񯾭ዷ𯾭ߤ𷺢ߤ𷺢񯾭ឳ𯾭񯾭ᤆ𷺢𯾭񯾭ᤆ𷺢𯾭񯾭ዷ𯾭񯾭𯾭񯾭ᆰ񯾭Ᏻᬰ𯾭񯾭ዷ𯾭񯾭ዷ𯾭𯾭񯾭Ћ߻߻𯾭߱񯾭ዷ񯾭ᆰ𯾭𯾭߱񯾭ᆰ𯾭񯾭ᒪ𯾭񯾭ዷ񯾭߈߈񯾭ዷ𯾭񯾭ᆰ𯾭񯾭ᆰ𯾭񯾭ᆰ𯾭𯾭񯾭ዷ𯾭𯾭߱𯾭𯾭߱߫񯾭ᆰ񯾭ᆰ߳ዷዷ߾ᆰCPPG ᆰᆰߤ𷺢ߤ𷺢𷶲ߖ䬣ߤ𷺢𷶲ߤ𷺢𷶲𷺢𷶲ߤ𷺢𷶲𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷺢ߤ𷺢𷶲𷺢ߤ𷺢𷶲𷺢ߤ𷺢𷶲𷺢ߤ𷺢𷶲𷺢ߤ𷺢ߤ𷺢𷺢ߤ𷺢ߤ𷺢ߤ𷺢ߤ𷺢𷺢߾ߤ𷺢𷶲𷺢ߤ𷺢ߤ𷺢𷶲ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢𺻢߱ߤ𷺢𷶲ߋ䬣𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߐ𷶲ߤ𷺢𺻢ߤ𷺢ߤ𷺢𺻢ߤ𷺢𺻢ߤ𷺢𷺢ߤ𷺢𷺢ߤ𷶲ߤ𷶲ߐ𷶲ߐ𷶲ߤ𷺢𺻢𷶲ߐߤ𷺢𷶲ߜߤ𷺢𺻢𷶲ߓߤ𷺢𺻢𷶲ߊߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷶲ߤ𷺢ߤ𷺢ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷺢hߖ𷶲𷺢𷶲ߤ𷺢𷶲𷺢ߤ𷺢𷶲𷶲ߋ𷶲ߤ𷺢𷶲ߋߤ𷺢𯺻𷶲ߛ߻ߤ𷺢𯺻𷶲ߤ𷺢ߤ𷺢𷶲𷶲ߛߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢ߤ𷺢ߤ𷺢ߤ𷺢𷶲𷶲߼ߤ𷺢𷶲𷶲ߤ𷺢𷺢ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢𷺭𷶲ߤ𷺢𷺭𷶲ߤ𷺢𷶲ߤߤ𷺢𷺢𷶲ߤ𷺢𷺭𷶲ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߋ䬣𷶲𷶲ߋ䬣𷶲𷶲ߋ𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷶲ߋ䬣𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲CPPG ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߴߤ𷺢𺻢𷶲𷶲𷶲ߤ𷺢𺻢𷶲𷶲ߤ𷺢𺻢𷶬𷶲ߧߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲߷ߤ𷺢𷶲𷶲߆䳳ߤ𷺢ߖ䬣𷺢ߤ𷺢ߤ𷺢߻ߤ𷺢𷺢߻ߤ𷺢𷺢ߤ𷺢𷺢𷶲ߤ𷺢ߤ𷺢ߖ䬣ߤ𷺢𷶲ߤ𷺢𷺢߫ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ឹ𷺢𷶲𷺢𷶲ߤ𷺢ߤ𷺢ߋߤ𷺢𷺢𺻢ߤ𷺢𺻢𷺢ߤ𷺢𺻢𺻢𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߋ䬣ߤ𷺢𷶲߶߫ߤ𷺢𷶬ߤ𷺢𷶲߰ߤ𷺢ߤ𷺢𷶲ߋ𷶲䫣䫢ߋ𷶲䫣䫢ߋ𷶲䫣䫢ߋ𷶲䫣䫢ߘ𷶲𷶲䫣䫢ߘ𷶲䫣䫢ߌ𷶲𷶲䫣䫢ߌ𷶲䫣䫢ߤ𷺢𷶲ߤ𷺢𷶲񯾭ዷ𯾭񯾭ዷ𯾭𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢ߤ𷺢𷺢𷶲ߤ𷺢ߋ𷺢𷶲ߤ𷺢𺻢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߏ𷶲ᜳߤ𷺢ߤ𷺢𺻢𷶲𷺭ߏ𷶲ߤ𷺢ߤ𷺢𺻢𷶲𷺭Nߤ𷺢𺻢𷶲𷺭𷺢𷶲ߒ𷶲ߋߋᤆ𷺢𺻢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߐ𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢ߐ𷺢ߐ𷺢ߤ𷺢𺻢𷶲ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷶲𺻢ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߋ䬣𷶲ߤ𷺢𷶲ߝ𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢𷺢ߤ𷺢𷺢ߤ𷺢CPPG  ߤ𷺢ߤ𷺢ߤ𷺢ߤ𷺢ߤ𷺢𷺢ߤ𷺢𷺢ߤ𷺢𺻢ߤ𷺢𺻢𷶲ߤ𷺢ߤ𷺢ߖ䬣𷶲ߖ䬣𷶲ߤ𷺢ߴ߭ߤ𷺢𶺻𷶲𷺢𫺻ߤ𷺢𶺻𷶲𷺢ߤ𷺢ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢ߤ𷺢𷺢ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߖ𷺢𺻢𷶲ߤ𷺢𷶲ߤ𷺢ߤߤߤߤߤߤߤߤߋ䬣𷶲ߋ䬣𷶲ߋ䬣𷶲ߐߐߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢ߤ𷺢𷶲ߤ𷺢𷶲𷶲𷺢߫ߤ𷺢𷶲ߤ𷺢𷶲񯾭ᆰߦ𷶲𷶲𯾭߯ߤ𷺢𷶲ߤ𷺢𺻢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲߼߼ߤ𷺢𷶲߱ߔ𷶲ߤ𷺢ߤ𷺢ߖߤ𷺢𺻢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷺢ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𶺻𷶲ߤ𷺢𶺻𷶲ߤ𷺢𶺻𷶲ߤ𷺢𷺢𷺢߆䳳𷺢ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߤ𷺢𷺢𷶲ߋ䬣𷶲ߤ𷺢𷶲ߤ𷺢ߤ𷺢ߤ𷺢𺻢𷶲ߤ𷺢𺻢𷶲ߤ𷺢𺻢𷶲ߤ𷺢𺻢𷶲ߤ𷺢𶺻𷺢𷺢ᐴ𷶲𷶲ߤ𷺢𺻢𷶲ߤ𷺢𺻢𷶲𷶲ߤ𷺢𺻢𷶲𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲ߤ𷺢𷶲𷶲ߤ𷺢𷶲𷶲ߖߋߑ߱Zߞߌߙ߹9ߜߙ߹ߖߋߙ߆ߋߚCPPG ߺߓߚߺߋߚߺߋߚߺߖߋߊߪߤ𷺢ߤ𷺢ߖ𷺢ߖ𷺢𷺢𷺢ߋߞ򷾱㭯߶ߤߖߖ߼߯ߤ𷺢𷶲ߤ𷺢𷶲߯ߐ𷶲ߤ𷺢𺻢𷶲ߒߊߝߞሰߋߐ񭰰񭰰񯾭ᐽߋᚧᑰᚧߘ߾ߤ𷺢ߤ𷺢𷶲߾𷶲߫𷶲߾𷶲߸𷶲߬𷶲߬߬漾߈߽߽尫񼰲ߐ㰫񼰲ߛߛߏ*ߏߚ@򷾶򽾼߹ߖ߆߬򽾼ߨߖ߲򲾶𾭲߬ߖ򲾶ߋ򾧺߳𾧺𽳾𺻸𽳰߽Rߖ߬򷾶𯶼𲺱𲾱߯ߋ򹾬ߗߋ򳶫ߔߋ߷𰩺𼰱𻰰߬ߖ߹򽭰𽭺𼭪߳ߖ򽭰U߆߶𶼺߶bߝᖫ߈񶱯㙐龹Ꮽߜ߫𙐑𶱯㲾㦼𦼰㧼𧼰𲾯ዷ𲺱񷫲ߨ𲺱񷫲𲾯񷫲ߨ𲾯񷫲߬񽰫߼񽰫߲㲾㦼𦼰㧼𧼰𲾯߲߉߉ߚ㬫𬫾㬫𬫰ߞkkߺߺכkߨ׍kߨ׻k׸kߨߨכk׍k]k߬׍kכk~kyk߳ߪfkck߱߱כk׍kk?k߬߬כk׍kjj߹߸jjjj߼߭j߹גj߹ׄj߬׵j߫קj߹Xj߹Jj߬|j߬ljߺj߱j߫jߺ0j߫ j߫i߹i߹i߬זiׂ߬iߺׯi߱\i߫Ji~iri쭻fi뫷i꫷i髷i諷6i竷*i櫷h﫷hhh쫷h뫷םh꫷אh髷׃h諷׶h竷שh櫷\h﫷Oh߳Bh߳sh߰gh߫h߫ h߹h߹3h߬'h߬gߺg߱g߫gߺg߫הg߫׃g߹ײg߹עg߬Rg߬@gߺog߱g߫g߫g߹2g߹$g߬f߬fߺf߱߾߭߭ߺ߹߯߼߼߰߬߼߬ߩ߲߷߬ߺߺ߽߭߬߰߯߬߹߱߬߾߳߯߳߾߫߻߫߾߾߼߲߬߫߶߼߾߲߭߱߫߫߬߳߫߾߽߲߬߶߽߹߶߽߰߭߼߯߫߱ߺߴ߹߶߯߰ߺ߭߼߷߬߬߼߭߬߭߮߭߳߬߬߻ߺ߫ߩ߮߭ߧ߲ߺ߹߼ߪߺ߻߷߸߬߯߫߷ߨ߯߭ߪ߼߼߯߯߯߾߫߭߫߯߬߫߹߯߹߻ߪ߹߼ߺ߹߹߫߹߻߼߶߽߽߫߭߬߳߹߼߰߾߰߶߰߾߼߾߾߳߼߳߳߹߲߲߭߭߭߰߳߶߹߱߰߫߫߰߶߭߶ߺߨ߱ߨ߾߲߭߭߻߾߾߭߯ߩ߭߾߯߻߾߰߯߼߱߼߭߭߼߰ߪ߽߻ߴߺ߾CPPG߾߾߶߽߭߾߰߰߻߻߲߬߰߳߹ߴ߭߰߸߸߰߹߰߰߰߰߭߶߯߯߻߶߫߻߻߳߫߻߳߶߯߰߮߱߫߾߻߾߬߬߰߼߾ߺ߾߻߾߳߯߬߶߱߼߻߳߾߫߬߫߰߬߱߼߰߰߱߯߹߼߽߭߫ߨ߭߭߶ߺ߾߳߱߶߰߻ߺ߻߭߯ߺ߾߯߯߰߱߼߷߫߼߲߰߬߭߱߬߳߱߻߯߼߽߰߰߶߫߰߰߾߯߱ߩ߰߼߼߫߹߼߬߼ߺ߰ߩ߫߶߶߳߼ߪ߲߽߰߫߬߫߬߻߹߳߳ߺ߷߯߾߳߬߾߰߾߾߽߲߰߰߰߻߲߭߰߯ߩ߼ߺ߮߭߭߫ߺ߾ߺ߰߬߶߫߹߾߹߾߱ߺ߱ߺ߭߾߭߼߭߭߾߫߾߶߶߱߶߲߱߱߬߫߬߶߳߰߻߬߻߻߼߼߲߲߬߱߱߫߶߼߳߭߹߾߲߻߶߻߶߻߶߹ߺ߳߯߾߻ߨ߹ߴ߫߫߶߸߸߰߬߯߶߹߼߯߶߲߲߰߶ߨ߲߳߻߸߬߶߰߻߯߫߫߼ߪ߯߯߳ߨߨ߼߼ߨ߼߲߲߬߼߯ߪ߽߬߹߹ߵ߭߭ߨ߭߬߳߳ߩ߰߬߰߱߷ߩ߼ߧ߱ߺ߱߰߹߶߲߫߱߫߯߰߹߱ߺߪ߾ߧ߾߬߶߾߫߶߯߯߱߯߰߼ߨ߼߯߼߾ߺ߽߽߼߯߭߹߮߾߳߯߯߱߼߼߲߳߰߯߫߫߾߽߼߼ߵߵ߹߹߻߼߬߰߱ߺ߯߹߼߼߰ߪ߽߱߶߼߬߻ߺ߯߭߱߯߬߯߻߽߭ߪ߬߱߰ߨ߲߾߹߾߽߭߼߲߾߰߰߻߬߹߲߯߫߭߯߭߶ߨ߻߲߶߶߹ߺ߰߸߬߰߫߯߬߹߬߬߳߯߱߬߹߳߫߯߹߳߹߽߽߭߫߰߱߱߬߶߯߬߬߬߬ߺ߽߽߬߬߬߯߭߭߫߼߰߼߽߱߶߲߽߲߯߰߼߸߲߫߾߲߲߯߰߬߰߬߱߾߳߹߳߹߰߫߰߰߫߻߻߯߯߼߷߯߯߰߶߾߯߹߻߱ߨ߰߭߹߯߳߭߾߰߱߳߻߲߬ߨ߾߼߯߬߼߲߰ߩߩ߶߹߹߬߳߳߰߰߾߸߬߼߬ߺ߹߳߬߹߲߭ߺߺ߰ߪ߼߫߬߾߻߯߫߱߰߾߱߱߱ߨ߬߼߽߷߷߰߷ߙ߷߭߫ߌ߯߻߻ߩ߻߶߶߽߳߫߬߫ߩ߲߬߸߬߭ߴ߯߷߷߶߻߯߬߸߻ߨ߯߻߰߰߰߱߻ߴߴ߷߭߼߯߬߻߼߾MCLD)tads-object/030005dR list/030008 Im'4Jzr[}'dictionary2/030001F:*grammar-production/030002AHNeZ vector/030005#yJz}Ir[xanon-func-ptr/000000int-class-mod/0300003lookuptable/030003 wxyz{|}z'a~+root-object/030004 @ABCDEFGHintrinsic-class/030001+collection/030000TU iterator/0300013wxyzindexed-iterator/030000#character-set/030001-bytearray/030002 L string/030008_s\regex-pattern/030000],stack-frame-desc/030000/&rlstack-frame-ref/030000 weakreflookuptable/030001"lookuptable-iterator/030000!http-server/0300005q{<http-request/030001^3*stringbuffer/0300007R8filename/030000hSv'gs" B file/030003\x2VQpUg0tempfile/030000qg#string-comparator/030000OBJSS   B4K- d% b b>[>m    dv  g 2 " k{ b " { 2)  m;"  Ej m,Mm{ m" @"{ o& G4{ : m" fm  ") L{ i  %"{ ' ))| QgA*1{ +1{ ,1{ -1{ .1{ 0L  M{ 1"{ 41{ 51{ 61{ 8>p /9  R     l9"{ (:&9 8{ q  A ; DC" ];DD"  9E0q(s(t(r(u(FIG HJ{ IJ-" vKsR{ rKILT ; D2M;i [ 8q  8 8 6 9Nw_RL  )Rq Rq V R  ST  ; 2T7; a*^ + L+ *dUC  h 9[   x Z p ΁ v^ u C~1 7^3 uT// k i- h`: `_> |D hW \ki :} u[ mv Z Ts 00 5-7 .A CZK _Z 4`[ <}m }Z [ e v  `> L%{ 9y {y } *W[ 4 \  ~ _ } I 1il o} `3z33i3Z3Z33s32h5~3 jO !Y l_ w ` qnf )g"l&n mp m :e Y"  Pm fj  l m ;q j  l m j i h k  yg   P) j" j1 l8 +"X k[ _ "ma ;ob lc d nj !!q Enz k| 4 h `m 4d  o m n j j k o mE gZ  H tP 3 p p p 82  ^  '_ 2 ^  te c c d" F'. &4 5)8 ?= H eK f#U 2fX ?#] #{^ [{i {j G| C- l q iq  r, 'k Mq g o /+  j2 . ҆ ! t# +t2 2> sG jJ 6uN tY ^ <e sz w{   ~ ~ gf ~6~? aB KbC #aD bE `J EhO aP M   'c lc M] B\ \ {] \ f \ Tx [ ^ [ ]> wD~] vg xn tww l1 z iz    8 ]  r b n Mr Dl r f gg f q qVTz ; 1W0 v ; 0 }/  dZ) ?z r?! 5? [bl(\TB; S]&T V ; > >^&W  0_W i J D[ Dq  fD D D RDp C- C` "ai  9[ :q  V9  =RD %9 <C ; '= /; </" &k*i+ <1 <6 v:b4T; , - Jn( u- G,cTr; )SdT - ; ?=eT ? ; =f # h) ) a$ * & %$ 8$ 0%  H' b& $ ( %& C)  ( &# K$$ $% u$(} , 5'/ 4 #g $* hT; Sie b[ 5qS 5  5 k5 6 5 @5 6 H Q6j-T ; ]1 1$ 1kT; RlZ  ? B 3Aj($  \B  A =@ @ k(mbm(pT; pSq; L. .rT; Rs&Rv 0txux \)Rv4x ]90> ^Aw" e^ { ^x [:{ yI x E\7^80; ^=@ B =C zw{" ^<7?8| 8 b?} U~; I j   9    "^  H kv `c(b(a(e(g($`(;d(Jf() a) b ;] |   -0 9  z QW Z Q X[e(g(`(   <  " 7. 8o K8s * + Z(  * A+ (  +. +> 8,B u,C ([m ~+o R+r (L  62A  b       )U U _ o ٬ . b   \  4  " '| [ m v m3)٬L ~{ 4 I Ȭ p --k ~8 8. u9= 09o 8;m ,  - ,. ^.5 -D .o .))& u-  &    ; TM [ & & < e w &* oZ5    3"   v | 9 : ; > '? | @ B >C E L :N O %T :W  Z  - M S[  ω< P  0 0  1 d0. 3= 3E I4MPo 2P / I1ld0 I J I4 J =J2&6  J F&u W5. 5o 5 W5o zP ]. |o ~ <    Ľ  R& ޽0 3B 9C DE  = - M [ U  ~ ; & ޥ  < E K If z Ǿ _ r  3 &; &@  f 9{ b7  k{   -X 6 U6. 7= 6o 6 % Y& &7 B9 W C Ɠ  ɖ  e  & Õ~   M @ XJ AO bo 7 ,    $: 1 < y    /CZ8_& IJ\  Z U # !< !G @] n  " Z  C M[e  J O o P(  [  ! : G ,/& D7\IJ 5 * g2 5 4 19 2 L5 r4  6  7  d6& 4~ Q6 0 1 0@ 1O 2o }1 j1 3 5 5 2 W1: W7 j7 6 1 3 ,2 .Z    %7    6 74 i/    7    1    6       6  0  "   B U ! U V U U 2V P vU   & V   G ?    1S/& e$ ~#&   6# q" 9  " 5!  #  $  $& !~ #   @ J !O o     # " Y m: p$ {$  #  D d/3 7 iX [  .$ D$  Z$ 4 9$ O$Z X& c  $   w ״9  %< J@  I - X $ /   w " ;XQ&5 IQY w;YQ;ZQ4, $J t GT wQ4. $M tGd wQ-Q  WQ  p wp;Q&c ^_ ` _p;Q;eQ &? dei;iQ5 Q-<<<<<&? deb* >Qx $;sQ;Q4Ff $tGQW Zn$w 8t' %G JQU 4b$tGQ4pk $ t GQ4wm $ t GQ* DQ4~o $t GQ4$t4GQZ Q4"t $tAGQ;($ tJ&GQe A(.=$ 8t %] J G QU* ?Q^ ;Yv ~ $ 8 tY %$ [GQU e A;Ec$ 8/t %r GQU4Fy $teGQ4N$$tmGQ4O} $ t G Q* Q* Q5 VQ&5 Qe  4j$:tzGQ5 Q4o$@tGQ4y $ tG Q5 mQ4 $ tNG*Q 5 Qe  $l8 t1 %G$ G4QU?;Q^ - ${8 t>V %G=QUs * PQ4 $ tIGJQW   $8tTW %GLQUc;Q4 $ tkGQ-3 Jk'(;&(<e5@5 7@QM L {6 zI z z z %W Q{A /{Q* QJ@@4$ t G QP 5  Qk~ 9 I `  5 Q;Q& $ t GEQ4^ $#tGKQ4m$(tGUQ^ t  $ 8  tz.GXQUj4 $ t%G]Q4 $ t&G^Q4$?t6GrQp;Q4 $ t;GwQ * Q  )) W  $ 8 t>GyQ U BQ k9 I .`  4 $ tZGQ 4 $ tGQ6 T ^ + $ 8  tGQUp;Q4  $ tGQ* OQ;<$t&GQ4=$tGQ;Qk9 I ` x 4"E$%tGQ-* M ; Q 5 fQ4/ $QtGQ b* /;1  $9tGQ;Qk9 I <`  47c$DtGQ5 Q 4@ $dtGQ !&> [ O@@"#^ VgMP$^8bt$ GQ#U$Vw % X c9 9&E ," d#,"} d|d,"," eU e't@@( ")-<<<<<*4$tG~Q*+ ,^ eh$t8xt  $ GQ,U-# yQ-d -.4 i${ tG Q./4 j$" tGQ/0;Q01^ -  q$+8t  GQ1U s 3;m$} tGQ34p;!Q45* Q564* 2 Q6 74r$tGQ78;Q895 %8Q9:4 s$tGQ:;W %<uy$8t GQ;U<* ]Q<=W ,?v!$8ut GQ=U >5 ?Q>?-* cZ>!Q?@ Q@A5 JQAB 5 QBCW \n$8t  GQCU- DW aq$8t GQDUEp;'QEFQFGI * b&$  t6G QGH45 bQHF  < I5 dQIJ&5 fIQJ> K5 IQKL4$t GQLM4$tIGQMN5 QNO&> [  A@P^ -$%8tb G3QPUP s Q ?>R;$(td NG7QRT QU09@@V* *QVW X x8 8X&>> [ @@Y?9?>Z&B> -[ g@@[&@@C \;8$st GQ\^4$tGQ^_;&$t Gl Q_`W Qa$8t GQ`Uae ATd$8t  GQaUr b;&$tGQbc4$tGQcd* jQdeT`?>f;!Qfg^ - !Y$8t' GQgUu s h5 !Qhi]?>jW .E$8t) / GQjUke 2Mx$8t $ 3GQkUl4R$tG Qlm;S$t3 GQmn4R $t4 G Qnop;OQop; Qps qp;QQqr* Qrsj?>t* {Qtu4e$t GQuvW f}$8t; A GQvUw;l; $tG Qwx^ m$8tD $ LGQxUye n$8t E $ GQyUz {5 TQ{|W |$8t? 1 GQ|U}-Q}  %Q j /~4$tC G Q~;'Qp;UQz?>4* PQ  C Q  ~W $8tN W GQU2p;XQp;YQ4$tT G-Q^ $8tV \ $ G0QULp;ZQW $8tZ ^ G7QUPBQ}  V Q m  3 R  @  J (    9' ) 6 7 5  mQ"Q* Qp;eQH5 EQ* ^Qp;iQ>>p;sQ>>p;Q&5 Q 44 $ tG Q46 $ tG Q4E $ tG Q4F$tG Q^ G]  $ 8! t J ($ G QU 9W Xk$ 8t J  (GQUBAX`$t  G JQ;o& $$ t G Q4q$tG Q4v$tG QBzz &$( tGQ;OQ& q; s&Q;QQ^ - $ 80 t GQU s -V* BQk9 I ` &5  Q4 $+ t#GQi q' v B  f ĩi l U(n p hq @v 'z V&5 ?Q O4$t1G  Q5 }Q4$t5G" Q5 cQ5 Q* Q* QBA $2 t?  G& QI  C ̴v  9 ?   T( d&  Q * DQ4$5 tKG, Q4 $6 tMG- Q4 $t G. Q;UQ4 $: tYG2 Q*  Q4# $= taG7 Q4% $@ tpG@ Q^ & * $B 8H t  $ GB QUR * ZQB v  mA F R(I P  Q d7/v * ]     /@dzv  G) /  /' &* + A z[ ]  [      Q =   2 -  T   3  &   3     '_  X  # c   5 ]  :    -? \de] k{pv$ ѻ+ +- x8 h<xHP_nb =eyi +n ժqJt _u mwN} ~OǀKR 0I;y Z3J wVN O+2u p n d Pc y$]|( Ф)c-q2 5 l< K;~NMPLS%Vt]W Xz`܇anb g6vhtwibmk nFjojpK'Q  y ^RhI ?kL^ X y 'r i~JrQn4s ir‡H&u   ϴ(p8P!r? fFhGIxL%S Z _`>`n  Ap1tk Xv^JTJxd M  v ڹ%|~l R  O v~ ԣ NhEilo    ڱ  L    $\U' (t)xz*O.z/J4hP9]=j> E jN 'R ۬S UqsX}{Y þ]8fEguj{kws w E:r_I x   آ dqO^_|T@ V j^hP^ =  5N i"gy# *( 0 6-U<X?JC MDH (IhPKJQUOT?zUVXvY^p`CLa!ibqc`i ,j "kz`t uƊvJTD{  }su8Vd| h7E jle^l~\a*]m k  ȭ}I =K"O'( )+Qw.c8>D`IOST <U W\m^tcKdn}u{vx~ U [ UTp ZYl  O "_YN_tUS\_NbJ VP /e {ac%ֆ0 1 )3sj6 .9t:txD AMNrPAV W rX ^}_JTa i ܡjvo *qpnt uvN|LB&u {%Of t q %F d w` z   mx4  } "  K ) 4 ղ3 E   [p F<c  ) (@)    @) ?)   @) ,^ # (|  <  '   T *  E  $# f  sd a t Ӣu Ţ _ !:ZM =4 !: c4W L3 % | >|  | w|Xt du źT 0|@) F# == &    3  -GB Z -  C  3  W 1  ʔ  X !  ( ē  2 8 9 -    3   * &2 Y3 3 36 2F9c = S*<?   U  ?) ()    B & !) 2 ! !x %(-@ # # #    _ 9nm x 98>!n<t Bl)5Pْbvkz+MLܓ.ΑL [  v*l4w   : I͐Va)i[Mj Tvt \.D\țuܘ ' k3×9NNW_M 44 4: $5 ==&  c  3 % &    3 ( " |g nn p oA Vn/BI n g m j j }o eo jL ZkW "l p k g `l mR ji m j" h& ]p' l+ +1 m2 o3 p4^ 8 9  '' g  <'f'k m /P M >. 0$ 0D 'r . 0 / / 0.9 /! &   &   3 4 _ @) Y(@,)  (@,)  W  ,   4 .  / / x  /* m) x M  ! +y@  v #  * A @]  a (     ~      m  $ +  (@,)   -(@) Y ` ] 2 " U w `  " N Q 1  X(   ]  Y( Zk   X  `l/  )  () u | `^ z   ) 4  D   _  E2  Ty ! y 4 d. d$y<) )) B   < ()  )  ) $ 3        ]Y  2  54 N     3   -.i<<<v<<!2 8 8"F7c #@) 3$ %@) &7 9)   3  H') ( H" ")) * +I .  V, QB i  Z*l t 0 C? |K a ‚p    ,&    3 O -) ." xR  3 /I M 3 0 l2 2 j1  32 & 00_(01&Us      3 e z . ؊N <O m a @ J O ` t     ׉ h  A  %  ׌ o   8  p Y 27 a8  6 d9  S8 l837 ba8g  d  Nl84) 6  t ` v   83% 7 \(? 4H z N <  x  Z(1 8 [(7) 8F:c \9& yX\+ ! 8z:@) %;F8c < g  =@ g>" 8  ! ?4 ^  @ $p  v @-(@) g n $2 "A@) ` B()  C  D@) MEvC 2L M nL K> JD Li M[ J M> L J -K[ M K pK RMF&() 9 @  2 {"G8K+ x bxB$ { DZ6i @xHF;c xI4. dJ K % e j  a  /   7 n         z    a   ?    :$ 6 K S Yx y { |   DL/s >@ ~3 `  ~6(*(  [     B+(    @   U   .2(  q(( f  L  | /(      ն1(3(     f  ,(     Ĺ ٹ B  ÷ )  ".(# m$ %% ' * , X- J0 2 4 и6 7 9 -: Z? @ OA K ŻL PN ƼOe5R #S T &W X (\i5^ dc -d h)(j3n x Ƕy yz { /} r~0(-(   s  =  pM E 3(4 )l ) , , 4) ,: %,* #) ". Z c) ( ( ,$ *C )F  T .Y j*j  @k * - . ( ( }- a( + + 9 +N) O 4 Wl P9 +  WQlM 5C 6 7l 5D 6[ I6 G7 u7[ 6 7 5 6  &  R&57v 8U4Y; M = zD I N ܑWz   v   Gܑ J V v J~ 5 &  c  X&W  ’v M Y);   'K L M | [-W l G  | \;  r  .  ͚U s]d  ^c 9T  _.H `&n  ` 3{ a-r   I { bdR c "df + ,,e-    y{ Lf W+ +g-  f  , { h" ^,  -K ic Q 4_ ,jc g,k / } ]l eH  m- R U ] n;{ $b Jc dh |u <x sop Bpn ʨm (y q"d h o r \rs su 2m tM q Xf sg Sl V(s 7u ov v Ju&{ ۫h j ͫw vw wu m Qy xm yz hzn m Q{> m y lw bk Ǧm { |&blv %}+v ~"v - }<R{ < < k ǁ& 8 <8X <[ <" g+<8 =~ + L Bf Y  T r  } ' ' ((v (5& 3((((F(0 . FHf V}  & <=X H[ V y)0*& + :*V ' * x) &9 g' ) (  L+  y*& )~ * % & &@ &J E)O r'o & & o( c* ) 4' &: + #+ n* & ( &/ * * ( + * N& * *- % %V t% 5%W  K_ u "X "   g'   2 r'< C"E) +L dv    y       % &  43_  7 f  +;5_    N  d> & + +   o Ov 0 +x 3+s  4 ~> Q[  5(B k  3 o O v ' 8 \  #' ߙ ! ); G P 8 rJ ` = kq  t + ~: B `C Gc*H UL ܖT \Y \  J2 /dW )t vsP N *  F$ 8m T      ` `g A>-&   i -"  a  C@ ~     M     L  (  F      W y   Y    c  %     h    # G$ % i' X* - 2 6 7 9 $@ 5A K {R S oW RX ^ a d }y jz {   t 6 z ^ 0 AW     n  tw      -    G  e LM  b Ǣ#' b &  Ȥ k 6  (T 2 -&   3 % L  0 #   eX [   4 MH k&'L $" S n' ( [ s R      Y 0    5 ( /  ,    W/ w@ l K( ~S          # G  !  1 D \ /8  a   GT T) -   /    YP 0  } + q  } }   N @NdI     +   }  L G jc  7   l   M ] h  r ; g 4   >     !  7  + 4 oX _    +. }E ~ $ | , GB h b} } ~ Z l jm ~| /~J X R~` } ~ ~ u~` 9}#: ' -   j   #= T# I  e Ֆ  O   R7 7`,  ^ D F E  U r  Y ؚ1 82   e m  L ZO J;L Y ;^ g M #  8O8\9gh5> :>W  Q } 5     l h(  l 0 Q# = +  : |$Q[ D&-  l 7d %&     9 R = 2 s: <`     R  [:   ֨B n   5 W ]    R #1  |^ =  m &' ݪr  e H 1 C:] . Z8O8\8&=   g ݪI  S _ddb3 ~  'dP . @E f D ƀh  _ | X `  k n#  9 B= Z!E+n1 R *  F 5& 2  qq I 3  " V   & 7&( ~/ ~8 E*O/  :^ Q uT /a Zd  : 9  P        ( }  l S' sZ n  {  6  S :    # x  v  B   W    " Q @ BB: %4 4   T /  ;  V p L G 4 w   %    9 S(& w v   F v y B  9  v  0   Q /  M lL QM (MI  HQM' G XH     H WJ I? ) 0 5    6 /  6 F         # ($ % & ( ) * + , - / 1 23 /5 R7 Z?  !  05 B B  D C CB   D Q(J Q 2S f N "<  G > M V . - e.rM _ .X . / x- 6-  <} #=  <f #=r ~< _ 9X 8- 2_ q1 r2X D1 1X [7 7$ ( %= K&| K% # (_ &u x& $ ( (X % $ % & $ % ) ( .' &L #} (i  W[ \ _ e $!g h o ) " ! Q"  f/ 0X =/& X* * *& :6 w6 6e 6-c  [>b >d ?m _ -8X 8&_ -X - 8.X U) ~) %)-# 5i s3q {  _ )g *_ 2 !3X .7& F5 5 5e 5 t=f 5z > > : l:` ";a ;c ;f (l };p      V?a ?f a S  +_ 5, K+ + + ,} , ,k  $o %-u (; t E(x & D(   F( G(- q$(GBV!(M   5-u$ov w C(  '& v .DT~ ; J-T F F u; ~F [F7 iF   s YD5 PEF )E  qE  E |E D D gD *F D D E  'v U) * p Хy ݦM8qcqw u% ^n _o  Po % l % E |E Ym Ql m % m) D* m+ %- %@  C W nX n[ L ` %b %c mg [ j }mk mU8Jqc`qw %e A m  #٧%qwBΧ [8qpcwpw $! *"7 Y ! w  ;Y # Z#?+ Xl ! X Yϼ # X $78!c XX !w ! ] # ;^% V&&$XX w ^'6L, y W| v v [{ v }2 {L xr |z ~} } Iv+ {; vP |W { x lx #|   9yy yK |} | y 'u ~( {w^ 5wq z | [y c| u { U6 }8 L}9 A }I .~K ~O }c 5q y{ v {2 W R , (8wrc75w %*w %+) 4U c d  IU,&D  W W V-W \O 35 T    m % 5 9 Wa |. V/  _0-/ P!_ +1-+ ev S2-" Z+ \Z   4 58qcqw I%6 W78/rc7w %88pcpw $98qc75w $:;/ =_ g X   ; < V> "?)  \ c d eX @#+ W -W WAD Ug} sg|B#& ev e eC4&x$#a$|2$Q$Do3 E&} g|'Fk   G OHwV N ?    1  w [$ M_z ~  m i I^ j  m H t P  0 te Ą4 C ` '  J PKk   LP L# v Rt udt~P $vt u$ u? ]uM+B Z N-?  c dejOK &_ l ^P5 ]QL r I (sRsn1 L 4 ܞx   > 2 x j l = /  S4 Dq+ `q- |q[ ܞ` Rqb nqT x^ _Ua )V;a 2/ '   v w iW" ˕  :  XLp  Yb 3 Z@  y[WS \> y 5 d ƌC > v " q< H] 5 ^g_&   _ l `-S q9 [a;L     ί_  R $bnc c edq5Oq5\8e 1Qfg4 B ψ k  0 e h^ I  V   ] , J ֆ` Å M i#d  ] j? {) %k  ) ' qlE)m-L y1 y 8z4 z0 ]n  54 j  ܜ   C2 H  + q p S  S ` v  / xU lxp o ZpL%  Ƕ  *" b  8  B [  ķ# BE p  & & &E y _ lx Kr \q4 iN)Ƕ N Nb Or\7 / O GR Ho ؐ Q T &  C! u  O \  j3 m sZ ]t^ L( u z& ZR / ױJ  m&  ٰ\ _ l u  rvghA Aw  rx `y ؁{  Zr|Z`}D Ukij j k pj6 Clm k l qk7 Gkr l 9k  j k ke j k k5 k k 5l k" k? 'lC jb kc ck~B Z   iBt    x V<  j\ Ly   P j q p % p p (q: qz 6q p p ] $^c g AB5 5 {) %# ' Z gh HhAL x y-G]bij> ވO  , _Q ܉S h 1q L B   Pe L i *j0 Mj % %Xh 1dq 6qpp l9k` j+B4 ZIP  p qp % p  pe cp o Up p &p4 q+ q- q[ p` qb qI L K a ôp t   L = pk   &  (((&'&&&k'u'{'''4   & & & &'4' ' & ' &&&& / M y M L )f   v r A  u } 9 ?(  B( e ߘ @( A(  >(& ' ' '&%('' & & (&&& (((& ' ' '#  e   (-     f O. ) "d &R   L  ( )  )     X     5 B y p! L   n ] ? @   *      -   @  m  ) )f -  - '~   @ m    ; !  L    d     (-       m  k I W  f -&  D    )*    /* N &  K    w  S  ~ f , -; !  t    3d  }     /&    BN" $= ~* |* y) d  |P  , M p[    b< f ŋ= d   L  r: 9; : ; :O ; l;} 9< ; =  q    q (d dZ f n, ,^  P   B  # 9   Y    * ? qd q f _-   _- m d   &   - B  ) ^ k 8 I9 :M 9 9# >:, 9K W9Y  y)    f .    -f {   9 9   )B  y    _>:9d   ) Z [ [ \ |\ Y Y|\ /R p_  oY- ?U mU X S- RS S  zZ Z ] _S _ _S!-7 +fb_S.S58 f":9 Pq#7 fHA$2 xc. c%47 fB g f #f/ 8g&!f',f(:9 x))*-U+7 Xg. lg,2. $d-T ('f.:9 Pr/7HI0-:, u- Ww0 |v9 ys16* J- 2L  gb+J &cr b9 / !% b( b7 b37 eH &42 rdm  &5))6S7-2 @e. eHr V+8 ~e82 c. d9 ,: 9 q; <kcK x=I  P= z _ u  1X ' >E k w +@ F tGH M :Q ?> w ˑR+@e  H xPB ~v  _ A K L<(A;~G ͓v QIxNB-/ +_͓ +C-G]bijD-G]bijE-G]bijF8M  KX Y g^ ` ԠG) k]bijH-G]bijI9 S l JK-J   )_LK  T Me r7  t cm  ˞ 3T O TX Y ɚ^ ` #aNE  Mz ! èm q hr St mzO } O" n ީuè{m P; c Pm ֤s9(w+x;(|:(Q  n dS&---T-/ D-_- +U5 [QUV0w -  ̰  W&  v X) '3s3w &Y&  U Z - [- ]    \ ˲ ];0 _  JX   T^#B@ Q _J  a`E( a?   ̋O m   v  1o{ | Ԉ C k A*`  ͇ b#@  uc) ٻb  *O d>  H  :O m   )e&   f @ Mg-@ , \h 3b "h  jiZj-" zu 0 c L/ k&q5? Oq5\8l&B => ( Km pnLoX 1(p? @"  ݠ w /F ` q&B m> X tr L [  D\ [ X [ n[ T [  V kV W/ V0 X2 Y4 .V@ ZPAR BYU KT[ nX`4(i Zk `Xl 92p Wu 3U} V NW T WV YsB j j j j~ jF k0 8j[ {jt> {u4> bW bi Vckj cv o  V wB d> xZy8> [ z&FB ôs>  ʴ{Z\| p}&LB > Ѵ ~ 2PX 1Ѵw X = z) Qr /h }f e hu ej[ gS f f g? f@ fi (gt lgvV@} Gf e0>"V `d ?2p a| `/ t4 tr yt    ` ZeZRZ\ U> B k Pk Bk ^kF 4k &k0 lk[ k&; 3= 03Gd ]X 10 \@ ] 4]} K] F E $] Xb} bq5Oq5\8# Xps (p!   { `8 Sm  l  lj lm m~ Yl\ 2+ hT E/ 4  (`} [`# p o@ oT  nF n =n nmq +n Sn {mU mb mu  $e&.X B   & s b } j> P +I q qB Z- Pq< pS pX q^P o i _i h Ci h 'i@ i 5i0 h[ ch)  o o  r2 <7o q5Oq5\8 *od # -  a A2 ad a} %b c/ c / n  8o5 eoe5Oe5\i5. άd3_Zάd3B _άd3 } %b "   {b rc& CI C & @ @ a" ic @  ǐ :  ! t &    R{  CZ ڏA F Ï` r K( |^  J a s w W;x^x eX ]a y^ O ]X aB+4 =L L -LcPw I0   } [  M (w  !  [- g & Re CP  Py6 RO  GOmP q| P0F O O  ! ]O |O  g P  A  ٠& O Oo +^ @w  jX& d P h& (,e P  |$X k$OBJSiRS     0 : ? ON O \ } & Dj P /ga ǒ! !km ! ۇ& 8i P  ?^o - ?~ ?~M:  ߃  a ( l   nn " _yP Hm5 ^ LX~X!  >(Y  gd4f 5  Y _ X ~- e O > `- v@] f@ M`& Xh P C XX!  ]4R *& gte P R-* J v J?} L   ƹ J , HJ S ҺdS W , ]O P) e& d P  vX4 P=F{ 0= @={P, H cY @& b Re  P M  oK`4 :7w w #o #& ?~ ?~  h-: & N?6} >? } &3 qF Fk  Og> -9 Q2oM/ E20 i[ {i a-b = ={q{P{i :kw?w k"5n S" h4& A? A  RlA-  Dt DCP{i1  F^ g{  &; H Hj* tZ . tz Bg Tb S  t2 be j{ d  m yq yD 9eE 8fF bG (eH kJ qM jR bS UZ {^ d (vg h *jn ipzs dnv tw ]ix sy oc{ j f d q b.~ c ){ Ge p u Oi d  m|a y kd j{   t f t z{ @} { C l } u i t es { и j Tq X t Sk %o ~ Ż k Sj j# x u  l! r& |j( ro* d- v/)(1 r=A xC iEG onH &dI eK h Y &U ڄa  5O  E Nԛ Z F!& K@= ;@ " _ 0>#3 ~$ ދ% G;&BL0G  }[ ȸ &k &F ' ?@e5N) `*- lEY \E'Pȸ+ K, q:tvIv- JC  /C.ll  Ƙ 9M  -&= 2 w, w 0K ԘV .\  ^ ,/f |X0 }}}h}1 Gے2 Hԓ3  <4& A  A߀5 T$X C$6 r7I C8 9 ?@N: SX; :wsw<&  UJQ_=& [k  >-G dG G^? 0J@ 8*`A^ 5d B _!%m '!D  OE e jFwG $o #H XI-# RP؞ BPP^J V_ LwM$ V ^N& e ~P O .^Q ]9T& ?d P xU$ ^W q^X bY -\ 4Z *Z Y[$ F^\& d dP ] OV`^ _& ZFYv ` b]4R a ]4R b& {f y P c H`d $U^e& Fd P ,f& f J P g-C IAW 9AM^h&* Im I?i&` K -Kޗj4b > @>{ >q{P&k {* Ƕqe  '  WX, w^ vlK n w { չ   $ ۸ o  N2 < @R C Yl4, C C CP m? ~* eA $P e , w   $չ  %t$  6GM N O W ^ ` h k an )r Wv w {    ox  S  ג Q ׎  . k )  ߈     n @ %  K }& d+ , jC hF yI nh So&0 7 1p&. !D~ DQq-/ M 2Mc5 BMr&h,  K ?s&1 Pp3P?t&U a3u&3 VF FFkv5 D~Z~5~w[  g ]   mp \  ', w ^3 eMxi  j&t  a%m  $)  .% $5 & ~$) 7 $y&m F /FXz7 GpC{&v OO sO0֜|: :vv}- > >g||P  ~9 ͇zM c &l pm e' ݭ1 P  *]5) C  r 4v3 T-; I H H[ r `e I^ ^ _  _ =2 N_3Zo*]< }~}&= @ @ NX&w Aр; A @@N! X> )4$tG Q  P   <" n '" X@ ~A :UB Bp&C A5 A *`&D OȜ N X3 wR &f_& Re oP F *J z` Of -G IGP 9GP > gr ?sb } 5ti rN sh s =2H Je7&I B BEJ ?Z+k& k X ? Z W K Ԉ X- Kj g > x& (Y } L +Pa [h9:&M "G Gב Y@t@N'N Bljr [  _`& (Y [P P Pf$-R bN7 N5 No = f Eb Y  \44 X \94 f Q&  J_G* jJ ͣe 6&  8  ;Zo $ U  M [Q ޠT Y ^ a h lO n o t ev ©w {{ Q   9    r   ߡ  L  ט  ͥ ݞ  d ȫ  ?  r      B * ~& ) W.@}4 5 6lC I 3L  Z Ig9f  V`V +Go " j& "i P  _ !m ! X >| >|Ph hOi #  &j N@ N cW 4k LY K K#PP  O C ; <3= M3B G H  9 #  ^ M& j2a : xMT' ,1    k u *5b v"    T l Ea&m E+ E mm "p P mPr ۙ8  \: #n #t ~0~ ~&u <.y ;x&v 4O  $O֜w A dA4x D D D|P4y eH+ EH UHP3 i 6h4{ vR d_- j d P R Q | z< zy} xxx& ʌ7 2&~ HD 8D3 [& k#k  6  A ANO  X  >?NQ &h, U K & yIŕ iI [o|o # Eg N S] T  Sp 32 pN 52 FSi Q S 6N. P0 mPN N[ 7Oe Qh Ox TS N|J- 3 e P  Q - AE !E 1E X l  ,$X $& I Iϕ @9@NU3 p^ h L B y_X yz_! W " JBg9# X&" i~ % M Y[   ?  1+ . 0 - He   ~, ~< `r ~y ~  H K Zf ʆ + E : < = > 'k"( @@NX) ?9?NY* f94f + !l , q- y@@NZ.o / :[0 _ 1& e P 2  f_4f 6 3m$Q ~!8  ΁9 xB7:& A?d mP < !m != Xh >_?y_@h :A "n "B_ D& d aP E  fZ4f {F V\>4H (Y J ??`?NeK ^M dN g{ O "\P- Ng {  Q (pDp $R ??NiS O#%o 7#Ty_xU :V :wvW @;Xx0xX e E   j ^ Tn )w {   W  ް  C hY k??NsZ =zz[& QJԖ AJ\- Jx J?P  ]y_@^& (Li P _ <bz*z`& < <z|ya ?:vub Dl Sc ??Nd_ #?}|e KØf qg G h- g>>| W>|P  i QĠjy_k <zzl ͅm o Չp ^Zq Khr *QDs ;wwt u ކv `d_w :v|vx xjy Âzy_{ l~~^~| }& qC aC~ D= .Hy ={z&' 6zh o x  ( 2  V t" / Հ' s v l 2   ~P ǂ  O Մ (: 2  B- ( 4 ` b ߃ ׅ DŽ ^  2  vD 8#l a d_& j a  0  I I _ I b _D- PV PP ٍ MQM '  *11 \ 5  K*5.vQ    ŏ& j7 0S k ؂ɂ X& 5M7 /# RIc E X Jyly8y ZX l  *`& fY  T& (Y E  2a 7X X >>N XX! & `l Z & `f P F >>N$ ^"  -  Ɯ(   d Ԝ  7    b4 A ЛF A v, ; 3= 3B SG'H J 3a  ) %Y y_$ UjFY>  Á k -V`y_L- M0 L 5 L  < .YFY & ZiFY ?P $ X^ Z& TK( DK$ w^ );$xw 2 ocY '- k= =e{{PL- u? >}\} e? } J8 'g9f s Hy& I8 I& N yNt& @ $@~~ g_4- Lԙ XL5 hL3 b? 2( ( ')  e' '- E EP)& 4<y $<|y }}}- /B B BG" & d&v *& gZ4& _<y O<y KB ,",",","w," ," " f N 1" ," e ," " ," ;" ," ," ," d" L x ,", ZD. q4 <C FE vpg ?x L? -P jG p DF  q  N A k; d" E% r, g^. r3 ><Q qY] @l < r ? s Drb ߶ Y P r Z  @$' >> DD uFE ] e Tf ei 9G ^ ; c U< < kY#', p: Z[ D\ pn pp @ G s s s +_ h& * <5 N> FX td esl psd| p @A Q E  @ < Mq =tY qZ <[ F` Jpw ;z s `p E q ?] TE C)( G  x< x 2 y5on` ; W[ cb tXu d Y > G X [: cR SS iU j] wi i O C  O \S ?ip Jx{ X Q a N O S  ڹ j m l S Q Rs F@ O )c I S n X h S; V i H! n) H, J. `/ k0 E:1 92 l4 =8 : *J; y`< w>= xz> 9@ mB )nC hG eI *\J  K 89L CO vP  R S QU V 8X @\ Z_ {` 7b ;Cc [e Pf +<h ek /Yl n dOs ku  v lw oy H{ yA| ek} I~ VI  V uP %W 9 }h ^  =  \n :m c[ d R U   N Aw  tR w O e hP f Hu A T H H = NK > [ ` b bJ T e X JT D{ p (= B A u : DX l NQ a w ^l st qo u * l ; l = } K  = K I  \ Bo Qw ' " o# =% jv( u, >-  . t0 Wm4 v5 m9 I: ]; b< B= @d> UW? OSA U 1]4R h Hy ;I\3  \" 63# ( ,, . 4 6g7 -: ; %%= w > f7E F 0N 5.O 2Y R%` sc !h j k %o f6x y   5 J 1 g <7 1 1 6 V5 3  t7 0 t% x5 .  0 m1 0 0 1   1 8 80 "/ ; . y / )/` 0/ 1  '.  C1  /   7/  C . 2   .* 1+ 8, / 4 .16 "; %= .7> +? 51J *2L 1Q "R YdZ @-^ /c 1l)(m t `" =" 4 9(  "   t $ 6 + $ 9  0   - 4 Q  "  - # w ', 81" t% 6' / -0 1 t$37 @ 45K 0T U .V "6X o/Y 7[ ^ 1b ge $g D6j v/m  o Nr vw /y 0 M0 1 5 }/ 7 p0 =  #0 1 *8 . b0     0 R ) (0 1  &  * O* z3 V G b 2  8 }- . 2 y  88  $)    . W  % 0, &1^8 v: D< J0? &L tQ kT W \ )_ a 4b 7h 2n A)q 4r u z n{ w~ 0 ,  / 6 , / 1 6 3 7 [0  7 ~8  w0 , _. ) 0 /  # 0 7 7 v / 3 p8 1 1 ) , 0$ B /   F0 # 1 z _    7 % w+ =/ 2 B ?0! 7* ++ , C. $42 Z13 65 ; sB E F4G eM |1O P R ,S [-X [Tp\ ] _ d e f 4l %m O+n .| 2!} 5 -* + . =! N H2 .P - h4 I, j2ֆ 2 ' . ' / k, 2b 0 . <1 7 0   1 G. 1 9 1 '1 Q. T0 q  h . 1 0 0 >/ 1 F 0 0 E/ X7 Q1 8! $ C.& - 10 *02 X14 C5 ;6 ]7 _19 = ? C &E !F 0G 2J K -+L 10M .P f1X _!Y !Z -] 3` a *j k j!o R$p *q 1v -w z ${ J7 b8 S ! 0 8 0 B ) ;  + m. 6  - ! * q* 5 j 5 &2 ,    p. . 0 t1 L/ {1 .     S/   1  i0 T8 h  1" .& /) *f+ 1#, /- ~0. #22 Z/5&7 /? @ /B 1C F X3LVO R 2T J1Y 0%Z _/\ ` lb ]d 0hk -m _.n /p 2w /y z ~ l0  3 ;V   h% o g  P  z & j- x   l      X _S    + 2 n &s wv w sx | ~   %    r  1 ; nD E 2P R V d 7 f o    xT UW Z[ h % k   & L d    0 A I i   O R ^       c) L ( ]. k !      7  [     D  l  c 4'  &    d    &   2 M "  4 *  6 \ G   -     ?  D  s  y  n    M C  9    ~   ! " ?# $ R% G&' ( *  ͛)|@ ah ,    ƚT  =o  ! ۡ @  B H  h "`n " Rk2 zg{ i Z  ƅ& ;x n;x- 3 xv  ) 7 ) F) T) c) q) ) ) ) )  ) ɵ ) ׵ )  )  ) ) ) 0) B) V) i ) |!) ") #) $) %) &) ʶ') ֶ() ))  *)  +)  ,)  -) ! .) . /) ; 0) H1) U2) b3) o4) |5) 6) 7 8 9 : ˷; ط< = > ?  @o  Ao & Bo 5 Co D !Do U"Eo f#Fo v$Go %Ho &Io 'J (K ɸ)L ظ(*M 2+N <,O F-P P.Q !Z/VNZ0WNX1oXN2YN'34 k  45 U 'Q4 X Y +U =Y ͢ & Y U X yX X ( rY X X bY Vf65 A6;1 3_ ] X   74 o  9  84     494|   B  : A; ' ژ e  | _  9 ݛ Ҝ D     j  W<V w = A>V w s?V w @&|    eA&| ~   B CV w %DV w E    F CGl* { . T   О ^   ܠ  N II N w '   J;K N 5$J 4$P 2$ 3$ 6$KX  LY 6 MI LƬl I& w  O 4N^  KOP NI1 Lw   PF+ \rNw   QB~Nw ϭĭ  R[ c  M ܤK Dw  @ *SB ӯ Rw   TT c  Ȱ @Kw ذ  V; e $ t GQX4 LGww Y;KNG7w @Z\dz[\\L]\ܳ^I LOGOw  ,-$bRc .$GbA/$bTc 0$&pW$b'y 1$-p$bc y 3$MJ'Gb'% b6OBJS(" [ v(8 * O ! ! ! < A" " " L? -P X! m" !!/ N0p(n-Th(; P >QoqMb GO  H[ qE| L Ji( m" E p( I K F 6O.  N  WJ  /E F K N. "O3;W  H  '4 7 s =( 12"l \  ])w |  A5) d &t ' &SaTW  [V_U Z" B E 'L !  BW B'1v B  #Bi' ' 4W )B1 ')-W V1B '3;W ''i- ' =z V7(R<'"z wv L8(  -H*aU Ҳq߲UiqWa  o O ҲY ϫ~ F   !  4  lOBJSl`'##)##P#M##)M#M)#)))#)#)#)###S)####)C###M#3#####I)#*)BLBBFB}BzBqBlB####S)#O z ##)TSRR }# )##K4ِ#;#### #Q)MI))I# )Q )## )+# (GV# )(S ؐI )))#)o;3# ĐQ;T4;#####;#t ##I # # l ####3# ## z# ###"o ) } ## ## ###%o ) q#### ӐS)TI###M)K4#o#)# ####I#I&o )##I I RIQ# #;#ɐP-  #)P)##### #  I o )## #### )####o# ###M)# # $o )###!o ),)Q.o Q  )##3# )#### ## ;#####S  #(# ## ###I I#R###  ##' ## l#I###LF}zql#K4##K4##OR)#K4#7########RR ####3###3###3####### q#3##I #####I  # GK4#3## K43 # M4I#K43##3#  # MRSOBJSFBasktoBtelltoBatoBttoB!,B#G'enter%in%into%into%walkto%goto%walkin%goin%walkinto%gointo%walkinto%gointo%pauseshow%taste%tersebriefscrew%saveLunlock%with;push%in;pull%in;drag%in;move%in;push%into;pull%into;drag%into;move%into;push%into;pull%into;drag%into;move%into;exitsunplug%from;throw%toss%restoredefaults footnotesmediumgive;%offer;%backgobackreturn extinguish%douse%putout%blowout%blow%output%outshow;%climbupgoupwalkup footnotesask%forQa%forQaskforQfrom%aforQfrom%detach% disconnect%lookin%lin%lookinside%linside%give%to;offer%to; plug%in plugin% enterS greet% sayhelloto% talkto%aQsearch%getoutgetoffgetdown disembarkclimboutattack%kill%hit%kick%punch%push%to;move%to;push%under;move%under;cut%tipsontipontypeon%recordrecordonfasten%buckle%buckleup%!show%to;%activate%%turnon%%switchon%%turn%on%switch%on$go$walk+unlock%*turn%toS*twist%toS*rotate%toS-go7  -7  ,remove%2strike%1switch%0topics4follow%7rqL7rq6script6scripton;lookthrough%;lthrough%;lookthru%;lthru%;lookout%;lout%>lookupQ>lupQ>lookforQ>lforQ>findQ>searchforQ>readaboutQ>lookQup>lQup?notifyDunplug%Iunscrew%with;gthrow;%iattach%iconnect%hclimbup%hgoup%hwalkup%jask%ldig%with;ldigin%with;qstandon%qstandin%qstandonto%qstandonto%qstandinto%qstandinto%qclimbon%qclimbonto%qclimbonto%pjumpoff%oenterspush%up;spull%up;sdrag%up;smove%up;uenterSon%uenterSin%uenterSinto%uenterSinto%uenterSwith%ttipsoffttipoffvnovnegativevsaynoytypeS xspcltopicSz deactivate%zturnoff%zswitchoff%zturn%offzswitch%off{plug%~savedefaults}tQ|examine%|inspect%|x%|lookat%|lat%|look%|l%cut%with;throw%at;toss%at;saygoodbyegoodbyesaygood-byegood-byesaygoodbyegoodbyesaybyebyethrowdown%throwd%credits xspcltopicrecordeventsrecordeventsonrecordLverbosethrow%to;toss%to;unfasten%from;unbuckle%from; footnotesoffconsult%onQconsult%aboutQsearch%forQlookupQin%lupQin%lookforQin%lforQin%findQin%searchforQin%readaboutQin%lookQupin%lQupin%replayquietLreplayJnonstopLreplayLreplayquietreplayJnonstopreplayrestorerestartversionunscrew%consult%scriptLpush%through;pull%through;drag%through;move%through;push%thru;pull%thru;drag%thru;move%thru;getoff%getoffof%getdownfrom%jumpoffoutof%getoutof%climboutof%leave%exit%climbdowngodownwalkdowndig%digin%ask%Qattach%to;connect%to;take%from;get%from;take%outof;get%outof;take%off;get%off;take%offof;get%offof;remove%from;lieliedowneat%consume%ask%aboutQa%aboutQpush%outof;pull%outof;drag%outof;move%outof;push%out;pull%out;drag%out;move%out;tell%hintsofffasten%to;buckle%to;lock%with;typeSon%set%push%7  pull%7  drag%7  move%7  sitsitdownyes affirmativesayyesplug%in;plug%into;plug%into;hear%listento%light%with;burn%with;ignite%with;setfireto%with;move%with;throw%tothe7  toss%tothe7  throw%to7  toss%to7  throw%7  toss%7  3 instructionsnotifyonput%under;place%under;set%under;jump%jumpover%exitleavesmellsniffpour%in;pour%into;pour%into;oopsounfasten%unbuckle%turn%with;twist%with;rotate%with;pour%on;pour%onto;pour%onto;recordeventsL"footnoteL "noteL , footnotesfull/iwide/ inventorywide9restoreL8itall8 inventorytallUrecordoffWfeel%Wtouch%Vread%\clean%with;[climbdown%[godown%[walkdown%Zdoff%Ztakeoff%Ztake%off_put%behind;_place%behind;_set%behind;`put%`place%atake%apickup%aget%apick%upelock%dtell%Qgflip%htell%aboutQht%aboutQipush%down;ipull%down;idrag%down;imove%down;msavelfullscorel fullscorelfullkwear%kdon%kputon%kput%onoscriptoffounscriptrexitsK onrexitsK allrexitsYoffrexitsYnonerexitsstatuslineK lookrexitsstatusK lookrexits statuslineK lookrexitsK lookstatuslinerexitsK lookstatusrexitsK look statuslinerexitsastatuslinerexitsastatusrexitsa statuslinerexitsZlookqopen%thintthintsslookunder%slunder%unotifyoffylookylookaroundylylaroundxzxwaitwdrop%wputdown%wsetdown%wput%downwset%down|undo{yell{scream{shout{hollerburn%ignite%setfireto%~move%}walkthrough%}gothrough%}walkthru%}gothru%kiss%jumppull%screw%with;push%press%oopsSoSgoto starboardgotosbquitqgotoportgotoppour%turn%twist%rotate%listenheari inventorytake inventorylieon%liein%liedownon%liedownin%detach%from; disconnect%from;lookbehind%lbehind%againgaskforQaforQclean%talkbreak%ruin%destroy%wreck%climb%board%getin%getinto%getinto%geton%getonto%getonto%climbin%climbinto%climbinto%give%offer%siton%sitin%sitdownon%sitdownin%set%toSput%in;place%in;set%in;put%into;place%into;set%into;put%into;place%into;set%into;put%inside;place%inside;set%inside;put%insideof;place%insideof;set%insideof;attack%with;kill%with;hit%with;kick%with;punch%with;strike%with;sayhellohellosayhallohallosayhihiclose%shut%put%on;place%on;drop%on;set%on;put%onto;place%onto;drop%onto;set%onto;put%onto;place%onto;drop%onto;set%onto;put%upon;place%upon;drop%upon;set%upon;put%downon;drink%quaff%imbibe%light%sleepstandstandupgetupaboutscorestatussmell%sniff%._AAC$:AEA?A3A;HCS.L,asktotelltoatottoMB#B# #  *55.thenandthen,then,andthen;.!*//. <,and,and3AoutofA-all everythingAGoopsoops,oo,QoopsQo2APAP CtheonesthatareinrItheonesthatwereinrItheonesinrI everythingthatisinrIallthatisinrI everythingthatwasinrIallthatwasinrI everythingthatAinrIallthatAinrI everythinginrIallinrItheonesthatareinsiderItheonesthatwereinsiderItheonesinsiderI everythingthatisinsiderIallthatisinsiderI everythingthatwasinsiderIallthatwasinsiderI everythingthatAinsiderIallthatAinsiderI everythinginsiderIallinsiderItheonesthatareinsideofrItheonesthatwereinsideofrItheonesinsideofrI everythingthatisinsideofrIallthatisinsideofrI everythingthatwasinsideofrIallthatwasinsideofrI everythingthatAinsideofrIallthatAinsideofrI everythinginsideofrIallinsideofrItheonesthatareonrItheonesthatwereonrItheonesonrI everythingthatisonrIallthatisonrI everythingthatwasonrIallthatwasonrI everythingthatAonrIallthatAonrI everythingonrIallonrItheonesthatarefromrItheonesthatwerefromrItheonesfromrI everythingthatisfromrIallthatisfromrI everythingthatwasfromrIallthatwasfromrI everythingthatAfromrIallthatAfromrI everythingfromrIallfromrIbothAbothofAcanyA$cA$all2AKall2ofAany2AK2AKany2ofA2ofAallAallofA@A]AthatareinrI]AthatwereinrI]AinrI]AthatareinsiderI]AthatwereinsiderI]AinsiderI]AthatareinsideofrI]AthatwereinsideofrI]AinsideofrI]AthatareonrI]AthatwereonrI]AonrI]AthatarefromrI]AthatwerefromrI]AfromrIathousandthousandathousandandlAthousandandlthousandlYA7:H71ofIG:H71of_theIG<,and,and minehersyourstheirshisitstheAAAAthe A Athe B BDfQllL(AAIAA; A))onethe)the)oneC./A/aboutAK AAnAthatareinrIAnthatareinrIAthatwereinrIAnthatwereinrIAinrIAninrIAthatareinsiderIAnthatareinsiderIAthatwereinsiderIAnthatwereinsiderIAinsiderIAninsiderIAthatareinsideofrIAnthatareinsideofrIAthatwereinsideofrIAnthatwereinsideofrIAinsideofrIAninsideofrIAthatareonrIAnthatareonrIAthatwereonrIAnthatwereonrIAonrIAnonrIAthatarefromrIAnthatarefromrIAthatwerefromrIAnthatwerefromrIAfromrIAnfromrIP$ZAIp RHAS Q5QQ5QoneN\5eQASXDQVrtheAXthe2AK A 2AK=QbQAQ@+AatAdAinAintoAintoA2( (dAonAontoAontoA!,+AB?.@,+AB@?.+AtoA$l%l+r 2AKOtheAKthe2AKv AKl?A I"theonethatisinrI"theonethatwasinrI"theonethatAinrI"theoneinrI"theonethatisinsiderI"theonethatwasinsiderI"theonethatAinsiderI"theoneinsiderI"theonethatisinsideofrI"theonethatwasinsideofrI"theonethatAinsideofrI"theoneinsideofrI"theonethatisonrI"theonethatwasonrI"theonethatAonrI"theoneonrI"theonethatisfromrI"theonethatwasfromrI"theonethatAfromrI"theonefromrI)anythingthatisinrI)onethatisinrI)anythingthatwasinrI)onethatwasinrI)anythingthatAinrI)onethatAinrI)anythinginrI)oneinrI)anythingthatisinsiderI)onethatisinsiderI)anythingthatwasinsiderI)onethatwasinsiderI)anythingthatAinsiderI)onethatAinsiderI)anythinginsiderI)oneinsiderI)anythingthatisinsideofrI)onethatisinsideofrI)anythingthatwasinsideofrI)onethatwasinsideofrI)anythingthatAinsideofrI)onethatAinsideofrI)anythinginsideofrI)oneinsideofrI)anythingthatisonrI)onethatisonrI)anythingthatwasonrI)onethatwasonrI)anythingthatAonrI)onethatAonrI)anythingonrI)oneonrI)anythingthatisfromrI)onethatisfromrI)anythingthatwasfromrI)onethatwasfromrI)anythingthatAfromrI)onethatAfromrI)anythingfromrI)onefromrIWtheA4WtheoneA4Wthe1A4WA4anyofAanyA4oneA41A4anyoneA4any1A4KaA4KanA4 A4JAthroughAthruA B'in9up9uaftaout fore forward f east edownd( starboard(sb)west)w-port-pJ northeastJneR southeastRseY northwestYnwc southwestcswnorthnsouths, A forApBe-Blo+l6zerol$$ARAV%5lll3dH3withI3dH3intoI3dH3intoI3dH3throughI3dH3thruI3dH3forI3dH3toI3dH3ontoI3dH3ontoI3dH3atI3dH3underI3dH3behindI3#A#AI4AAthatisinrIAthatwasinrIAthatAinrIAthatareinrIAthatwereinrIAinrIAthatisinsiderIAthatwasinsiderIAthatAinsiderIAthatareinsiderIAthatwereinsiderIAinsiderIAthatisinsideofrIAthatwasinsideofrIAthatAinsideofrIAthatareinsideofrIAthatwereinsideofrIAinsideofrIAthatisonrIAthatwasonrIAthatAonrIAthatareonrIAthatwereonrIAonrIAthatisfromrIAthatwasfromrIAthatAfromrIAthatarefromrIAthatwerefromrIAfromrI5'@M7tBQA7lA7,1^QonesX;allbut  everythingbut allexcept  everythingexcept allexceptfor  everythingexceptfor anyAbut anyAexcept anyAexceptfor anyAbutnot dAexcept dAexcept dAexceptfor dAexceptfor dAbut dAbut dAbutnot dAbutnot C2CA;GYASTHS1ofIGTHS1of_theIGI4itselfQmeQmyselfYit`himselfaherselfhimheryourself yourselvesyouFthemOAthemself themselvesL=.n'Q+nQ =ones=theonesall everythingallofthemanyanyofthembothbothofthemrb)andb),t)andu)t),u)$AKAlahundredandlghundredlghundredUandlahundred<ghundred-AfromA$AAamillionandlG`millionandl`millionq`millionqamillionS`million-AwithA$A;A3 theirmy*her2hisEitsNtheAANAAPtheAAPAAPtheABPAByour@A3H3I3OBJS2 2$6$' 5(TQYesiU'O!ZX7(8(<( P( R( S(T( [U(V('''1$0$/$.$-$Z( v( OBJS' ' ' '  '' =6i( p( OBJS' D'OBJShPQP Z XY-X >    ' OBJSpt4${   '  U  ' O    toB    a5${   '  U  ' O    toB    a'K @   ('k'''''%((('-S  N .  3 D b ߶  P r  Z  @ $ ' >> DD uF  ]  e f i 9G ^  ;  c    < Y# ' p :  [ \ n p @  G  s  !  s" _ h#  * $ 5 >% X  t&  es  p' d | (  A) Q E*   +  ,  =t- q  <.  F ` / w ;z0 s `p1 E  2  ? 3  C4 )(  G5  x< 6 x 27 5 o8 ;  9 [ b: u d; Y  ><  G  =  [:> cR S? i U @ ] w 'K @)' '  k'  '  '  '  '  %(  (  (  '  - S  N .  3 D b ߶  P r  Z  @ $ ' >> DD uF  ]  e f i 9G ^  ;  c    < Y# ' p :  [ \ n p @  G  s  !  s" _ h#  * $ 5 >% X  t&  es  p' d | (  A) Q E*   +  ,  =t- q  <.  F ` / w ;z0 s `p1 E  2  ? 3  C4 )(  G5  x< 6 x 27 5 o8 ;  9 [ b: u d; Y  ><  G  =  [:> cR S? i U @ ] w ' `WT3*M1AL@R;=SD2&V (Q5?U<PNOcD HDD rDD DD DE D%E EIE .EmE REE wE9 EE EE E9: F :F FgF LF: yFF FF FF FG F >G #GdG IG75 pGG GG GG G35 G$H H.5 +HaH FH  8 jH8 H8 H7 H%8 H8 H'I I!/8 -I"cI HI98 iII IC8 I$I IO8 IJ I[8 JVJ ;J+f8 \J,s8 wJ8 J8 J J J08 J  K KBK 'K.cK HK%K jK-K KK K'K K#L K61L L7TL 9LvL [LL ~LL L8L L:M L(M M4NM 3M/uM ZM)M {MEM MFM MCN M)N NB4 1NgN LNJN wNKq5 N>e5 NGN NHO N97O OIXO =OyO ^O 9 lX = 8 Y : J;Z `< w>[ x > \ @ B ] C h^ e *\_  K ` L COvP4(  -I "c HI 9 iI  I C I $ I O I  I [ J V ;J +f \J ,s wJ  J  J  J 0 J   K B 'K .c HK % jK - K  K ' K # K 61 L 7T 9L v [L  ~L  L 8 L : L ( M 4N! 3M /u" ZM )# {M E$ M F% M C& M )' N B( 1N g) LN J* wN Kq+ N >e, N G- N H. N 97/ O IX0 =O y1 ^O 2  X 3 8 Y 4 J Z `5 w> [ 6 > \ @7 B ] 8 h ^ 9 * _ : K ` ; C v<  R = S QU> V 8? @ \ @ _  {` A b ;CB [ PC +< h D k YlE n dOF k u G v w H y HI yA ekJ I ~ K  VL P %WM 9 }N  ^ O = P \n :mQ c[ R RS U  T N AU  V  wW O eX  P Y uZ A T[ H \  ]  >^ [ `_ b ` Ta e Xb J c  d  Be A uf : g lh Q ai w j s  k  ul * lm ;  n = o } Kp  =  q    r  \s Bo t ' u " o#v =% jvw u , x -  . y 0 Wmz v m{ I : | ; b<} B= @d~ UW ? O A  U [   U     l    O      "_   tՊ U S \_     V P /e {   c% 0  ) 3s 6 . 9 : tx A  r P V W rX  } _J a i j v * pn  u v N| B  &u      %O t  q  % F    `      m x    7  E  d D  x     B   A     ? ܊ >      <   ;    8  9 T 8  h     6 c  5     3 ̋ 2      0 9  /       - D ,  X     *  )    i  '  &  Ќ     $ V  #      ! K 4  H   k   +] p                  荮           8        `   t   B                 ؎  쎮         (        P   d       !         ! m ȏ" # ܏ $  %  & ' (  )  *  +  , f rQ- >.  indQ/ >0  sea1  forQ2 >3  rea4 a outQ5 >6  loo7  8 9 l :  p;  < ify D=  unp> % ?  @ scre %A  withB gC  thD ; %E iF  attaG  H  I nect J h K cl mbL up% M h N go upO hP  waQ  p%R jS  sk%T lU  dig%V wW ; X  Y g in%Z w[ ; \  ] and o^  _  s` d in%a qb  stac  nto%d qe  staf  ng o% h q i tand j o% k q l tand m  o%n qo  limbp on q q r cl mbs onto t q u cl mbv on tow px  juy  ff%z o{  ente| s } pus %~  up; s  pull      g%  up;  s  mov %  up; u  ente S  %    ter S i %  e ter  i t %  e ter  i %    ter S w th% t  tips of   t  ff    n gati v  sa    t pe   xspc pic S z  dea tiva     n off z  sw h off z  tu   ff z  swit   ff {  plug ~  sa  efau  t  |  ex mine |  in ct%  |  x%  |  ook a    l at  |  lo k% |  %  c t%  wit    t w%  at;    tos %  at;   say goo bye   oodb e    good e   ood-  s y ood b  go d ye   say bye   by   thr w own%    thr w %    edit    x ltop c   ord e ts   reco  vent o   r rd L   verb se   row% t6(K @                          ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  [(                           `(K @&                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  a(K @&                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  b(K @&                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  c(K @f&i                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  d(K @&                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  e(K @&0                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  f(K @&                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  g(K @&                        ! " # $ % & -I ' c HI ( 9 iI )  I * C I +  I , O I -  I . [ J / V ;J 0 f \J 1 s wJ 2 J 3  J 4  J 5  J 6  K 7 B 'K 8 c HK 9  jK :  K ;  K <  K =  K > 1 L ? T 9L @ v [L  n(_7-)        q(@                                          !  "  #  $  % & ! -I ' c" HI ( 9# iI ) $ I * C% I + & I , O' I - ( I . [) J / V* ;J 0 f+ \J 1 s, wJ 2 - J 3 . J 4 / J 5 0 J 6 1 K 7 B2 'K 8 c3 HK 9 4 jK : 5 K ; 6 K < 7 K = 8 K > 19 L ? T: 9L @ v; [L < ~L = L 8> L :? L (@ M 4NA 3M /uB ZM )C {M ED M FE M CF M )G N BH 1N gI LN JJ wN KqK N >eL N GM N HN N 97O O IXP =O yQ ^O R  X S 8 Y T J Z `U w> [ V > \ @W B ] X h ^ Y * _ Z K ` [ C v\  R ] S QU^ V 8_ @ \ ` _  {` a b ;Cb [ Pc +< h d k Yle n dOf k u g v w h y Hi yA ekj I ~ k  Vl P %Wm 9 }n  ^ o = p \n :mq c[ r Rs U  t N Au  v  ww O ex  P y uz A T{ H |  }  >~ [ ` b Ter(@                                          !  "  #  $  % & ! -I ' c" HI ( 9# iI ) $ I * C% I + & I , O' I - ( I . [) J / V* ;J 0 f+ \J 1 s, wJ 2 - J 3 . J 4 / J 5 0 J 6 1 K 7 B2 'K 8 c3 HK 9 4 jK : 5 K ; 6 K < 7 K = 8 K > 19 L ? T: 9L @ v; [L < ~L = L 8> L :? L (@ M 4NA 3M /uB ZM )C {M ED M FE M CF M )G N BH 1N gI LN JJ wN KqK N >eL N GM N HN N 97O O IXP =O yQ ^O R  X S 8 Y T J Z `U w> [ V > \ @W B ] X h ^ Y * _ Z K ` [ C v\  R ] S QU^ V 8_ @ \ ` _  {` a b ;Cb [ Pc +< h d k Yle n dOf k u g v w h y Hi yA ekj I ~ k  Vl P %Wm 9 }n  ^ o = p \n :mq c[ r Rs U  t N Au  v  ww O ex  P y uz A T{ H |  }  >~ [ ` b Tes(@                                          !  "  #  $  % & ! -I ' c" HI ( 9# iI ) $ I * C% I + & I , O' I - ( I . [) J / V* ;J 0 f+ \J 1 s, wJ 2 - J 3 . J 4 / J 5 0 J 6 1 K 7 B2 'K 8 c3 HK 9 4 jK : 5 K ; 6 K < 7 K = 8 K > 19 L ? T: 9L @ v; [L < ~L = L 8> L :? L (@ M 4NA 3M /uB ZM )C {M ED M FE M CF M )G N BH 1N gI LN JJ wN KqK N >eL N GM N HN N 97O O IXP =O yQ ^O R  X S 8 Y T J Z `U w> [ V > \ @W B ] X h ^ Y * _ Z K ` [ C v\  R ] S QU^ V 8_ @ \ ` _  {` a b ;Cb [ Pc +< h d k Yle n dOf k u g v w h y Hi yA ekj I ~ k  Vl P %Wm 9 }n  ^ o = p \n :mq c[ r Rs U  t N Au  v  ww O ex  P y uz A T{ H |  }  >~ [ ` b Tet(                                    ! -I  c" HI  9# iI  $ I  C% I  & I  O' I  ( I  [) J  V* ;J f+ \J ! s, wJ " - J # . J $ / J % 0 J & 1 K ' B2 'K ( c3 HK ) 4 jK * 5 K + 6 K , 7 K - 8 K . 19 L / T: 9L 0 v; [L 1 < ~L 2 = L 3 > L 4 ? L 5 (@ M 6 NA 3M 7 uB ZM 8 C {M 9 D M : E M ; F M < )G N = H 1N > gI LN ? J wN @ qK N A eL N B M N C N N D 7O O E XP =O F yQ ^O G R  H S 8 I T J J `U w> K V > L @W B M X h N Y * O Z K P [ C Q v\  R ] S S U^ V T 8_ @ U `  V a b W b [ X Pc +< Y d k Z le n [ f k \ g ] h y ^ i A _ kj I ` k  a l  b m 9 c }n  d o e p n f mq c[ g r h s  i t N j Au  k v l w  m ex  n y o z  p { H q | r } s ~ [ t ` b u v   w b J x c y d z e A { uf : | g } h  ~ i w  j s k l * lm ; n o } p  = q  r  s o t ' u " #v % w u x  y 0 mz v m{ I | ; <} = ~ UW O   U       O    "_   Պ   \_    P e {   % 0  ) s  .  :  r  V W X  } J   j  n  v |   &u       %              D x     A    >     ;    8 h   c  5    2   9  /     , X    )    & Ќ   V  #   K    u(                                    ! -I  c" HI  9# iI  $ I  C% I  & I  O' I  ( I  [) J  V* ;J f+ \J ! s, wJ " - J # . J $ / J % 0 J & 1 K ' B2 'K ( c3 HK ) 4 jK * 5 K + 6 K , 7 K - 8 K . 19 L / T: 9L 0 v; [L 1 < ~L 2 = L 3 > L 4 ? L 5 (@ M 6 NA 3M 7 uB ZM 8 C {M 9 D M : E M ; F M < )G N = H 1N > gI LN ? J wN @ qK N A eL N B M N C N N D 7O O E XP =O F yQ ^O G R  H S 8 I T J J `U w> K V > L @W B M X h N Y * O Z K P [ C Q v\  R ] S S U^ V T 8_ @ U `  V a b W b [ X Pc +< Y d k Z le n [ f k \ g ] h y ^ i A _ kj I ` k  a l  b m 9 c }n  d o e p n f mq c[ g r h s  i t N j Au  k v l w  m ex  n y o z  p { H q | r } s ~ [ t ` b u v   w b J x c y d z e A { uf : | g } h  ~ i w  j s k l * lm ; n o } p  = q  r  s o t ' u " #v % w u x  y 0 mz v m{ I | ; <} = ~ UW O   U       O    "_   Պ   \_    P e {   % 0  ) s  .  :  r  V W X  } J   j  n  v |   &u       %              D x     A    >     ;    8 h   c  5    2   9  /     , X    )    & Ќ   V  #   K    OBJS  3 3 3 Q3 3 3 3 Y3 3 3 3 3 3 3 3 3 3 3 3! 3$ 3& 3/ 32 33 37 3B 3 3OBJSJL;%<W=>?@1''W'KW' W'V'V'V')V'U'U(U(EU(T(T (WT (S (S(S(cS(7S(S(R(R(P(P([P(,P(P(O (O!($(B%(=&(='(=((7)(7*(7+(7,(~7-(u7.(]7/(H70(%71(72(H53(:59(,:(j,;(U,=()>((?((@(|(A(l(B(c(C(2(D(''E('F('G(&Q(iX(Y(\(oh(!j($ k( l(+ m( OBJS' OBJS'CPPGڑRrڥڢڳڮڍڋڌږڕRrڥڢڳڮڐڏڎڒRrڥڢڳڮ?a? ?߶߶߽߶>>%?>>>߼߽߽߳߸򽭰߹װ>׶>׃>׉>U>\>ע>ש>߻߼߰߬߬߶߽߷>c>h>m>>>߲߲߯߯߸򷾶߬&>==߼ߨ߬򽾼==߻߹ס=צ=׭=׳=׺=G=N=V=]=߽ߺ߽߾߾߻߽߬߳a=h===߾߬߯򲾶߲߰󰹹ګڕځڰگڱڲڴڗڭډڌڊډڄڃڌړڵںڷڂڻڼN[7><#<;;Ւ;շ;\;A;f; ;0;::՟:Մ:թ:N:s:(ٯ&x$ٴƉ"Z ZqZ׫1uZu׫ח،0㱰㻰=_])(Er;МЏߙׇЋГЈМǏҐјHf6МЏߙׇЋГЈМќO6МЏߙׇЋГЈМїS5МЏߙׇЋГЈМѕd:МЏߙׇЋГЈМј8МЏߙׇЋГЈЛќ48МЏߙׇЋГЈЛї7МЏߙׇЋГЈЛѕ;МЏߙׇЋГЈЛѕ  <МЏߙׇЋГЈЛҜј=МЏߙׇЋГЈЛҜј=МЏߙׇЋГЈЛҜјS;МЏߙׇЋГЈЛҜј@SAМЏߙׇЋГЈЛҋҜј"<МЏߙׇЋГЈЛјY);МЏߙׇЋГЈЛјP/p?МЏߙׇЋГЈЛј5>МЏߙׇЋГЈЛј;{@МЏߙׇЋГЈЛј5@X=МЏߙׇЋГЈКјD#9МЏߙׇЋГЈГќEe9МЏߙׇЋГЈГїHb.8МЏߙׇЋГЈГѕwvq'4МЏߙׇЋГЈВќ4МЏߙׇЋГЈВїr3МЏߙׇЋГЈВѕ 9МЏߙׇЋГЈВяٕ ;МЏߙׇЋГЈВҜяߖ :МЏߙׇЋГЈБјڢ %7МЏߙׇЋГЈЌќ 7МЏߙׇЋГЈЌї 6МЏߙׇЋГЈЌѕ 4МЏߙׇЋГЈЋќ Y4МЏߙׇЋГЈЫь" r3МЏߙׇЋГЈЊѕq D?МЏߙׇЋГЈЈјu  ыGIF89a !, H*\ȰÇ;GIF89a '+/3CGKS[_cko!, OHx t`a,L @xXp&d*pиP "x4 ;/* * TADS Web UI - style sheet for command window. The command window is * where we display the traditional terminal-style conversation between * player and game, with game output alternating with command-line * input. */ html { border: 0px; margin: 0px; padding: 0px; border-collapse: collapse; } body.cmdwin { border: 0px; border-collapse: collapse; margin: 8px; } div.outputdiv { padding: 0px; margin: 0px; } span.cmdlineActive { white-space: nowrap; } input.cmdline { background: transparent; outline: 0px; border: 0px; margin: 0px; white-space: nowrap; border: 0px solid black; vertical-align: baseline; } .cmdline { /* the following can be controlled by the player via the preferences mechanism - we mention them here for documentation only, since the default is to inherit the body settings */ /* font-family: inherit; */ /* font-size: inherit; */ /* color: inherit; */ /* font-weight: normal; */ /* font-style: normal; */ } .cmdlineOld { white-space: pre; } .cmdlineInterrupted { white-space: pre; color: #707070; } span.cmdFromUser { } div.bottomSpacerDiv { height: 2em; } div#maindiv { margin: 0px; padding: 0px; } div#maindiv_lineht { position: absolute; top: -3em; height: 1.4em; } div#cmdline_probe { position: absolute; top: -3000px; } div.morePrompt { color: #ffffff; border-top: 1px dotted #808080; background: #0000a0; margin: 0px -1em; padding: 4px 2em; }
 
/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Command input window scripts for TADS 3 Web UI. These scripts are for * the command-line window. This window type simulates the traditional * console model, with a mix of text output and command-line input. This * is a "widget" that can be plugged into an iframe in a layout window, so * a single browser window can contain multiple command windows. For * example, you could use one command window for a game transcript, and * another for player-to-player chat. */ // command window initialization function cmdWinInit() { // set up our window and document event handlers addEventHandler(window, "scroll", onWinScroll); addEventHandler(window, "resize", onWinResize); addEventHandler(document, "mouseup", onDocMouseUp); addEventHandler(document, "mousedown", onDocMouseDown); addKeyHandler(document, onDocKey); // initialize the utility package utilInit(); // get the initial state getInitState(); // load preference styles $win().prefsToCSS(null, window); // Set up a workaround for an annoying IE bug. Here's the situation: // when an is dragged and dropped, it gets into this weird half-focus // state where IE thinks it has focus, but Windows *doesn't*. Windows // leaves focus wherever it was before the drag. When a keyboard event // comes in, IE does the JS event bubble according to its internal notion // that the has focus, but Windows actually sends the keystroke to // the old focus control. So what you get is a keystroke going to a // control without any JS events being generated. This is a problem // when the keystroke is Enter and the control is our command line field: // IE simply inserts the newline into the field without telling us about // it. I can't find any good workaround other than a timer, to check // after the fact for this weird situation. Checking a few times a // second shouldn't bog things down too much but should correct the // problem before it's noticeable in the UI. setInterval(function() { var fld = $("cmdline"); if (fld && fld.nodeName == "SPAN") { var txt = fld.innerHTML; if (txt && txt.search(/
|[\r\n]/) >= 0) { setCommandText(fld, getCommandText(fld).replace(/[\r\n]/g, "")); handleEnterKey(fld); } } }, 250); } /* ------------------------------------------------------------------------ */ /* * Process a game event from the server */ function onGameEvent(req, resp) { // check what we have if (xmlHasChild(resp, "say")) { window.closeCommandLine(null, null, false); window.writeText(xmlChildText(resp, "say")); } if (xmlHasChild(resp, "closeInputLine")) { var c = xmlChild(resp, "closeInputLine")[0]; var txt = xmlChildText(c, "text"); var user = xmlChildText(c, "user"); window.closeCommandLine(txt, user, false, null); } if (xmlHasChild(resp, "cancelInputLine")) { window.cancelCommandLine( xpath(resp, "cancelInputLine.reset") == "yes"); } if (xmlHasChild(resp, "inputLine")) { window.openCommandLine(); } if (xmlHasChild(resp, "scrollToBottom")) { scrollToAnchor(); } if (xmlHasChild(resp, "clearWindow")) { window.clearWindow(); } if (xmlHasChild(resp, "morePrompt")) { window.showMorePrompt(); } if (xmlHasChild(resp, "setsize")) { // update our window layout parent.setWinLayout(this, xmlChildText(resp, "setsize")); } if (xmlHasChild(resp, "resize")) { // recalculate our window layout parent.calcLayout(); } } function onGameState(req, resp) { // read the scrollback items and write them as text var hist = []; xpath(resp, "scrollback/sbitem[*]").forEach(function(s) { // write this output text passage window.writeText(xpath(s, "text#text")); // if there's an input line for this block, add it var i = xpath(s, "input"); if (i) { // get the appropriate style for the old command line text var style = xmlHasChild(i, "interrupted") ? "cmdlineInterrupted" : "cmdlineOld"; // add the command line text var t = xmlNodeText(i); window.writeText("" + t.htmlify() + "
"); // save it in our new history list hist.push(t); } }); // if there's no global history list (probably because we're refreshing // the page), use the new history list that we built from the scrollback // information as the active history list if (cmdLineHistory.length == 0) { // trim it to the maximum retention count while (hist.length > cmdLineHistoryLimit) hist.shift(); // install it as the active command history cmdLineHistory = hist; // select the last item cmdLineHistoryIdx = hist.length; } switch (xmlChildText(resp, "mode")) { case "working": break; case "inputLine": window.openCommandLine(); break; case "moreMode": window.showMorePrompt(); break; } } /* ------------------------------------------------------------------------ */ /* * Document event handlers */ function onDocKey(desc) { // if there's an explicit More prompt showing, release it if (morePromptDiv && desc && !desc.modifier) { // set the new scroll anchor scrollAnchor = getObjectRect(morePromptDiv).y - 5; // remove the division $("maindiv").removeChild(morePromptDiv); morePromptDiv = null; // let the server know that the More prompt is done serverRequest("/webui/morePromptDone?window=" + pageName, ServerRequest.Command); // we're done with the key - don't process it further return false; } // do the generic focus processing genericDocKeyFocus(desc); } function onDocMouseDown(ev) { // apply the default processing return true; } function onDocMouseUp(ev) { // apply the default processing return true; } var docFocusThread = null; function onFieldFocus() { if (docFocusThread) { clearTimeout(docFocusThread); docFocusThread = null; } } /* * Process a selection change in the field. We call this from the * 'onselect' event in the field, which fires whenever the selection * changes to a new non-empty selection; and also on mouseup and mousedown * messages (since these can move the cursor, but don't generate 'onselect' * events if the selection range is empty); and also on key events (since * these can also change the selection, and again don't generate 'onselect' * events for empty selections). */ var lastFldSel = null; function onFieldSelect() { // remember the new selection range in the field, if we have one var sel = getSelRangeInEle("cmdline"); if (sel) lastFldSel = sel; } /* * Process a mouse event (mousedown or mouseup) in the field. Mouse clicks * can cause selection changes that don't generate 'onselect' events, so * note the selection after processing each click. */ function onFieldMouse() { // note the new selection after the click has been fully processed setTimeout(onFieldSelect, 0); } /* * Set the default focus for a keyboard event. This moves focus to the * command line if there's an active command line, and focus isn't already * in the command line or some other focus-taking object. */ function setDefaultFocusChild(desc, fromWin) { // if the command line doesn't exist, do nothing var fld = $("cmdline"); if (!fld) return; // if the command line already has focus, do nothing var ae = document.activeElement; if (ae == fld && document.hasFocus && document.hasFocus()) return; // if some other input control has focus, do nothing if (ae && ae != fld && eleKeepsFocus(ae, desc)) return; // remember current selection, since focusing will set a new default // selection in the field var sel = lastFldSel; // note the current text in the field var oldTxt = getCommandText(fld); // set focus after finishing with this key event setTimeout(function() { // move focus to the field try { fld.focus(); } catch (e) { // couldn't set focus - the field must have been deleted return; } // Note if the field's contents have changed since we read them. // IE has some buggy behavior in certain situations where the // field *kind of* loses focus, but not quite: for JS purposes, // IE reports that the field doesn't have focus, but internally // the underlying control actually does have focus. The result // is that the field turns out to process the key even though // IE assures us that it won't. We can detect many of these // cases by checking the before and after field contents; if // the field changed between the key event and the timeout, // the event must have made some change to the field after // all, so we don't want to insert buffered characters // after all. var newTxt = getCommandText(fld); var changed = newTxt != oldTxt; // re-select the original range as of when we last lost focus if (sel && !changed) setSelRangeInEle(fld, sel); // make sure the field is fully in view var rcFld = getObjectRect(fld); var rcDiv = getObjectRect(fld.parentNode); var s = rcDiv.y + rcDiv.height - getWindowRect().height + rcFld.height/2; var scur = getScrollPos(); if (s > scur.y) window.scrollTo(scur.x, s); // process buffered keys readKeyBuf(fld, changed); }, 0); // If it's an Up or Down key, cancel the default behavior, so that // the browser doesn't scroll the window. These keys step through // the command history when a command line is active, so we don't // want to also scroll the window. if (desc && (desc.keyName == "Up" || desc.keyName == "Down")) preventDefault(desc.event); } // Process keys in the top window's keyboard buffer. The buffer captures // keystrokes when there's no focus, so that we can deliver them to the // field when it initially gains focus or regains focus. function readKeyBuf(fld, changed) { // get the key buffer var buf = $win().keybuf; // read each key for (var done = false ; buf.length && !done ; ) { // get the next key - take the oldest key first so that we process // keys in the same order as they were typed var desc = buf.shift(); // if the field changed since we buffered the key, ignore everything // except certain command keys if (changed && desc.keyName != "Enter") continue; // note the current selection in the field var r = getSelRangeInEle(fld) || { start: 0, end: 0 }; // if it's a character key, replace the selection with the // character; otherwise process the special key if (desc.ch && desc.ch.charCodeAt(0) > 27) { // pretend we typed the character replaceSelRange(fld, desc.ch, false); } else switch (desc.keyName) { case "U+0008": // Backspace - delete the selection, or the character to the left. // If nothing's selected, select the character to the left. if (r.start == r.end && r.start > 0) setSelRangeInEle(fld, {start: r.start - 1, end: r.start}); // delete the selection replaceSelRange(fld, "", false); break; case "U+007F": // Delete - delete the selection, or the character to the right. // If nothing's selected, select the character to the right. if (r.start == r.end) setSelRangeInEle(fld, {start: r.start, end: r.start + 1}); // delete the selection replaceSelRange(fld, "", false); break; case "Enter": // process the enter key handleEnterKey(fld); // stop processing buffered keys, since we're no longer active done = true; break; case "Left": // move to the start of the selection, or left one character if (r.start == 0) r = {start: 0, end: 0}; else if (r.start == r.end) r = {start: r.start - 1, end: r.start - 1}; else r = {start: r.start, end: r.start}; setSelRangeInEle(fld, r); break; case "Right": // move to the end of the selection, or right one character if (r.start == r.end) r = {start: r.start + 1, end: r.start + 1}; else r = {start: r.end, end: r.end}; setSelRangeInEle(fld, r); break; default: // try running others through the normal key handler cmdlineKey(desc, fld); break; } } } /* * IE-specific Paste filtering for the command line. We make sure that the * pasted data doesn't contain any newlines or formatted text. */ function onFieldPaste() { // get the clipboard contents var txt = window.clipboardData.getData("Text"); if (txt != null) { // remove any newlines (replace each run of newlines with one space) txt = txt.replace(/[\r\n]+/g, " "); // Manually do the past into the field's current selection. Do // this rather than allowing the native paste to proceed, because // the native paste will insert formatted HTML text. There's not // a way to tell IE to paste the plain text version that it shows // us; it always just pastes the HTML. So we have to simulate the // effect by doing the replacement manually. replaceSelRange("cmdline", txt, true); // stop the rest of the paste return false; } else { // no text available - do the native paste operation instead return true; } } function onWinScroll(ev) { // the user has presumably scrolled the window manually, so set // the new anchor position to the current scroll position (assuming // it's below the old scroll anchor) var y = getScrollPos().y; if (y > scrollAnchor) scrollAnchor = y; // if there's a pending command line, try opening it if (cmdLinePending) maybeOpenCommandLine(); // continue to the default browser processing return true; } function onWinResize(event) { // if there's a pending command line, try opening it - the resize might // have brought the new command line real estate into view maybeOpenCommandLine(); // adjust the command line width adjustCmdlineWidth(); // apply the default processing return true; } /* ------------------------------------------------------------------------ */ /* * Clear the window */ function clearWindow() { // end the current output division outputDiv = null; // If the active element is the body, explicitly move focus there if // possible. This avoids a weird glitch on IE. I think what's going // on is that IE has some residual memory of the focus being in the // division that contained the most recent command line, and removing // that division below causes IE to send the focus off into some // off-screen limbo to follow the removed element. Once focus goes // to limbo, we can't seem to get it back programmatically; the user // has to manually click on the window, which is irritating for the // user. Explicitly establishing focus in the body element before we // remove anything seems to keep focus in the window after the clear. var ae = document.activeElement; if (ae == document.body && ae.focus) ae.focus(); // remove all of the "outputdiv" children of the "maindiv" division var main = $("maindiv"); for (var chi = main.firstChild, nxt = null ; chi ; chi = nxt) { // note the next child, in case we delete this one nxt = chi.nextSibling; // if this is an "outputdiv" division, delete it if (chi.className == "outputdiv") main.removeChild(chi); } } /* ------------------------------------------------------------------------ */ /* * Window text writer */ // current output division, and current source text for the division var outputDiv = null; var outputDivTxt = null; /* * Write text to the window. This adds the text at the end of the current * output division in the document. */ function writeText(txt) { // open an output division if necessary openOutputDiv(); // add the text to the running source for the division outputDivTxt += BrowserInfo.adjustText(txt); // set the text in the division outputDiv.innerHTML = outputDivTxt; } /* * Open an output division */ function openOutputDiv() { // if there's no output division open, create one if (!outputDiv) { // create a new division outputDiv = document.createElement("div"); outputDiv.className = "outputdiv"; // add it at the end of the main division var md = $("maindiv"); md.appendChild(outputDiv); // start a new running division text section outputDivTxt = ''; } } /* ------------------------------------------------------------------------ */ /* * "More" prompt */ function showMorePrompt() { // if there's already a More prompt division, do nothing if (morePromptDiv) return; // create the "More" prompt division var mp = morePromptDiv = document.createElement("div"); mp.className = "morePrompt"; // add it at the end of the main division var main = $("maindiv"); main.appendChild(mp); // fill it in mp.innerHTML = "Press a key to continue..."; // scroll it into view, if possible scrollToAnchor(); } /* ------------------------------------------------------------------------ */ /* * Command line processing */ /* command-line globals */ var scrollAnchor = 0; var cmdLinePending = null; var cmdLineHistory = []; var cmdLineHistoryIdx = 0; var cmdLineHistoryLimit = 20; var cmdLineNum = 1; /* * Get the contents of an open text field */ function getCommandText(fld) { // we use a on some browsers, and an // field on others var txt = fld.nodeName == "SPAN" ? fld.innerText : fld.value; // if it's null, make it an empty string if (txt == null) txt = ""; // return the text return txt; } /* * Set the contents of an open text field */ function setCommandText(fld, txt) { // before setting the text, make sure the field is big enough for it var probe = $("cmdline_probe."); probe.innerHTML = (txt.htmlify() + 'MMMM').replace(/ /g, ' '); if (probe.offsetWidth > fld.offsetWidth) fld.style.width = probe.offsetWidth + "px"; // set the contents, according to the input element type if (fld.nodeName == "SPAN") fld.innerText = txt; else fld.value = txt; // move the cursor to the end of the new text setSelRangeInEle(fld, {start: txt.length, end: txt.length}); } /* * Open a command line. This closes the current output division and adds a * new input editor after it. */ function openCommandLine() { // add an empty span as a wrapper for the command line var id = "cmdline_" + cmdLineNum; writeText(""); cmdLinePending = $(id); // Try opening the new command line immediately, if possible. (Whether // we can depends on the scrolling situation.) maybeOpenCommandLine(); // scroll down as far as possible scrollToAnchor(); } /* * Scroll down as far as possible to bring new text into view. This * scrolls down to the bottom of the document, or to the point where the * "scroll anchor" just goes out of view, whichever distance is shorter. * The scroll anchor is the last vertical position that we can safely * assume the user has seen: it's the bottom of the document at the time of * the last pause for user interaction, such as a command line input or key * input wait. */ var inMoreMode = false, morePromptDiv = null; function scrollToAnchor() { // get the main division, and figure its bottom position in doc coords var main = $("maindiv"); var rcMain = getObjectRect(main); // get the window size and current scroll position var rcWin = getWindowRect(); var curScroll = getScrollPos(); // the most we'd want to scroll would be far enough to bring the bottom // of the main division into view, so start with that var s = rcMain.y + rcMain.height - rcWin.height; // if that's beyond the anchor position, only scroll as far as the anchor if (s > scrollAnchor) s = scrollAnchor; // if we're not already scrolled past the target, scroll there now if (s > curScroll.y) window.scrollTo(curScroll.x, s); } /* * Try opening the command line for editing. This creates the actual * text field that we use to present the input editor in the UI. */ function maybeOpenCommandLine() { // IE seems to trigger some events recursively at surprising times. // In particular, IE seems to do queued processing on virtually any // system call, even seemingly non-blocking things like object size // retrievals, and this can trigger event calls. Since this routine // is called from a number of events, that means that we can be // invoked recursively seemingly at random. To avoid reentrancy // problems, privatize our globals while we're working. var cl = cmdLinePending; cmdLinePending = null; // if there's no command line pending, there's nothing to do if (!cl) return; // Check to see if the new command line is in view. We've set up // the wrapper division for the new command line at the bottom of // the stream division, so we simply have to see if the bottom of // division is in view. var rcMain = getObjectRect($("maindiv")); // figure the distance from the scroll anchor to the bottom of the div var dist = rcMain.y + rcMain.height - scrollAnchor; // if that doesn't fit in the window, don't open the command line yet var rcWin = getWindowRect(); if (dist > rcWin.height) { // set a More prompt in the status line (this doesn't work on most // newer browsers, and there's no workaround, since most browsers // now prohibit javascript from tampering with the status line for // security reasons; but just in case) window.status = "*** More ***"; // note that More mode was activated inMoreMode = true; // restore the pending command line and return cmdLinePending = cl; return; } // if we activated More mode in the past, turn it off if (inMoreMode) { // remove any More prompt window.status = ""; // Consider any keys that were typed up to this point to have been // consumed by the More mode, so clear the keystroke buffer. $win().keybuf = []; // no longer in More mode inMoreMode = false; } // hide the bottom spacer while the command line is open, so that // the command line is at the very bottom of the window // $("bottomSpacerDiv").style.display = "none"; // figure the font - use the computed font of the enclosing span var font = "font: " + getStyle(cl, "font").replace(/'/g, '') + ";"; // set up the event handlers for the input field var events = "onkeypress=\"javascript:return $kp(event, cmdlineKey, this);\" " + "onkeydown=\"javascript:return $kd(event, cmdlineKey, this);\" " + "onmousedown=\"javascript:onFieldMouse();return true;\" " + "onmouseup=\"javascript:adjustCmdlineWidth();onFieldMouse();" + "return true;\" " + "ondrop=\"javascript:adjustCmdlineWidth();return true;\" " + "onpaste=\"javascript:adjustCmdlineWidth();return true;\" " + "onfocus=\"javascript:onFieldFocus();\" "; // fill in the command line wrapper with the input field if (BrowserInfo.ie) { // IE seems impossibly fiddly with text field alignment; as far as // I can tell it's simply not possible to get a field to line up // seamlessly with the surrounding text for arbitrary font size. // We could always make manual adjustments, but I can't even figure // out a way to predict what the adjustment needs to be - it seems // to vary across different fonts and sizes. Fortunately, there's // the contentEditable property, which lets us make an ordinary SPAN // into an editable field. This actually works on most of the other // browsers as well, but it has some other quirks of its own, so // we'll only use it where we absolutely have to (so far just IE). // Add the IE-specific "before deactivate" event, to capture the // selection range before we lose focus. Also intercept Paste // events, so that we can limit pasting to single-line unformatted // text. events += "onbeforedeactivate=\"" + "javascript:onFieldSelect();return true;\" " + "onpaste=\"javascript:return onFieldPaste();\" "; // create the content-editable span cl.innerHTML = ""; } else { // some browsers need some slight tweaks to the positioning // to get everything aligned properly var hacks = ""; if (BrowserInfo.safari || BrowserInfo.opera) hacks = "margin: -1px 0 0 -1px;"; else if (BrowserInfo.firefox && BrowserInfo.firefox >= 3.05) hacks = "position: relative; left: -1px;"; // We're using an , so monitor selection changes // directly on the input field. Also, adjust the field width as // we edit it to keep it large enough for the text. events += "onselect=\"javascript:onFieldSelect();\" " + "oninput=\"javascript:adjustCmdlineWidth();" + "return true;\" "; // all of the other browsers seem to get the alignment fine with // a regular text field, so use a regular text field cl.innerHTML = ""; } // set focus on the new input field - do this in a timeout to give // the browser a chance to process events related to creating the // new objects, to make sure the object exists in the UI before we // try to give it the focus docFocusThread = setTimeout(function() { // if we have an interrupted command line (such as from a timeout // interruption), restore the saved state if (interruptedCommandLine) { // get the field var fld = $("cmdline"); // restore the editing state setCommandText(fld, interruptedCommandLine.text); setSelRangeInEle(fld, interruptedCommandLine.sel); // done with the interrupted editing state interruptedCommandLine = null; } // set the default focus $win().setDefaultFocus(null, null); // We only open the command line when it's in view, so it must // be in view. Reset the scroll anchor to the new command line // on the assumption that the user has had a chance to view // everything up to this point. scrollAnchor = getObjectRect("cmdline").y - 5; }, 0); // set the initial field width adjustCmdlineWidth(); // add a history item for the active command line cmdLineHistory.push(""); } /* * Explicitly cancel the command line. This is used to interrupt a command * line in progress, usually due to a timeout. If 'reset' is true, clear * the input line state, so that the next new command line starts out * empty. Otherwise, save the state from the current command line editing * session, so that we can restore it on the next open. */ var interruptedCommandLine = null; function cancelCommandLine(reset) { // get the active command field; if there isn't an open command line, // there's nothing we need to do var fld = $("cmdline"); if (!fld) return; // close the command line, using the interrupted style closeCommandLine(null, null, false, "cmdlineInterrupted"); // remove the placeholder top history item cmdLineHistory.pop(); } /* * Close the command line. If 'style' is specified, it's the CSS class * name to use to show the static text of the now-closed command line. */ function closeCommandLine(txt, user, reset, style) { // if the style is null, use the default if (!style) style = "cmdlineOld"; // get the text input field var fld = $("cmdline"); var div = null; var height = ""; if (fld) { // if we're not resetting, save away the current editing info var s; if (reset) s = null; else { s = { text: getCommandText(fld), sel: getSelRangeInEle(fld), lastSel: lastFldSel }; } // store the saved command line info interruptedCommandLine = s; // get its contents, if the caller didn't specify overriding text if (txt == null) txt = getCommandText(fld); // Note the actual height of the input field. This allows us to // set a matching height for the static text span that will replace // the field now that we're closing the editor. An input field // doesn't usually have the exactly same native height as a regular // text span with the same contents, so this explicit setting is // needed to keep the line spacing from visibly changing when we // change the command line from an input field to a . height = "height:" + getObjectRect(fld).height + "px"; // remove focus from the field fld.blur(); // forget the last selection lastFldSel = null; // the command line division is the field's parent div = fld.parentNode; } else if (cmdLinePending) { // We have a pending command line that we never activated. This // happens when there's unviewed text past the bottom of the // window; we wait to activate the command line until the user // scrolls its location into view. We do have a placeholder // division in this case, though, so we can just plug in the // entered command there - that's where it would ultimately have // gone anyway, it's just that we skip the entire creation and // deletion of the field within the division. div = cmdLinePending; // there's no longer a pending command line cmdLinePending = null; } else { // no command line is open or pending - there's nothing to close return; } // IE doesn't respect the "white-space: pre" style on inline elements, // so to work around this we change spaces into  's. var spanTxt = txt.htmlify(); if (BrowserInfo.ie) spanTxt = spanTxt.replace(/ /g, ' '); // if a user name was provided add it var userPrefix = ""; if (user) userPrefix = "[" + user + "] "; // replace the field with the text that was entered var lastCmdLineID = "closedCmdLine" + (cmdLineNum++); div.innerHTML = "" + userPrefix + spanTxt + ""; // remember the last command line object var lastCmdLine = $(lastCmdLineID); // start a new output division outputDiv = null; // return the command line text return txt; } /* * Set the command line font */ function setCommandLineFont() { // get the active input field var fld = $("cmdline"); // if we have a field, set its font if (fld) fld.style.font = getStyle(fld.parentNode, "font"); } /* * Adjust the command-line width. We call this automatically as the user * types, to keep the field as least as wide as the text it contains. This * ensures that we don't scroll the contents of the input field, but rather * scroll the whole window. Scrolling the input field's contents is * confusing because we try to make the field fade into the background as * much as possible: we don't show a border or separate background color. * We want it to look like an input line in a regular terminal window, * which doesn't look any different from the surrounding output text. */ function adjustCmdlineWidth() { // give the browser a few moments to digest events, then adjust the width // of the field so that it's large enough for the field's contents setTimeout(function() { // get the field var fld = $("cmdline"); if (!fld) return; // get the "probe" element, for measuring the font size var probe = $("cmdline_probe."); // set the probe's font and contents to match the real input's, // with a little extra space to spare for the cursor at the end var txt = getCommandText(fld); probe.innerHTML = (txt.htmlify() + 'MMMM').replace(/ /g, ' '); // set the new width fld.style.width = probe.offsetWidth + "px"; }, 10); } // Common keypress/keydown handler. function cmdlineKey(desc, fld) { // Set the scroll anchor to the command line field. Since the user // is typing into the field, it's safe to assume they've had a chance // to read everything above this point in the document. (Take off // a few pixels for a little margin of visual context.) scrollAnchor = getObjectRect(fld).y - 5; // Check for a selection change after processing the key through // the default handling. setTimeout(onFieldSelect, 0); // check for a change in the field width adjustCmdlineWidth(); // see what we have switch (desc.keyName) { case "Up": // up arrow // recall the previous history item if (cmdLineHistoryIdx > 0) { // if we're at the top of the list, save the current text if (cmdLineHistoryIdx + 1 == cmdLineHistory.length) cmdLineHistory[cmdLineHistoryIdx] = getCommandText(fld); // recall the previous item setCommandText(fld, cmdLineHistory[--cmdLineHistoryIdx]); } // we've fully handled the event return false; case "Down": // down arrow // recall the next history item if (cmdLineHistoryIdx + 1 < cmdLineHistory.length) setCommandText(fld, cmdLineHistory[++cmdLineHistoryIdx]); // we've fully handled the event return false; case "PageUp": // Page Up case "PageDown": // Page Down // Input fields sometimes capture the page up/down keys for // cursor navigation. For our single-line inputs, that's // not desirable; instead, treat it as a document scroll event. // get the direction var dir = (desc.keyName == "PageUp" ? -1 : 1); // get the window height, for the scroll distance var wrc = getWindowRect(); // scroll by a little less than a page var dist = wrc.height; if (dist > 20) dist -= 20; // do the scrolling window.scrollBy(0, dist * dir); // handled return false; case "U+001B": // Escape // Clear the command line. Do this after finishing with the // keystroke: in Firefox, at least, Escape seems to be sort of // a super-Undo that restores the last thing that was in the // field, so the browser undoes our work here if we do it in-line. // This seems to happen even with the preventDefault() below, so // I'm not sure where the value reset is coming from, but it's // someplace we can't seem to override. Clearing the field in a // timeout lets us take care of it after the browser has finished // the un-overridable super-Undo. setTimeout(function() { setCommandText(fld, ""); }, 0); // prevent the default handling for Escape - this is a command key // in many browsers meaning "cancel page load", which can also // cancel in-progress ajax requests preventDefault(desc.event); // handled return false; case "U+0055": // 'U' // check for Ctrl+U if (desc.ctrlKey) { // clear the line setCommandText(fld, ""); // on IE, block the Ctrl+U underline handling if (BrowserInfo.ie) { preventDefault(desc.event); return false; } } // it's not Ctrl+U, so we don't handle it here return true; case "U+0042": // 'B' case "U+0049": // 'I' case "U+004B": // 'K' // When editing a 'contentEditable' span in IE, IE enables the // traditional rich-text formatting keys: Ctrl+B for Bold, // Ctrl+I for italic, Ctrl+U for underline, Ctrl+K for Hyperlink. // We don't want to allow formatted text, so disable these for IE. if (BrowserInfo.ie && desc.ctrlKey && document.activeElement && document.activeElement.nodeName == "SPAN" && document.activeElement.isContentEditable) { // in a content-editable span - suppress formatting command keys preventDefault(desc.event); return false; } // otherwise, apply the default handling return true; case "Enter": handleEnterKey(fld); // no need for further handling return false; default: // we don't handle anything else, so use the default behavior return true; } } // handle (or simulate) an enter key function handleEnterKey() { // immediately scroll down by the height of the field, and to the left // edge var rc = getObjectRect("cmdline"); var curScroll = getScrollPos(); window.scrollBy(-curScroll.x, rc.height); // close the current command line var txt = closeCommandLine(null, null, true); // If they selected and entered a past history item without changing // anything, stay at the current position in the history. Otherwise, // add the new item to the end of the history and reset the index // to the latest item. if (cmdLineHistoryIdx + 1 < cmdLineHistory.length && cmdLineHistory[cmdLineHistoryIdx] == txt) { // We've selected and entered a past item without making any // changes. The temporary last history item we added for the // new command line was never committed, so delete it. cmdLineHistory.pop(); } else if (txt == "") { // don't add empty lines to the history cmdLineHistory.pop(); } else { // We've entered a new item. Save it as the last history item. // There's already an array item for it, since we added a temporary // item for the new command line when we opened it - so we just // need to set the value in the last item. cmdLineHistory[cmdLineHistory.length-1] = txt; // trim the history if it's getting too long if (cmdLineHistory.length > cmdLineHistoryLimit) cmdLineHistory.shift(); } // put the history cursor at the end of the list cmdLineHistoryIdx = cmdLineHistory.length; // process the command through the registered callback if (window.onCloseCommandLine) window.onCloseCommandLine(txt); // send the text to the server serverRequest("/webui/inputLine?txt=" + encodeURIComponent(txt) + "&window=" + pageName, ServerRequest.Command); } // handle a command hyperlink function gamehref(ev, href, win, ele) { // if we have a window path, ask our parent to find the window if (win) { // send it to the given window, if we can find it if (win = $win(win)) return win.gamehref(ev, href, null, ele); else return false; } // there's no path, so it's directed to us - put it in our command buffer var fld = $("cmdline"); if (fld) { // set the text in the field setCommandText(fld, href); // enter the command handleEnterKey(); // we've handled the event, so don't bubble it up return false; } else { // there's no active command line, so send the click to the main // window, in case the server wants it as an href event $win().clickToServer(ev, href, ele); } // bubble the event, but prevent the default browser handling preventDefault(getEvent(ev)); return true; } GIF89af!,AH*\ȐaQĊ pF5r8$B$M&DRƖ a6I͆;body { font: 9pt/12pt Verdana, Arial, Helvetica, Sans-serif; background: #0000ff; color: #ffffff; } div#debugLogDiv { } a:link { color: #ffa0ff; } TADS Debug Log
/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Debug log window scripts for TADS 3 Web UI. */ function debugLogInit() { // initialize the utilities package utilInit(); // tell our parent window that we're done loading if (window.opener) window.opener.debugLogLoaded.fire(); } function debugLogPrint(msg) { // add the message to the debug log division var d = $("debugLogDiv"); var s = document.createElement("SPAN"); s.innerHTML = msg; d.appendChild(s); // scroll the window to the bottom var ht = d.offsetHeight; var wrc = getWindowRect(); var y = ht - wrc.height; if (y > 0) window.scrollTo(0, y); } /* * Default display preference settings for this game. Edit this file if * you want to customize the default appearance. */ var defaultPrefs = { // Main font. This is the font used throughout the transcript // window and status line. If you list multiple fonts with commas, // the browser will go through the list looking for one that's // installed on the run-time system. mainFont: "Georgia, Times New Roman, Times, New York, serif", mainFontSize: "10pt", mainColor: "#000000", mainLinkColor: "#0000ff", mainLinkHover: "#ff00ff", mainLinkActive: "#ff0000", // main window background color mainBkg: "#ffffff", // Command-line font. Leave these null to inherit the main font // attributes for the command line. cmdFont: null, cmdFontSize: null, cmdColor: null, cmdBold: "bold", cmdItalic: "normal", // Status line font and background colors. Leave these null to inherit // the main font attributes. statusFont: null, statusFontSize: null, statusColor: "#000000", statusBkg: "#c0c0c0", statusLinkColor: "#0000ff", statusLinkHover: "#ff00ff", statusLinkActive: "#ff0000", // Generic fonts. These correspond to the HTML TADS "parameterized" // fonts - TADS-serif, TADS-sans, etc. Null inherits the main font // settings. serifFont: "Georgia, Times New Roman, Times, New York, serif", serifFontSize: null, sansFont: "Verdana, Arial, Helvetica, sans-serif", sansFontSize: null, scriptFont: "Comic Sans MS, Zapf Chancery, ZapfChancery, cursive", scriptFontSize: null, ttFont: "Courier New, Courier, monospace", ttFontSize: null }; GIF89a*1-4*4.5.5-74<3:0:3<5>9<:=F8@>H8A:C8A?G>I=@AKAKCLCMLUQXADCFAIGOJMFRMTCQGRIQJTNUITMWNZNXPYPZQ]T]T_Q^S]YdDGHKHKJRHQMPLTNYTWP[PXR]R]T^Y\\_WaV`WaVcUbWdXbYcZfXe\f]g_i_k_ibnbicjamgneqgphsiwkykynyozs~t|t}u~vwv{}ӀҁӃՄԆԉՍ׏אؒڕٜޠޢޤਭય⭱!,%:&2t3`dEG! b= ) j0*n#2L& Z1# (HxL ,PA Qx;GIF89a#(!#$&!#**,.,.22703759;=?ADFHJLNLRUWY]_bhjlpr}!k,׬IjAeJ%CqcEg֨F-VHqrȌVC ,U@IRĤO)Xɒ;hP .ǃF\ 1E(_P⅃84qPD *T 9 ; !?898::;:<<>46<=>??@?A>@deHIFHKKXX[[Y[[[]^@A@BBCBDLMHJKLMNIJMNPQQR[\\][\WXZ[]_^`ijstef`acdbdjkllfgcddeghhjklnnopxx}~uvyz}}kllmlnrs~̓̓Ά͉юӒßĦªǪŵüƸȷȼԯҷлѾԹܼ!,(( Hp2[#BL!EJ("ODbV^61!KZ(@Isf% K9d֬I{reB^;J#ׯ6&TCjQҤ@`7nV,bgb ZւMɘA\FRl* qԬ&ߙAb !˕+TRi>~, ɹ~ӕOeR̤@>+ܽy Lƻ DC7n}* ELv=0W|!#@IAQ"MP "YTLlBu!qzHQ 2nDu t@kK`VaTGBr|Du Hs'qs<01?%BQ /9 2S ɬuJl"I̿!D(rG# CNIL"l(I4 -7 $S4;GIF89a((KLJJKJLMKLNPPS%L%N!N#O N O R"Q#Q"Q%R'S'V&T(U)V)Y+Y/Y,\"V%V'Z(Y)\.Z0Z3\6]6^1\1_5_/`>c:a;eiAgBgChEiFhGkNoHlKmLn@iEjGoBnAoHlOqOqNrQrWvUtVx^{_}StUuPyVxV~XwZw\z_XyZzf~a|d}NxU_kiaddjmkuuupsqtx}{~VZ[inutwΐ–͓ٟۢǤ¦Ţ̧̭̦ճͱϴθƺɳѿŶڶںЭ!,(( H`?h"4Ȱ!ChJ-4i38#h56jЕ)0`P!U Y%rpɳ'?(%&i> D9p6G_sD},&4d]|0Rz{x!'>O1Ǐ5癏 &$g >˗}m0ug#!7"T5w7o=xHևoODQF0a#|k^:]zz73 HAFQ64H|V=:30 sxEDm41@߈C9T,40331579>\T^khoqz{xqfdCEEKKIBEPT]¿¾Ľľ¾ƎȄØÖŘǘǜȓˁɊʊநìĬť·³¾ÿ!,(( H@vʱ3ȰÂ%ŋ&TƏ+-/H;GIF89a `!, 8@AXHPB> qÂJ̸blj5f92@ȅ'MTY2˕/[œ)Ș7iQgO} RhQ$w*thӣO2*TRj5+ׯ^â4*eԱHў5Um[]ƅ Vn]eq;]y ÆFX`DžK~<92˖3sg{=WtiMFMѯO^=5۲qm;ݿ{.8c!>Κr弝G\zuĭgǞtoX͹_=yݛ>߯ X  ) (.XFHrv(#H&^@;GIF89adljtrx%]vffc`bddfade &&+05 ?">(<t?LLTTKpSUcU@IN]܀݄؉ٗڙ /!̑GԖ_ԡdݯ}fzFPiz6Odpޯ⼘ĉ!h,d,xE #J@[dL8LʐQ/d#ʆg\lË\VK3 ] $GCX2e%E"xT">$9b$(@,&N@GNTC (;tQ<@@B ,0PϞA+z#XQVujס]f=[umԷKtϿG&;<6|haC't㶭kΝvݥ5^x叧Ͼy㓗>翯?~W {6Z@;body { width: 100%; height: 100%; margin: 0px; padding: 0px; overflow: hidden; } html { overflow: hidden; height: 100%; } iframe { margin: 0px; padding: 0px; height: 100%; width: 100%; position: absolute; border: 0px; } TADS /* Copyright 2010 Michael Roberts */ /* * Layout window scripts. */ // initialize the layout window function layoutInit() { // set up our event handlers window.onresize = calcLayout; window.onGameEvent = layoutGameEvent; window.onGameState = layoutGameState; // set up some window methods window.windowFromPath = windowFromPath; } // server-to-client game event handler for a layout window function layoutGameEvent(req, resp) { if (xmlHasChild(resp, "subwin")) { // subwindow creation - go create the window createLayoutFramesXML(xmlChild(resp, "subwin")); } if (xmlHasChild(resp, "shutdown")) { // propagate shutdown notifications to subwindows layoutWindows.forEach(function(w) { if (w.onGameEvent) w.onGameEvent(req, resp); }); } } // server-to-client game state event handler for a layout window function layoutGameState(req, resp) { // create subwindows if (xmlHasChild(resp, "subwin")) { // create each subwindow in the list createLayoutFramesXML(xmlChild(resp, "subwin")); } } // set a new size for an existing layout window function setWinLayout(win, rc) { // find the window var l = layoutWinFromWindow(win); // if we found it, set the new size information if (l) { // convert 'rc' from a comma list, if applicable if (typeof(rc) == "string") rc = rc.replace(/^ +| +$/g, "").replace(/, +| +,/g, ",") .split(","); // replace the old rectangle with non-null new elements for (var i = 0 ; i < rc.length && i < l.rc.length ; ++i) { // copy non-empty replacements if (rc[i] != "") l.rc[i] = rc[i]; } } } // Window tracker list. We keep an object here representing each IFRAME // window we're managing. var layoutWindows = { }; // create layout frames from a list of XML tags function createLayoutFramesXML(subs) { // run through each tag for (var i = 0 ; i < subs.length ; ++i) { // get this child and decode its XML var sub = subs[i]; var name = xmlChildText(sub, "name"); var rc = xmlChildText(sub, "pos"); var src = xmlChildText(sub, "src"); // turn the size list into an array rc = rc.replace(/^ +| +$/g, "").replace(/, +| +,/g, ",").split(","); // create the IFRAME for this window createLayoutFrame(name, rc, "", src); } // recalculate the layout for the new windows calcLayout(); } // create a new iframe layout window function createLayoutFrame(name, rc, style, src) { var obj; // check to see if the window already exists if (name in layoutWindows) { // we already have a window by this name - reuse it obj = layoutWindows[name]; // update the window layout rectangle obj.rc = rc; } else { // create the IFRAME element var ele = document.createElement("iframe"); ele.setAttribute("id", name); ele.setAttribute("name", name); ele.setAttribute("style", style); // this is for IE, to turn off the border (the capitalization counts) ele.setAttribute("frameBorder", "0"); // add the new element to the document at the root level var body = document.body || document.documentElement; body.appendChild(ele); // set up functions to retrieve its document and window objects var getDoc = function() { var f = this.ele; return f.contentWindow ? f.contentWindow.document : f.contentDocument; }; var getWin = function() { var f = this.ele; return f.contentWindow || f.contentDocument.window; }; // set up the tracker object obj = { name: name, rc: rc, ele: ele, getDoc: getDoc, getWin: getWin }; // save it in the window list layoutWindows[name] = obj; // navigate to the location if (src) obj.getWin().location = src; } // recalculate the window layout for the new addition calcLayout(); // return the new tracker return obj; } // get a window tracker object from the IFRAME element function layoutWinFromEle(ele) { return layoutWindows.valWhich(function(w) { return w.ele == ele; }); } // get the layout object for a given window function layoutWinFromWindow(win) { // scan our list of windows for a match to the target window return layoutWindows.valWhich( function(w) { return w.getWin() == win; }); } // Get the window for a relative path. The first path element is // interpreted as a child of the current window. function windowFromPath(path) { // if the argument is a string, split it up on '.' separators if (typeof(path) == "string") path = path.split("."); // look up the first path element in our child list var w; if (!(w = layoutWindows[path.shift()])) return null; // if that's the only path element, return the child if (path.length == 0) return w.getWin(); // look up the rest of the path via the child return w.getWin().windowFromPath(path); } // get the name for a child window function pathFromWindow(win) { // find the layout window object for the window var l = layoutWinFromWindow(win); // if we found it, build the path from our page name plus the window name if (l) return pageName + "." + l.name; else return null; } // recalculate the layout function calcLayout() { // clear the cache of content rectangles layoutWindows.forEach(function(w) { w.rcContent = null; }); // run through our list of tracker objects layoutWindows.forEach(function(w) { // calculate each coordinate for this window var rc = []; for (var j = 0 ; j < 4 ; ++j) rc[j] = calcLayoutCoord(w, j); // limit the coordinates to the window size var wrc = getWindowRect(); rc[0] = (rc[0] < 0 ? 0 : rc[0] > wrc.width ? wrc.width : rc[0]); rc[1] = (rc[1] < 0 ? 0 : rc[1] > wrc.height ? wrc.height : rc[1]); rc[2] = (rc[2] < 0 ? 0 : rc[0] + rc[2] > wrc.width ? wrc.width - rc[0] : rc[2]); rc[3] = (rc[3] < 0 ? 0 : rc[1] + rc[3] > wrc.height ? wrc.height - rc[1] : rc[3]); // set the new layout coordinates in the IFRAME's style properties var ele = w.ele; try { ele.style.left = rc[0] + "px"; ele.style.top = rc[1] + "px"; ele.style.width = rc[2] + "px"; ele.style.height = rc[3] + "px"; } catch (exc) { } }); } // Calculate one coordinate for an iframe window. This resolves the // current values of the constraint-based properties. function calcLayoutCoord(win, i) { // get the target element from the (left, top, width, height) list var val = win.rc[i]; // first, turn typographical units into pixel sizes: em, en, ex val = val.replace(/([0-9.]+)(em|en|ex)/gi, function(m, val, unit) { // Create a probe item on the page to measure the em size. This is // simply a DIV element with its height explicitly set to 1 unit of // whichever unit we're measuring. var d = win.getDoc().createElement("DIV"); var b = win.getDoc().body; d.style.height = "1" + unit; b.appendChild(d); // measure the height of the division to get the pixels per unit, // then we're done with the probe element var pixels = d.offsetHeight; b.removeChild(d); // return the number times the pixes per per return parseFloat(val) * pixels; }); // Next, apply percentages: these are relative to the window interior // dimensions. Left/width percentages are relative to the window width, // and top/height percentages are relative to the height. val = val.replace(/([0-9]+)%/gi, function(m, val) { return Math.floor(parseInt(val)/100 * getWindowRect()[(i % 2) ? "height" : "width"]); }); // Apply object.prop items val = val.replace(/([a-z_0-9]+)\.([a-z]+)/gi, function(m, obj, prop) { // check for special names switch(m) { case "window.width": return getWindowRect().width; case "window.height": return getWindowRect().height; case "content.width": // if we don't have a cached value, cache it now if (!win.rcContent) win.rcContent = getContentRect(win.getWin()); // return the width from the cached content rectangle return win.rcContent.width; case "content.height": // if we don't have a cached value, cache it now if (!win.rcContent) win.rcContent = getContentRect(win.getWin()); // return the height from the cached content rectangle return win.rcContent.height; } // if 'obj' is the name of one of our layout windows, get a property // of that window if (obj in layoutWindows) { // get the window object var w = layoutWindows[obj]; // look up the property switch (prop) { case "left": return calcLayoutCoord(w, 0); case "top": return calcLayoutCoord(w, 1); case "width": return calcLayoutCoord(w, 2); case "height": return calcLayoutCoord(w, 3); case "right": return parseInt(calcLayoutCoord(w, 0)) + parseInt(calcLayouttCoord(w, 2)); case "bottom": return parseInt(calcLayoutCoord(w, 1)) + parseInt(calcLayoutCoord(w, 3)); } } // if we didn't find a translation, just return the original text return m; }); // The whole thing should be expressed as an integer expression now, // with each integer being implicitly pixels (but with no explicit // units in the string). Apply any arithmetic operators to the pixel // expression. if (typeof(val) == "string" && val.match(/^[- +*/0-9]+$/)) val = eval(val); // turn it into an integer if (typeof(val) == "string") val = parseInt(val); // return the final value return val; } /* * Set the default focus for a keyboard event to the appropriate child * window. If we have a default command-line window - which is identified * as a window named "command" - we'll send the focus there. */ function setDefaultFocusChild(desc, fromWin) { // if we have a child named "command", set focus there var w = layoutWindows["command"]; if (w && w.getWin().setDefaultFocusChild) w.getWin().setDefaultFocusChild(desc, fromWin); } /* * Apply a set of CSS styles to this window and its children */ var origApplyCSS = applyCSS; applyCSS = function(tab) { // apply the styles to this window origApplyCSS(tab); // apply the changes to each child layoutWindows.forEach(function(w) { w.getWin().applyCSS(tab); }); } /* * TADS Web UI - style sheet for the main, top-level window. The main * window is mostly a frame container for subwindow widgets and doesn't * show much content of its own, except that the main window handles * our in-window dialog framework and has a special widget for error * notification. */ body { width: 100%; height: 100%; margin: 0px; padding: 0px; overflow: hidden; } html { overflow: hidden; height: 100%; } iframe { margin: 0px; padding: 0px; height: 100%; width: 100%; position: absolute; border: 0px; } div#errorPopupDiv { position: absolute; top: -1000px; /* to initially hide it */ z-index: 10000; height: 12pt; width: 45ex; color: #ffffff; text-align: center; margin: 0; padding: 0; font: bold 8pt/12pt Verdana, Helvetica, Sans-Serif; } div#errorPopupDiv div.icon { background: url("errorPopupBkg.gif"); position: absolute; left: 0px; top: 0px; width: 20px; height: 100%; margin: 0px; } div#errorPopupDiv div.content { background: #ff0000; color: #ffffff; margin: 0px 0px 0px 20px; height: 100%; font: bold 8pt/12pt Verdana, Helvetica, Sans-Serif; border-top: 1px solid #ffffff; } div#errorPopupDiv a { color: #ffffff; } div#downloadPopupDiv { position: absolute; top: -1000px; /* to initially hide it */ z-index: 10000; width: 45ex; color: #ffffff; text-align: left; margin: 0; padding: 0; font: 8pt/12pt Verdana, Helvetica, Sans-Serif; } div#downloadPopupDiv div.icon { background: url("downloadPopupBkg.gif"); position: absolute; left: 0px; top: 0px; width: 10px; margin: 0px; height: 100%; } div#downloadPopupDiv div.content { background: #6000e0; color: #ffffff; margin: 0px 0px 0px 10px; font: 8pt/12pt Verdana, Helvetica, Sans-Serif; border-top: 1px solid #ffffff; } div#downloadPopupDiv div.dlfile { padding-left: 2ex; } div#downloadPopupDiv div.dlfile a:link { color: #ffffff; } div#downloadPopupDiv div.dlfile a:visited { color: #ffffff; } div.dialogCoverDiv { position: fixed; z-index: 10100; top: 0px; left: 0px; width: 100%; height: 100%; background: url("modal-cover.png"); opacity: 0.70; filter: alpha(opacity=70); } div.dialogCanvas { position: fixed; z-index: 10101; top: 0px; left: 0px; width: 100%; height: 100%; overflow: auto; opacity: 0.00; } div.dialogDiv { position: relative; z-index: 10101; background: transparent; width: 70%; } div.dialogTitleBar { position: absolute; top: -12pt; left: 0px; width: 100%; height: 12pt; background: #4040ff; border-bottom: 1px solid #9090ff; cursor: move; } div.dialogTitle { position: absolute; left: 0px; top: -5px; width: 100%; height: 12pt; text-align: center; color: #ffffff; font: bold 9pt/12pt Verdana, Helvetica, Sans-Serif; cursor: move; } div.dialogCloseBox { position: absolute; right: 10px; top: -5px; width: 16px; height: 16px; cursor: pointer; background: url("dlg-closebox.gif") no-repeat top right; } div.dialogCloseBoxH { background: url("dlg-closeboxH.gif") no-repeat top right; } div.dialogCloseBoxA { background: url("dlg-closeboxA.gif") no-repeat top right; } div.dialogMoveCover { display: none; position: absolute; z-index: 12000; } div.dialogDiv-tl { position: absolute; background: url("dlg-title-corners.gif") no-repeat top left; left: 0px; top: -10px; width: 52%; height: 10px; } div.dialogDiv-tr { position: absolute; background: url("dlg-title-corners.gif") no-repeat top right; right: 0px; top: -10px; width: 52%; height: 10px; } div.dialogDiv-bl { position: absolute; background: url("dlg-corners.gif") no-repeat bottom left; left: 0px; bottom: -10px; width: 52%; height: 10px; } div.dialogDiv-br { position: absolute; background: url("dlg-corners.gif") no-repeat bottom right; right: 0px; bottom: -10px; width: 52%; height: 10px; } div.dialogWrapper { text-align: center; /* IE hack - center the dialogContent block */ background: #ffffff; overflow: auto; } div.dialogContent { color: #000000; font: 9pt/11pt Verdana, Helvetica, Sans-Serif; /* the real CSS way to center the dialog content */ margin: 0px auto; /* automatic left/right margins */ display: inline-block; /* use the content width as the DIV width */ /* ...but, of course, IE has to do things its own bizarre way */ zoom: 1; /* force the block to have an IE "layout" object */ * display: inline; /* "layout" makes this work like inline-block */ text-align: left; /* uninherit the IE centering hack in the wrapper */ } div.dialogContent a { color: #0000ff; } div.dialogContent a:hover { color: #ff00ff; } div.dialogContent a:active { color: #ff0000; } div.dialogMarginDiv { margin: 1em 2em 1ex 2em; } div.dialogZeroMarginDiv { margin: 0px; } div.dialogButtons { text-align: center; margin: 1.5em 1em 1ex 1em; clear: both; } div#debugLogDiv { position: absolute; right: 0px; top: 50px; z-index: 11000; width: 35%; height: 75%; overflow: auto; background: #ff8000; font: 8pt/12pt Verdana, Helvetica, Sans-Serif; color: #ffffff; display: none; } div.errorListDiv { margin: 1em; overflow: auto; border: 1px inset #c0c0ff; background: #f0f0f0; padding: 1em; max-height: 20em; } div.dialogContent span.details { font-size: 90%; } div.errorFooter { font-size: 85%; color: #303030; margin-top: 1ex; } div.errorDetail { font-size: 85%; color: #303030; display: none; } div.errorDetail div.errorSubDetail { padding: 0 1em 0 2em; } div.dialogContent a.colorbutton { height: 1.5em; width: 3em; background: #ff00ff; border: 1px solid #0000ff; display: table-cell; display: inline-block; margin: 0.75em 0.75ex 0.75em 0.75ex; vertical-align: middle; color: #ffffff; text-decoration: none; text-align: center; } div.prefsSection { margin: 1ex 0 1ex 0; } div.prefsSection select { margin: 0.5em 0.75ex 0.5em 0.75ex; vertical-align: middle; } div.prefsSection input { margin: 0.5em 0.75ex 0.5em 0.75ex; vertical-align: middle; } div.prefsSection div.combobox { margin: 0.5em 0.75ex 0.5em 0.75ex; vertical-align: middle; } span.prefsLabel { width: 30ex; display: table-cell; display: inline-block; } a.dlgButton { margin: 0 1ex 0 1ex; } div.colorPicker { position: absolute; z-index: 19900; display: none; background: #000080; color: #ffffff; font: 8pt/10pt Verdana, Helvetica, Sans-Serif; border: 1px solid #808080; } div.colorPicker input { border: 1px solid #303030; font: 8pt/10pt Verdana, Helvetica, Sans-Serif; color: #000000; background: #c0c0ff; } a.colorPickerButton { display: table-cell; display: inline-block; width: 2ex; height: 2ex; margin: 2px; border: 2px inset gray; cursor: pointer; } div.colorPickerDefault { text-align: center; width: 100%; } div.colorPickerDefault div.off { padding: 0px 1ex; display: inline-block; zoom: 1; * display: inline; } div.colorPickerDefault div.on { padding: 0px 1ex; display: inline-block; zoom: 1; * display: inline; background: #0080ff; } div.colorPickerDefault a { color: #ffffff; text-decoration: none; border: none; } div.colorPickerDefault img { border: none; vertical-align: middle; } div.inputDialog { text-align: center; margin: 1em; padding: 1em; } table.inputDialogTab { width: 100%; border: none; margin: 0px; } table.inputDialogTab tr { vertical-align: top; height: 40px; } td.inputDialogPrompt { vertical-align: middle; text-align: center; padding: 0px 60px; } img.inputDialogIcon { position: absolute; } img.inputDialogStrut { height: 40px; width: 0px; } iframe.downloadFile { position: absolute; width: 1px; height: 1px; left: -100px; top: -100px; } div.menuSys { padding: 3em 0px; text-align: center; } div.menuSysItem { padding: 1ex 0px; } div.menuSysTopicFooter { padding: 1em 0px 0px 0px; text-align: center; } div.menuSysItem a:link { text-decoration: none; } div.menuSysItem a:active { text-decoration: underline; } div.menuSysItem a:hover { text-decoration: underline; } div.menuSysLongTopic { overflow-y: auto; overflow-x: hidden; margin: 2em 0px 1em 0px; text-align: left; padding-right: 24px; /* hack for IE scrollbar overlap */ } div.menuSysChapterButtons { } div.menuSysChapterButtons a { padding: 0px 1.5em; } div#pageMenuDiv { position: absolute; z-index: 20000; display: none; background: #ffffff; padding: 0px; margin: 0px; border: 0px; } div.pageMenuWrapper { color: #00000; font: bold 8pt/16pt Verdana, Helvetica, Sans-Serif; border: 1px solid #c0c0c0; padding: 6px 1.5em; } div#pageMenuDiv a { text-decoration: none; color: #0000ff; } div#pageMenuDiv a:hover { color: #ff0000; } div.menuShadow { position: absolute; z-index: 19999; background: #b0b0b0; opacity: 0.60; filter: alpha(opacity=60); } div#__TADS_swf_div { position: absolute; left: -10000px; top: -10000px; } TADS
File Downloads
 

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%?
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%?
/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Main window scripts for TADS 3 Web UI. These scripts are for the * top-level layout window, which usually contains one or more "widget" * windows, such as the main game transcript window and the status line. */ /* ------------------------------------------------------------------------ */ /* * main window initialization */ function mainInit() { // the top window is always called "main" pageName = "main"; // check for the Flash plug-in if (getFlashPlayerVersion()) { // Flash is available, so load our SWF object. We don't embed the // object statically, because doing so provokes some browsers to // display a UI prompt about installing Flash every single time the // page is loaded, which is annoying to users who intentionally run // sans Flash. But now that we know Flash is present, we can safely // load our SWF, so dynamically insert the tag for it. $("__TADS_swf_div.
").innerHTML = "" + "" + "" + "" + "" + "" + "" + ""; } else { // The Flash plug-in isn't available, so we won't be able to run // our SWF object. Fire our load-completion event to signal that // there's nothing to wait for. TADS_swf.loaded.fire(); } // initialize the utilities and layout window packages utilInit(); layoutInit(); // initialize the XML request frame initXmlFrame(); // override some handlers window.onresize = mainResize; window.onGameEvent = mainGameEvent; window.onGameState = mainGameState; window.onUload = mainUnload; // Cancel any pending events from past incarnations, in case we just // refreshed the page. Lingering events from past loads refer to old // objects from the previous DOM tree, so they won't work with the new // incarnation of the page. serverRequest("/webui/flushEvents"); // get the initial state getInitState(); // cache some images that we might need if we lose our connection cacheImage("/webuires/dlg-corners.gif"); cacheImage("/webuires/dlg-title-corners.gif"); cacheImage("/webuires/modal-cover.png"); cacheImage("/webuires/errorPopupBkg.gif"); cacheImage("/webuires/warningPopupBkg.gif"); cacheImage("/webuires/modal-cover.png"); } /* ------------------------------------------------------------------------ */ /* * Main game window events and state */ // are we in an inputEvent() wait? var wantInputEvent = false; // event handler function mainGameEvent(req, resp) { // check for title changes if (xmlHasChild(resp, "setTitle")) document.title = xmlChildText(resp, "setTitle"); // check for preference settings changes if (xmlHasChild(resp, "setPrefs")) xmlToPrefs(xmlChild(resp, "setPrefs")[0]); // check for preference dialog invocations if (xmlHasChild(resp, "showPrefsDialog")) showPrefsDialog(); // check for file dialog invocations if (xmlHasChild(resp, "fileDialog")) showFileDialog(xmlChild(resp, "fileDialog")[0]); else if (xmlHasChild(resp, "uploadFileDialog")) showUploadDialog(xmlChild(resp, "uploadFileDialog")[0]); // check for downloadable files if (xmlHasChild(resp, "offerDownload")) { var file = xmlChildText(resp, "offerDownload"); addDownloadFile(file); addDownloadFrame(file); } if (xmlHasChild(resp, "cancelDownload")) { var file = xmlChildText(resp, "cancelDownload"); removeDownloadFile(file); removeDownloadFrame(file); } // check for input dialog invocations if (xmlHasChild(resp, "inputDialog")) showInputDialog(xmlChild(resp, "inputDialog")[0]); // check for event input waits and cancellations if (xmlHasChild(resp, "getInputEvent")) wantInputEvent = true; else if (xmlHasChild(resp, "cancelInputEvent")) wantInputEvent = false; // check for menu system events if (xmlHasChild(resp, "menusys")) showMenuSys(xmlChild(resp, "menusys")[0]); // "inherit" the layout window handling layoutGameEvent(req, resp); } // state handler function mainGameState(req, resp) { // check for title settings if (xmlHasChild(resp, "title")) document.title = xmlChildText(resp, "title"); // check for preference settings if (xmlHasChild(resp, "prefs")) xmlToPrefs(xmlChild(resp, "prefs")[0]); // "inherit" the layout window handling layoutGameState(req, resp); // check for file dialog invocations if (xmlHasChild(resp, "fileDialog")) showFileDialog(xmlChild(resp, "fileDialog")[0]); else if (xmlHasChild(resp, "uploadFileDialog")) showUploadDialog(xmlChild(resp, "uploadFileDialog")[0]); // check for input dialog invocations if (xmlHasChild(resp, "inputDialog")) showInputDialog(xmlChild(resp, "inputDialog")[0]); // check for input event waits if (xmlHasChild(resp, "getInputEvent")) wantInputEvent = true; // check for downloadable files if (xmlHasChild(resp, "offerDownload")) { var lst = xmlChild(resp, "offerDownload"); for (var i = 0 ; i < lst.length ; ++i) addDownloadFile(xmlNodeText(lst[i])); } // check for menu system dialogs if (xmlHasChild(resp, "menusys")) showMenuSys(xmlChild(resp, "menusys")[0]); // This is the main, top-level window, so once we get the initial // state set up, start the "event thread". This is only conceptually // a thread, since javascript is single-threaded, but it acts roughly // like a thread in that the browser sends the request asynchronously // and "resumes" our processing when the request completes, by // invoking our event callback. // Ask for our first event. Do this in a timeout rather than directly, // to allow the browser to process any outstanding events in its own // queue; this avoids a problem that shows up in Mobile Safari and IE9 // that seems to be related to the timing of receiving the IFRAME // contents on the initial page setup. A zero timeout is fine; we // don't need a delay per se, we just need to order things so that we // don't generate this request until the browser has finished processing // UI events already in its internal queue. setTimeout(function() { serverRequest( "/webui/getEvent", ServerRequest.Subscription, cbGetEvent); }, 0); } // getEvent request callback. This handles a response from the server to // a "get event" request we sent earlier. This allows the server to send // us events: we ask the server for an event via a request, and as soon as // the server has an event available, it replies to the request. function cbGetEvent(req, resp) { // get the target window var w = getEventWin(req, resp); // If we got found a window, forward the event. If we didn't find // the window, or it's not loaded yet, drop the event. This should // be okay, since the window will ask for a full status refresh when // it first loads - this will compensate for any missed events that // came in before it loaded. if (w && w.onGameEvent) w.onGameEvent(req, resp); // if we're shutting down, skip asking for another event if (xmlHasChild(resp, "shutdown")) { // display the disconnecting dialog showDialog({ title: "TADS", id: "shutdown", contents: "The game server has terminated the connection." }); // done return; } // go back for the next request serverRequest("/webui/getEvent", ServerRequest.Subscription, cbGetEvent); } // Get the window for a state/event reply function getEventWin(req, resp) { // if there's a window spec, get it; assume it's directed to the // main frame window otherwise if (xmlHasChild(resp, "window")) { // get the window from the path return windowFromAbsPath(xmlChildText(resp, "window")); } else { // no window spec, so use the main window return window; } } // get a window from the absolute path function windowFromAbsPath(path) { // turn the path string into a list if (typeof(path) == "string") path = path.split("."); // make sure it starts with our page name if (path.shift() != pageName) return null; // if it's just the one element, it's the main window if (path.length == 0) return window; // resolve the rest as a relative path return windowFromPath(path); } /* ------------------------------------------------------------------------ */ /* * Key event handler for inputEvent() requests from the server. When the * server program wants to solicit an event from the client, it sends us a * status message. We set the global variable * wantInputEvent to true when we receive this message; this tells us that * when we get a keystroke or hyperlink click event that isn't handled in * the UI itself, we should send it back to the server as an input event * message. * * All subwindows should forward their unhandled key events here so that we * can transmit the appropriate events to the server. Only unhandled * events should be sent here; if the event is handled within the client * UI, it doesn't have to be sent to the server. */ function keyToServer(desc) { // if the server wants an input event, send it if (desc && wantInputEvent) { // translate the key from DOM 3 naming to TADS naming var key = DOM3toTADSKey[desc.keyName] || desc.ch; // if we don't have a TADS translation for the key, don't send an // event for this key after all if (!key) return; // translate control keys to [ctrl-x] notation var ch = key.charCodeAt(0); if (ch == 10 || ch == 13) ch = '\n'; else if (ch < 32) key = "[ctrl-" + String.fromCharCode(ch + 64) + "]"; // send the event serverRequest("/webui/inputEvent?type=key¶m=" + encodeURIComponent(key)); // we're no longer waiting for an input event wantInputEvent = false; // consider this key handled - don't do the default browser action preventDefault(desc.event); } } /* event map from DOM 3 key names to TADS key names */ var DOM3toTADSKey = { "U+001B": "[esc]", "Up": "[up]", "Down": "[down]", "Left": "[left]", "Right": "[right]", "Home": "[home]", "End": "[end]", "U+007F": "[del]", "Insert": "[insert]", "ScrollLock": "[scroll]", "PageUp": "[page up]", "PageDown": "[page down]", "F1": "[f1]", "F2": "[f2]", "F3": "[f3]", "F4": "[f4]", "F5": "[f5]", "F6": "[f6]", "F7": "[f7]", "F8": "[f8]", "F9": "[f9]", "F10": "[f10]", "F11": "[f11]", "F12": "[f12]", "U+0008": "[bksp]" }; /* * Document-level click handler for inputEvent() requests from the server. * Unhandled hyperlink clicks should be sent here, so we can send them on * to the server as inputEvent() results. */ function clickToServer(ev, href,ele) { // if the server wants an input event, send it if (wantInputEvent) { // send the event to the server serverRequest("/webui/inputEvent?type=href¶m=" + encodeURIComponent(href)); // we're no longer waiting for an input event wantInputEvent = false; } } /* ------------------------------------------------------------------------ */ /* * resize the main window */ function mainResize() { // move the errorPopupDiv to the bottom right, if it's visible if (errorPopupVis) { var rc = getWindowRect(); var ediv = $("errorPopupDiv"); ediv.style.left = (rc.width - ediv.offsetWidth) + "px"; ediv.style.top = (rc.height - ediv.offsetHeight) + "px"; } if (downloadFiles.length) { var rc = getWindowRect(); var ddiv = $("downloadPopupDiv"); ddiv.style.left = (rc.width - ddiv.offsetWidth) + "px"; var top = rc.height - ddiv.offsetHeight; if (errorPopupVis) top -= ediv.offsetHeight; ddiv.style.top = top + "px"; } // reposition any dialogs for (var i = 0 ; i < dialogStack.length ; ++i) { if (!dialogStack[i].manuallyPositioned) positionDialog(dialogStack[i].ele, false); } // inherit the layout window handling calcLayout(); } /* ------------------------------------------------------------------------ */ /* * Close the window */ function mainUnload() { if (debugLogWin) { debugLogWin.close(); debugLogWin = null; } } /* ------------------------------------------------------------------------ */ /* * Error logging. This is used mainly for network connection errors. * Network errors are often due to temporary conditions - network * congestion, router latency, server hiccups. Many times, an XML request * will fail due to a network hiccup, but retrying the same request will * succeed. We therefore don't want to be too strident about network * errors, but at the same time we don't want to completely ignore them. * Our compromise is a small message panel that appears at the bottom * corner of the main window when an error occurs. This alerts the user to * the error, and offers an option to see more details. This doesn't * intrude too much into the UI visually, and doesn't intrude at all * modally, since there's no need to acknowledge or dismiss the popup. */ // error log var errorLog = []; // Is the error popup visible? This is the little button that we show // in the bottom right corner when a new error is available to be viewed. var errorPopupVis = false; // Add an error to the error log. 'msg' is the explanatory error message // text, and 'detail' is addition technical detail that might be helpful // for a programmer debugging the error (javascript stack trace, bad XML // text, HTTP result code, etc, as appropriate to the specific error // condition). // // 'id' is an optional object that tells us the status of an error that // might be possible to resolve automatically with retries. First, this // serves to tie together a series of errors that results from a series // of retries for the same operation. For example, for XML requests, // the 'id' is the request descriptor object. Tying the retry errors // to the original error is useful because (a) the error log can show // the relationship, so that the user doesn't think five separate things // went wrong when they were really five repeated failed attempts at // the same thing, and (b) if a retry eventually succeeds, the error log // can show that the earlier attempts were eventually resolved and thus // can be ignored. Second, the 'id' object has two fields that tell us // whether the operation is in fact being retried: // // id.failed - true if the operation has permanently failed and will not // be retried, false if the operation is being retried // id.succeeded - true if the operation has succeeded on a retry // // If 'id' is null, we'll supply a default. function logError(msg, detail, id) { // for testing purposes, it's sometimes useful to pop this up // in the separate debug log window, but we don't want to do // this in release builds // debugLog("Error: " + msg + "
" + detail + "

"); // if there's no 'id' object, supply a default if (!id) id = new LogErrorID(); // add the message at the start of the error list errorLog.unshift({msg: msg, detail: detail, id: id, viewed: false, timestamp: new Date()}); // if the error frame isn't visible, show it if (!errorPopupVis) { // mark it as visible, but make it completely transparent errorPopupVis = true; var d = $("errorPopupDiv"); setAlpha(d, 0); // do an explicit size recalc to position the popup mainResize(); // start fading it in startFade(d, 1, 700); } // If all unviewed errors are being retried automatically, set the // error popup background to orange. Otherwise set it to red. var allRetry = true; for (var i = 0 ; i < errorLog.length ; ++i) { // get this error var e = errorLog[i]; // if it's unviewed, and it's marked as failed, we have an error // that won't be retried if (!e.viewed && e.id.failed) { allRetry = false; break; } } // set the appropriate background color in the popup $("errorPopupDiv.[content]").style.background = (allRetry ? "#ff8000" : "#ff0000"); $("errorPopupDiv.[icon]").style.backgroundImage = (allRetry ? "url(/webuires/warningPopupBkg.gif)" : "url(/webuires/errorPopupBkg.gif)"); } function LogErrorID() { this.succeeded = false; this.failed = true; } function openErrorDetail(i) { $("errorDetailDiv-" + i).style.display = "block"; $("openErrorDetailLink-" + i).style.display = "none"; } // Un-log a network error. Call this when a request that failed with an // error subsequently succeeds on a retry. function unlogError(id) { // find the error in the error log var openCnt = 0; for (var i = 0 ; i < errorLog.length ; ++i) { // get this error var e = errorLog[i]; // if this error isn't resolved and hasn't been viewed, // count it as open if (!e.id.succeeded && !e.viewed) ++openCnt; } // if there are now no open errors, the error popup is no longer needed if (openCnt == 0) hideErrorPopup(); } // display the error log var errorLogViewCount = 0; function showErrorLog() { // build the error list var lst = []; for (var i = 0 ; i < errorLog.length ; ++i) { var msg = errorLog[i].msg; var id = errorLog[i].id; var detail = errorLog[i].detail; var ts = errorLog[i].timestamp; if (detail) { msg += "
" + ts.toLocaleTimeString() + (errorLogViewCount == 0 ? "" : errorLog[i].viewed ? " | Previously viewed" : " | New") + (id.succeeded ? " | Error was resolved automatically" : !id.failed ? " | Request is being retried automatically" : "") + " | " + "View Details" + "
" + "
" + detail + "
"; } lst.push(msg); } // count this view of the error log errorLogViewCount++; // mark all messages as viewed for (var i = 0 ; i < errorLog.length ; ++i) errorLog[i].viewed = true; // If there's a single error, show it as the whole dialog contents. // Otherwise show a scrolling list of the errors. var cont; if (errorLog.length == 0) { cont = "This window shows any errors encountered in the browser " + "since you loaded the page, including network problems " + "and javascript errors. So far, no errors have occurred " + "since you loaded or refreshed the page."; } else if (errorLog.length == 1) { // one error - just show the error cont = lst[0]; } else { // Multiple errors - show a scrolling list cont = "The game encountered the following errors (the most " + "recent is listed first):" + "
" + lst.join("
") + "
"; } // hide the error popup hideErrorPopup(); // show the dialog showDialog({ title: "Error Log", id: 'errorLog', contents: cont }); } // hide the error log popup function hideErrorPopup() { // move the popup division off-screen errorPopupVis = false; var d = $("errorPopupDiv"); startFade(d, -1, 250, function() { d.style.top = "-1000px"; mainResize(); }); } /* ------------------------------------------------------------------------ */ /* * Download popup. This appears at the lower right corner of the window, * just above the error popup if there is one. This shows the list of * outstanding downloadable files sent by the game server. */ // list of downloadable files var downloadFiles = []; // add a downloadable file function addDownloadFile(fname) { // add the file to the list downloadFiles.push(fname); // adjust the height var d = $("downloadPopupDiv"); // rebuild the download list rebuildDownloadDiv(); // if this is the first file, show the popup if (downloadFiles.length == 1) { // make it completely transparent, and start fading it in setAlpha(d, 0); startFade(d, 1, 700); } // do an explicit size recalc to position and resize the popup mainResize(); } // remove a downloadable file function removeDownloadFile(fname) { // find the file var idx = downloadFiles.indexOf(fname); if (idx >= 0) { // remove it from the list downloadFiles.splice(idx, 1); // rebuild the download list rebuildDownloadDiv(); // if the list is empty, hide the popup, otherwise resize it var cnt = downloadFiles.length; var d = $("downloadPopupDiv"); if (cnt == 0) { // empty - fade it out startFade(d, -1, 250, function() { d.style.top = "-1000px"; }); } else { // do an explicit resize to position and size the popup mainResize(); } } } // rebuild the download file division contents function rebuildDownloadDiv() { var s = ["Download Files"]; downloadFiles.forEach(function(fname) { var shortName = fname.replace(/^.*\//, "").htmlify(); var href = encodeURI(fname); s.push("
• " + "" + shortName + "" + " | Cancel" + "
"); }); $("downloadPopupDiv.[content]").innerHTML = s.join(""); } function cancelDownloadFile(fname) { serverRequest(fname + "?cancel"); } /* ------------------------------------------------------------------------ */ /* * Debugging messages. During development, it's often useful to instrument * the code with message displays; this provides a place to put them that's * less intrusive than 'alert()'. Messages written here are displayed in a * separate popup window that we open the first time this is called. */ var debugLogWin = null, debugLogLoaded = new AbsEvent(); function debugLog(msg) { // if the log window isn't open yet, or it's been closed, open it if (!debugLogWin || debugLogWin.closed) { debugLogLoaded.isDone = false; debugLogWin = window.open( "/webuires/debuglog.htm", "tadsDebugLog", "location=no, resizable=yes, scrollbars=yes, status=yes, " + "width=550, height=400"); } // when the log window is loaded, add the message debugLogLoaded.whenDone(function() { debugLogWin.debugLogPrint(msg); }); } /* ------------------------------------------------------------------------ */ /* * Show an HTML dialog. This shows the dialog as a foreground DIV within * the main window, not as an 'alert()' or new window popup. * * 'id' is an optional identification string for identifying the dialog * within the program. This is useful if you want to be sure that the * active dialog is a particular dialog, rather than a nested dialog it * opens. We don't make any of this here; it's purely for the dialog's own * use in other scripts. * * 'params' is an object with the properties listed below. All properties * are optional except the ones marked required. * * title: a string to display in the dialog's title bar. Required. * * contents: a string giving the HTML contents of the dialog, OR a DOM * element whose contents will be duplicated to populate the dialog. * Required. * * buttons: an array of button objects. Each object has these members: *. { *. name: string with the display name of the button *. isDefault: true if pressing the Return key activates this button *. isCancel: true if pressing the Escape key activates this button *. onclick: function to call when clicking the button; if not set, *. clicking the button simply dismisses the dialog *. key: the key that can be used to select this button; applies only *. when focus isn't in an input control; you can specify either an *. ordinary character (using upper-case for letters), or a key *. name (e.g., 'U+0008' for backspace) *. } * * If you omit the name of the button, the button won't be displayed, but * will still be available as a keystroke handler. * * init: a function to call to initialize the dialog. This is called after * the new dialog has been set up, but before it's been displayed. The * dialog's root element is passed as the argument - init(dialogElement). * * initFocus: the control that should initially receive focus. If this * isn't set, we'll search for a focusable control (INPUT, SELECT, ) and * set focus there. * * initAfterVisible: a function to call to initialize after the dialog has * been displayed and focus has been set. As with init(), this is called * with the dialog's root element as the argument. * * dismiss: a function to call when dismissing the dialog. This will be * called as dismiss(dialogElement, button), where 'button' is the button * object from the 'buttons' list corresponding to the button the user * clicked to close the dialog. Return true to allow the dialog to close, * false to prevent it from closing. You can use a false return to perform * validation. * * dimensions: an object { width:, height: } with style strings to use for * the dimensions of the dialog. If not provided, we'll let the browser * set dimensions based on the content size. * * fillWidth: true if the dialog contents should be stretched to fill the * whole width of the containing dialog. By default, we'll show the * contents at their natural width, centered in the dialog container. In * some cases we instead want the contents to scale according to the * container, though, such as when the dialog contains an iframe. Set this * to true to stretch the contents to the container width. */ var dialogStack = []; var fontSelectorOptions = null; function showDialog(params) { // get the contents, retrieving HTML source if it's a DOM object var contents = params.contents; if (typeof(contents) == "object") contents = contents.innerHTML; // assume all we'll need to do is show the dialog var go = function() { showDialogMain(params); }; // check for $[fontpicker] macros if (contents.match(/\$\[fontpicker([^\]]*)\]/)) { // we have one or more font pickers, so we need to build the // list of installed fonts before we can show the dialog var go1 = go; go = function() { TADS_swf.waitForFontList(go1); }; } // execute the dialog display procedure we've built up go(); } // main dialog builder function showDialogMain(params) { // figure the z-index for the new dialog var z = 10500 + dialogStack.length*10; var dlgID = "dialogDiv" + (dialogStack.length + 1); // create the global descriptor for the dialog var dlgDesc = { title: params.title, dd: dlgID, manuallyPositioned: false, dismiss: params.dismiss, initFocus: params.initFocus, id: params.id }; // push it onto the active dialog stack dialogStack.push(dlgDesc); // create the background cover for the new dialog var bkg = dlgDesc.bkg = document.createElement("div"); bkg.className = "dialogCoverDiv"; bkg.style.zIndex = z; document.body.appendChild(bkg); // create the canvas var canvas = dlgDesc.canvas = document.createElement("div"); canvas.className = "dialogCanvas"; canvas.style.zIndex = "" + (z + 1); document.body.appendChild(canvas); // create the new dialog division var dlg = dlgDesc.ele = document.createElement("div"); dlg.id = dlgID; dlg.innerHTML = $("dialogTemplate").innerHTML; dlg.className = "dialogDiv"; dlg.style.zIndex = "" + (z + 1); dlg.onkeydown = function(ev) { return $kd(ev, dlgKey, dlgDesc); }; dlg.onkeypress = function(ev) { return $kp(ev, dlgKey, dlgDesc); }; canvas.appendChild(dlg); // if dimensions were given, set them in the dialog element if (params.dimensions && params.dimensions.height) dlg.style.height = params.dimensions.height; if (params.dimensions && params.dimensions.width) dlg.style.width = params.dimensions.width; // get a couple of elements for further manipulation var ttl = $(dlg, "[dialogTitle]"); var cont = $(dlg, "[dialogContent]"); // if desired, stretch the content division to fill the dialog if (params.fillWidth) cont.style.width = "100%"; // fill in the title ttl.innerHTML = params.title; // set default buttons if none were given var buttons = params.buttons; if (!buttons) buttons = [{name: "OK", isDefault: true, isCancel: true}]; // save the buttons in the dialog descriptor dlgDesc.buttons = buttons || []; // if it's a pre-defined division, fetch its contents var contents = params.contents; if (typeof(contents) == "object") contents = contents.innerHTML; // expand $[fontpicker] macros var fontSizeOptions = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 36, 48, 72] .map(function(x) { return ""; }).join(""); fontSizeOptions = "" + fontSizeOptions; var fontPickers = []; var preFontCont = contents; contents = contents.replace( /\$\[fontpicker([^\]]*)\]/gm, function(match, attrs) { // parse the attributes var pattrs = parseTagAttrs(attrs); // add this to the font select pending list fontPickers.push({ attrs: attrs, pattrs: pattrs }); // Build the font picker: a combo for the font name, another // for the font size, and a color picker for the text color. // Set the font picker select list to initially empty, since // we might have to wait for the font lister thread to finish. return createCombo(pattrs.id, 40, "") + createCombo(pattrs.id + "Size", 5, fontSizeOptions); }); // note if we actually needed any font pickers - we do if we found // any $[fontpicker] macros to expand var needFonts = (contents != preFontCont); // expand $[colorpicker] macros var colorPickers = []; contents = contents.replace( /\$\[colorpicker([^\]]*)\]/gm, function(match, attrs) { var pattrs = parseTagAttrs(attrs); var btnID = dlgID + "." + pattrs.id; colorPickers[pattrs.id] = pattrs; return "" + ""; }); // expand $[checkbox] contents = contents.replace( /\$\[checkbox([^\]]*)\]/gm, function(match, attrs) { var pattrs = parseTagAttrs(attrs); return ""; }); // add the buttons to the contents if (buttons.length) { contents += "
"; for (var i = 0 ; i < buttons.length ; ++i) { var b = buttons[i]; if (b.name) { contents += "" + "" + b.name + ""; } } contents += "
"; } // fill in the contents cont.innerHTML = "
" + contents + "
"; // call the initializer function if (params.init) params.init(dlg, params); // initialize the contents and adjust the size immediately positionDialog(dlg, true); // just in case the browser needs a moment to finish processing the // innerHTML changes, resize it again in a few moments setTimeout(function() { // set the size again positionDialog(dlg, true); // set it opaque setAlpha(canvas, 100); // set the initial focus initDialogFocus(); // run the after-visible function if (params.initAfterVisible) setTimeout( function() { params.initAfterVisible(dlg, params); }, 10); }, 1); // If we needed any font pickers, load the system's font list, if // we haven't already. When that's done, populate the font pickers. if (needFonts) { // get the cached font list var fonts = TADS_swf.fontList; // build the font select list fontSelectorOptions = fonts.map(function(ele) { ele = ele.htmlify(); return "" }).join(""); // add "Default" as the first item fontSelectorOptions = "" + fontSelectorOptions; // populate each font picker for (var i = 0 ; i < fontPickers.length ; ++i) { // get this font picker's field, and from that get the sel.outerHTML = sel.outerHTML.replace( /<\/select>/im, fontSelectorOptions + "") .replace(/size=("[^"]+"|'[^']'|[^'"][^\s>]*)/im, "size=\"20\""); } else { // no outerHTML - replace just the option list sel.innerHTML = fontSelectorOptions; sel.size = "20"; } } } } /* * Initialize focus in the currently active dialog */ function initDialogFocus() { // ignore this if there's no dialog if (dialogStack.length == 0) return; // get the dialog description var desc = dialogStack.top(); var dlg = desc.ele; // get the initial focus control specified in the parameters var f; if (desc.initFocus) f = $(dlg, desc.initFocus); // if that wasn't specified, search for the first focusable object // within the dialog if (!f) { f = breadthSearch(dlg, function(ele) { return (ele.focus && (ele.nodeName == "INPUT" || ele.nodeName == "TEXTAREA" || ele.nodeName == "SELECT" || ele.nodeName == "A")); }); } // Move focus to the control, if we found one, or to the dialog // itself if not. If the dialog doesn't accept focus, just // remove focus from wherever it is now. if (f) { // move focus to the control f.focus(); // select its initial text if it's an INPUT or TEXTAREA if (f.nodeName == "INPUT" || f.nodeName == "TEXTAREA") setSelRangeInEle(f); } else if (dlg.focus) dlg.focus(); else if (document.activeElement) document.activeElement.blur(); } /* * Reposition a dialog for the current window size */ function positionDialog(dlg, init) { // get the dialog and window areas var drc = getObjectRect(dlg); var wrc = getWindowRect(); // center the dialog in the window var x = (wrc.width - drc.width)/2, y = (wrc.height - drc.height)/2; // if this is a sub-dialog, show it relative to the parent dialog // instead of centered if (init && dialogStack.length > 1) { // get the preceding dialog position var par = dialogStack[dialogStack.length - 2]; var prc = getObjectRect(par.ele); // move the new dialog below and right of the parent x = prc.x + 25; y = prc.y + 25; // mark this dialog and the parent as manually positioned par.manuallyPositioned = true; dialogStack.top().manuallyPositioned = true; } // set the position dlg.style.left = (x > 0 ? x : 0) + "px"; dlg.style.top = (y > 0 ? y : 0) + "px"; } /* * handle mouse tracking in a dialog title bar */ var dlgTrackInfo = null; function dlgTitleDown(ev) { // if there's no dialog, ignore it if (dialogStack.length == 0) return; // get the dialog and its bounding box var dlg = dialogStack.top().ele; var rc = getObjectRect(dlg); // get the event ev = getEvent(ev); // note the start offset of the tracking dlgTrackInfo = { x: ev.clientX - rc.x, y: ev.clientY - rc.y, dlg: dlg }; // track the mouse throughout the document addEventHandler(document, "mousemove", dlgTitleMove); addEventHandler(document, "mouseup", dlgTitleUp); addEventHandler(document, "selectstart", dlgTitleSelect); // Show the dialog's "move cover" division. This is a transparent // division that covers the whole dialog at a higher z-index than // any of its contents. The point is to block out any IFRAME within // the dialog for the duration of the move, so that events are directed // to our document rather than to the IFRAME's document. If we didn't // have this cover, mouse events would disappear into the IFRAME any // time the mouse moves quickly enough to stray into the IFRAME before // it generates a mousemove event within our title bar. Cross-domain // scripting limits prevent those events from bubbling up to our // document handlers if the IFRAME is showing an outside page (such // as from the storage server). The dialog cover blocks the IFRAME // and keeps all the events within our document. var cover = $(dlg, "[dialogMoveCover]"); cover.style.display = "block"; cover.style.width = rc.width + "px"; cover.style.height = rc.height + "px"; // skip the default handling return false; } // prevent text selection while tracking the mouse in IE function dlgTitleSelect(ev) { return false; } function dlgTitleMove(ev) { // if we're not tracking a dialog, skip it if (!dlgTrackInfo) return; // get the event ev = getEvent(ev); // move the dialog var dlg = dlgTrackInfo.dlg; dlg.style.left = (ev.clientX - dlgTrackInfo.x) + "px"; dlg.style.top = (ev.clientY - dlgTrackInfo.y) + "px"; // mark the dialog as manually moved dialogStack.top().manuallyPositioned = true; // suppress the default handling preventDefault(ev); cancelBubble(ev); return false; } function dlgTitleUp(ev) { // if we're not tracking a dialog, skip it if (!dlgTrackInfo) return; // remove the event handlers that we installed just for the move removeEventHandler(document, "mousemove", dlgTitleMove); removeEventHandler(document, "mouseup", dlgTitleUp); removeEventHandler(document, "selectstart", dlgTitleSelect); // remove the dialog cover division $(dlgTrackInfo.dlg, "[dialogMoveCover]").style.display = "none"; // done tracking dlgTrackInfo = null; // suppress the default handling preventDefault(ev); cancelBubble(ev); return false; } /* * Handle a keystroke in a color button in the main dialog. We'll let * Enter keystrokes get handled in the , but we'll block these from * bubbling up to the dialog. This lets us use the keyboard interface to * select a color button without dismissing the dialog. */ function colorbuttonKey(desc, ctx) { var ele = ctx[0]; var hasDefault = ctx[1]; if (desc.keyName == "Enter" || desc.keyName == "U+0020") { openColorPicker(desc.event, ele, hasDefault); cancelBubble(desc.event); preventDefault(desc.event); return false; } return true; } /* * Open a color picker */ var colorPickerBuilt = false; var colorPickerTarget = null; function openColorPicker(ev, ele, hasDefault) { // get the button and the color picker popup ele = $(ele); var cp = $("colorPicker"); // if it's already open, close it if (cp.style.display == "block") { closePopup(cp); return; } // build out the buttons if we haven't already done so if (!colorPickerBuilt) { var colors = ["ff8080", "ffff80", "80ff80", "ff80ff", "80ffff", "0080ff", "ff80c0", "ff80ff", "ff0000", "ffff00", "80ff00", "00ff40", "00ffff", "0080c0", "8080c0", "ff00ff", "804040", "ff8040", "00ff00", "008080", "004080", "8080ff", "800040", "ff0080", "800000", "ff8000", "008000", "008040", "0000ff", "0000A0", "800080", "8000ff", "400000", "804000", "004000", "004040", "000080", "000040", "400040", "400080", "000000", "808000", "808040", "808080", "408080", "C0C0C0", "400000", "ffffff"]; // set up the template for each button var keyTpl = "\"javascript:return @FUNC@" + "(event,colorBtnKey,['#@COLOR@',this]);\""; var btnTpl = ""; // build the button array - eight buttons per line for (var i = 0, s = [] ; i < colors.length ; ) { // build this line for (var j = 0, sl = [] ; j < 8 ; ++j, ++i) { // build this button and add it to the line list sl.push(btnTpl .replace(/@COLOR@/gm, colors[i]) .replace(/@I@/gm, i)); } // combine the line into a and add it to the master list s.push("" + sl.join("") + "
"); } // add the custom color entry field s.push("
Custom (rrggbb): " + "
"); // add the Use Default button s.push("
" + "
"); // combine the element list into a string and plug it into the // popup division cp.innerHTML = s.join(""); // we've now built the color picker colorPickerBuilt = true; } // remember the opener button as the value destination on close colorPickerTarget = ele; // move the popup under the opener buton var rc = getObjectRect(ele); cp.style.left = rc.x + "px"; cp.style.top = (rc.y + rc.height) + "px"; cp.style.display = "block"; openPopup(ev, cp, null, ele, ele); // Force the browser to recalculate the layout after digesting its // internal event queue. IE for one has problems figuring containing // box sizes on the first pass, but gets it right if we explicitly // resize the box (even setting it to its current size) later. setTimeout(function() { cp.style.width = getObjectRect(cp).width + "px"; }, 1); // get the current color var curColor = canonicalizeColor(getStyle(ele, "background-color")); // set the custom field to the current color var customFld = $("colorPickerCustom"); customFld.value = curColor.substr(1); // if there's a button showing the current selected color, focus it var sel = breadthSearch(cp, function(ele) { return (ele.nodeName == "A" && ele.className == "colorPickerButton" && ele.style.backgroundColor == curColor); }); // set up the Default button var dflt = $("colorPicker.colorPickerDefault"); if (hasDefault) { dflt.style.display = "block"; checkColorPickerDefault(ele.useDefaultColor); } else dflt.style.display = "none"; // if we have a selection, set focus there; otherwise set focus on // the custom field if (sel) sel.focus(); else { customFld.focus(); setSelRangeInEle(customFld); } } /* * handle a key in the color picker popup background */ function colorPopupKey(desc) { switch (desc.keyName) { case "Up": case "Down": case "Left": case "Right": // if I have focus directly (i.e., it's not in a child), // move focus to the first button on any arrow key if (document.activeElement == $("colorPicker")) $("colorPicker.").focus(); break; } // skip default actions on all keys return false; } /* * Click on a color picker opener button */ function colorBtnClick(ev, color) { // select the color, and de-select "Use Default" colorPickerApply(color, false); // close the popup closePopup(); // don't process the event further return false; } /* * apply a change to a color picker button */ function colorPickerApply(color, useDefault) { // set the color of the color element setColorButton(colorPickerTarget, color, useDefault); // generate an onchange event sendEvent(colorPickerTarget, createEvent("Event", "change")); } /* * click on the Use Default button in a color picker */ function colorDefaultClick(ev, ele) { // set Use Default and close the popop colorPickerApply(null, true); closePopup(); return false; } function checkColorPickerDefault(checked) { var span = $("colorPicker.colorPickerDefault.
"); var img = $("colorPicker.colorPickerDefault."); if (checked) { span.className = "on"; img.src = "/webuires/ckbox8pt-on.gif"; } else { span.className = "off"; img.src = "/webuires/ckbox8pt-off.gif"; } } /* * handle a key in a color picker button */ function colorBtnKey(desc, ctx) { var color = ctx[0], ele = ctx[1]; var b = null; var nextButton = function(b, dir) { var idx = parseInt(b.id.substr(18)); return $("colorPicker.colorPickerButton#" + (idx + dir)); }; switch (desc.keyName) { case "Up": for (var i = 0, b = ele ; i < 8 && b ; ++i) b = nextButton(b, -1); break; case "Down": for (var i = 0, b = ele ; i < 8 && b ; ++i) b = nextButton(b, 1); break; case "Left": b = nextButton(ele, -1); break; case "Right": b = nextButton(ele, 1); break; case "U+0020": case "Enter": // select the color, and de-select "Use Default" colorPickerApply(color, false); // close the popup and swallow the event closePopup(); cancelBubble(desc.event); preventDefault(desc.event); break; case "U+0009": // we're on a button, so tab into the field var fld = $("colorPickerCustom"); fld.focus(); setSelRangeInEle(fld); // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; } // move the focus to the new target, if we found one if (b) { // move focus to the new button, and select its color b.focus(); $("colorPickerCustom").value = b.style.backgroundColor.substr(1); // uncheck "Use default" checkColorPickerDefault(false); } // don't bother bubbling the event return false; } /* * Keystroke in color picker custom color field */ function colorPickFldKey(desc) { switch (desc.keyName) { case "Enter": var v = $("colorPickerCustom").value; if (v.charAt(0) != '#') v = '#' + v; if (v.match(/^#[0-9a-f]{6}$/i)) { // valid - select the color and de-select "Use Default" colorPickerApply(v, false); // close the popup closePopup(); } else { cancelBubble(desc.event); alert("Please use the HTML-style hexadecimal RRGGBB format. " + "For example, for bright red, enter FF0000."); } // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+0009": // if the custom color corresponds to a button, tab to the button var fld = $("colorPickerCustom"); var val = parseColor(fld.value); var b = breadthSearch($("colorPicker"), function(ele) { return (ele.nodeName == "A" && ele.className == "colorPickerButton" && parseColor(ele.style.backgroundColor) == val); }); if (b) { b.focus(); fld.value = b.style.backgroundColor.substr(1); } // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; default: // do the default processing for other keys return true; } } /* * Click a dialog button */ function clickDialogButton(n) { // get the button info var b = dialogStack.top().buttons[n]; // call the button function, or just dismiss the dialog if there isn't one if (b.onclick) b.onclick(); else dismissDialog(b); } /* * Dismiss the dialog */ function dismissDialog(btn) { // get the dialog info var dinfo = dialogStack.top(); var dlg = dinfo.ele; var bkg = dinfo.bkg; // if there's not button, it's the close box in the title bar - create // a pseudo button structure for it if (!btn) btn = { name: "(Close Box)", isCancel: true }; // call the dismiss callback, if present if (dinfo.dismiss && !dinfo.dismiss(dlg, btn)) return; // remove the dialog from the stack dialogStack.pop(); // remove focus from any active dialog element if (document.activeElement) document.activeElement.blur(); // remove them from the document document.body.removeChild(dinfo.canvas); document.body.removeChild(bkg); // move focus to the default input field in the document setDefaultFocus(); } /* * Dismiss a dialog by ID. If the given dialog is open, we'll cancel it * and any active child dialogs. */ function dismissDialogById(id, btn) { // scan to see if this dialog is open in the first place for (var i = 0 ; i < dialogStack.length ; ++i) { if (dialogStack[i].id == id) { // this is the one - dismiss all nested child dialogs while (dialogStack.length > i) dismissDialog(btn); // no need to look any further break; } } } /* * Set the default focus. If a dialog is active, set focus to the dialog. * Otherwise set the focus to the default input field in the default child * window. */ function setDefaultFocus(desc, fromWin) { // if the event is directed to a focus-keeping control, leave focus // where it is if (evtKeepsFocus(desc)) return; // if a dialog is active, set focus there; otherwise refer this to // the default focusable child window if (dialogStack.length) initDialogFocus(); else setDefaultFocusChild(desc, fromWin); // If the original event target isn't a control that reads keystrokes, // buffer the keystroke for the next time we want to read input. This // ensures that we won't drop keystrokes between command input field // activations. bufferKey(desc); } // Type-ahead buffer. When we receive keystrokes at the document level, // and focus isn't in a control that reads keystrokes, we'll buffer the // keys for future use when a focus-taking control is activated. // // The idea is that the IF user interface is "conversational": the UI // interaction consists primarily of alternating prompts and user // keyboard inputs. Users become accustomed to this and will anticipate // that a new command line (or other input reader) will be forthcoming // shortly after each input is completed, so they'll sometimes start // typing even before the new command line is visually activated. In // traditional terminal interfaces, these in-between keystrokes are // nearly always buffered. Our key buffering scheme here is designed to // simulate that behavior. var keybuf = []; function bufferKey(desc) { // don't buffer keystrokes if the server is waiting for an input // event - we'll instead send the key to the server (later) if (wantInputEvent) return; // if there's no descriptor, there's nothing to save if (!desc) return; // if the key has already been buffered, don't buffer it again if (desc.keyBufferedInMain) return; // discard CRs - IE9 sends CR LF for a newline, but we only want the LF if (desc.keyCode == 13) return; // note that the key has been buffered desc.keyBufferedInMain = true; // If the buffer's full, drop the oldest key. The limit is arbitrary; // the point isn't to conserve memory or anything like that, but simply // to limit the UI weirdness that can happen if we buffer too many // keystrokes. The problem is that if there's an extended period where // the UI is buffering keystrokes rather than reading them, then when we // do finally get around to activating an input control, the UI can seem // to be possessed by demons as the huge buffer plays back. Limiting // the buffer size avoids this - if the buffer's small enough, playback // will be virtually instantaneous, so no demons. There's really not // much of a tradeoff here, either, because most users will naturally // stop typing ahead anyway if the UI is unresponsive for any extended // period. if (keybuf.length >= 16) keybuf.shift(); // make a copy of the key event for buffering, but remove the event // object, as it might not be valid after the handler returns var d = desc.copy(false); d.event = { }; // buffer the event copy keybuf.push(d); } /* * If the main body should get focus at any point, and gets a keystroke, * send focus back to the default focus element. This is mostly for the * sake of dialog focus: we try to keep focus within the dialog by handling * keyboard events in the dialog itself, but there are odd edge cases where * key events bypass the dialog handlers and go to the browser's default * handler, which can set focus into the main body. This catch-all ensures * that if focus is outside the dialog while the dialog is active, any * keystrokes will trigger refocusing. */ function mainBodyKey(desc) { // if focus is in the main document body, move it back to the default // location (usually the command line, but sometimes a dialog object) if (isFieldKey(desc) && document.activeElement == document.body) setDefaultFocus(desc, window); // handle a generic document key genericDocKey(desc); // bubble the event return true; } /* * Handle a key within one of our popup DIVs */ function popupDivKey(desc) { // move focus back to the default location if (isFieldKey(desc)) setDefaultFocus(desc, window); // bubble the event return true; } /* * Handle a key in a dialog */ function dlgKey(desc, dlg) { // Check for explicit shortcut keys defined in the button list - these // override the default handling. Shortcut keys apply only if the // current focus element doesn't use the keystroke directly (e.g., if // focus is in a text field, regular characters go to the field as // text editing entries.) var idx, ch = desc.ch ? desc.ch.toUpperCase() : ""; if (!eleKeepsFocus(document.activeElement, desc) && (idx = dlg.buttons.indexWhich(function(b) { return b.key && (b.key == ch || b.key == desc.keyName); })) >= 0) { // click this button clickDialogButton(idx); // don't process the event any further cancelBubble(desc.event); preventDefault(desc.event); return false; } // check the key switch (desc.keyName) { case "Enter": // ignore this if the focus is in a button or link var e = document.activeElement; if ((e.nodeName == "INPUT" && e.type == "submit") || e.nodeName == "A") { // cancel the bubble, but let the default action occur cancelBubble(desc.event); return true; } // look for a default button for (var i = 0 ; i < dlg.buttons.length ; ++i) { // if this is the default button, click it if (dlg.buttons[i].isDefault) { // click the button clickDialogButton(i); break; } } // do no further processing on this key cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+001B": // look for a cancel button for (var i = 0 ; i < dlg.buttons.length ; ++i) { // if this is the cancel button, click it if (dlg.buttons[i].isCancel) { // click the cancel button clickDialogButton(i); break; } } // do no further processing on this key cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+0009": // check immediately after the browser processes the key to see // if focus has left the dialog, and move it back if so setTimeout(function() { // get the current focus and the active dialog var f = document.activeElement; // walk up the parent list to see if f is within the dialog for ( ; f && f != dlg.ele ; f = f.parentNode) ; // if not, move focus back to the dialog if (dlg.ele && !f) dlg.ele.focus(); }, 0); // allow the default processing to proceed return true; default: // do the normal thing for other keys return true; } } /* ------------------------------------------------------------------------ */ /* * Support functions for the TADS.swf Flash object. This is an invisible * Flash object that we use for certain functionality that we can't get * directly from the standard browser/javascript APIs. We use this for * programmatic access only; it doesn't have any visual or UI presence. * * Flash is ubiquitous, but it's not universal: we can't count on it being * available everywhere. Our Flash object is invisible, so there's nothing * visually missing when Flash isn't available, but we obviously won't have * access to the API extensions we use it for. We thus have to be careful * to recover gracefully when the Flash object isn't present: we can use * defaults, omit the desired functionality, or find some other approach to * get similar functionality (e.g., proprietary browser features). * * We use the Flash object for the following features: * * - Sound playback. Flash provides a good set of audio tools, including * concurrent playback of multiple sounds, support for common audio * formats, event notifications, volume control, and fades. * * - Font enumeration. Flash can give us a list of the fonts installed on * the system. */ var TADS_swf = { // Event coordinator for TADS.swf load completion. The swf object calls // loaded.fire() from its constructor via the Flash external interface // (the Flash mechanism that allows Flash scripts to call Javascript on // the embedding page). This is specified via the "onload" parameter to // the Flash object loader in the and tags that embed the // TADS.swf object, so be sure to specify the "onload" in those tags. loaded: new AbsEvent(), // cached font list fontList: null, // Wait for the font list to load, then proceed with the callback. // If the font list is already loaded, this simply invokes the callback // immediately. If the font list isn't loaded, we'll populate it. // // The reason that we have to structure this as a wait-for routine // with a callback is that we rely on a Flash object to enumerate // installed fonts, and Flash objects can load asynchronously. We // can't just assume that our Flash object is ready; we have to await // a go-ahead signal that it generates when it finished loading. // The wait-for structure lets us ensure that the Flash object is // ready before we try to invoke it. waitForFontList: function(doneFunc) { // First, we have to make sure we've loaded the Flash object. // Flash objects can be loaded asynchronously, so we have to // await the ready event from Flash before we can proceed. TADS_swf.loaded.whenDone(function() { // if we haven't already done so, build the font list if (!TADS_swf.fontList) { // Build the candidate font list. First try the TADS.swf // embedded Flash object. Flash isn't everywhere (especially // on mobile devices), so if we don't find it, fall back on // our canned list of candidate fonts. var fonts; try { // get the flash object var ie = navigator.userAgent.indexOf("Microsoft") != -1; var mobj = (ie ? window["__TADS_swf"] : document["__TADS_swf"]); // ask the Flash object for the list of installed fonts var sfonts = mobj.getFonts(); // Filter the list to keep only the first font of // each family. Style variations (bold, italic, etc) // are represented internally on most systems as whole // separate font objects, but for UI purposes we only // want to show one list entry per font family. // Keep only the font names in the final list. fonts = []; for (var i = 0 ; i < sfonts.length ; ++i) { // if this is a new name, add it to the list if (sfonts[i].fontStyle == "regular") fonts.push(sfonts[i].fontName); } } catch (e) { // An error occurred; we'll assume that the problem is // that Flash isn't available. Default to a canned list // of common fonts. We'll check each entry in a moment // to see if it's actually installed, so the final list // will only include fonts that are locally available. // Of course, it won't necessarily include *all* of the // available fonts, since we obviously can't include all // possible fonts in our prefab list. This list is only // meant to capture the basic set of fonts that most // operating systems include in their base installs. // Note that this list is actually a union of common // fonts from various operating systems, so it's unlikely // that all of them will be installed on any one machine. fonts = [ "Arial", "Arial Black", "Book Antiqua", "Charcoal", "Comic Sans MS", "Courier", "Courier New", "Gadget", "Geneva", "Georgia", "Helvetica", "Impact", "Lucida Console", "Lucida Grande", "Lucida Sans Unicode", "Monaco", "MS Sans Serif", "MS Serif", "New York", "Palatino", "Palatino Linotype", "Symbol", "Tahoma", "Times", "Times New Roman", "Trebuchet MS", "Verdana", "Webdings", "Wingdings", "Zapf Chancery", "ZapfChancery", "Zapf Dingbats" ]; } // sort the font list by name (ignoring case) fonts.sort(function(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); // save the list TADS_swf.fontList = fonts; } // the font list is now ready - fire the callback doneFunc(); }); } }; /* ------------------------------------------------------------------------ */ /* * Preferences dialog */ function showPrefsDialog() { showDialog({ id: 'prefs', title: "Customize Display", contents: $("prefsDialog"), buttons: [ { name: "OK", isDefault: true, save: true }, { name: "Cancel", isCancel: true } ], init: initPrefsDialog, dismiss: onClosePrefsDialog }); } // List of preference profiles. This is a table of preference objects, // keyed by profile ID. var prefProfiles = { }; // Currently selected profile, by ID. Start with a default profile // that doesn't exist, which will trigger automatic profile creation // when needed. var curPrefProfile = 0; // Next profile ID. This is a high water mark (plus one) of all of the // profiles we've seen since loading; when we create a new profile, we'll // assign it this ID and increment this value. var nextPrefProfile = 1; // Mapping between dialog elements and preference settings. Each key is // the ID of a dialog element; the corresponding value explains how to // map between the dialog element and the style setting. // // type: specifies the control type: // // "color" specifies a color picker control. The value is in the // picker's style.backgroundColor property. // // "font" specifies a font list var proNames = []; dlgProfiles.forEach(function(pro, id) { var name = pro.profileName || "Untitled"; proNames.push({ id: id, name: name, sortName: name.toLowerCase() }); }); // sort it by name proNames.sort(function(a, b) { return a.sortName.localeCompare(b.sortName); }); // populate the profile list with the profile names var sel = $(dlg, "curProfile"); var idx = 0; proNames.forEach(function(pro) { var o = new Option(pro.name, pro.id); try { sel.add(o); } catch (e) { sel.add(o, null); } }); // select the current item setSelectByValue(sel, dlgCurProfile); // set the preferences settings in the dialog prefsToDlg(dlg, dlgProfiles[dlgCurProfile], true); // set up onchange event handlers for all controls breadthSearch(dlg, function(ele) { // if this is a control with a value, set an onchange handler if (prefsMapper[ele.id]) ele.onchange = function(ev) { onPrefItemChange(ev, dlg, ele); }; // continue the traversal return false; }); } // dialog copy of the profiles var dlgProfiles = null; var dlgCurProfile = null; // translate profile descriptions from XML sent by the server to our // in-memory settings list function xmlToPrefs(prefs) { // replace the profile table with the new one var newPrefs = { }; // run through the list of children if (xmlHasChild(prefs, "profile")) { var pros = prefs.getElementsByTagName("profile"); for (var i = 0 ; i < pros.length ; ++i) { // create a new empty profile, and add it to the master list // under the given ID var pro = pros[i]; var tab = { }; var pid = xmlChildText(pro, "id"); newPrefs[pid] = tab; // if this is a new high-water mark, note it var pidn = parseInt(pid); if (pidn && pidn >= nextPrefProfile) nextPrefProfile = pidn + 1; // read the elements var items = pro.getElementsByTagName("item"); for (var j = 0 ; items && j < items.length ; ++j) { // retrieve the item ID and value var item = items[j]; var id = xmlChildText(item, "id"); var val = xmlChildText(item, "value"); // if it's an empty string value, use null if (val == "") val = null; // set the item tab[id] = val; } // if this entry doesn't have a name, give it a default if (!("profileName" in tab)) tab.profileName = "Untitled"; } } // set the current profile ID var newCur = null; if (xmlHasChild(prefs, "currentProfile")) newCur = xmlChildText(prefs, "currentProfile"); // if we have any profiles, and the current profile is defined, // commit the new settings; otherwise ignore them if (newPrefs.propCount() > 0 && newCur && newCur in newPrefs) { // replace the memory profile set with the newly loaded set prefProfiles = newPrefs; curPrefProfile = newCur; // update the display prefsToCSS(); } } // load a preference set into the live CSS function prefsToCSS(prefs, win) { // create a default profile, if necessary initDefProfile(); // if the preference set wasn't specified, use the current setting if (!prefs) prefs = prefProfiles[curPrefProfile]; // if no window was specified, apply to myself and all children if (!win) win = window; // Build a table of styles to set. Each entry is keyed by the // selector, converted to all upper-case for matching; each value // is a list of [property, value] pairs for the selector. var tab = { }; prefsMapper.forEach(function(m, id) { // get the selector var sels = m.selector.toUpperCase(); // break up the list of selectors in case there are multiples sels.split(",").forEach(function(sel) { // get the table entry for the selector, or add a new one if // it's not already there sel = sel.trim(); var e; if (sel in tab) e = tab[sel]; else tab[sel] = e = []; // add this style and its computed value to the list e.push([m.style, getPrefVal(prefs, id)]); }); }); // apply the styles to this window and its children win.applyCSS(tab); // recalc the window layout calcLayout(); } // load a preference set into the preferences dialog function prefsToDlg(dlg, prefs, init) { // visit all controls in the dialog breadthSearch(dlg, function(ele) { // if this control is in the mapper, set its value if (ele.id && prefsMapper[ele.id]) prefToControl(ele, prefs, init); // continue the tree traversal return false; }); } // Set the display value of a control in the preferences dialog based // on the current value function prefToControl(ele, prefs, init) { // get the element ID and preferences mapper for the item var id = ele.id, m = prefsMapper[id]; // get the value from the current preference set or defaults var val = prefs[id]; var defVal = getPrefVal(prefs, id); if (val == undefined) val = null; // note if it's a default, and if so, apply the default var isDef = (val == null); if (isDef) val = defVal; // set the element's value from 'prefs' switch (m.type) { case "color": // set the control display if it's changes or we're initialing if (init || ele.useDefaultColor != isDef || ele.style.backgroundColor != val) setColorButton(ele, val, isDef); break; case "font": case "fontsize": // set the value in the field if (isDef) val = "*" + val; // set it if it's changed or we're initializing if (init || ele.value != val) ele.value = val; break; default: if (m.checkbox) { // checkbox - check it if the 'on' value is active ele.checked = (val == m.checkbox[1]); } else if (ele.type == "select-one") { // select list - find the value in the option list var opts = ele.options; for (var i = 0 ; i < opts.length ; ++i) { // if this value matches, select it if (opts[i].value == val) { ele.selectedIndex = i; break; } } } else { // for anything else, just set the value directly if (isDef) val = "*" + val; // set it if we're initializing or the value changed if (init || ele.value != val) ele.value = val; } break; } } function setColorButton(ele, color, isDef) { ele.useDefaultColor = isDef; if (color) { ele.style.backgroundColor = color; ele.style.color = contrastColor(color); } ele.innerHTML = (isDef ? "*" : ""); } // Calculate a constrasting color. For most colors we'll just calculate // the RGB complement, but for middle grays that gives us roughly the // same color, so we'll simply use black. function contrastColor(color) { // parse the color to an int color = parseColor(color); // calculate the inverse of each component var r = 255 - ((color >> 16) & 0xff); var g = 255 - ((color >> 8) & 0xff); var b = 255 - (color & 0xff); // if the color is near a middle gray, the RGB complement is just // another middle gray, which isn't very contrasting; black is a // safe choice in these cases if (r >= 0x70 && r <= 0x90 && g >= 0x70 && g <= 0x90 && b >= 0x70 && b <= 0x90) r = g = b = 0; // generate the result color in #rrggbb format return unparseCColor(r, g, b); } // save the preferences dialog's current settings into the in-memory // preferences table function dlgToPrefs(dlg, prefs) { // Traverse the dialog DOM tree and save each control value. Note // that we use the "search" function for the traversal, but we're not // really doing a search - we just want to traverse the tree for the // side effects. We can coopt the search function for this purpose // simply by always returning false from the test callback: the // search will never find what it's looking for, so it'll traverse // the entire tree before giving up and returning null. breadthSearch(dlg, function(ele) { // if this item has a mapping, save its value if (prefsMapper[ele.id]) controlToPref(ele, prefs); // return false so that we continue the "search" return false; }); } // set the preferences array value for a given control in the dialog function controlToPref(ele, prefs) { // get the mapper var id = ele.id; var m = prefsMapper[ele.id]; // figure the value based on the type var val; switch (m.type) { case "color": val = ele.useDefaultColor ? null : ele.style.backgroundColor; break; case "font": case "fontsize": // if it starts with "*", it's a default val = ele.value; if (val.charAt(0) == '*') val = null; break; default: if (m.checkbox) val = m.checkbox[ele.checked ? 1 : 0]; else val = ele.value; break; } // save the value prefs[id] = val; } function getPrefVal(prefs, id) { // first look at the given preference set, then at the defaults for (var i = 0 ; i < 2 ; ++i) { // get the current set var s = [prefs, defaultPrefs][i]; // scan up the inheritance tree for this ID for (var cid = id ; cid ; ) { // get the mapper entry var m = prefsMapper[cid]; // if this set has a non-null value for this item, it's the result var val = s[cid]; if (val != null && val != undefined) return val; // get the parent ID cid = m ? m.parent : null; } } // didn't find it return null; } function onPrefItemChange(ev, dlg, ele) { // get the active profile var profile = dlgProfiles[dlgCurProfile]; // save this control's value in the current preference set controlToPref(ele, profile); // refresh all controls to show new inherited defaults as needed prefsToDlg(dlg, profile, false); } function newPrefProfile() { // Create a temporary name for the profile of the form "Profile N+1", // where N is either the maximum of any existing profiles of the form // "Profile n", otherwise simply the number of existing profiles. var n = null; dlgProfiles.forEach(function (pro) { if (pro.profileName.match(/^\s*profile\s+(\d+)\s*$/i)) { var m = parseInt(RegExp.$1); if (n == null || m > n) n = m; } }); // if we didn't find any matching profiles, use the number of profiles if (n == null) n = dlgProfiles.propCount(); // generate the name var initName = "Profile " + (n+1); // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // show the create/rename dialog createOrRenamePrefProfile("Create Profile", initName, function(newName) { // copy the current profile var newPro = dlgProfiles[dlgCurProfile].copy(false); // set the name newPro.profileName = newName; // make sure the next profile ID is above any existing IDs dlgProfiles.forEach(function(id) { id = parseInt(id); if (nextPrefProfile <= id) nextPrefProfile = id + 1; }); // generate a new profile ID and store it in the dialog table var newID = (nextPrefProfile++).toString(); dlgProfiles[newID] = newPro; // add it to the profile selector control var sel = $(prefsDlg, "curProfile"); addSelectOptionSort(sel, newName, newID); // switch to the new profile dlgCurProfile = newID; setSelectByValue(sel, newID); }); } function renamePrefProfile() { // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // run the rename dialog createOrRenamePrefProfile( "Rename Profile", dlgProfiles[dlgCurProfile].profileName, function(newName) { if (newName != dlgCurProfile) { // update the name in the profile table var pro = dlgProfiles[dlgCurProfile]; pro.profileName = newName; // update the ", buttons: [{ name: "OK", isDefault: true, save: true }, { name: "Cancel", isCancel: true }], dimensions: { width: "70ex" }, // on close, add the new profile if they clicked OK dismiss: function(dlg, btn) { if (btn && btn.save) { // get the new name and trim leading and trailing spaces var newName = $(dlg, "").value.trim(); // make sure the name is non-empty and unique var err = null; if (newName.match(/^\s+$/)) err = "Please enter a name for the new profile."; else if (newName != initName && dlgProfiles.propWhich(function(ele, prop) { return prop.trim().toLowerCase() == newName.toLowerCase(); })) err = "There's already a profile with this name. " + "Please choose a different name."; // if there's an error, show it and return failure if (err) { showDialog({ title: "Profile Name", contents: err }); return false; } // the name is valid - tell the caller to save it setName(newName); } // success - close the dialog return true; } }); } function delPrefProfile() { // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // don't allow deleting the last profile if (dlgProfiles.propCount() == 1) { $alert("This is the only profile. If you wish to delete this " + "profile, please create a replacement first.", "Error"); return; } // run the delete confirmation dialog $confirm("Do you really want to delete the profile \"" + dlgProfiles[dlgCurProfile].profileName + "\"?", function(del) { // proceed with the deletion if desired if (del) { // remove this profile from the field, which allows the user to * select a local file to upload to the server. * * TADS uses file uploads when running in client/server mode without a * network storage server. In this configuration, the game server doesn't * have anywhere else to put saved games and other files, so it uses * uploads and downloads to the client PC. This dialog handles the upload * side. */ function showUploadDialog(desc) { // get the dialog description var prompt = xmlChildText(desc, "prompt"); // initialization - fill in the iframe var init = function(dlg, params) { var doc = getIFrameDoc($(dlg, "
 

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%?
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%?
/* Copyright (c) 2010 by Michael J. Roberts. All Rights Reserved. */ /* * Main window scripts for TADS 3 Web UI. These scripts are for the * top-level layout window, which usually contains one or more "widget" * windows, such as the main game transcript window and the status line. */ /* ------------------------------------------------------------------------ */ /* * main window initialization */ function mainInit() { // the top window is always called "main" pageName = "main"; // check for the Flash plug-in if (getFlashPlayerVersion()) { // Flash is available, so load our SWF object. We don't embed the // object statically, because doing so provokes some browsers to // display a UI prompt about installing Flash every single time the // page is loaded, which is annoying to users who intentionally run // sans Flash. But now that we know Flash is present, we can safely // load our SWF, so dynamically insert the tag for it. $("__TADS_swf_div.
").innerHTML = "" + "" + "" + "" + "" + "" + "" + ""; } else { // The Flash plug-in isn't available, so we won't be able to run // our SWF object. Fire our load-completion event to signal that // there's nothing to wait for. TADS_swf.loaded.fire(); } // initialize the utilities and layout window packages utilInit(); layoutInit(); // initialize the XML request frame initXmlFrame(); // override some handlers window.onresize = mainResize; window.onGameEvent = mainGameEvent; window.onGameState = mainGameState; window.onUload = mainUnload; // Cancel any pending events from past incarnations, in case we just // refreshed the page. Lingering events from past loads refer to old // objects from the previous DOM tree, so they won't work with the new // incarnation of the page. serverRequest("/webui/flushEvents"); // get the initial state getInitState(); // cache some images that we might need if we lose our connection cacheImage("/webuires/dlg-corners.gif"); cacheImage("/webuires/dlg-title-corners.gif"); cacheImage("/webuires/modal-cover.png"); cacheImage("/webuires/errorPopupBkg.gif"); cacheImage("/webuires/warningPopupBkg.gif"); cacheImage("/webuires/modal-cover.png"); } /* ------------------------------------------------------------------------ */ /* * Main game window events and state */ // are we in an inputEvent() wait? var wantInputEvent = false; // event handler function mainGameEvent(req, resp) { // check for title changes if (xmlHasChild(resp, "setTitle")) document.title = xmlChildText(resp, "setTitle"); // check for preference settings changes if (xmlHasChild(resp, "setPrefs")) xmlToPrefs(xmlChild(resp, "setPrefs")[0]); // check for preference dialog invocations if (xmlHasChild(resp, "showPrefsDialog")) showPrefsDialog(); // check for file dialog invocations if (xmlHasChild(resp, "fileDialog")) showFileDialog(xmlChild(resp, "fileDialog")[0]); else if (xmlHasChild(resp, "uploadFileDialog")) showUploadDialog(xmlChild(resp, "uploadFileDialog")[0]); // check for downloadable files if (xmlHasChild(resp, "offerDownload")) { var file = xmlChildText(resp, "offerDownload"); addDownloadFile(file); addDownloadFrame(file); } if (xmlHasChild(resp, "cancelDownload")) { var file = xmlChildText(resp, "cancelDownload"); removeDownloadFile(file); removeDownloadFrame(file); } // check for input dialog invocations if (xmlHasChild(resp, "inputDialog")) showInputDialog(xmlChild(resp, "inputDialog")[0]); // check for event input waits and cancellations if (xmlHasChild(resp, "getInputEvent")) wantInputEvent = true; else if (xmlHasChild(resp, "cancelInputEvent")) wantInputEvent = false; // check for menu system events if (xmlHasChild(resp, "menusys")) showMenuSys(xmlChild(resp, "menusys")[0]); // "inherit" the layout window handling layoutGameEvent(req, resp); } // state handler function mainGameState(req, resp) { // check for title settings if (xmlHasChild(resp, "title")) document.title = xmlChildText(resp, "title"); // check for preference settings if (xmlHasChild(resp, "prefs")) xmlToPrefs(xmlChild(resp, "prefs")[0]); // "inherit" the layout window handling layoutGameState(req, resp); // check for file dialog invocations if (xmlHasChild(resp, "fileDialog")) showFileDialog(xmlChild(resp, "fileDialog")[0]); else if (xmlHasChild(resp, "uploadFileDialog")) showUploadDialog(xmlChild(resp, "uploadFileDialog")[0]); // check for input dialog invocations if (xmlHasChild(resp, "inputDialog")) showInputDialog(xmlChild(resp, "inputDialog")[0]); // check for input event waits if (xmlHasChild(resp, "getInputEvent")) wantInputEvent = true; // check for downloadable files if (xmlHasChild(resp, "offerDownload")) { var lst = xmlChild(resp, "offerDownload"); for (var i = 0 ; i < lst.length ; ++i) addDownloadFile(xmlNodeText(lst[i])); } // check for menu system dialogs if (xmlHasChild(resp, "menusys")) showMenuSys(xmlChild(resp, "menusys")[0]); // This is the main, top-level window, so once we get the initial // state set up, start the "event thread". This is only conceptually // a thread, since javascript is single-threaded, but it acts roughly // like a thread in that the browser sends the request asynchronously // and "resumes" our processing when the request completes, by // invoking our event callback. // Ask for our first event. Do this in a timeout rather than directly, // to allow the browser to process any outstanding events in its own // queue; this avoids a problem that shows up in Mobile Safari and IE9 // that seems to be related to the timing of receiving the IFRAME // contents on the initial page setup. A zero timeout is fine; we // don't need a delay per se, we just need to order things so that we // don't generate this request until the browser has finished processing // UI events already in its internal queue. setTimeout(function() { serverRequest( "/webui/getEvent", ServerRequest.Subscription, cbGetEvent); }, 0); } // getEvent request callback. This handles a response from the server to // a "get event" request we sent earlier. This allows the server to send // us events: we ask the server for an event via a request, and as soon as // the server has an event available, it replies to the request. function cbGetEvent(req, resp) { // get the target window var w = getEventWin(req, resp); // If we got found a window, forward the event. If we didn't find // the window, or it's not loaded yet, drop the event. This should // be okay, since the window will ask for a full status refresh when // it first loads - this will compensate for any missed events that // came in before it loaded. if (w && w.onGameEvent) w.onGameEvent(req, resp); // if we're shutting down, skip asking for another event if (xmlHasChild(resp, "shutdown")) { // display the disconnecting dialog showDialog({ title: "TADS", id: "shutdown", contents: "The game server has terminated the connection." }); // done return; } // go back for the next request serverRequest("/webui/getEvent", ServerRequest.Subscription, cbGetEvent); } // Get the window for a state/event reply function getEventWin(req, resp) { // if there's a window spec, get it; assume it's directed to the // main frame window otherwise if (xmlHasChild(resp, "window")) { // get the window from the path return windowFromAbsPath(xmlChildText(resp, "window")); } else { // no window spec, so use the main window return window; } } // get a window from the absolute path function windowFromAbsPath(path) { // turn the path string into a list if (typeof(path) == "string") path = path.split("."); // make sure it starts with our page name if (path.shift() != pageName) return null; // if it's just the one element, it's the main window if (path.length == 0) return window; // resolve the rest as a relative path return windowFromPath(path); } /* ------------------------------------------------------------------------ */ /* * Key event handler for inputEvent() requests from the server. When the * server program wants to solicit an event from the client, it sends us a * status message. We set the global variable * wantInputEvent to true when we receive this message; this tells us that * when we get a keystroke or hyperlink click event that isn't handled in * the UI itself, we should send it back to the server as an input event * message. * * All subwindows should forward their unhandled key events here so that we * can transmit the appropriate events to the server. Only unhandled * events should be sent here; if the event is handled within the client * UI, it doesn't have to be sent to the server. */ function keyToServer(desc) { // if the server wants an input event, send it if (desc && wantInputEvent) { // translate the key from DOM 3 naming to TADS naming var key = DOM3toTADSKey[desc.keyName] || desc.ch; // if we don't have a TADS translation for the key, don't send an // event for this key after all if (!key) return; // translate control keys to [ctrl-x] notation var ch = key.charCodeAt(0); if (ch == 10 || ch == 13) ch = '\n'; else if (ch < 32) key = "[ctrl-" + String.fromCharCode(ch + 64) + "]"; // send the event serverRequest("/webui/inputEvent?type=key¶m=" + encodeURIComponent(key)); // we're no longer waiting for an input event wantInputEvent = false; // consider this key handled - don't do the default browser action preventDefault(desc.event); } } /* event map from DOM 3 key names to TADS key names */ var DOM3toTADSKey = { "U+001B": "[esc]", "Up": "[up]", "Down": "[down]", "Left": "[left]", "Right": "[right]", "Home": "[home]", "End": "[end]", "U+007F": "[del]", "Insert": "[insert]", "ScrollLock": "[scroll]", "PageUp": "[page up]", "PageDown": "[page down]", "F1": "[f1]", "F2": "[f2]", "F3": "[f3]", "F4": "[f4]", "F5": "[f5]", "F6": "[f6]", "F7": "[f7]", "F8": "[f8]", "F9": "[f9]", "F10": "[f10]", "F11": "[f11]", "F12": "[f12]", "U+0008": "[bksp]" }; /* * Document-level click handler for inputEvent() requests from the server. * Unhandled hyperlink clicks should be sent here, so we can send them on * to the server as inputEvent() results. */ function clickToServer(ev, href,ele) { // if the server wants an input event, send it if (wantInputEvent) { // send the event to the server serverRequest("/webui/inputEvent?type=href¶m=" + encodeURIComponent(href)); // we're no longer waiting for an input event wantInputEvent = false; } } /* ------------------------------------------------------------------------ */ /* * resize the main window */ function mainResize() { // move the errorPopupDiv to the bottom right, if it's visible if (errorPopupVis) { var rc = getWindowRect(); var ediv = $("errorPopupDiv"); ediv.style.left = (rc.width - ediv.offsetWidth) + "px"; ediv.style.top = (rc.height - ediv.offsetHeight) + "px"; } if (downloadFiles.length) { var rc = getWindowRect(); var ddiv = $("downloadPopupDiv"); ddiv.style.left = (rc.width - ddiv.offsetWidth) + "px"; var top = rc.height - ddiv.offsetHeight; if (errorPopupVis) top -= ediv.offsetHeight; ddiv.style.top = top + "px"; } // reposition any dialogs for (var i = 0 ; i < dialogStack.length ; ++i) { if (!dialogStack[i].manuallyPositioned) positionDialog(dialogStack[i].ele, false); } // inherit the layout window handling calcLayout(); } /* ------------------------------------------------------------------------ */ /* * Close the window */ function mainUnload() { if (debugLogWin) { debugLogWin.close(); debugLogWin = null; } } /* ------------------------------------------------------------------------ */ /* * Error logging. This is used mainly for network connection errors. * Network errors are often due to temporary conditions - network * congestion, router latency, server hiccups. Many times, an XML request * will fail due to a network hiccup, but retrying the same request will * succeed. We therefore don't want to be too strident about network * errors, but at the same time we don't want to completely ignore them. * Our compromise is a small message panel that appears at the bottom * corner of the main window when an error occurs. This alerts the user to * the error, and offers an option to see more details. This doesn't * intrude too much into the UI visually, and doesn't intrude at all * modally, since there's no need to acknowledge or dismiss the popup. */ // error log var errorLog = []; // Is the error popup visible? This is the little button that we show // in the bottom right corner when a new error is available to be viewed. var errorPopupVis = false; // Add an error to the error log. 'msg' is the explanatory error message // text, and 'detail' is addition technical detail that might be helpful // for a programmer debugging the error (javascript stack trace, bad XML // text, HTTP result code, etc, as appropriate to the specific error // condition). // // 'id' is an optional object that tells us the status of an error that // might be possible to resolve automatically with retries. First, this // serves to tie together a series of errors that results from a series // of retries for the same operation. For example, for XML requests, // the 'id' is the request descriptor object. Tying the retry errors // to the original error is useful because (a) the error log can show // the relationship, so that the user doesn't think five separate things // went wrong when they were really five repeated failed attempts at // the same thing, and (b) if a retry eventually succeeds, the error log // can show that the earlier attempts were eventually resolved and thus // can be ignored. Second, the 'id' object has two fields that tell us // whether the operation is in fact being retried: // // id.failed - true if the operation has permanently failed and will not // be retried, false if the operation is being retried // id.succeeded - true if the operation has succeeded on a retry // // If 'id' is null, we'll supply a default. function logError(msg, detail, id) { // for testing purposes, it's sometimes useful to pop this up // in the separate debug log window, but we don't want to do // this in release builds // debugLog("Error: " + msg + "
" + detail + "

"); // if there's no 'id' object, supply a default if (!id) id = new LogErrorID(); // add the message at the start of the error list errorLog.unshift({msg: msg, detail: detail, id: id, viewed: false, timestamp: new Date()}); // if the error frame isn't visible, show it if (!errorPopupVis) { // mark it as visible, but make it completely transparent errorPopupVis = true; var d = $("errorPopupDiv"); setAlpha(d, 0); // do an explicit size recalc to position the popup mainResize(); // start fading it in startFade(d, 1, 700); } // If all unviewed errors are being retried automatically, set the // error popup background to orange. Otherwise set it to red. var allRetry = true; for (var i = 0 ; i < errorLog.length ; ++i) { // get this error var e = errorLog[i]; // if it's unviewed, and it's marked as failed, we have an error // that won't be retried if (!e.viewed && e.id.failed) { allRetry = false; break; } } // set the appropriate background color in the popup $("errorPopupDiv.[content]").style.background = (allRetry ? "#ff8000" : "#ff0000"); $("errorPopupDiv.[icon]").style.backgroundImage = (allRetry ? "url(/webuires/warningPopupBkg.gif)" : "url(/webuires/errorPopupBkg.gif)"); } function LogErrorID() { this.succeeded = false; this.failed = true; } function openErrorDetail(i) { $("errorDetailDiv-" + i).style.display = "block"; $("openErrorDetailLink-" + i).style.display = "none"; } // Un-log a network error. Call this when a request that failed with an // error subsequently succeeds on a retry. function unlogError(id) { // find the error in the error log var openCnt = 0; for (var i = 0 ; i < errorLog.length ; ++i) { // get this error var e = errorLog[i]; // if this error isn't resolved and hasn't been viewed, // count it as open if (!e.id.succeeded && !e.viewed) ++openCnt; } // if there are now no open errors, the error popup is no longer needed if (openCnt == 0) hideErrorPopup(); } // display the error log var errorLogViewCount = 0; function showErrorLog() { // build the error list var lst = []; for (var i = 0 ; i < errorLog.length ; ++i) { var msg = errorLog[i].msg; var id = errorLog[i].id; var detail = errorLog[i].detail; var ts = errorLog[i].timestamp; if (detail) { msg += "
" + ts.toLocaleTimeString() + (errorLogViewCount == 0 ? "" : errorLog[i].viewed ? " | Previously viewed" : " | New") + (id.succeeded ? " | Error was resolved automatically" : !id.failed ? " | Request is being retried automatically" : "") + " | " + "View Details" + "
" + "
" + detail + "
"; } lst.push(msg); } // count this view of the error log errorLogViewCount++; // mark all messages as viewed for (var i = 0 ; i < errorLog.length ; ++i) errorLog[i].viewed = true; // If there's a single error, show it as the whole dialog contents. // Otherwise show a scrolling list of the errors. var cont; if (errorLog.length == 0) { cont = "This window shows any errors encountered in the browser " + "since you loaded the page, including network problems " + "and javascript errors. So far, no errors have occurred " + "since you loaded or refreshed the page."; } else if (errorLog.length == 1) { // one error - just show the error cont = lst[0]; } else { // Multiple errors - show a scrolling list cont = "The game encountered the following errors (the most " + "recent is listed first):" + "
" + lst.join("
") + "
"; } // hide the error popup hideErrorPopup(); // show the dialog showDialog({ title: "Error Log", id: 'errorLog', contents: cont }); } // hide the error log popup function hideErrorPopup() { // move the popup division off-screen errorPopupVis = false; var d = $("errorPopupDiv"); startFade(d, -1, 250, function() { d.style.top = "-1000px"; mainResize(); }); } /* ------------------------------------------------------------------------ */ /* * Download popup. This appears at the lower right corner of the window, * just above the error popup if there is one. This shows the list of * outstanding downloadable files sent by the game server. */ // list of downloadable files var downloadFiles = []; // add a downloadable file function addDownloadFile(fname) { // add the file to the list downloadFiles.push(fname); // adjust the height var d = $("downloadPopupDiv"); // rebuild the download list rebuildDownloadDiv(); // if this is the first file, show the popup if (downloadFiles.length == 1) { // make it completely transparent, and start fading it in setAlpha(d, 0); startFade(d, 1, 700); } // do an explicit size recalc to position and resize the popup mainResize(); } // remove a downloadable file function removeDownloadFile(fname) { // find the file var idx = downloadFiles.indexOf(fname); if (idx >= 0) { // remove it from the list downloadFiles.splice(idx, 1); // rebuild the download list rebuildDownloadDiv(); // if the list is empty, hide the popup, otherwise resize it var cnt = downloadFiles.length; var d = $("downloadPopupDiv"); if (cnt == 0) { // empty - fade it out startFade(d, -1, 250, function() { d.style.top = "-1000px"; }); } else { // do an explicit resize to position and size the popup mainResize(); } } } // rebuild the download file division contents function rebuildDownloadDiv() { var s = ["Download Files"]; downloadFiles.forEach(function(fname) { var shortName = fname.replace(/^.*\//, "").htmlify(); var href = encodeURI(fname); s.push("
• " + "" + shortName + "" + " | Cancel" + "
"); }); $("downloadPopupDiv.[content]").innerHTML = s.join(""); } function cancelDownloadFile(fname) { serverRequest(fname + "?cancel"); } /* ------------------------------------------------------------------------ */ /* * Debugging messages. During development, it's often useful to instrument * the code with message displays; this provides a place to put them that's * less intrusive than 'alert()'. Messages written here are displayed in a * separate popup window that we open the first time this is called. */ var debugLogWin = null, debugLogLoaded = new AbsEvent(); function debugLog(msg) { // if the log window isn't open yet, or it's been closed, open it if (!debugLogWin || debugLogWin.closed) { debugLogLoaded.isDone = false; debugLogWin = window.open( "/webuires/debuglog.htm", "tadsDebugLog", "location=no, resizable=yes, scrollbars=yes, status=yes, " + "width=550, height=400"); } // when the log window is loaded, add the message debugLogLoaded.whenDone(function() { debugLogWin.debugLogPrint(msg); }); } /* ------------------------------------------------------------------------ */ /* * Show an HTML dialog. This shows the dialog as a foreground DIV within * the main window, not as an 'alert()' or new window popup. * * 'id' is an optional identification string for identifying the dialog * within the program. This is useful if you want to be sure that the * active dialog is a particular dialog, rather than a nested dialog it * opens. We don't make any of this here; it's purely for the dialog's own * use in other scripts. * * 'params' is an object with the properties listed below. All properties * are optional except the ones marked required. * * title: a string to display in the dialog's title bar. Required. * * contents: a string giving the HTML contents of the dialog, OR a DOM * element whose contents will be duplicated to populate the dialog. * Required. * * buttons: an array of button objects. Each object has these members: *. { *. name: string with the display name of the button *. isDefault: true if pressing the Return key activates this button *. isCancel: true if pressing the Escape key activates this button *. onclick: function to call when clicking the button; if not set, *. clicking the button simply dismisses the dialog *. key: the key that can be used to select this button; applies only *. when focus isn't in an input control; you can specify either an *. ordinary character (using upper-case for letters), or a key *. name (e.g., 'U+0008' for backspace) *. } * * If you omit the name of the button, the button won't be displayed, but * will still be available as a keystroke handler. * * init: a function to call to initialize the dialog. This is called after * the new dialog has been set up, but before it's been displayed. The * dialog's root element is passed as the argument - init(dialogElement). * * initFocus: the control that should initially receive focus. If this * isn't set, we'll search for a focusable control (INPUT, SELECT, ) and * set focus there. * * initAfterVisible: a function to call to initialize after the dialog has * been displayed and focus has been set. As with init(), this is called * with the dialog's root element as the argument. * * dismiss: a function to call when dismissing the dialog. This will be * called as dismiss(dialogElement, button), where 'button' is the button * object from the 'buttons' list corresponding to the button the user * clicked to close the dialog. Return true to allow the dialog to close, * false to prevent it from closing. You can use a false return to perform * validation. * * dimensions: an object { width:, height: } with style strings to use for * the dimensions of the dialog. If not provided, we'll let the browser * set dimensions based on the content size. * * fillWidth: true if the dialog contents should be stretched to fill the * whole width of the containing dialog. By default, we'll show the * contents at their natural width, centered in the dialog container. In * some cases we instead want the contents to scale according to the * container, though, such as when the dialog contains an iframe. Set this * to true to stretch the contents to the container width. */ var dialogStack = []; var fontSelectorOptions = null; function showDialog(params) { // get the contents, retrieving HTML source if it's a DOM object var contents = params.contents; if (typeof(contents) == "object") contents = contents.innerHTML; // assume all we'll need to do is show the dialog var go = function() { showDialogMain(params); }; // check for $[fontpicker] macros if (contents.match(/\$\[fontpicker([^\]]*)\]/)) { // we have one or more font pickers, so we need to build the // list of installed fonts before we can show the dialog var go1 = go; go = function() { TADS_swf.waitForFontList(go1); }; } // execute the dialog display procedure we've built up go(); } // main dialog builder function showDialogMain(params) { // figure the z-index for the new dialog var z = 10500 + dialogStack.length*10; var dlgID = "dialogDiv" + (dialogStack.length + 1); // create the global descriptor for the dialog var dlgDesc = { title: params.title, dd: dlgID, manuallyPositioned: false, dismiss: params.dismiss, initFocus: params.initFocus, id: params.id }; // push it onto the active dialog stack dialogStack.push(dlgDesc); // create the background cover for the new dialog var bkg = dlgDesc.bkg = document.createElement("div"); bkg.className = "dialogCoverDiv"; bkg.style.zIndex = z; document.body.appendChild(bkg); // create the canvas var canvas = dlgDesc.canvas = document.createElement("div"); canvas.className = "dialogCanvas"; canvas.style.zIndex = "" + (z + 1); document.body.appendChild(canvas); // create the new dialog division var dlg = dlgDesc.ele = document.createElement("div"); dlg.id = dlgID; dlg.innerHTML = $("dialogTemplate").innerHTML; dlg.className = "dialogDiv"; dlg.style.zIndex = "" + (z + 1); dlg.onkeydown = function(ev) { return $kd(ev, dlgKey, dlgDesc); }; dlg.onkeypress = function(ev) { return $kp(ev, dlgKey, dlgDesc); }; canvas.appendChild(dlg); // if dimensions were given, set them in the dialog element if (params.dimensions && params.dimensions.height) dlg.style.height = params.dimensions.height; if (params.dimensions && params.dimensions.width) dlg.style.width = params.dimensions.width; // get a couple of elements for further manipulation var ttl = $(dlg, "[dialogTitle]"); var cont = $(dlg, "[dialogContent]"); // if desired, stretch the content division to fill the dialog if (params.fillWidth) cont.style.width = "100%"; // fill in the title ttl.innerHTML = params.title; // set default buttons if none were given var buttons = params.buttons; if (!buttons) buttons = [{name: "OK", isDefault: true, isCancel: true}]; // save the buttons in the dialog descriptor dlgDesc.buttons = buttons || []; // if it's a pre-defined division, fetch its contents var contents = params.contents; if (typeof(contents) == "object") contents = contents.innerHTML; // expand $[fontpicker] macros var fontSizeOptions = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 36, 48, 72] .map(function(x) { return ""; }).join(""); fontSizeOptions = "" + fontSizeOptions; var fontPickers = []; var preFontCont = contents; contents = contents.replace( /\$\[fontpicker([^\]]*)\]/gm, function(match, attrs) { // parse the attributes var pattrs = parseTagAttrs(attrs); // add this to the font select pending list fontPickers.push({ attrs: attrs, pattrs: pattrs }); // Build the font picker: a combo for the font name, another // for the font size, and a color picker for the text color. // Set the font picker select list to initially empty, since // we might have to wait for the font lister thread to finish. return createCombo(pattrs.id, 40, "") + createCombo(pattrs.id + "Size", 5, fontSizeOptions); }); // note if we actually needed any font pickers - we do if we found // any $[fontpicker] macros to expand var needFonts = (contents != preFontCont); // expand $[colorpicker] macros var colorPickers = []; contents = contents.replace( /\$\[colorpicker([^\]]*)\]/gm, function(match, attrs) { var pattrs = parseTagAttrs(attrs); var btnID = dlgID + "." + pattrs.id; colorPickers[pattrs.id] = pattrs; return "" + ""; }); // expand $[checkbox] contents = contents.replace( /\$\[checkbox([^\]]*)\]/gm, function(match, attrs) { var pattrs = parseTagAttrs(attrs); return ""; }); // add the buttons to the contents if (buttons.length) { contents += "
"; for (var i = 0 ; i < buttons.length ; ++i) { var b = buttons[i]; if (b.name) { contents += "" + "" + b.name + ""; } } contents += "
"; } // fill in the contents cont.innerHTML = "
" + contents + "
"; // call the initializer function if (params.init) params.init(dlg, params); // initialize the contents and adjust the size immediately positionDialog(dlg, true); // just in case the browser needs a moment to finish processing the // innerHTML changes, resize it again in a few moments setTimeout(function() { // set the size again positionDialog(dlg, true); // set it opaque setAlpha(canvas, 100); // set the initial focus initDialogFocus(); // run the after-visible function if (params.initAfterVisible) setTimeout( function() { params.initAfterVisible(dlg, params); }, 10); }, 1); // If we needed any font pickers, load the system's font list, if // we haven't already. When that's done, populate the font pickers. if (needFonts) { // get the cached font list var fonts = TADS_swf.fontList; // build the font select list fontSelectorOptions = fonts.map(function(ele) { ele = ele.htmlify(); return "" }).join(""); // add "Default" as the first item fontSelectorOptions = "" + fontSelectorOptions; // populate each font picker for (var i = 0 ; i < fontPickers.length ; ++i) { // get this font picker's field, and from that get the sel.outerHTML = sel.outerHTML.replace( /<\/select>/im, fontSelectorOptions + "") .replace(/size=("[^"]+"|'[^']'|[^'"][^\s>]*)/im, "size=\"20\""); } else { // no outerHTML - replace just the option list sel.innerHTML = fontSelectorOptions; sel.size = "20"; } } } } /* * Initialize focus in the currently active dialog */ function initDialogFocus() { // ignore this if there's no dialog if (dialogStack.length == 0) return; // get the dialog description var desc = dialogStack.top(); var dlg = desc.ele; // get the initial focus control specified in the parameters var f; if (desc.initFocus) f = $(dlg, desc.initFocus); // if that wasn't specified, search for the first focusable object // within the dialog if (!f) { f = breadthSearch(dlg, function(ele) { return (ele.focus && (ele.nodeName == "INPUT" || ele.nodeName == "TEXTAREA" || ele.nodeName == "SELECT" || ele.nodeName == "A")); }); } // Move focus to the control, if we found one, or to the dialog // itself if not. If the dialog doesn't accept focus, just // remove focus from wherever it is now. if (f) { // move focus to the control f.focus(); // select its initial text if it's an INPUT or TEXTAREA if (f.nodeName == "INPUT" || f.nodeName == "TEXTAREA") setSelRangeInEle(f); } else if (dlg.focus) dlg.focus(); else if (document.activeElement) document.activeElement.blur(); } /* * Reposition a dialog for the current window size */ function positionDialog(dlg, init) { // get the dialog and window areas var drc = getObjectRect(dlg); var wrc = getWindowRect(); // center the dialog in the window var x = (wrc.width - drc.width)/2, y = (wrc.height - drc.height)/2; // if this is a sub-dialog, show it relative to the parent dialog // instead of centered if (init && dialogStack.length > 1) { // get the preceding dialog position var par = dialogStack[dialogStack.length - 2]; var prc = getObjectRect(par.ele); // move the new dialog below and right of the parent x = prc.x + 25; y = prc.y + 25; // mark this dialog and the parent as manually positioned par.manuallyPositioned = true; dialogStack.top().manuallyPositioned = true; } // set the position dlg.style.left = (x > 0 ? x : 0) + "px"; dlg.style.top = (y > 0 ? y : 0) + "px"; } /* * handle mouse tracking in a dialog title bar */ var dlgTrackInfo = null; function dlgTitleDown(ev) { // if there's no dialog, ignore it if (dialogStack.length == 0) return; // get the dialog and its bounding box var dlg = dialogStack.top().ele; var rc = getObjectRect(dlg); // get the event ev = getEvent(ev); // note the start offset of the tracking dlgTrackInfo = { x: ev.clientX - rc.x, y: ev.clientY - rc.y, dlg: dlg }; // track the mouse throughout the document addEventHandler(document, "mousemove", dlgTitleMove); addEventHandler(document, "mouseup", dlgTitleUp); addEventHandler(document, "selectstart", dlgTitleSelect); // Show the dialog's "move cover" division. This is a transparent // division that covers the whole dialog at a higher z-index than // any of its contents. The point is to block out any IFRAME within // the dialog for the duration of the move, so that events are directed // to our document rather than to the IFRAME's document. If we didn't // have this cover, mouse events would disappear into the IFRAME any // time the mouse moves quickly enough to stray into the IFRAME before // it generates a mousemove event within our title bar. Cross-domain // scripting limits prevent those events from bubbling up to our // document handlers if the IFRAME is showing an outside page (such // as from the storage server). The dialog cover blocks the IFRAME // and keeps all the events within our document. var cover = $(dlg, "[dialogMoveCover]"); cover.style.display = "block"; cover.style.width = rc.width + "px"; cover.style.height = rc.height + "px"; // skip the default handling return false; } // prevent text selection while tracking the mouse in IE function dlgTitleSelect(ev) { return false; } function dlgTitleMove(ev) { // if we're not tracking a dialog, skip it if (!dlgTrackInfo) return; // get the event ev = getEvent(ev); // move the dialog var dlg = dlgTrackInfo.dlg; dlg.style.left = (ev.clientX - dlgTrackInfo.x) + "px"; dlg.style.top = (ev.clientY - dlgTrackInfo.y) + "px"; // mark the dialog as manually moved dialogStack.top().manuallyPositioned = true; // suppress the default handling preventDefault(ev); cancelBubble(ev); return false; } function dlgTitleUp(ev) { // if we're not tracking a dialog, skip it if (!dlgTrackInfo) return; // remove the event handlers that we installed just for the move removeEventHandler(document, "mousemove", dlgTitleMove); removeEventHandler(document, "mouseup", dlgTitleUp); removeEventHandler(document, "selectstart", dlgTitleSelect); // remove the dialog cover division $(dlgTrackInfo.dlg, "[dialogMoveCover]").style.display = "none"; // done tracking dlgTrackInfo = null; // suppress the default handling preventDefault(ev); cancelBubble(ev); return false; } /* * Handle a keystroke in a color button in the main dialog. We'll let * Enter keystrokes get handled in the , but we'll block these from * bubbling up to the dialog. This lets us use the keyboard interface to * select a color button without dismissing the dialog. */ function colorbuttonKey(desc, ctx) { var ele = ctx[0]; var hasDefault = ctx[1]; if (desc.keyName == "Enter" || desc.keyName == "U+0020") { openColorPicker(desc.event, ele, hasDefault); cancelBubble(desc.event); preventDefault(desc.event); return false; } return true; } /* * Open a color picker */ var colorPickerBuilt = false; var colorPickerTarget = null; function openColorPicker(ev, ele, hasDefault) { // get the button and the color picker popup ele = $(ele); var cp = $("colorPicker"); // if it's already open, close it if (cp.style.display == "block") { closePopup(cp); return; } // build out the buttons if we haven't already done so if (!colorPickerBuilt) { var colors = ["ff8080", "ffff80", "80ff80", "ff80ff", "80ffff", "0080ff", "ff80c0", "ff80ff", "ff0000", "ffff00", "80ff00", "00ff40", "00ffff", "0080c0", "8080c0", "ff00ff", "804040", "ff8040", "00ff00", "008080", "004080", "8080ff", "800040", "ff0080", "800000", "ff8000", "008000", "008040", "0000ff", "0000A0", "800080", "8000ff", "400000", "804000", "004000", "004040", "000080", "000040", "400040", "400080", "000000", "808000", "808040", "808080", "408080", "C0C0C0", "400000", "ffffff"]; // set up the template for each button var keyTpl = "\"javascript:return @FUNC@" + "(event,colorBtnKey,['#@COLOR@',this]);\""; var btnTpl = ""; // build the button array - eight buttons per line for (var i = 0, s = [] ; i < colors.length ; ) { // build this line for (var j = 0, sl = [] ; j < 8 ; ++j, ++i) { // build this button and add it to the line list sl.push(btnTpl .replace(/@COLOR@/gm, colors[i]) .replace(/@I@/gm, i)); } // combine the line into a and add it to the master list s.push("" + sl.join("") + "
"); } // add the custom color entry field s.push("
Custom (rrggbb): " + "
"); // add the Use Default button s.push("
" + "
"); // combine the element list into a string and plug it into the // popup division cp.innerHTML = s.join(""); // we've now built the color picker colorPickerBuilt = true; } // remember the opener button as the value destination on close colorPickerTarget = ele; // move the popup under the opener buton var rc = getObjectRect(ele); cp.style.left = rc.x + "px"; cp.style.top = (rc.y + rc.height) + "px"; cp.style.display = "block"; openPopup(ev, cp, null, ele, ele); // Force the browser to recalculate the layout after digesting its // internal event queue. IE for one has problems figuring containing // box sizes on the first pass, but gets it right if we explicitly // resize the box (even setting it to its current size) later. setTimeout(function() { cp.style.width = getObjectRect(cp).width + "px"; }, 1); // get the current color var curColor = canonicalizeColor(getStyle(ele, "background-color")); // set the custom field to the current color var customFld = $("colorPickerCustom"); customFld.value = curColor.substr(1); // if there's a button showing the current selected color, focus it var sel = breadthSearch(cp, function(ele) { return (ele.nodeName == "A" && ele.className == "colorPickerButton" && ele.style.backgroundColor == curColor); }); // set up the Default button var dflt = $("colorPicker.colorPickerDefault"); if (hasDefault) { dflt.style.display = "block"; checkColorPickerDefault(ele.useDefaultColor); } else dflt.style.display = "none"; // if we have a selection, set focus there; otherwise set focus on // the custom field if (sel) sel.focus(); else { customFld.focus(); setSelRangeInEle(customFld); } } /* * handle a key in the color picker popup background */ function colorPopupKey(desc) { switch (desc.keyName) { case "Up": case "Down": case "Left": case "Right": // if I have focus directly (i.e., it's not in a child), // move focus to the first button on any arrow key if (document.activeElement == $("colorPicker")) $("colorPicker.").focus(); break; } // skip default actions on all keys return false; } /* * Click on a color picker opener button */ function colorBtnClick(ev, color) { // select the color, and de-select "Use Default" colorPickerApply(color, false); // close the popup closePopup(); // don't process the event further return false; } /* * apply a change to a color picker button */ function colorPickerApply(color, useDefault) { // set the color of the color element setColorButton(colorPickerTarget, color, useDefault); // generate an onchange event sendEvent(colorPickerTarget, createEvent("Event", "change")); } /* * click on the Use Default button in a color picker */ function colorDefaultClick(ev, ele) { // set Use Default and close the popop colorPickerApply(null, true); closePopup(); return false; } function checkColorPickerDefault(checked) { var span = $("colorPicker.colorPickerDefault.
"); var img = $("colorPicker.colorPickerDefault."); if (checked) { span.className = "on"; img.src = "/webuires/ckbox8pt-on.gif"; } else { span.className = "off"; img.src = "/webuires/ckbox8pt-off.gif"; } } /* * handle a key in a color picker button */ function colorBtnKey(desc, ctx) { var color = ctx[0], ele = ctx[1]; var b = null; var nextButton = function(b, dir) { var idx = parseInt(b.id.substr(18)); return $("colorPicker.colorPickerButton#" + (idx + dir)); }; switch (desc.keyName) { case "Up": for (var i = 0, b = ele ; i < 8 && b ; ++i) b = nextButton(b, -1); break; case "Down": for (var i = 0, b = ele ; i < 8 && b ; ++i) b = nextButton(b, 1); break; case "Left": b = nextButton(ele, -1); break; case "Right": b = nextButton(ele, 1); break; case "U+0020": case "Enter": // select the color, and de-select "Use Default" colorPickerApply(color, false); // close the popup and swallow the event closePopup(); cancelBubble(desc.event); preventDefault(desc.event); break; case "U+0009": // we're on a button, so tab into the field var fld = $("colorPickerCustom"); fld.focus(); setSelRangeInEle(fld); // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; } // move the focus to the new target, if we found one if (b) { // move focus to the new button, and select its color b.focus(); $("colorPickerCustom").value = b.style.backgroundColor.substr(1); // uncheck "Use default" checkColorPickerDefault(false); } // don't bother bubbling the event return false; } /* * Keystroke in color picker custom color field */ function colorPickFldKey(desc) { switch (desc.keyName) { case "Enter": var v = $("colorPickerCustom").value; if (v.charAt(0) != '#') v = '#' + v; if (v.match(/^#[0-9a-f]{6}$/i)) { // valid - select the color and de-select "Use Default" colorPickerApply(v, false); // close the popup closePopup(); } else { cancelBubble(desc.event); alert("Please use the HTML-style hexadecimal RRGGBB format. " + "For example, for bright red, enter FF0000."); } // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+0009": // if the custom color corresponds to a button, tab to the button var fld = $("colorPickerCustom"); var val = parseColor(fld.value); var b = breadthSearch($("colorPicker"), function(ele) { return (ele.nodeName == "A" && ele.className == "colorPickerButton" && parseColor(ele.style.backgroundColor) == val); }); if (b) { b.focus(); fld.value = b.style.backgroundColor.substr(1); } // bypass all the normal handling cancelBubble(desc.event); preventDefault(desc.event); return false; default: // do the default processing for other keys return true; } } /* * Click a dialog button */ function clickDialogButton(n) { // get the button info var b = dialogStack.top().buttons[n]; // call the button function, or just dismiss the dialog if there isn't one if (b.onclick) b.onclick(); else dismissDialog(b); } /* * Dismiss the dialog */ function dismissDialog(btn) { // get the dialog info var dinfo = dialogStack.top(); var dlg = dinfo.ele; var bkg = dinfo.bkg; // if there's not button, it's the close box in the title bar - create // a pseudo button structure for it if (!btn) btn = { name: "(Close Box)", isCancel: true }; // call the dismiss callback, if present if (dinfo.dismiss && !dinfo.dismiss(dlg, btn)) return; // remove the dialog from the stack dialogStack.pop(); // remove focus from any active dialog element if (document.activeElement) document.activeElement.blur(); // remove them from the document document.body.removeChild(dinfo.canvas); document.body.removeChild(bkg); // move focus to the default input field in the document setDefaultFocus(); } /* * Dismiss a dialog by ID. If the given dialog is open, we'll cancel it * and any active child dialogs. */ function dismissDialogById(id, btn) { // scan to see if this dialog is open in the first place for (var i = 0 ; i < dialogStack.length ; ++i) { if (dialogStack[i].id == id) { // this is the one - dismiss all nested child dialogs while (dialogStack.length > i) dismissDialog(btn); // no need to look any further break; } } } /* * Set the default focus. If a dialog is active, set focus to the dialog. * Otherwise set the focus to the default input field in the default child * window. */ function setDefaultFocus(desc, fromWin) { // if the event is directed to a focus-keeping control, leave focus // where it is if (evtKeepsFocus(desc)) return; // if a dialog is active, set focus there; otherwise refer this to // the default focusable child window if (dialogStack.length) initDialogFocus(); else setDefaultFocusChild(desc, fromWin); // If the original event target isn't a control that reads keystrokes, // buffer the keystroke for the next time we want to read input. This // ensures that we won't drop keystrokes between command input field // activations. bufferKey(desc); } // Type-ahead buffer. When we receive keystrokes at the document level, // and focus isn't in a control that reads keystrokes, we'll buffer the // keys for future use when a focus-taking control is activated. // // The idea is that the IF user interface is "conversational": the UI // interaction consists primarily of alternating prompts and user // keyboard inputs. Users become accustomed to this and will anticipate // that a new command line (or other input reader) will be forthcoming // shortly after each input is completed, so they'll sometimes start // typing even before the new command line is visually activated. In // traditional terminal interfaces, these in-between keystrokes are // nearly always buffered. Our key buffering scheme here is designed to // simulate that behavior. var keybuf = []; function bufferKey(desc) { // don't buffer keystrokes if the server is waiting for an input // event - we'll instead send the key to the server (later) if (wantInputEvent) return; // if there's no descriptor, there's nothing to save if (!desc) return; // if the key has already been buffered, don't buffer it again if (desc.keyBufferedInMain) return; // discard CRs - IE9 sends CR LF for a newline, but we only want the LF if (desc.keyCode == 13) return; // note that the key has been buffered desc.keyBufferedInMain = true; // If the buffer's full, drop the oldest key. The limit is arbitrary; // the point isn't to conserve memory or anything like that, but simply // to limit the UI weirdness that can happen if we buffer too many // keystrokes. The problem is that if there's an extended period where // the UI is buffering keystrokes rather than reading them, then when we // do finally get around to activating an input control, the UI can seem // to be possessed by demons as the huge buffer plays back. Limiting // the buffer size avoids this - if the buffer's small enough, playback // will be virtually instantaneous, so no demons. There's really not // much of a tradeoff here, either, because most users will naturally // stop typing ahead anyway if the UI is unresponsive for any extended // period. if (keybuf.length >= 16) keybuf.shift(); // make a copy of the key event for buffering, but remove the event // object, as it might not be valid after the handler returns var d = desc.copy(false); d.event = { }; // buffer the event copy keybuf.push(d); } /* * If the main body should get focus at any point, and gets a keystroke, * send focus back to the default focus element. This is mostly for the * sake of dialog focus: we try to keep focus within the dialog by handling * keyboard events in the dialog itself, but there are odd edge cases where * key events bypass the dialog handlers and go to the browser's default * handler, which can set focus into the main body. This catch-all ensures * that if focus is outside the dialog while the dialog is active, any * keystrokes will trigger refocusing. */ function mainBodyKey(desc) { // if focus is in the main document body, move it back to the default // location (usually the command line, but sometimes a dialog object) if (isFieldKey(desc) && document.activeElement == document.body) setDefaultFocus(desc, window); // handle a generic document key genericDocKey(desc); // bubble the event return true; } /* * Handle a key within one of our popup DIVs */ function popupDivKey(desc) { // move focus back to the default location if (isFieldKey(desc)) setDefaultFocus(desc, window); // bubble the event return true; } /* * Handle a key in a dialog */ function dlgKey(desc, dlg) { // Check for explicit shortcut keys defined in the button list - these // override the default handling. Shortcut keys apply only if the // current focus element doesn't use the keystroke directly (e.g., if // focus is in a text field, regular characters go to the field as // text editing entries.) var idx, ch = desc.ch ? desc.ch.toUpperCase() : ""; if (!eleKeepsFocus(document.activeElement, desc) && (idx = dlg.buttons.indexWhich(function(b) { return b.key && (b.key == ch || b.key == desc.keyName); })) >= 0) { // click this button clickDialogButton(idx); // don't process the event any further cancelBubble(desc.event); preventDefault(desc.event); return false; } // check the key switch (desc.keyName) { case "Enter": // ignore this if the focus is in a button or link var e = document.activeElement; if ((e.nodeName == "INPUT" && e.type == "submit") || e.nodeName == "A") { // cancel the bubble, but let the default action occur cancelBubble(desc.event); return true; } // look for a default button for (var i = 0 ; i < dlg.buttons.length ; ++i) { // if this is the default button, click it if (dlg.buttons[i].isDefault) { // click the button clickDialogButton(i); break; } } // do no further processing on this key cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+001B": // look for a cancel button for (var i = 0 ; i < dlg.buttons.length ; ++i) { // if this is the cancel button, click it if (dlg.buttons[i].isCancel) { // click the cancel button clickDialogButton(i); break; } } // do no further processing on this key cancelBubble(desc.event); preventDefault(desc.event); return false; case "U+0009": // check immediately after the browser processes the key to see // if focus has left the dialog, and move it back if so setTimeout(function() { // get the current focus and the active dialog var f = document.activeElement; // walk up the parent list to see if f is within the dialog for ( ; f && f != dlg.ele ; f = f.parentNode) ; // if not, move focus back to the dialog if (dlg.ele && !f) dlg.ele.focus(); }, 0); // allow the default processing to proceed return true; default: // do the normal thing for other keys return true; } } /* ------------------------------------------------------------------------ */ /* * Support functions for the TADS.swf Flash object. This is an invisible * Flash object that we use for certain functionality that we can't get * directly from the standard browser/javascript APIs. We use this for * programmatic access only; it doesn't have any visual or UI presence. * * Flash is ubiquitous, but it's not universal: we can't count on it being * available everywhere. Our Flash object is invisible, so there's nothing * visually missing when Flash isn't available, but we obviously won't have * access to the API extensions we use it for. We thus have to be careful * to recover gracefully when the Flash object isn't present: we can use * defaults, omit the desired functionality, or find some other approach to * get similar functionality (e.g., proprietary browser features). * * We use the Flash object for the following features: * * - Sound playback. Flash provides a good set of audio tools, including * concurrent playback of multiple sounds, support for common audio * formats, event notifications, volume control, and fades. * * - Font enumeration. Flash can give us a list of the fonts installed on * the system. */ var TADS_swf = { // Event coordinator for TADS.swf load completion. The swf object calls // loaded.fire() from its constructor via the Flash external interface // (the Flash mechanism that allows Flash scripts to call Javascript on // the embedding page). This is specified via the "onload" parameter to // the Flash object loader in the and tags that embed the // TADS.swf object, so be sure to specify the "onload" in those tags. loaded: new AbsEvent(), // cached font list fontList: null, // Wait for the font list to load, then proceed with the callback. // If the font list is already loaded, this simply invokes the callback // immediately. If the font list isn't loaded, we'll populate it. // // The reason that we have to structure this as a wait-for routine // with a callback is that we rely on a Flash object to enumerate // installed fonts, and Flash objects can load asynchronously. We // can't just assume that our Flash object is ready; we have to await // a go-ahead signal that it generates when it finished loading. // The wait-for structure lets us ensure that the Flash object is // ready before we try to invoke it. waitForFontList: function(doneFunc) { // First, we have to make sure we've loaded the Flash object. // Flash objects can be loaded asynchronously, so we have to // await the ready event from Flash before we can proceed. TADS_swf.loaded.whenDone(function() { // if we haven't already done so, build the font list if (!TADS_swf.fontList) { // Build the candidate font list. First try the TADS.swf // embedded Flash object. Flash isn't everywhere (especially // on mobile devices), so if we don't find it, fall back on // our canned list of candidate fonts. var fonts; try { // get the flash object var ie = navigator.userAgent.indexOf("Microsoft") != -1; var mobj = (ie ? window["__TADS_swf"] : document["__TADS_swf"]); // ask the Flash object for the list of installed fonts var sfonts = mobj.getFonts(); // Filter the list to keep only the first font of // each family. Style variations (bold, italic, etc) // are represented internally on most systems as whole // separate font objects, but for UI purposes we only // want to show one list entry per font family. // Keep only the font names in the final list. fonts = []; for (var i = 0 ; i < sfonts.length ; ++i) { // if this is a new name, add it to the list if (sfonts[i].fontStyle == "regular") fonts.push(sfonts[i].fontName); } } catch (e) { // An error occurred; we'll assume that the problem is // that Flash isn't available. Default to a canned list // of common fonts. We'll check each entry in a moment // to see if it's actually installed, so the final list // will only include fonts that are locally available. // Of course, it won't necessarily include *all* of the // available fonts, since we obviously can't include all // possible fonts in our prefab list. This list is only // meant to capture the basic set of fonts that most // operating systems include in their base installs. // Note that this list is actually a union of common // fonts from various operating systems, so it's unlikely // that all of them will be installed on any one machine. fonts = [ "Arial", "Arial Black", "Book Antiqua", "Charcoal", "Comic Sans MS", "Courier", "Courier New", "Gadget", "Geneva", "Georgia", "Helvetica", "Impact", "Lucida Console", "Lucida Grande", "Lucida Sans Unicode", "Monaco", "MS Sans Serif", "MS Serif", "New York", "Palatino", "Palatino Linotype", "Symbol", "Tahoma", "Times", "Times New Roman", "Trebuchet MS", "Verdana", "Webdings", "Wingdings", "Zapf Chancery", "ZapfChancery", "Zapf Dingbats" ]; } // sort the font list by name (ignoring case) fonts.sort(function(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); // save the list TADS_swf.fontList = fonts; } // the font list is now ready - fire the callback doneFunc(); }); } }; /* ------------------------------------------------------------------------ */ /* * Preferences dialog */ function showPrefsDialog() { showDialog({ id: 'prefs', title: "Customize Display", contents: $("prefsDialog"), buttons: [ { name: "OK", isDefault: true, save: true }, { name: "Cancel", isCancel: true } ], init: initPrefsDialog, dismiss: onClosePrefsDialog }); } // List of preference profiles. This is a table of preference objects, // keyed by profile ID. var prefProfiles = { }; // Currently selected profile, by ID. Start with a default profile // that doesn't exist, which will trigger automatic profile creation // when needed. var curPrefProfile = 0; // Next profile ID. This is a high water mark (plus one) of all of the // profiles we've seen since loading; when we create a new profile, we'll // assign it this ID and increment this value. var nextPrefProfile = 1; // Mapping between dialog elements and preference settings. Each key is // the ID of a dialog element; the corresponding value explains how to // map between the dialog element and the style setting. // // type: specifies the control type: // // "color" specifies a color picker control. The value is in the // picker's style.backgroundColor property. // // "font" specifies a font list var proNames = []; dlgProfiles.forEach(function(pro, id) { var name = pro.profileName || "Untitled"; proNames.push({ id: id, name: name, sortName: name.toLowerCase() }); }); // sort it by name proNames.sort(function(a, b) { return a.sortName.localeCompare(b.sortName); }); // populate the profile list with the profile names var sel = $(dlg, "curProfile"); var idx = 0; proNames.forEach(function(pro) { var o = new Option(pro.name, pro.id); try { sel.add(o); } catch (e) { sel.add(o, null); } }); // select the current item setSelectByValue(sel, dlgCurProfile); // set the preferences settings in the dialog prefsToDlg(dlg, dlgProfiles[dlgCurProfile], true); // set up onchange event handlers for all controls breadthSearch(dlg, function(ele) { // if this is a control with a value, set an onchange handler if (prefsMapper[ele.id]) ele.onchange = function(ev) { onPrefItemChange(ev, dlg, ele); }; // continue the traversal return false; }); } // dialog copy of the profiles var dlgProfiles = null; var dlgCurProfile = null; // translate profile descriptions from XML sent by the server to our // in-memory settings list function xmlToPrefs(prefs) { // replace the profile table with the new one var newPrefs = { }; // run through the list of children if (xmlHasChild(prefs, "profile")) { var pros = prefs.getElementsByTagName("profile"); for (var i = 0 ; i < pros.length ; ++i) { // create a new empty profile, and add it to the master list // under the given ID var pro = pros[i]; var tab = { }; var pid = xmlChildText(pro, "id"); newPrefs[pid] = tab; // if this is a new high-water mark, note it var pidn = parseInt(pid); if (pidn && pidn >= nextPrefProfile) nextPrefProfile = pidn + 1; // read the elements var items = pro.getElementsByTagName("item"); for (var j = 0 ; items && j < items.length ; ++j) { // retrieve the item ID and value var item = items[j]; var id = xmlChildText(item, "id"); var val = xmlChildText(item, "value"); // if it's an empty string value, use null if (val == "") val = null; // set the item tab[id] = val; } // if this entry doesn't have a name, give it a default if (!("profileName" in tab)) tab.profileName = "Untitled"; } } // set the current profile ID var newCur = null; if (xmlHasChild(prefs, "currentProfile")) newCur = xmlChildText(prefs, "currentProfile"); // if we have any profiles, and the current profile is defined, // commit the new settings; otherwise ignore them if (newPrefs.propCount() > 0 && newCur && newCur in newPrefs) { // replace the memory profile set with the newly loaded set prefProfiles = newPrefs; curPrefProfile = newCur; // update the display prefsToCSS(); } } // load a preference set into the live CSS function prefsToCSS(prefs, win) { // create a default profile, if necessary initDefProfile(); // if the preference set wasn't specified, use the current setting if (!prefs) prefs = prefProfiles[curPrefProfile]; // if no window was specified, apply to myself and all children if (!win) win = window; // Build a table of styles to set. Each entry is keyed by the // selector, converted to all upper-case for matching; each value // is a list of [property, value] pairs for the selector. var tab = { }; prefsMapper.forEach(function(m, id) { // get the selector var sels = m.selector.toUpperCase(); // break up the list of selectors in case there are multiples sels.split(",").forEach(function(sel) { // get the table entry for the selector, or add a new one if // it's not already there sel = sel.trim(); var e; if (sel in tab) e = tab[sel]; else tab[sel] = e = []; // add this style and its computed value to the list e.push([m.style, getPrefVal(prefs, id)]); }); }); // apply the styles to this window and its children win.applyCSS(tab); // recalc the window layout calcLayout(); } // load a preference set into the preferences dialog function prefsToDlg(dlg, prefs, init) { // visit all controls in the dialog breadthSearch(dlg, function(ele) { // if this control is in the mapper, set its value if (ele.id && prefsMapper[ele.id]) prefToControl(ele, prefs, init); // continue the tree traversal return false; }); } // Set the display value of a control in the preferences dialog based // on the current value function prefToControl(ele, prefs, init) { // get the element ID and preferences mapper for the item var id = ele.id, m = prefsMapper[id]; // get the value from the current preference set or defaults var val = prefs[id]; var defVal = getPrefVal(prefs, id); if (val == undefined) val = null; // note if it's a default, and if so, apply the default var isDef = (val == null); if (isDef) val = defVal; // set the element's value from 'prefs' switch (m.type) { case "color": // set the control display if it's changes or we're initialing if (init || ele.useDefaultColor != isDef || ele.style.backgroundColor != val) setColorButton(ele, val, isDef); break; case "font": case "fontsize": // set the value in the field if (isDef) val = "*" + val; // set it if it's changed or we're initializing if (init || ele.value != val) ele.value = val; break; default: if (m.checkbox) { // checkbox - check it if the 'on' value is active ele.checked = (val == m.checkbox[1]); } else if (ele.type == "select-one") { // select list - find the value in the option list var opts = ele.options; for (var i = 0 ; i < opts.length ; ++i) { // if this value matches, select it if (opts[i].value == val) { ele.selectedIndex = i; break; } } } else { // for anything else, just set the value directly if (isDef) val = "*" + val; // set it if we're initializing or the value changed if (init || ele.value != val) ele.value = val; } break; } } function setColorButton(ele, color, isDef) { ele.useDefaultColor = isDef; if (color) { ele.style.backgroundColor = color; ele.style.color = contrastColor(color); } ele.innerHTML = (isDef ? "*" : ""); } // Calculate a constrasting color. For most colors we'll just calculate // the RGB complement, but for middle grays that gives us roughly the // same color, so we'll simply use black. function contrastColor(color) { // parse the color to an int color = parseColor(color); // calculate the inverse of each component var r = 255 - ((color >> 16) & 0xff); var g = 255 - ((color >> 8) & 0xff); var b = 255 - (color & 0xff); // if the color is near a middle gray, the RGB complement is just // another middle gray, which isn't very contrasting; black is a // safe choice in these cases if (r >= 0x70 && r <= 0x90 && g >= 0x70 && g <= 0x90 && b >= 0x70 && b <= 0x90) r = g = b = 0; // generate the result color in #rrggbb format return unparseCColor(r, g, b); } // save the preferences dialog's current settings into the in-memory // preferences table function dlgToPrefs(dlg, prefs) { // Traverse the dialog DOM tree and save each control value. Note // that we use the "search" function for the traversal, but we're not // really doing a search - we just want to traverse the tree for the // side effects. We can coopt the search function for this purpose // simply by always returning false from the test callback: the // search will never find what it's looking for, so it'll traverse // the entire tree before giving up and returning null. breadthSearch(dlg, function(ele) { // if this item has a mapping, save its value if (prefsMapper[ele.id]) controlToPref(ele, prefs); // return false so that we continue the "search" return false; }); } // set the preferences array value for a given control in the dialog function controlToPref(ele, prefs) { // get the mapper var id = ele.id; var m = prefsMapper[ele.id]; // figure the value based on the type var val; switch (m.type) { case "color": val = ele.useDefaultColor ? null : ele.style.backgroundColor; break; case "font": case "fontsize": // if it starts with "*", it's a default val = ele.value; if (val.charAt(0) == '*') val = null; break; default: if (m.checkbox) val = m.checkbox[ele.checked ? 1 : 0]; else val = ele.value; break; } // save the value prefs[id] = val; } function getPrefVal(prefs, id) { // first look at the given preference set, then at the defaults for (var i = 0 ; i < 2 ; ++i) { // get the current set var s = [prefs, defaultPrefs][i]; // scan up the inheritance tree for this ID for (var cid = id ; cid ; ) { // get the mapper entry var m = prefsMapper[cid]; // if this set has a non-null value for this item, it's the result var val = s[cid]; if (val != null && val != undefined) return val; // get the parent ID cid = m ? m.parent : null; } } // didn't find it return null; } function onPrefItemChange(ev, dlg, ele) { // get the active profile var profile = dlgProfiles[dlgCurProfile]; // save this control's value in the current preference set controlToPref(ele, profile); // refresh all controls to show new inherited defaults as needed prefsToDlg(dlg, profile, false); } function newPrefProfile() { // Create a temporary name for the profile of the form "Profile N+1", // where N is either the maximum of any existing profiles of the form // "Profile n", otherwise simply the number of existing profiles. var n = null; dlgProfiles.forEach(function (pro) { if (pro.profileName.match(/^\s*profile\s+(\d+)\s*$/i)) { var m = parseInt(RegExp.$1); if (n == null || m > n) n = m; } }); // if we didn't find any matching profiles, use the number of profiles if (n == null) n = dlgProfiles.propCount(); // generate the name var initName = "Profile " + (n+1); // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // show the create/rename dialog createOrRenamePrefProfile("Create Profile", initName, function(newName) { // copy the current profile var newPro = dlgProfiles[dlgCurProfile].copy(false); // set the name newPro.profileName = newName; // make sure the next profile ID is above any existing IDs dlgProfiles.forEach(function(id) { id = parseInt(id); if (nextPrefProfile <= id) nextPrefProfile = id + 1; }); // generate a new profile ID and store it in the dialog table var newID = (nextPrefProfile++).toString(); dlgProfiles[newID] = newPro; // add it to the profile selector control var sel = $(prefsDlg, "curProfile"); addSelectOptionSort(sel, newName, newID); // switch to the new profile dlgCurProfile = newID; setSelectByValue(sel, newID); }); } function renamePrefProfile() { // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // run the rename dialog createOrRenamePrefProfile( "Rename Profile", dlgProfiles[dlgCurProfile].profileName, function(newName) { if (newName != dlgCurProfile) { // update the name in the profile table var pro = dlgProfiles[dlgCurProfile]; pro.profileName = newName; // update the ", buttons: [{ name: "OK", isDefault: true, save: true }, { name: "Cancel", isCancel: true }], dimensions: { width: "70ex" }, // on close, add the new profile if they clicked OK dismiss: function(dlg, btn) { if (btn && btn.save) { // get the new name and trim leading and trailing spaces var newName = $(dlg, "").value.trim(); // make sure the name is non-empty and unique var err = null; if (newName.match(/^\s+$/)) err = "Please enter a name for the new profile."; else if (newName != initName && dlgProfiles.propWhich(function(ele, prop) { return prop.trim().toLowerCase() == newName.toLowerCase(); })) err = "There's already a profile with this name. " + "Please choose a different name."; // if there's an error, show it and return failure if (err) { showDialog({ title: "Profile Name", contents: err }); return false; } // the name is valid - tell the caller to save it setName(newName); } // success - close the dialog return true; } }); } function delPrefProfile() { // note the preferences dialog object var prefsDlg = dialogStack.top().ele; // don't allow deleting the last profile if (dlgProfiles.propCount() == 1) { $alert("This is the only profile. If you wish to delete this " + "profile, please create a replacement first.", "Error"); return; } // run the delete confirmation dialog $confirm("Do you really want to delete the profile \"" + dlgProfiles[dlgCurProfile].profileName + "\"?", function(del) { // proceed with the deletion if desired if (del) { // remove this profile from the field, which allows the user to * select a local file to upload to the server. * * TADS uses file uploads when running in client/server mode without a * network storage server. In this configuration, the game server doesn't * have anywhere else to put saved games and other files, so it uses * uploads and downloads to the client PC. This dialog handles the upload * side. */ function showUploadDialog(desc) { // get the dialog description var prompt = xmlChildText(desc, "prompt"); // initialization - fill in the iframe var init = function(dlg, params) { var doc = getIFrameDoc($(dlg, "