From 47b9b68328f7a091911b67edd0393b8c5e244e76 Mon Sep 17 00:00:00 2001 From: ville rantanen Date: Mon, 1 Oct 2018 17:07:36 +0300 Subject: [PATCH] markslider-packager --- reporting/markslider.packager.sh | 45 +++++++++++ reporting/markslider.py | 126 +++++++++++++++++-------------- reporting/markslider.tar.gz | Bin 0 -> 11336 bytes 3 files changed, 113 insertions(+), 58 deletions(-) create mode 100644 reporting/markslider.packager.sh create mode 100644 reporting/markslider.tar.gz diff --git a/reporting/markslider.packager.sh b/reporting/markslider.packager.sh new file mode 100644 index 0000000..b3fcaea --- /dev/null +++ b/reporting/markslider.packager.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e +set -x + +rm -rf markslider +mkdir -p markslider/scripts markslider/markslider +cp -v ansi.py md_color.py markslider.py markslider/markslider/ + +echo '#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +import re +import sys + +from markslider import main + +if __name__ == "__main__": + sys.exit(main()) +' > markslider/scripts/markslider + +echo 'from markslider import *' > markslider/markslider/__init__.py + +echo 'Markdown slideshow. Display your markdown file directly in the terminal!' > markslider/README.md +echo '[metadata] +description-file = README.md' > markslider/setup.cfg + +echo "from distutils.core import setup +setup( + name = 'markslider', + packages = ['markslider'], + scripts = ['scripts/markslider'], + version = '$( ./markslider.py -v 2>&1 )', + description = 'Markdown viewer as slides', + author = 'Ville Rantanen', + author_email = 'ville.q.rantanen@gmail.com', + url = 'https://bitbucket.org/MoonQ/tools', + download_url = 'https://bitbucket.org/MoonQ/tools/raw/tip/reporting/markslider.tar.gz', + keywords = ['markdown', 'console'], + classifiers = [], + license = 'MIT', +)" > markslider/setup.py + +tar czf markslider.tar.gz markslider +rm -rf markslider + diff --git a/reporting/markslider.py b/reporting/markslider.py index c71b032..949a608 100755 --- a/reporting/markslider.py +++ b/reporting/markslider.py @@ -23,7 +23,7 @@ __author__ = "Ville Rantanen " __version__ = "0.9" import sys,os,argparse,re,datetime -from argparse import ArgumentParser +from argparse import ArgumentParser import traceback,tty,termios,subprocess,signal sys.path.append(os.path.dirname(os.path.realpath(__file__))) import ansi,md_color @@ -48,7 +48,7 @@ class EndProgram( Exception ): pass class slide_reader: - """ Class for reading files. """ + """ Class for reading files. """ def __init__(self,files,opts): self.filename=files[0] self.files=files @@ -65,9 +65,9 @@ class slide_reader: self.re_image_convert=re.compile("(.*)(!\[.*\])\((.*)\)>") self.re_command=re.compile("(.*)`(.*)`>(.*)") #~ self.control_chars = ''.join(map(unichr, range(0,32) + range(127,160))) - #~ self.control_char_re = re.compile('[%s]' % re.escape(self.control_chars)) + #~ self.control_char_re = re.compile('[%s]' % re.escape(self.control_chars)) self.read() - + def read(self): ''' Read a file, set pages and data ''' @@ -111,7 +111,7 @@ class slide_reader: self.max_height=max(self.max_height, len(page)) for row in page: self.max_width=max(self.max_width, len(row)) - + def get_data(self): return self.data def get_current_filename(self): @@ -127,13 +127,13 @@ class slide_reader: return self.data[page] except IndexError: return None - + def get_pages(self): return self.pages def get_current_page(self): return self.data[self.page] - + def get_page_no(self): return self.page def inc_page_no(self,inc=1,loop=False): @@ -160,7 +160,7 @@ class slide_reader: return self.max_height def get_max_width(self): return self.max_width - + def get_toc(self,display_position=False): title=self.opts.toc if self.opts.toc else "Table of Contents" TOC=["# "+title,""] @@ -186,7 +186,7 @@ class slide_reader: subh=[ subh[0], subh[1], subh[2]+1 ] TOC.append(" %d.%d.%d.%d. %s"%(h1+1,subh[0],subh[1],subh[2],title)) return TOC - + def toc(self): if self.opts.toc: TOC=self.get_toc() @@ -207,7 +207,7 @@ class slide_reader: return [s] def launch(self,command): - """ Launch in a string using tags `command`> + """ Launch in a string using tags `command`> Remove empty lines from beginning and end of stdout. """ #~ if not self.opts.execute_read: @@ -230,7 +230,7 @@ class slide_reader: # return [s] def convert_image(self,image): - """ comnvert image using tags ![]()> + """ comnvert image using tags ![]()> Remove empty lines from beginning and end of stdout. """ #~ 2=title @@ -248,9 +248,9 @@ class slide_reader: return return_value # return [s] - + def get_interactive_help_text(): - return ''' left/right,page up/down,home,end + return ''' left/right,page up/down,home,end change page c list contents (toc) M modify file with VIM @@ -261,7 +261,7 @@ def get_interactive_help_text(): ,/. scroll page up/down move highlight enter execute highlighted line - h help''' + h help''' def setup_options(): ''' Create command line options ''' @@ -274,21 +274,21 @@ Special syntaxes: * Execute shell: ` command -switch `! Beware of malicious code! * Execute and print output: ` command `> Beware of malicious code! * Convert images to ASCII, with jp2a: ![](file.jpg) - + Keyboard shortcuts: '''+get_interactive_help_text() - + parser=ArgumentParser(description=usage, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__author__) parser.add_argument("-v","--version",action="version",version=__version__) parser.add_argument("--export",action="store",dest="screenshots",default=False, type=str, help="Take screenshots of the slideshow in the given folder.") - + content = parser.add_argument_group('content') execution = parser.add_argument_group('execution') control = parser.add_argument_group('controls') - + content.add_argument("--center",action="store_true",dest="center",default=False, help="Center slides on screen.") content.add_argument("--dc",action="store_true",dest="dark_colors",default=False, @@ -297,15 +297,15 @@ Keyboard shortcuts: help="Disable color by markdown structure.") content.add_argument("--no-color","-n",action="store_false",dest="color",default=True, help="Disable color.") - + execution.add_argument("-e",action="store_true",dest="execute",default=False, help="Execute commands in `! or `> tags at show time with Enter key. WARNING: Potentially very dangerous to run others' slides with this switch!") execution.add_argument("-E",action="store_true",dest="execute_read",default=False, help="Execute commands in ``> tags at file read time. WARNING: Potentially very dangerous to run others' slides with this switch!") - + control.add_argument("--exit",action="store_true",dest="exit_last",default=False, help="Exit after last slide.") - + control.add_argument("-s",action="store_false",dest="menu",default=True, help="Disable status bar.") control.add_argument("--timer",action="store",dest="slideTimer",default=False, type=int, @@ -315,10 +315,10 @@ Keyboard shortcuts: content.add_argument("--toc",action="store",dest="toc",default=False, const="Table of Contents", type=str, nargs='?', help="Insert table of contents. Define the header, or use default: %(const)s") - content.add_argument("--toc-page",action="store",dest="toc_page",default=2, type=int, + content.add_argument("--toc-page",action="store",dest="toc_page",default=2, type=int, help="Insert table of contents on a chosen page. default: %(const)s") - content.add_argument("--toc-depth",action="store",dest="toc_depth",default=2, type=int, - choices=xrange(1,5), + content.add_argument("--toc-depth",action="store",dest="toc_depth",default=2, type=int, + choices=list(range(1,5)), help="Table of contents display depth. default: %(const)s") parser.add_argument("files",type=str, nargs='+', help="File(s) to show") @@ -332,7 +332,7 @@ Keyboard shortcuts: def page_print(reader,opts,offset): ''' Print a page ''' - + page=reader.get_current_page() scrsize=opts.size # clear page @@ -351,7 +351,7 @@ def page_print(reader,opts,offset): else: coloring="${U}${Y}" print(align_pad+colorify(coloring+page[0],opts)+bc.Z) - + # Print page rows if not opts.wrap: page=[cut_line(row,scrsize[1]-1) for row in page] @@ -373,12 +373,12 @@ def page_print(reader,opts,offset): row_lines=int(float(len(row))/scrsize[1]) else: row_lines=0 - row_lines=0 + row_lines=0 colored_row=colored[row_i]#=colorify(row,opts) if offset[1]==r+1+offset[0]: colored_row=add_highlight(row,opts) sys.stdout.write(align_pad+colored_row) - + if r>=scrsize[0]-2: break r+=row_lines+1 @@ -387,8 +387,8 @@ def page_print(reader,opts,offset): return def print_menu(reader,opts): - - bc.posprint( opts.size[0], 0, + + bc.posprint( opts.size[0], 0, colorify("${y}%d${Z}/%d %s|%s"%( 1+reader.get_page_no(), reader.get_pages(), @@ -459,7 +459,7 @@ def browser(opts,files): try: reader=slide_reader(files,opts) except: - print "Error in reading the file:" + print("Error in reading the file:") for o in sys.exc_info(): print(o) sys.exit(1) @@ -499,7 +499,7 @@ def browser(opts,files): #~ print(inkey) if inkey in [113,3,120]: #print('Exited in: ') - return + return if inkey in [67,54,32]: # PGDN or space if opts.exit_last: if reader.page+1==reader.pages: @@ -527,7 +527,7 @@ def browser(opts,files): reader.read() offset=offset_change(opts,reader,offset,(0, 0)) if inkey==ord('M'): - modify_file(reader,offset) + modify_file(reader,offset) reader.read() offset=offset_change(opts,reader,offset,(0, 0)) if inkey==ord(','): @@ -541,8 +541,8 @@ def browser(opts,files): if inkey==13: # enter launch(reader,opts,offset) break - - + + except IOError: pass except KeyboardInterrupt: @@ -550,8 +550,8 @@ def browser(opts,files): except EndProgram: pass except: - print "Unexpected error:" - print traceback.format_exc() + print("Unexpected error:") + print(traceback.format_exc()) sys.exit(1) def get_console_size(): @@ -568,9 +568,9 @@ def colorify(s,opts): def cut_line(s,i): """ cut a color tagged string, and remove control chars """ s=s[:i] - s=re.sub("\$$","", - re.sub("\$\{$","", - re.sub("\$\{.$","", + s=re.sub("\$$","", + re.sub("\$\{$","", + re.sub("\$\{.$","", s))) return s def add_highlight(s,opts): @@ -596,7 +596,7 @@ def launch(reader,opts,offset): images = re.findall('!\[[^]]+\]\([^\)]+\)', s) if s.find("`")==-1 and len(urls)==0 and len(images)==0: return - + run_command=re.match("(.*)`(.*)`!(.*)",s) show_command=re.match("(.*)`(.*)`>(.*)",s) if show_command != None: @@ -614,24 +614,24 @@ def launch(reader,opts,offset): inkey=getch.get() return if run_command != None: - subprocess.call(run_command.group(2), + subprocess.call(run_command.group(2), shell=True,executable="/bin/bash") inkey=getch.get() return # Open URLS last if len(urls)>0: # Remove ) at the end of url: [name](link) markdown syntax - subprocess.call("xdg-open '%s' &"%(urls[0].rstrip(")"),), + subprocess.call("xdg-open '%s' &"%(urls[0].rstrip(")"),), stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True) return if len(images)>0: image = re.sub('.*\(([^\)]+)\).*', "\\1",images[0]) - subprocess.call("xdg-open '%s' &"%(image,), + subprocess.call("xdg-open '%s' &"%(image,), stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True) return - return + return def modify_file(reader,offset): row=1 @@ -642,23 +642,33 @@ def modify_file(reader,offset): row+=len(reader.data[page]) if (page+1) in row_restarts: row=1 - subprocess.call("vim +%d -c 'exe \"normal! zt\"' -c %d %s"%(row,row+offset[1],reader.get_current_filename()), + subprocess.call("vim +%d -c 'exe \"normal! zt\"' -c %d %s"%(row,row+offset[1],reader.get_current_filename()), shell=True) - + def take_screenshot(reader,opts): out_file=os.path.join(opts.screenshots,"slide%03d.png"%(reader.page+1)) if not os.path.exists(opts.screenshots): os.mkdir(opts.screenshots) - subprocess.call("sleep 0.5; import -window $WINDOWID '%s'"%(out_file,), + subprocess.call("sleep 0.5; import -window $WINDOWID '%s'"%(out_file,), stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True) -bc=ansi.code() -getch=getch() -opts=setup_options() -browser(opts,opts.files) -if opts.screenshots: - print("\n\nCrop the images for terminal tabs, and PDFize e.g.:\n- mogrify -chop 0x50 %s/*png\n- convert %s/*png %s.pdf"%( - opts.screenshots, - opts.screenshots, - os.path.basename(opts.screenshots), - )) + + +def main(): + global bc + global getch + global opts + bc = ansi.code() + getch = getch() + opts = setup_options() + + browser(opts,opts.files) + if opts.screenshots: + print("\n\nCrop the images for terminal tabs, and PDFize e.g.:\n- mogrify -chop 0x50 %s/*png\n- convert %s/*png %s.pdf"%( + opts.screenshots, + opts.screenshots, + os.path.basename(opts.screenshots), + )) + +if __name__ == "__main__": + main() diff --git a/reporting/markslider.tar.gz b/reporting/markslider.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a73ef9241a51f8e6819e9baa00a63432867b6c0f GIT binary patch literal 11336 zcmV-OEVt7iiwFStDY9Dt1MEC&ciT3y{j6Vs(DgAHQ?w*M()BHUuj@Fe?24-}JY)cXxIMI}aW}d%w5a+xv#?{$(5$p1DZen6YnuUomb~|9|K+ z&DUReu|G@1=huS|U;hF0e_j7C@ieS|-t?Jh-~#XM?6j``V6a_T|DD0^;2YNaO!SvN zfAjTUySFtL@z%8;Zt?Ifn=O(_6b?FTtb4!9ylCi$<9#+yM&0kQrQ=U$QJk=tr=P?^ zbULFrnzCGNnChFle%R^wBjz}vJLQhUY@2D0gYAx^?ROYFfWYFnexhTmZWx`;|F=l~ zR!?F7Pyeb=;3Mq+-p=+{`~M}L=Jj_RKlBsFv1W_UKx38t?``+1_W!}o-dFqoC7u@3 zegCT!{(C&l`#=BqIV`|(`@aYKf3LFtcenSx#(!VrX^j6M{F9-dO_5(`4H}N3*&_DG zlY|X={XO=R9|WA8xnbglJfsm96JM}d9F1dl%HZQD=A4OWl-#&6-)D7wqu#CHwjC?CkLL;^lwASXdb7ww_t@!8Rj z0CD)k$&-_dm%u-JbaHWee16UzJv(EE?D^r@#mUi&Cx>V3`HQpX&(4o6#?Co1#Q}b` zh)1+mAjct3Tt5(U>Mvn&1uz&4*~ERzVUa!VzXkSO(4^Vo&MOG0+#m|aG+zM81qy7Q zj93^YCKJH!_md=aBfBMcq@=N#MS{a)!EqCc$(RMy)e|OTkH}9b_GJoO(4)V@K47 zTNQhN1%?t37ItP!lLq;;J|+z?!NQ5cQelr29%zhBLoRz)^=={i${ms^`!ou9xdVyV z_@~@FbH{+FS8uGgk(#30P8{mKtZ;PW50gm)-3dqk)zC3@Z=E(E>}v&t{p;4q$K3I! zz!&VA5UfFB#~iK047k=c-MVk+_g-CE_g`HZuXOzS$~e%B8VZ1zf@`b+zotJ2__qMH z_NN2{l1Ur|jyD0D0ouK>Vf_~Qp+0qI`aJZ#No+DOTw|{H%lwy! zl%QyUw7}BF<+`}qVC&e%h3C$=UgyK8%?>u2(a98%zE_L{YW^9tgW1OAMx&oF(hv+7 z7z&>D!yQzpagmnj(8!NP;>e9P!eGIE>tfk~*@L@oFRHliKPd*X{)cf$kh4I5VuXO^14gXm~4xt|`EJc1Bh81HB zGt;$K;j5TwMh$Ka?c@+VDmer6u1!24b{qUgGgH@~0dLwcPsoh0Bx1B&pb#J#QiT>m z7L5}aLFn3=W{|=tp_Kcevzej#>RrSQGE+3cJnHFc83(;)l=x)onEK=lsuiS73#5R6 zII$|-0EJ#*0wE>DDa1Bg@T65oYLomxjY(Ov_-%rdq1lZotV_uFL2IIe&?|2DE)u(= zq`=Swk>Gl{wP}N=c#v|2yug@ZD(|xG3R|)3LumGs8_fA}97nOPJpvMJ7=cMbaZ=pt z!lFopt(x>C@^qugT9wI`NBE&f+&d7w^eTP1)9Tebl*j6oj22?bY!*`6uZyh!3?KDM zn@K{VbuikD685$%XwGs8H#JGrQd^4Aql1iAy@Lv)vWb)cym=gh<~eEHQj0%@Gl-&@bkNn{7YA?JFsf)NvYzjI4O^%ZvMLxP(XuTFAQw}&-$?7AFrg`!m?adtdp`KCF52YZe%M0nM$(`ESVL;N$<5Buvga= z$=5P{kt6DmB#>o*G@t~R8_s2L`KTK3N}AcA^0q){L+U;B#Vl|a&MXo>CYCiNPy8fM z62^=S-BhM^Spx?crd_z#;O`*FJCd$lXc?A^XGiv>w#Kwg!e?q)Zd0VWwDl575^1+@ zw9qR1tx4ZZU020clU7Lr`&Si9EoX*+kg{4Tie0OQut2{mBZ?O%eLL5?t|C#}J(7mz ztP4k5*X7*H_(3vh=%K9-Ew(OT;H1CVH)(Xmjf(m8#J=pA@V~1nn`QY8!A?&O`gN92 zU;!R(wYuV*aU9nwnF47@?gV#Z4`SwD*VZn7UAxjuBGM8*=To77K>jda)`S1EdKB9v zDf|x7Dho`ZY82dZIobALzdep^oa9H%(v%W%Z2t}eM5M>(ydu|X@V_291I;+-HW;?_K&)~=1}-oG#TE5U0Z7#tc8|{PnpL^zPUQr31y;xor&%0Q zJ>9F*2f8!@sIofQA~}UZBGl<`O_eW2VZjuCv`i_J<;8kJBXI3qhL#=lZ7%R{;*JG- zt$kZL`2n@aU}IT5l$JYJhRE#FPgoM*Yk|ol3}&(B0q9=3NZ2qz2O=lrMJUW-9MKnGH_M`7`b)AW7rY zrY`KJdzV+b@z?F7flc16*tBg+CX^WNuCAJzEVxwmzt4{!?|0cB?6=v#W!)}a?g{uH zQ@#g1*45UDPA2BBT3yiUqG&!;r!&JN(>;^L@0akRS z(+UDFR{}mtwy2;(CRg*>7T$oHlW59Kp6V8O`w%+i8HxuDfe+z`3Ks;^A!q?^pF)SK z-@sr)Uf%z7^0dQ#XH4E*UdPc5UShMD*a#wbNb$`q<^oVrJw}HK=uD#V7;r$qlFS9W zb^#{IoAI_hW_rwj7Z#|j{EFxK-UJviw=5>SI0^#AvYHj6T{(f-1~Sj)C?(>JoKxNf zVGM5D4x2zP%8xFYP`Ho)&S#D+0TFuPg7yeJTaxWbnS7~X*_G{FU;u&Nuq5F;D@jNT z4^$~awL+nDKI5M6223nKMz`s~&wWV2anJ5E8G)vr{@Zs)?@ibpJm%~O zhYnJrw=D1|hIGPg&5$UShqwi3ICR%DqfbM>+)NyD9I6t1awX_K_Cfy@nX(H z5OE@~Y2?m>L>^VO&nH>TAS6#>leVd?UAS*JDj zxX=dv9ch_2)F5?HD{TiJ7%c_LhAA+0HNKh>3=$h<_G(h~$dFZ}>LlQ&Q_-d)?jltw z$RlYSrQEPON($A|jHRp$y=80+!O+PvqC2jz812aISI3XZj!SA%n?FgAk*@Ybv5y_lc41SjK`p_4Y8WSAW9%jZJv~=!)=jE$p z7)D)1RqKXJ8T=&XGOf>av&soywwB6ie#kne23#3yfqM}QKgbEpHU-nJbQ|R0r9D0- zHGIPt7S=(Yo}513XU`*)n~!x_SbMnu<$(AkMnezQJf4Rv!ir9@k*aTAM=0IRJQ>l{o262_iQU;pNR47xgq%U^-jmNIiG-?|>aa z>bFj2!S#ngSJ)f>z0rskkNJ!{; zr$rn093X7Wg7Ck*nM?o;#S#)~&STZW!DAUusqCzR%WCEYMcIIC8|A>{y*68^uqEqR4c)VjvJfV**CwRi1V z)S{qLk;q_mAI3iqT#rvEMkl-ZUGH|V%Va-;P%C!-GXi>XF*>sXV*Njls}P-&~N} zRnO}OQ`ux;er3)@SUIPEAvhv6Ge)Vytjf4)xia-GlPZ$C#2ta39eI|6Gldye=7$Uv zsAAhCx^oOlm?y3%5Ab z*x$@yuj-O4D~Uo!Dk9J;>7yWW6FsXa+{$&vSR%e0N3XN2O^MmTDX7nC7Hf947f@A6 z{Y%WtN%gO6JKikLv1u6zKRUGRbg}`ddVd8k5^iG1g{#|AqA@z9#W^V0r|zIkyj+YO zZ`zpzHv8>V^Xq^@*C?2a)bvVMq`VNC00$%bq89@7N`1pD64J)7%#TosK`(RLnV7*e zE#9vWzkT=1`>pjMTNi&&)j(-V{mp^}PRsu)fki*>Rb3|A=B}x+O>I-_YWF1$_>z`D6-VyC$Xqt;kIWNp>e{>g zU$srJue!uwY-;Z{!;;6gx`yYn8nmHm{=u|VY)AlET$suX=QV99!v#ZIWMy}A0n8v1 z@Akj15(Wwx>AdE?2);+)?X#}!X$h>itB)e$hi~}8rWgNkJLqN2jl2)hjEiSSnL>~< z6t)?nsueWSounL2HEqQz#=8|TE>oda^%*P?WOMXE3;S;nef(R7A1xa~A#B+XJD|{m zyX*%D+Y6VfRTSHRq6x#Q{j^Kuzs!E34JD%mN8ZU28Z+Hl)MMgHGPP9hNM7OM`9_*c zXO*teD9`Z7tz-ynJ&R2?^H#}m8p9Mj(!ff7UD)6&^s3WM^PX&3EEG2r`!nviTA zf%*vMoiC1RfJbSOJMvv|J^) z{4}IAG&Q0awASkgTLRjaad|LT8t~Efq5(lM@~%#y@!TnW688;=DjU&09VvPAzKt&! zR?e#E=Dr;EHdkZHA1|=vKf3yMe)9P6$=Or0vU8j3pcRZ8xbal4uu+HL!Sa>;3wY}9 zHq@%7CO@|xwJ{|my#^fe5QR`>@4Qx0jcgg(XS)V);PnC+hfbJ&C*`-fNZ5RaiH7`H z7W~bMCg7zCo<6PR=(QQ#MPt?qH~iCR*jnUXJ|k@}YxhTKE=ccw^=-TGwPD8;AC=@b zcM+1}3ut{a4_>QJ&Fod5mUpI8-$gQP)DbCNFw2+y{x$?Z{XzBkAwwonZQyMl{e;av z+h}H{W!@!fdk@Ur9dmndMaR33AD-ehi5NpHRXnB97 zIX7K}Zn7L*?G$7cKGB9RGtqWt!|~fHe92OL<=d%p(<;ny8)mg?>L)!KY0Cv$nY_pq zT`9AtZJfvgR62vxOPJeC))!{nY+anY(&_f^M9BJNLM!R)?cz?J&pughVB40`*+V+R zw3_r&C@m+{-$p`oyVyucy?C=KOTb4u=Cr;_zEA(G@-jXBf>U8YT4j2I48-$d#+Btm zy<$dI_}QpdthKD|MaXYw{Quf}^6w^cq<_y}p_s7kLw0-ti}{-Qa&QeWu*vfB2HP07 z-7((y$adQkf-!&lr>c@#QoDU5n_=gD&pmHQqN7xjN>Zs*M+eSwAMr6RYg8oiEhZ%& zEdj~&nq^+jB8_wVf+ZEr#YL5aJZL%`fgd7}o};rHC!9>K85DImgKb5wKq=U@uJq|X z`40&*9Db9~l;k-Q%35Blz+&T|+m-r)_?Q2D7kS`sE2M?ugRCP5FA5= zQ!kxr?Cqs^Dy0PZI=L}hno+QFLgn^J*`{F!?1!i1L7oO7$QhJ4DJpJ`Ho5>vw9Wtx zfRW#IU%nx#wcnm)=F-`q(SxUH5XQ^mByCSw^P%X3D#(+0$1tZ`stK{)pl)whs`Txl zGUMJ;^`|xqx`jy)w6^y&(iYSfLdX6@tlyA*IC*o5)gl#BsnWh%KNw6%YMtJP_p1HL zc)vgFkv6;R)51ECYK_TK)=1oz*Al@JFLJ5W2=fkr{1HgkySv7ovM9&2S6W2KOB71# zQPV{8(_j#i`!TQ$Y!3#4Bb!Vv>tXYk+Wvm}ms-7@|8|i7*!*5E<{yiH_wud(E&MGf ze%sA9(v1vneixVXu0pN>oiaAdt`xPRM4_itRb3|HTk#h+Tm`{!1T zTNpo@)wdRqwyhYwWDM+g3<)px&~7ZU>s-<@D~o03FeeKr4PyL@(x4KTm=>6jE#z(h zdvL?G3KwlnnTv#D(hIB@qi{qfs|-{SMnpZFwk6nB+!T5!_S)f>o9-|7*gL^@(2far zOY>O5b!oaEyQg;R3}|J8q)YD0qGXNL;cdT}7JK+b#!&7@)2yXqkowx~<$?8K&i&9~ ze@orRW`XFHYrRY=!&i(8oe8c1`2DN*&$vhyT--wPaxi*XE}2_nXEFzcOio?JKsW`l zm&*Mw)5sLEVhc<)8l@Cj&xX;1&W59bwf_74H}zANUnL{%Rw`eDZcN~l2DcjtPb_~6 zSk836Mp{*dh|1QhvK&dcE{U|XG<3mRMUrAw4jnJaCbXX~OBE?ikTZyq(TxR_DB0D8 zsr;iqWV!oYmhUhya$=1X366K!w@D-AKsqYqh&cweB|t0Mi16C5IQ?ey~_3 zc~HZF?_aWF;lUH-1v)R)BlgZ-{dWBB?A0+Y9f6LTtlZsU&7Th2q%Kd#IYF?kpK7JlK3By>e5}s z|5+aYqg;7Zc@U5PQLGfV@jrgXmmL2i7P27NEI`3sMI5->2n>}JhJs)xQ*k~JYX@;N zDDaUuzNaJbLNWfA-$4)iY=yT6uj9tgt=l<(FfI;K76|es0UF&EgoOmZ4>$ApAP$|K zgV8lR`0ezSmL>|=H74p*4n(EBS?Nio`lMPOia*4uTPWZ--3M(sVv)op68JN5$r2*~ z3=sC@4jdaKI3h4W|H+;Rj#rwg``)y76HC`)sDOG0tL)JJy4LYa(EW*q=qs>5RxLmA zhnEm-XVn18_uQ!D=3_|aNF8wujEDVih5^36kycC=qtCN zs{qj(pQ{%j8$a~yvd&@@4yy#C(WOSl+Gtc+jB=JIYi|fFvx)g=f}tkpjH63k`!yb&U(4TDGkcEOw36hFUKl(6-=)48+VC@0i#jjV3 zJuom8NWKA0>{qxT+r{J-ma5~w&AJo#PE(4^LC<^f>0A#XmRiW~pHsVLYzZeC;$@HV zP|Kc%<%&wn=PUj#>g2D2liVL$vn&BmW>qnU$1Z0`Ro{S^M8F9rxDe6OLqCX_ZAH+e zCLl{YSkU1P7?QWVso?!44h8kVg4VEOnfQ4nQK;&m~bKv@L#Ijzg!}6br{)Q3&=aN3S-5ij|@s zCl2kdn?P?gKzjJqpvavG1MWbY@%H|WHUUnL){=jbCy2q(s1)FoEdS#$eh|@xSXwwt zr~=o1zC_|O&RgNoRiRNjVvp{29CYceTPNEe$Llzphb%j)7VB1^=M~j2i%Lo<9Ba5 zXa}NMv`Ro@0IyRMJOZlusQudObhL%P;e(29QsfAVR*Hbz6=jWED;Ehy?QT*fV>JOW zyEB?>V{@ochi1^rn)4~mX4QH9Q?{7_)rUQYv&8kSJ~$p&%>mt^y;*18Xp|a_GAO8t zjyXDgEtswWW@v!v8eoQVXU@fr$ruV`s3&@zVQ*M1>IuuHKhG+B{{091n-PCDPP3Ui z>(E{GM|9}(=g)(-Jg?={{Chf{sJV%z=4qWhZDv_!UE?0L@fzBNrUVP9vEKl5DEPZk zStCso@KpSV2olC$g6SZlBMW0TEAq;`p3kG0Y_k1q1*(Au0nDl>kBg{>jKU`ZA*z?*9dH7ns z#EDO@MV%=?{v5mO(kBC68_s*|!r1$$=QO1^n!-ElIjwZTu~2l175E#Mwb=S+*-W~g z&E=cl>y2)sAi9!f8+Hd5^R=R(Mx%1`m4zSz8$^7SWq|`N6^x$HmC-(AK$h{-_W;5P zMLFhHbzAEANOOGija5}lK#uO~2^(qp)5aUm9pg1OISAKg9b>#jhKh!6-8#eg!Wqrw ziNW<=%=Lz4iz~Rrt8h7iT#Z$8XUDupSS-$X&@CB*NkbWkvHZ@y&ok*({Zp%%YprAm zjI{&1K^cd~PC>G7L1Qi5Zh1w!jjT9g%7VpeWScqp$DJUN%|g@JETLiYb`~1-&&^!@ z6a4deHMJ{>>O_Qreevu>~z((4;EyrjW&efMc|<7W3}J+sTQb-4{uw+RRG zh>O2Il^0y~Ca*T5(M95TH0l{x`c1%%B1=aadK+MQsMsXj7U8c-nhxvg-fTKw2|{+s zvp{%@Ah4TtQcXt{O6n1+-XswkYFjsA>eicdCa`C^r)K&d+m+25k#-zBx=;jDv)ljl zy>TYiR}03q1;g6faFyWzK*uXI994tV7xQT4wIVcHask4f{lcGo3C7dj7pJE2`tbuW z`T-b;U)PQY&Pq6;X$i;CiMJ$+R>Y4V)clVha#eu#w>SQ3;vuHzD{D43S_Rkklmy1& ztAA~>I%|q8+S~$Tdvttv%*evHOXCj7<&MSe6-}^ z#MD(O^{AZney66$t}Iz3U00S$s455EuD|&5U~oP$s1iVPS)Fz4+~QEIM`?i@i;q$w zQa9SbmOX1InotkXuA9oIfGvon+Apc+u5|v=Cwj4k!so6g6a@hZ1xwJ&a#KFZoPZbl zD5kdY$b4>Tse{};h`Z!S^zkYFFLZtEp);MF=l>2ipVIQ7F=_;6+)*>)yY^a%2pSLK z`uWH2y7p7l8uw^20p#A=5nl&-C&Afvx0W}aPQf2py|73N0fXX#Jt9dyeLiNEH=-@2 zOqiq$u>v=Mx`^H;>buGMpGcHa7Yg`dh6H%)dDecU_=YsZjb}$|h=@TMRzIIcMVx>|qJ*gCj^f+{)lS(q_ zg-$BVq(hxll}W$rq>4;>tCJqcq$8d5P$r$}q(?Fd|GALqMU7mblN542ourWSbdo~u zS|=&whB`?h*U?D|xhtJyA?GRVstPwxAy-wXc?!2`gqgyls<7}BvQ>qurx2?uv^<4Z zRbk~Rw5os^uIW&$#=bqT7$5I#@Mj7QO13WNb=7%V=Dl#MP8uO;QqLmp{{umV~ zihS7cBtJ?UVJFIdB(nQpT@m_LiLq|METZ6tlPLc&p)UA+CONCnsBGrSx<_wLbpEf7 zN_+O(ag_WzNU=ZF28|dja=E!E5e$+#7PQ!@Vmmn&gBmC+s+3 zlTvoMJY^iFEnwl@`p-c@s%)TcqvV*3mruA}?#;wKx0rwYq?xTB93CA%d;a3(udh%4 zc|Poq!YN#T=NZYt5SIX2bK{T#d0z9cu`r%5#-Nam zwio)fz%NjEUKlMHD3${d5bz>nL`TK&46(S`O90W7<|3frw2WnJP|8kuV~DWC5@1Ab z6E=qg%hKrC^pZw&gR&>I?f9U zD1S=4Ay^uHoMXSsz&?sL=I_qWDG@4Efp3KNKPZ+^RVY`1ZG1;O*dt&85vNzg*hOGj z`4KRGvfN`}^spnR$iwMkto#`GJSev}JDDGePQREB#_-Y+gt2bQE>?={LIAs%9}1ei zkbNn!*9;U=tPWO-f{7Qud7^A&XrrK%SinHued2c!5rT5U01X>TNihBZB}jl$W?<>n zo4)|7-U!MI2Hmisl*MHMp!5=;RM<5GQT*@zU@*Q?P=05iC1XRWK=euh!86#~b`PRIODJ!}&$lrs55z?VpbQhBJY+x^o%zvq zd!%tWLaR6%%0sX^7f?D0P#&=>k##jcLt`lg<&43zqz&bfxG({ft2h)OA6yQ~of9Z^ zVt^`p{kV%usr)Mv2HZ37CoU?o-)1uZ$;;>(p`vER3PO|ed=h-;^zsq{FAA0b0K8_Z z0pO@)JyA+p#Hedp>9r~b?2;~~-BcNc5wnmL@K6@W;2|8bp0IWc>O>W}(GjXjKA+w;!F_=>t-*aX})5=gbf`EEQtbp8-sP{(v zj4E9}`TCL}`ZiZe^1ov7QMDYC|CP(-%2xjOGd?tf?t&Tgq(1<$c@SB5BR%Zkzu>z99t}Xcp(zI6 z_AbRvD1D{X&AcXLl1(t>$=~STKhr!YJbwd+!vhi7!6*G!6*naEcCu1WWf|(jf1WpQ)mibrpeVN4IBz#!I(Y3 zf|xewcl=R^^Yr%BKLxOLJI=!T@0|DUn#R_x|KfwV{J&f&m$&QxXMFXcKWUTwsIltu zXk_fNlV=CVZ=V&0-M`$qwf*kqGuD4J*>~N>y7gZwm165(WIx=l|DW+`ZzMV}`LPTW zbv>R2+Rq_w3Eh6+cP0ajWq^S|q(6z>?FO;^w%_*Ke%o*RZNKfe{kGrs+kSuT@BaXK KT&QgT$N&Hc6bohm literal 0 HcmV?d00001