pax_global_header00006660000000000000000000000064122705636720014524gustar00rootroot0000000000000052 comment=b7fefe8f79fa8513fa711e04188c5d86f1a07db6 dosage-2.12/000077500000000000000000000000001227056367200127125ustar00rootroot00000000000000dosage-2.12/COPYING000066400000000000000000000021511227056367200137440ustar00rootroot00000000000000Copyright (C) 2004-2008 Jonathan Jacobs and Tristan Seligmann Copyright (C) 2012-2014 Bastian Kleineidam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dosage-2.12/MANIFEST.in000066400000000000000000000004141227056367200144470ustar00rootroot00000000000000include MANIFEST.in include COPYING Makefile requirements.txt include doc/Makefile doc/*.txt doc/*.1 doc/*.html doc/*.md doc/dosage-completion include doc/css/*.css doc/js/*.js include scripts/*.py scripts/*.sh scripts/*.bat scripts/*.iss recursive-include tests *.py dosage-2.12/Makefile000066400000000000000000000130131227056367200143500ustar00rootroot00000000000000# This Makefile is only used by developers. # See doc/install.txt on how to install dosage PYTHON:=python VERSION:=$(shell $(PYTHON) setup.py --version) MAINTAINER:=$(shell $(PYTHON) setup.py --maintainer) AUTHOR:=$(shell $(PYTHON) setup.py --author) APPNAME:=$(shell $(PYTHON) setup.py --name) LAPPNAME:=$(shell echo $(APPNAME)|tr "[A-Z]" "[a-z]") ARCHIVE_SOURCE:=$(LAPPNAME)-$(VERSION).tar.gz ARCHIVE_WIN32:=$(LAPPNAME)-$(VERSION).exe GITUSER:=wummel GITREPO:=$(LAPPNAME) WEBPAGE:=$(HOME)/public_html/dosage-webpage.git WEBMETA:=doc/web/app.yaml DEBUILDDIR:=$(HOME)/projects/debian/official DEBORIGFILE:=$(DEBUILDDIR)/$(LAPPNAME)_$(VERSION).orig.tar.gz DEBPACKAGEDIR:=$(DEBUILDDIR)/$(LAPPNAME)-$(VERSION) # Default pytest options # Note that using -n silently swallows test creation exceptions like # import errors. PYTESTOPTS?=--resultlog=testresults.txt --tb=short -n10 CHMODMINUSMINUS:=-- # directory or file with tests to run TESTS ?= tests # set test options, eg. to "--verbose" TESTOPTS= all: chmod: -chmod -R a+rX,u+w,go-w $(CHMODMINUSMINUS) * find . -type d -exec chmod 755 {} \; dist: [ -d dist ] || mkdir dist git archive --format=tar --prefix=$(LAPPNAME)-$(VERSION)/ HEAD | gzip -9 > dist/$(ARCHIVE_SOURCE) [ ! -f ../$(ARCHIVE_WIN32) ] || cp ../$(ARCHIVE_WIN32) dist sign: [ -f dist/$(ARCHIVE_SOURCE).asc ] || gpg --detach-sign --armor dist/$(ARCHIVE_SOURCE) [ -f dist/$(ARCHIVE_WIN32).asc ] || gpg --detach-sign --armor dist/$(ARCHIVE_WIN32) upload: cp dist/$(ARCHIVE_SOURCE) dist/$(ARCHIVE_WIN32) \ dist/$(ARCHIVE_SOURCE).asc dist/$(ARCHIVE_WIN32).asc \ $(WEBPAGE)/dist homepage: # update metadata @echo "version: \"$(VERSION)\"" > $(WEBMETA) @echo "name: \"$(APPNAME)\"" >> $(WEBMETA) @echo "lname: \"$(LAPPNAME)\"" >> $(WEBMETA) @echo "maintainer: \"$(MAINTAINER)\"" >> $(WEBMETA) @echo "author: \"$(AUTHOR)\"" >> $(WEBMETA) git add doc/web/app.yaml -git commit -m "Updated webpage meta info" # update documentation and release website $(MAKE) -C doc $(MAKE) -C doc/web release release: distclean releasecheck $(MAKE) dist sign upload homepage tag register changelog deb tag: git tag upstream/$(VERSION) git push --tags origin upstream/$(VERSION) register: @echo "Register at Python Package Index..." $(PYTHON) setup.py register @echo "Submit to freecode..." freecode-submit < $(LAPPNAME).freecode releasecheck: git checkout master $(MAKE) check test # test console output (behaves differently than redirected output) $(MAKE) test PYTESTOPTS="-s" TESTS=tests/test_dosage.py @if egrep -i "xx\.|xxxx|\.xx" doc/changelog.txt > /dev/null; then \ echo "Could not release: edit doc/changelog.txt release date"; false; \ fi @if [ ! -f ../$(ARCHIVE_WIN32) ]; then \ echo "Missing WIN32 distribution archive at ../$(ARCHIVE_WIN32)"; \ false; \ fi @if ! grep "Version: $(VERSION)" $(LAPPNAME).freecode > /dev/null; then \ echo "Could not release: edit $(LAPPNAME).freecode version"; false; \ fi $(PYTHON) setup.py check --restructuredtext git checkout debian @if ! head -1 debian/changelog | grep "$(VERSION)" > /dev/null; then \ echo "Could not release: update debian/changelog version"; false; \ fi @if head -1 debian/changelog | grep UNRELEASED >/dev/null; then \ echo "Could not release: set debian/changelog release name"; false; \ fi git checkout master # The check programs used here are mostly local scripts on my private system. # So for other developers there is no need to execute this target. check: check-copyright check-py-encoding patoolib tests check-pofiles -v py-tabdaddy py-unittest2-compat tests/ $(MAKE) doccheck doccheck: py-check-docstrings --force \ dosagelib/*.py \ dosage \ scripts \ *.py pyflakes: pyflakes dosage dosagelib scripts tests doc/web count: @sloccount dosage dosagelib/*.py clean: find . -name \*.pyc -delete find . -name \*.pyo -delete rm -rf build dist distclean: clean rm -rf $(APPNAME).egg-info $(LAPPNAME).prof test.sh Comics rm -f _$(APPNAME)_configdata.py MANIFEST localbuild: $(PYTHON) setup.py build test: localbuild env LANG=en_US.utf-8 http_proxy="" $(PYTHON) -m pytest $(PYTESTOPTS) $(TESTOPTS) $(TESTS) testall: localbuild env LANG=en_UR.utf-8 http_proxy="" TESTALL=1 $(PYTHON) -m pytest $(PYTESTOPTS) $(TESTOPTS) $(TESTS) deb: # Build an official .deb package; only useful for Debian maintainers. # To build a local .deb package, use: # $ sudo apt-get build-dep dosage; apt-get source dosage; cd dosage-*; debuild binary [ -f $(DEBORIGFILE) ] || cp dist/$(ARCHIVE_SOURCE) $(DEBORIGFILE) sed -i -e 's/VERSION_$(LAPPNAME):=.*/VERSION_$(LAPPNAME):=$(VERSION)/' $(DEBUILDDIR)/$(LAPPNAME).mak [ -d $(DEBPACKAGEDIR) ] || (cd $(DEBUILDDIR); \ patool extract $(DEBORIGFILE); \ cd $(CURDIR); \ git checkout debian; \ cp -r debian $(DEBPACKAGEDIR); \ rm -f $(DEBPACKAGEDIR)/debian/.gitignore; \ git checkout master) $(MAKE) -C $(DEBUILDDIR) $(LAPPNAME)_clean $(LAPPNAME) update-copyright: # update-copyright is a local tool which updates the copyright year for each # modified file. update-copyright --holder="$(MAINTAINER)" update-comics: # update all scripted comic plugins (takes ca. one hour on my computer) scripts/generate_json.sh scripts/update_plugins.sh changelog: # github-changelog is a local tool which parses the changelog and automatically # closes issues mentioned in the changelog entries. github-changelog $(DRYRUN) $(GITUSER) $(GITREPO) doc/changelog.txt .PHONY: update-copyright deb test clean distclean count pyflakes changelog .PHONY: doccheck check releasecheck release dist chmod localbuild sign .PHONY: register tag homepage dosage-2.12/README.md000077700000000000000000000000001227056367200164272doc/README.txtustar00rootroot00000000000000dosage-2.12/doc/000077500000000000000000000000001227056367200134575ustar00rootroot00000000000000dosage-2.12/doc/Makefile000066400000000000000000000005701227056367200151210ustar00rootroot00000000000000all: dosage.1.html dosage.txt dosage.txt: dosage.1 # make text file from man page for Windows builds cols=`stty size | cut -d" " -f2`; stty cols 72; man -l $< | sed -e 's/.\cH//g' > $@; stty cols $$cols dosage.1.html: dosage.1 man2html -r $< | tail -n +2 | sed 's/Time:.*//g' | sed 's@/:@/@g' > $@ patch --no-backup-if-mismatch --quiet $@ dosage.1.html.diff .PHONY: all dosage-2.12/doc/README.txt000066400000000000000000000001651227056367200151570ustar00rootroot00000000000000Dosage ======= Dosage is a comic strip downloader and archiver. See http://wummel.github.io/dosage/ for more info. dosage-2.12/doc/adding_new_comics.md000066400000000000000000000070031227056367200174350ustar00rootroot00000000000000How to add a new comic to Dosage ================================= To add a new comic, add a new class in one of the *.py files in the dosagelib/plugins module. The files in dosagelib/plugin and the classes inside those files are sorted alphabetically. Add your comic to the appropriate filename. For example if the comic name is "Super duper comic", the new class should be added to dosagelib/plugins/s.py. Here is a complete example which is explained in detail below. ``` class SuperDuperComic(_BasicScraper): description = u'A super duper comic by Mr Smith!' url = 'http://superdupercomic.com/' rurl = escape(url) stripUrl = url + 'comic/%s' firstStripUrl = stripUrl % '1' imageSearch = compile(tagre("img", "src", r'(%scomicimg/[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%scomic/\d+)' % rurl, after="prev")) help = 'Index format: n (unpadded)' ``` Let's look at each line in detail. ```class SuperDuperComic(_BasicScraper):``` All comic plugin classes inherit from ``_BasicScraper``. The classname (``SuperDuperComic`` in our example) must be unique, regardless of upper/lower characters. The user finds comics with this classname, so be sure to select something descriptive and easy to remember. ```description = u'A super duper comic by Mr Smith!'``` Next, a description should be added to the class in Unicode notation (u'...'). It is displayed when a user requests help for one comic with ``dosage -m superdupercomic``. ```url = 'http://superdupercomic.com/'``` The URL must display the latest comic picture. This is where the comic image search will start. See below for some special cases. ```rurl = escape(url)``` This defines a variable ``rurl`` which is used in the search patterns below. It properly escapes all regular expression special characters like dots or question marks. ```stripUrl = url + 'comic/%s'``` This defines how a comic strip URL looks like. In our example, all comic strip URLs look like ``http://superdupercomic.com/comics/NNN`` where NNN is the increasing comic number. ```firstStripUrl = stripUrl % '1'``` This tells Dosage what the earliest comic strip URL looks like. Dosage stops searching for more comics when it is encounterd. In our example comic numbering starts with ``1``, so the oldest comic URL is ``http://superdupercomic.com/comics/1`` ```imageSearch = compile(tagre("img", "src", r'(%simg/[^"]+)' % rurl))``` Each comic page URL has one or more comic strip images. The imageSearch pattern must match those images in the HTML content of the page URL. To make it easy to match HTML tags, the ``tagre()`` function is helpful. The first parameter is the tag name, the second the attribute name and the third the attribute value. So in our example the given pattern whould match a tag like ````` . ```prevSearch = compile(tagre("a", "href", r'(%scomic/\d+)' % rurl, after="prev"))``` To search for more comics, Dosage has to look for the previous comic URL. The ``after=`` value in ``tagre()`` matches anything between the attribute value and the end of the tag. So this pattern assumes each comic page URL has a link to the previous comic, for example ``http://superdupercomic.com/comic/100`` has a link ``Index Return to Main Contents
 

NAME

dosage - a comic strip downloader and archiver  

SYNOPSIS

dosage [options] module...  

DESCRIPTION

dosage is an application designed to keep a local mirror of specific web comics and other picture-based content, such as Picture Of The Day sites, with a variety of options for updating and maintaining collections.  

OPTIONS

-b PATH, --basepath=PATH
Specifies a base path to put comic subdirectories. The default is Comics.
--baseurl=PATH
Specifies the base URL for output handlers. The default is a local file URI.
-a, --all
Traverses all available strips backwards from the current one. This can be useful you want a full collection of a new comic strip, or update an existing one where files are missing. Catchups can start at a specific strip by using the index syntax, see the INDEX SYNTAX and SPECIAL SYNTAX sections for more information. This is useful when you missed some days and want only to download the missing files.
-c, --continue
Same as --all, but stop at the first existing image file. Useful for cron jobs that are not executed every day.
-h, --help
Output brief help information.
-l, --list
List available comic modules in multi-column fashion.
--singlelist
List available comic modules in single-column fashion.
-m MODULE, --modulehelp=MODULE
Output module-specific help for MODULE.
-o OUTPUT, --output=OUTPUT
OUTPUT may be any one of the following:

html - Writes out an HTML file linking to the strips actually downloaded in the current run, named by date (ala dailystrips). The files can be found in the html directory of your Comics directory.

rss - Writes out an RSS feed detailing what strips were downloaded in the last 24 hours. The feed can be found in Comics/dailydose.xml.

json - Write a JSON file with all download infos (URLs, images). Can be used with other scripts, eg. order-symlinks.py to add symbolic links.
This option can be given multiple times.
-t, --timestamps
Print timestamps for all output at any level.
-v, --verbose
Increase the output level by one with each occurence.
-V, --version
Display the version number.
--vote
Vote for the selected comics to tell others that you like them. The sum of all votes for a comic will be displayed at the comic index pages at http://wummel.github.io/dosage/comic-index.html module At least one valid module must be specified. A list of valid modules can be found by passing the -l option. Multiple module arguments can be specified on the command line. Module names are case insensitive, and it is sufficient to specify a unique substring of the module name.
 

INDEX SYNTAX

Instead of starting at the latest comic strip, an index lets dosage start at a certain strip. The index can be specified by appending a colon : and the index name after the module. Multiple comma-spearated indices can also be specified.

The index name itself usually is the part of the comic strip URL that identifiess a strip, eg. a number or a date. The expected format is documented when using the --modulehelp option.  

SPECIAL SYNTAX

@
This expands to mean all the comics currently in your Comics directory. All other specified comic module names will be ignored.
@@
This expands to mean all the comics available to Dosage.

INDEX SYNTAX can not be used with SPECIAL SYNTAX.  

EXAMPLES

Retrieve all Mega Tokyo comics:
dosage -a megatokyo

Retrieve the current comic of Cyanide and Happiness:

dosage cyanideandhappiness

Retrieve the current strip of all comics in your Comics directory:

dosage @

Vote for the comics in your Comics directory:

dosage --vote @

Retrieve the current strip of every comic that there is a module for:

dosage @@

Retrieve the Penny Arcade strip for a given index:

dosage pennyarcade:2004-07-22

Retrieve Calvin and Hobbes strips from a given index going backwards to the beginning.

dosage -a calvinandhobbes:2012/07/22

 

ENVIRONMENT

HTTP_PROXY
dosage will use the specified HTTP proxy when downloading URL contents.
 

NOTES

Should retrieval fail on any given strip dosage will attempt to retry. However the retry information is only outputted in the second and successive output levels.

At the time of writing, a complete Dosage collection weighs in at around 3.0GB.  

RETURN VALUE

The return value greater than zero when
a program error occurred.
comics could not be found or downloaded
the program run was aborted with Ctrl-C

Else the return value is zero.  

BUGS

Users can report or view bugs, patches or feature suggestions at https://github.com/wummel/dosage/issues  

AUTHORS

Jonathan Jacobs, Tristan Seligmann, Bastian Kleineidam <bastian.kleineidam@web.de>  

COPYRIGHT

Copyright © 2004-2005 Tristan Seligmann and Jonathan Jacobs
Copyright © 2012-2014 Bastian Kleineidam


 

Index

NAME
SYNOPSIS
DESCRIPTION
OPTIONS
INDEX SYNTAX
SPECIAL SYNTAX
EXAMPLES
ENVIRONMENT
NOTES
RETURN VALUE
BUGS
AUTHORS
COPYRIGHT

This document was created by man2html, using the manual pages.
dosage-2.12/doc/dosage.1.html.diff000066400000000000000000000010301227056367200166470ustar00rootroot00000000000000--- dosage.1.html.orig 2013-03-26 19:02:14.690504207 +0100 +++ dosage.1.html 2013-03-26 19:04:24.808185171 +0100 @@ -4,7 +4,7 @@

DOSAGE

Section: User Commands (1)
Index -Return to Main Contents
+Return to Main Contents
 

NAME

@@ -282,7 +282,7 @@
This document was created by -man2html, +man2html, using the manual pages.
dosage-2.12/doc/dosage.txt000066400000000000000000000140141227056367200154620ustar00rootroot00000000000000DOSAGE(1) DOSAGE(1) NAME dosage - a comic strip downloader and archiver SYNOPSIS dosage [options] module... DESCRIPTION dosage is an application designed to keep a local mirror of specific web comics and other picture-based content, such as Picture Of The Day sites, with a variety of options for updat†ing and maintaining collections. OPTIONS -b PATH, --basepath=PATH Specifies a base path to put comic subdirectories. The default is Comics. --baseurl=PATH Specifies the base URL for output handlers. The default is a local file URI. -a, --all Traverses all available strips backwards from the cur†rent one. This can be useful you want a full collection of a new comic strip, or update an existing one where files are missing. Catchups can start at a specific strip by using the index syntax, see the INDEX SYNTAX and SPECIAL SYNTAX sections for more information. This is useful when you missed some days and want only to download the missing files. -c, --continue Same as --all, but stop at the first existing image file. Useful for cron jobs that are not executed every day. -h, --help Output brief help information. -l, --list List available comic modules in multi-column fashion. --singlelist List available comic modules in single-column fashion. -m MODULE, --modulehelp=MODULE Output module-specific help for MODULE. -o OUTPUT, --output=OUTPUT OUTPUT may be any one of the following: html - Writes out an HTML file linking to the strips actually downloaded in the current run, named by date (ala dailystrips). The files can be found in the html directory of your Comics directory. rss - Writes out an RSS feed detailing what strips were downloaded in the last 24 hours. The feed can be found in Comics/dailydose.xml. json - Write a JSON file with all download infos (URLs, images). Can be used with other scripts, eg. order-sym†links.py to add symbolic links. This option can be given multiple times. -t, --timestamps Print timestamps for all output at any level. -v, --verbose Increase the output level by one with each occurence. -V, --version Display the version number. --vote Vote for the selected comics to tell others that you like them. The sum of all votes for a comic will be displayed at the comic index pages at http://wum†mel.github.io/dosage/comic-index.html module At least one valid module must be specified. A list of valid mod†ules can be found by passing the -l option. Multiple module arguments can be specified on the command line. Module names are case insensitive, and it is sufficient to specify a unique substring of the module name. INDEX SYNTAX Instead of starting at the latest comic strip, an index lets dosage start at a certain strip. The index can be specified by appending a colon : and the index name after the module. Multi†ple comma-spearated indices can also be specified. The index name itself usually is the part of the comic strip URL that identifiess a strip, eg. a number or a date. The expected format is documented when using the --modulehelp option. SPECIAL SYNTAX @ This expands to mean all the comics currently in your Comics directory. All other specified comic module names will be ignored. @@ This expands to mean all the comics available to Dosage. INDEX SYNTAX can not be used with SPECIAL SYNTAX. EXAMPLES Retrieve all Mega Tokyo comics: dosage -a megatokyo Retrieve the current comic of Cyanide and Happiness: dosage cyanideandhappiness Retrieve the current strip of all comics in your Comics direc†tory: dosage @ Vote for the comics in your Comics directory: dosage --vote @ Retrieve the current strip of every comic that there is a mod†ule for: dosage @@ Retrieve the Penny Arcade strip for a given index: dosage pennyarcade:2004-07-22 Retrieve Calvin and Hobbes strips from a given index going backwards to the beginning. dosage -a calvinandhobbes:2012/07/22 ENVIRONMENT HTTP_PROXY dosage will use the specified HTTP proxy when download†ing URL contents. NOTES Should retrieval fail on any given strip dosage will attempt to retry. However the retry information is only outputted in the second and successive output levels. At the time of writing, a complete Dosage collection weighs in at around 3.0GB. RETURN VALUE The return value greater than zero when · a program error occurred. · comics could not be found or downloaded · the program run was aborted with Ctrl-C Else the return value is zero. BUGS Users can report or view bugs, patches or feature suggestions at https://github.com/wummel/dosage/issues AUTHORS Jonathan Jacobs, Tristan Seligmann, Bastian Kleineidam COPYRIGHT Copyright © 2004-2005 Tristan Seligmann and Jonathan Jacobs Copyright © 2012-2014 Bastian Kleineidam DOSAGE(1) dosage-2.12/doc/icon/000077500000000000000000000000001227056367200144075ustar00rootroot00000000000000dosage-2.12/doc/icon/Makefile000066400000000000000000000004111227056367200160430ustar00rootroot00000000000000ICOICONS := logo16x16.png logo32x32.png ICNSICONS := logo16x16.png logo32x32.png logo48x48.png logo128x128.png all: favicon.ico favicon.icns favicon.ico: $(ICOICONS) png2ico favicon.ico $(ICOICONS) favicon.icns: $(ICNSICONS) png2icns favicon.icns $(ICNSICONS) dosage-2.12/doc/icon/favicon.icns000066400000000000000000001275541227056367200167300ustar00rootroot00000000000000icns¯lis32ë »€…‡ èßF–‰_žÈöƒ |¦‹Zjro[:`‚ €âòî»Wqs7idqü€ Ê•ÖÏÄ¿aæ„D]‚ s²¶¦«®OBvÉgï‚ R‘®G`AoÒyƒ SŒ‰TQ_;g1à‚ Tƒ›m:MSø‚S‚O7‚tƒZV €$Iý„…•Ì߀Ş ¹„„‡ àâQÏÀbšÆõƒ h@”PLzŠˆmOb‚ˆ€ÿ×j=l˜Šhnü€ Ç­ñêâäY:N<`‚ sÔØÌÍÓ?@ó‚ XÁ»¶ÎAQ(•nƒ X¶®¼`T91à‚ W§¸g1)Sø‚V¢K"‚tƒ[c €$Iý„…•Ì߀Ş ¹…‚‡ ßáWëßc˜Äôƒ c-•K;s‘”y[b‚Š€ÿäq6y«jmü€ Æ·ýöñøY?-+^‚ råéáßä\ö‚ ZÚÑËßV†‡Ü…Š…·Ø50$Sˆ à†€¯¡2HZ¤‡ äy‚–I^2Z$ ˆV‡ ëp|$*#‡¦‰ñ`¢'ˆ´Šü^V‚ Av±Š÷Œ·þìë«ùqÞøù¥°Þåëƒ8h—Æö‰y$0 -PtUg—”§Ò¸iIý‡0épbÿÿŸ9Kxi{|Vu’y~· ‡h½ÿÿýýÿÿö’(-u}¶d~’…vsBd†ÓCÿû€üû÷÷ÿ‰;‚÷ºdj•]Ý…)×ú÷óîæúV(5!"nFE@Y)ÿ…Úÿôõôðîêåßð/]W2ý‡jtüîîìéåâßá¾+.^h*Rˆ±îäßÛÙÚÝØë3=2]”‡üÆÜÖÔÒÏÏ×ã3]L $’CˆäËÖÐÎËÇÆà,‰4C,ªˆ×ÇÎÉÅľÜ)º_N1$ %½†Ù ÀÇ¿ºÚäAU$ †‡Ù ¶ÁºµÙÕ23,Sˆ Ý ­¹±Ñ%Ÿ3¤‡ á ¡±¾R[5$ˆV‡ê ’®Ž(ˆ¦‰ò ~Â0 ˆ´Šýpi‚ Av±Š÷Œ3F _¥`“Ƀ4ÍŽ¨ÝÿŠl8mk' ÿÿÿÿí°ÿÿýÿÿÒ ˆ_!ÿýÿÿÿÿÿÿéº`4ƒÿÿÿÿåÿýÿýÿýÿýÿÿÿÿÿÿÕ Æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ΋ÿÿýÿýÿýÿýÿýÿýÿýÿýÿýÿÿŒ)ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÁÿýÿýÿýÿýÿýÿýÿýÿýÿýÿýÿË#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ‚ÿÿýÿýÿýÿýÿýÿýÿýÿýÿýÿ§Ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿi ìÿÿýÿýÿýÿýÿýÿýÿýÿýÿþøùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL'üÿÿýÿýÿýÿýÿýÿýÿýÿýÿ§OA'ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ§PV%$ùÿÿýÿýÿýÿýÿÿŒoz¨ÿ“QT6!÷ÿÿÿÿÿÿÿÿÿöNRRQ}VSTULóÿÿýÿýÿýÿ±PRTRRRTRTSZ1îÿÿÿÿÿÿÿiRTTTTTTTTSæÿÿýÿÿßLTRTSUTVTTU ØÿÿÿÆxTVVVK:*%US¹ÿü5/!9L#ih32 Ýÿÿ£÷òîîïñôø¤ ÷Ë_X`jt¹óü¡ üëYEa^[\C3¨ô¡ô¨3‰—‡€˜H7Íðöù–û÷óóöøûúÏ6Fž ™‘‰‹Ž9G~Ÿ¾Üëîòõùüó›”™£ÇÐܹ+`uqŒŠ€Ž‚[D624Fbš·Öîö ÃvÝéÛˆ:.dºóü‡%îekÄÒØÝààÞÚÔÏȼ´¼_Q±²’wj6+'*aKEh¡‡E3®û‡%÷¹;§ÊÒÖØØÖÓÏËÅÀ¹±¯Me½Äû“\r335:eAHÚûˆ#ïd\½ËÏÑÑÐÍÊÆÁ¼¶°´˜;}ÃËȼz}+C1('C3Ëö‰#øË9…¿ÈÊÊÉÇÄÀ¼¸³°ªºy7®ÆÆ aizurT4.oèûŠ"ò;ž½ÃÃÂÀ½º¶²°®­¨·XS¢³ŠHwŽ›¥¶ U3¬ôŠ#ûê\J§¸»º¸µ¯««­¬«¬©¢5CjiT=Zg}œ¼Ê¸j8Å÷Š#øÖA[¡¨§¡›˜–•™¥ª©¡®D=€:  Ci޳×à˜ 2à’õÏM:b&5^Y4W}¢Èåìðô€ìJ6Çö“÷àq2GÆê€íîò÷…÷Îæö•ùñÞì÷ÿÿ¯ÿÿ£÷òíîïðóø¤ ÷Ë^X`is€¹òü¡ üëYQytmhP2§ô¡ó§5¯ÆÁº³¹`7Íðöù–úôððõøûúÎ4PÇÎÊÅ¿¼°EG}Ÿ½Üëîòõùüñ†Xr’½ÍÜ·(a‡ ¸»´·ŸkP<34Fb~š·Öíö ÀCXK?549K$liz}€wwŠ£¡•„saJ:11I¼ôŒ!cLXVh[űv84Ystcdv{mo€‰€C7Çö‹"blϲk¢íóîÜb3;Uv~jkw~€tdft†—EÙûˆ#ïcc׿êëëêèæãßÛÖÑз?2DOVkAM"?!@1Ëö‰#øË8˜ÛåææåãáÞÛ×ÔÑÌÒ–&&CfcL9GAM&KetwtW4,©óŠ#ûê\RÅ×ÛÚØÖÒÏÎÎÍËÊÆ¸6 % &.Ju•n>0Ã÷Š#øÕAiÃÍÎÊÆÃÁÀÁÇÉÈÁÄT6D%")23Da}˜¶Öíö ¿5:3--27H"i^v{…†ž´¸³¦‘}hO;0/G»ôŒ!aJR>FTDzv61H[^SYq~€‚€v}’¢¡¢Ÿ™‹H5Çö‹"`pØ·i¢ñöðÞža04Kk€ƒuciw‚ƒzmr„—¡?DßùŠ#ŠSßïðóùýýùóìÙœ^.:_€‘k{rXox…vy‹0iê÷‰È8ÀòúûüûúôìãÊz'"-T¤Ê£Š¼Â«bw}G)_¹òü‡%ícwè÷úûûüûúùöóíæß‡#($%7`Z¹ÐÎyF@g¡…A.­û‡÷·9ÆðøúøöóðíéãØg(30+&$5[‚z/>MFd_F ‹ "¢Šûñ 0q£¨¬—(I= !‹Iµ‹üò£0fž¤®../% ‹ªéôŒ üò«1[™­T*$2ŒŠöŽ üó¶4PŒ"%" ‚   ”÷ôÀ;C’H"  Ci޳×à˜ 2à’õÎI:n)4^Y4W}¢Èåìðô€ìJ6Çö“÷ßn.CÅê€íîò÷…÷Îæö•ùñÜëöÿÿ¯h8mk ,³¾´¨›ˆ? »øûûûúøðRQ÷þÿÿÿÿþûä$%èûþÿÿÿÿÿþúΉ`9 €À›o6&9÷ýþÿÿÿÿÿÿþüù÷õðÔ¯‰d?.ñúú÷õôëÄúþþÿÿÿÿÿÿÿÿÿÿþþýûù÷õòË:¦øþÿÿÿþýýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþúã-¤ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúÍq÷þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþø– 'ñýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýö©>­úþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýùîNAôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþúÌ«úþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýè(*ëüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü” r÷ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýíK ³ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþûè0Ùûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþú 9êüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþöYUóüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûµiõüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýîsõüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ÷uM>/uõüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿÿÿÿÿø‚USQ3rõüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýúùùùûüþø}UTR9oõüþÿÿÿÿÿÿÿÿÿÿÿÿÿþûº}Œ•ŸÕûõkTTS?kôüþÿÿÿÿÿÿÿÿÿÿÿÿþ÷yWVVXtÝÎXTTSQ;fóüþÿÿÿÿÿÿÿÿÿÿÿüØZTTTTVXXVTTSSRO7 aòüþÿÿÿÿÿÿÿÿÿþúœWTTTTTTTTTTTTSSRK[ñüþÿÿÿÿÿÿÿÿýòeUTTTTTTTTTTTTSRP;Tïûþÿÿÿÿÿþþû¿YTTTTTTTTTTTTTSNKëûþÿÿÿÿþýùƒVTTTTTTTTSSSSTSQ$@åûþÿÿþüøßZTTTSSSSRQPOLQSSP 4Ûúþÿþ÷‚jURRQQOJ=/!NRC%Èøüù½I45B5( FOY‡¿uJ$$’ò£1íU_±¿ÂÆÌÚáÔÒåáèíðóôõõòîêççêíéÚÄši<#!=^qz„…€†%…€qQ=5-+86/0?a…¨ÉÙÙÄ¥„cA0*+:–¸aD.Âö¢ó‚ A©ÅÊÇÈÌÑÞèààèëîð€òDðîëçâÝÛßæéäж`7!.CU`hnstoK)0K\T6'CbZL@5-1Hm³ÒÚÚË©W%Y¨¢S:Mèú¡]ù»&-ÃÎÐÏÌÎÒÖÚàçêìíîïïîìêçåßÙÔÑÑÚãæÜÃ¥~T, !,9FPS4$P~–©¸¼ˆ48^aa^YL@6.,3P~ØÞR!5U®‚L-"Œëõû @è?h½ÍÐÒÔÔÓÓØáåçéêëìëëéçåãàÝÙÒËÇÆÍÙãâѱi>2w_;&%%>`}›¶ÌÜÜ®58S€UTRPOH=0/²Å/VD,F™X?"T£âóúžAò€ ?ªÉÏÒÔÖØÙÛàâåæèèéèèæåãáÞÜÙÖÓÌÅÀ½ÀÍÜáÚ¼Œh*=¨¶žrF'$5Pu”²ÑæÖŒ%8335.*(<æ’! aïžõ¬%&ÃËÎÏÑÒÔÕÖ××€Ø××ÖÕÔÓÑÐÎÌËÈÆÄÂÀ¾¼¹µ¥­ÙÔ“m5xÁÉ„Ë#ÊÇľ¯‚P$ Œ±+.;60,02487*W±+ YäøŸë[>ªÇÌÍÏÐÑÒÓÔ‚ÕÔÓÓÒÐÏÎÌÊÉÇÅÃÁ¿½»¸¶°¡´ÚÌ‚e'!ÂʆËÊÆ½’eF%%˜!57/(%%&*14%CXâø  ö»' j½ÈËÍÎÏÐÑ„ÒÑÐÏÎÍÌÊÈÇÅÃÁ¿½»¹·µ²¬ÀÙÀvX)¡Ä†ËÉÃllzD;ŒB 61(!''1{ ?Û÷¢ îd/”ÂÉÊÌÍÎÎςРÏÏÎÍÌËÉÈÆÅÃÁÀ¾¼º·µ³±¯©žËجpE9¶È…ËÆ›ll•¹‰5) "" e0&±õ£ ÷Ê.K®ÃÇÉÊËÌÌƒÍ ÌËÊÉÈÇÆÄÃÁ¿¾¼º¸¶´²°¯­¦¢ÕÔ–j/tÃʃËÉ·rjˆ¾¡:.HMQTG=0"  "kaí¥ ò#"p·ÄÆÈÉÉÊʀˀÊÉÈÇÆÅÄÂÁ¿½¼º¸¶´²±°¯®¬£¬ÙÍ].°ÇƒËÇ–ko´·e#Zmsw{|}‡‚a9"J=$·õ¥ ûæK.»ÄÅÆÇÇ‚È"ÇÇÆÅÅÄÂÁÀ¿½»º¸·µ³±°¯¯®­« ºÙ¿rJe¾È‚ËÂ~j†À£3:f€“’‘Ž‹”¨«©Y+1$¹ñ¦ ö¿(D¢½ÂÄÄ„ÅEÄÄÃÂÁÀ¿¾¼»¹¸¶µ³±°¯¯®®­¬ªŸÉÖ¨j2"޶ÂÇÉÊÊ»sj›¾|"Pu”šœž ¡¢¢¯µ³³±žZ$.¤ñ¦ ñ‚!a©¾Á‚ÃÂÂÁÁÀ¿¾½¼º¹·¶´³±°¯¯®®­€¬.©¢ÔÒŽ_!6¡®¸ÀŶni—§W'Wl‰”—›ž¡¢¥«ÉÉ¿¸¸´8,£ò¥ úåP%z«½¿„À¿¿¾½¼¼º¹¸·µ´²±°¯¯®®­¬/«¤­ÙÈyJC|‹– ®°ieƒ…<.U]cip}’œ £¦µÏÒÎÁ»¹«Q -µô¥ öÅ.0®»½‚¾ ½½¼¼»º¹¸¶µ´²±°€¯®®­­¬0«©À׸j1?er}Š¡]Vgg,0RZ\`choƒ™Ÿ£§ÂÑÓÔÌ¿»³^ 3Íö¥òœ%B–°ºƒ» ºº¹¸·µ´±®ª¨¨©¬‚­¬2««ª¥›ÒÒŽU+HWdxTDLM#0O]X[_bflx“ž¢­ËÑÔÕÓû´ZOæù¤ ûîn Tš²·‚¸ ¶¶´±«¥ ›™˜€–—œ¢ª««¬¬€«1ª©§”­Ö«c# '29045%IV\X[^bfkr‹£ºÎÑÔÕÕÆº°K"‚ñ¥ùáG!e›«±²²¯¬¨¤Ÿœ›™—–••””“–Ÿª‚«2ªª§”–Ò¸f*(ˆY)5N]YVZ]aeio‚œ¨ÉÏÑÓÕÕǹ¥4-Æ÷¥÷Í3%s–œœ››š™˜˜—–••””€“’“—¥ª««€ª0§–”ѹf*@„¢t;HX^SVY]adin}¤ÅÌÏÑÓÕÕ͉#\î¦ô²),{’–€—––••‚”““€’‚‘8“ ©©ªª©§—–Óµe)0N-B€ªS$/9MZ\RUY\`dhoЏÇËÎÑÓÕÔ½°X&¸ö¦ ñ”%4€‘”••‚”“€’‘€¨€©1§™™Ô¯d$7¤–Z.?z¥šf1&IXZTQTX\_dk„‘µÇËÎÐÒÓεœ*[î¦ úïy"<…‘“”„“€’‘€;ŽŽœ¨¨©§›œÔªc!?’®›`/9pŸŸmBJX^NPTW[`m†°ÅËÎÐÒÒÁ­`(Âø¦ùê] E‡‚“’‘Ž6›§¨¦œ Ô¥a 8I,b¦°¡f.6‹†.HUQ[MPSW\oz€†¬ÅÊÍÐÑΰ•%ñ§øáRMˆ’ƒ‘Ž4™¦¦›£Ô¡aT°ƒ78‚®°˜,ml#@EYVSLOS[ouz…Œ¨ÄÉÍÏϼ¥J=è¨÷ÔCSˆ…‘€€€Ž€€Œ5•¢˜¤Ô]P±·¨_*Qš›)rT%\0HJRZOLP]kpuy~ƒ‹¥ÄÉÌÎǦr'Áù¨ õÌ7 W‰‘‘ƒ€Ž€€Œ7‹‹ŒŒŽ¥ÔšXJ›±¯Œ?-R$y@D‹1#9FQLSRM_ekoty}ƒŠ¤ÃÈËË«$ ‘ìö¨õÅ5"[‰‚Ž€ŒŒ€‹€Š8‹¦Ó˜U"&'aŸª¢f#".a’b)E?KMHU_afjosx}‚Š£ÃÈɲ˜78z¡Èåìñõ¤ôÁ4$]ˆŽ‚‚Ž€ŒŒ‹AŠŠ‰‰‹§Ó—P.OB(.g›žO%s'v’X.K@?IRX^bejnsw|‰¤ÃÆ·—K "Iq”¼âêðö ô¾,$^‡ƒŽ€Œ€‹€ŠA‰‰ˆŠ§Ó–M6`gfI(2ŒJ,`)†9N6-GPGOU[_djnorw|€ˆ¦Â¹•X :h¯ö ó±+&^‡Œ€Ž€Œ‹€Š6‰‰ˆˆŠ¦Ó–KT­¸­R*')6AKScmoqsvx}„ˆ…a†Dë£óÁ4(]…Š‚‹Š‰‰‚ˆ4‡‡ˆÓFM•¡¦£\>ˆ$C!dw)y¯³œ*R(!%+027BLZny|\†2é¤ôÁ4(]„‰ƒŠ‰€ˆ ‡‡††‡™Ó¢GR¡¯´­RJ{"=!q\)š¯«i)Wƒ€ %GoR†$ç¥ ôÂ5(\ƒ‰ŠŠ‰ˆ€‡"††…†•Ó§HN­¼Á·DTp 4)vAE¢¬¡6B9‡3A‡à¦ôÂ5'[‚ˆ€‰‚ˆ‡‡€†€…#‘Ñ®LK¸ÆÇ·1^d 27o*g£¦z$W  ‡Ô§ôÂ5(Z‡ˆ‡††‚…ŒÏ¶OGºÆÃ¨*gW"(DX%‡¢žI2F ƒ€  ‡˨ôÂ4&X€†‚‡††…€„‹É¾R8µÂ½’)rK'O?7–Ÿ‰&K*ˆ ˆ wâò§ ôÂ5&W††‡€†…€„ƒ‰ÁÆY/¯½·+x?#Z)U—™Z&O‰€ˆ /†Þò¦õÂ=%U}„…€„ƒƒ‚‡¹Ëc#¦¹±j2x2R"s–Ž/<6‹‰ (åô¥ ôÊA#S|ƒ„……€„€ƒ‚‚†¯Îr"–³«X.> ¢@²î¤öÓC!Myƒ€‚‚•Ï‘2n§Ÿ7Me3_‰v!@(¥]Óó£öÓC Jx€‚‚ŠÎ£=X –+UY*x„M#D§ "žî£ öÔCFv‚€€€†Â¶HA™‡%]N!4~{(20 ªxî£ öÔPBt€€„²ÇX)‘u$cCM{Y?«ʤ öØU >r~€€ Ìr#}b&h9_s2(5 © {ì¥%øáV 9o|~~~ŽÍŒ2]Q/f.+K3%§$mÌð§÷áU 4l{‚~„À¨A=>7c$%"5¥ Wžáðª÷á[!/jz}ªÃU-=^2/1( ¥5Ëéõ­øãj"*dx€|~’Êu*DW.2¦ …ò¯ øêl"&_w{||ƒ¿š>IN#-#§#Ú°ùên$"Zvzz~£½SNC*)©¨±ùî†$Rtxz†Æw2:7-©œ²úïˆ%Iqu|«¦G*(( ©°³ûð¡(?muˆÃe.,€˜ …#â´ûñ¤)6iv¨ŸD(  “ 6^€¨Àšb ƒ vñ¶ò·1.e}¾i2Ž (Nt™ÀÞêîòôíÒN +Ú¸ôÃ=(`•®K'  :aˆ«Òæíñö‡ß9€±ô¹)õÏJ (c¬‘A1  .PxšÂáêîóïz ”ð»#öÝa",\«\' +X…£D?bЬÕèíñõ’ó‚ œð½øèx$1'(Iv¤Ìçíòî”}›Äâêïó™§F¿ñ¿úï/€)±êñö‚öóõžë›¶ëöÁ ûóÍW'!$2ñô÷í´}u”Öóûö÷óòöúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ûõîëììíîïðòóõõ÷øúëúï£RAAX^_i{}‡š›­¹Ìãòùèñ‰$ "#&6†ß÷æ÷É*FÖöåí^ engc\QKC82+$!Nãùã ö¬#\¿ÄÁ¿¾€½ ¿ÀÁ¾•t2sðãéI/¶ÍÐÍËÉÆÄ¿½»ÇÚ§ˆ/#¨ôáõ¥"‚ÎÔÒÐÎËÈÅÃÀ½ººÖÕz"9×øßòÂO5ÅØØÕÓÐÍËÈÅ¿¼º¿ßÅ–]jéôøÜ&ö¨*_ÑÜÚØÕÒÐÍÊÇÄÀ½»¹Éá±: f™¹ÞëîòöùÅùùŒ,òu\ÓßÝÚ×ÕÒÏÌÉÅ¿¼¹¹ÕÛ¡}"#)Ee„¤ÄÞêîòõù½ ôá¹´Õëîòöú†2ñtD·ÐÒÔÕÕÓÑÍÊÇÄÀ½º¸ºÜÏ™`!$-D_ŸÃÝêîñõù¶Mó°7('*Aa¥Ïèïôöõöùò‡824Ec ¼ÆÇÈÈÇž»¸¶¾ÞÉ¡qO6$ $+?_zš¹Ôèìðó÷û®QúÐ1(0+$  #'8h¨ ž»ãðìŽ ¸Ó¯ŠjJ77Jfœ³¹º¹¹¸¶´¼ÎÒÑÆ¹©ŒpWC/" $'7Tp°Ôæñø«Eô•'MNG?70' ! !$2gŸw%½¥x–¹ÒãƒbB48Le}–¨ª««­¯°³ºÀÄÉǾ²žƒhQ@/!€ "&5hÂñú¨4ùàbSc`ZRJB:2) %²O0//:\~¡ÄÒ¾œ{[;37Mbx ¢¤§«±¸½Âù¬šeQA0"€(’ðû§î_Acmjc\TL@'+4,"€;  4Vzn\K:01Dg‰«ÊÒ·–uS819Lbw˜šœŸŸ Ÿž¡¨°·½¼´¦–}cQB3$%¢ò§?Ì(#%<`srmdK"mØãÙ²|B%8 T€…††…~lYH7/5Nq”¶ÒαnM41;NbvŠ’“”––—–™¡ª³¹¸­žt\<'µó¦B¼$˜¨c+$6\tsA0ÉòùùöïæÀ‚>!'FfƒŠŽŒˆ~jVD309Y{žÁÕË«ŠhF31`Ž’••”‘Œ{fR@21Bd‡©Êׯ¦…cC10=M]o…†ƒ‡ ˆ˜Ì²q@?Ýù¤Ë&%¨ÝèíãÂu2"'ˆíöøùûüAùòä¹}D$8]‘–šœœš˜•“‹xcO<04Lo‘³ÑÖã‚a?00;J[ky~€„œÏ¤l5díú£á0 Øâçíïä˃MÄñöøúú€ûBúùùøõíỀE%8_ƒ“›žŸ  Ÿž›˜–‹rYB.(2TyœÀ×Öã_>0/9GUcq{ Ìe)!ò£íSiÓâåçëñòèçðóöøùúùø÷õö÷øóêÝ·}E$!Co‰”ž¢¤€¥%£œˆ]C9//@>21?a…§ÉÙÙĤ„bB0+/E©Æ}\+Áö¢[ó€CÇäèèéìîòöóóö÷øùùúùùø÷öôòñóõõñåÔ®u?"7Wt‡“¥¤a-6Xj]8'T‹mVA24Im³ÑÙÚ˨U(wºµpMKèú¡]ùº$-£àéëììíîðñóö÷÷øøùùøø÷öôóñïîîðóóìÝÉ¡i20On„‰J*k¥¹ÅÍË6K¦¦¢˜hQ;27Q}ØÝP!Bjº˜h8Šêõû  è=rÙèëíî€ïQðóõöö÷÷øø÷÷öõôóòðîëéçèíñïäѺŠN '0Z†¢»Ðßçä·8^©²²±¯¬©¦–zR/±Ä-UC,GŸlW R¢âòúžò~@Ãåêìîïðññóôõõƒö)õôôóòðïîêæãààæíïê×¹2" ;b‹§¿Ùêà—.³ƒ·µ¬o9ʉ2©¶¦uk+% 7‡äõùÎ&%ßéëíîðñòòóôô‚õ,ôôóóòñðïíëéæâÝÚÙÞéíã»™0/.)#:f–´»³C[±º»¼»°MjÌQ(c˜¶º½¼›V)@¬óî_KÎæêëíïðñòòóó‚ôIóóòòñðïíìêèæåâÝØÔÔäìÞ´(==81+%2YvV.›²»¾¿ÀÀ¾¢2š¸'5d˜ÙåÝ“O$0Ù÷µ#(”ßèêìíïðññòò‚óIòòññðïíìëéçæäâàÞÙÓÑåëÖ­'LLG@:3-& c‹™§µÀ½|8À…''!"=v²Ìâä³3)Ìž ëMLÌäéêìíîïð€ñòIññðïîíìëéèæåãáßÝÜÙÐÐçé˧e3ZZUOHB;5.("!?fŒž­²RbÁQ&$!! !$8Œæ_ïžõ«"$ŒÜçéêëíîîï„ð<ïîîíìêéèæåãâàÞÝÛØÖÍÐéæ¾¢I;\``\VPJD=70*#C€ˆ,±(# €"U±)WãøŸëZ?Ãáçéêëìííîî€ïIîîííìëêéèæåãâàßÝÜÚØÖÓÉÔêá³–0g—ž¡¡S9$Àù¨ õË4d¯¸»»º¹¹¸¸··¶¶µµ´³³²±±€°3±Àà¶}H›±¯Š:#x>B‹-) '#"%(+.16=f—ž¡›]Eìö¨õÄ2!i¯·‚¹¸¸··€¶Dµ´´³³²±±°¯¯®®Áàµw(+'`žª¢c,`‘a ""! "%'+.15=e—žœnH 6z¡Èåìñõ¤ôÁ1#l®¶€¸€·¶¶€µI´³³²²±°°¯®®­®Áà³n9hQ+-f›N#r%u’V '!"$'+-054L2Š{ JW$ (;8&(,0023/15989?n@.†Mì¢ ó·2(k¨±³³²²€± °°¯®®­­¬««€ª1»ß³_M–¡¥¢i2‰-A)T‚:/X^Z0&#.8876758=?9,†Dë£óÀ2(i§¯€±€°(¯¯®­­¬¬«ªª©©ª¹Þµ]R ¬¯«_>‡"Bcw"=UXOQ%"&',13)†2é¤ ôÁ2'h¦®°°¯¯€®­­¬««ªª©€¨µÞ¸]T©¶¹²RI{ ;qZGRQ7#V„€ !-%†$ç¥ôÁ2(f¤­€®€­¬¬«ªª©©¨€§±Þ¼`O±¿Ã¸BSp2'v<%GMJ"?8€ˆ‡ঠôÁ3'e¢«­­€¬.««ª©©¨¨§§¦¦®ÝÁdJ¹ÆÅ´/]c16o#.DG8V   ‡Ô§ôÁ2'd ª«)ª©©¨¨§§¦¦¥¥ªÜÇhE¸Â½¢(gV!&CW6@@%.E ‚€ ‡˨ôÁ2%bž¨€ª'©¨¨§§¦¦¥¥¤£¨ÖÍl5¯º´Š'qJ%N<7<6I(‰ ˆ wâò§2ôÁ3%_œ§©©¨¨§§¦¥¥¤¤£¢¦ÑÔu,¥±ªt)x>!Y##46&N ‰ €ˆ /†Þò¦ôÁ;$]›¦€§$¦¦¥¤¤££¢¢¥Ê×€ ˜¨Ÿ`0x0Q&0084‹€ ‰ (åô¥ôÉ>"Z˜¤¥"¤¤£¢¢¡¡£ÂÙ$†Ÿ–M:v#%:'+#LŒ€2–èõ¤õË@!W–¢€¤"££¢¢¡  ¢¹Ú™0r”Œ=Cn0!$&)> ¢@²î¤,öÒ@S” ££¢¢¡¡ ŸŸ ®Û¦<\‹ƒ0Ld1 >'¥]Óó£õÓAO‘Ÿ€¡  Ÿ€ž¥ÙµKHw%TY'B§ "žî£ õÓAKŽŸ ŸŸž€¡ÏÄ[4vg!\M//ªxî£õÓNF‹›€žœœ›ŸÃÑq!kV cB>«ʤ'ö×S@ˆ™œ››šœµÖŠ&XE#h8 #4 © {ì¥÷àT;…˜€›š™š¥× ;?8+f- 2#§$mÌð§$÷àS5€–šš™˜˜Í·S)+3b"#5¥ Wžáðª÷àX/{”€˜—š»Îm ;^2.1' ¦5Ëéõ­!øâh +u’–—–—§ÔŒ1BV.2¦ …ò¯ øêj&n”••šËªNHM-?! §#Ú°ùêl!!gŽ’“–´Èk MB;7©¨±ùî…!]‹’›ÐŒ=96@"€©œ²úï†"R‡Œ’¹´^)76 ©°³ûð %F‚‹›Ì{:>"€˜ …#â´ûñ£';|е­[(5  “ 6^€¨Àšb ƒ vñ¶ò¶/1uÇ}A%  (Nt™ÀÞêîòôíÒN +Ú¸ôÂ:)n¢¸a0" :aˆ«Òæíñö‡ß9€±ô¹)ôÎH(mµžWC  .PxšÂáêîóïz ”ð»#öÝ_*`±m1 +X…£D?bЬÕèíñõ’ó‚ œð½øèv!0)&Hv¤Ìçíòî”}›Äâêïó™§F¿ñ¿úïœ,€&¯êñö‚öóõžë›¶ëöÁ ûòÌU$!/œñô÷í³|s“Õóúö÷óòöúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ûõîëììíîïðñóõõöøúëúï¢P>>V\]gz|…˜š­¸Ëãòùèñ‡!!#3…Þ÷æ÷È'€CÕöåí\mxnjaVOE:2+# Kãùã ö« bÕÛÙÖÕÓÒ€Ñ Îɨ†6qïãéF0ÉåèæäãáßÜÛÙ×Ýæ¾Ÿ3 §ôáõ¤ãëêèæäâàÞÛÙ×׿ã·"7×øßòÂM7×ìîìêèæäáßÝÚØÖÙëØ¯khéôøÜ&ö¦'eãñðîìêçåãáÞÜÙ×ÖßìǨ?d˜¸ÞëîòöùÅùùŒ,òsaåóòðîëéçåâàÝÛØÖÖå繑"!&Bc‚£ÃÞêîòõù½ ôá·²Õêîòöú†2ñrFÅâåçêëêèæäáßÜÙ×ÕÖéÞ±n"*B]~žÂÜêíñôù¶Mó¯4$#&>^¤Ïèïóöõöùò…603Fh‹­ÏÚÝßàáßÝÚØÕÔØêÙ¶€W8$!(=]x˜¸Ôçìðó÷ú®QúÏ-$5f§Ÿœºãï쌷Ү‰iI56Mm®ÉÑÓÔÕÔÔÒÖààÝÒȸš{`H0!!%4Rnޮӿñø«.ô”!/0+'"!/eu#¼¤u”·Ðâ‚aA39Pn«ÂÆÈÊ€Ì!ÎÒÔÕÖÓÊ¿¬sZE0 #2fÂñú¨Uùß`5=;72-(#"±L(')6X| ÃѾ›zZ;29So‹¨º½¿ÂÃÄÃÂÄÈËÎÐÍŹ¨ŒpYF2"&ðû§î^*=C@=94.((2) €ž0>WOD90)+@d‡ªÊѶ•tR81Ì%)b…¨ÈÖÆ¥„bB10AVk‚•›€žŸžªÔ¼G<Üù¤Ê##³ìôôìÎ{0"‡ñü„ýAúóè½D"1Qr‚‡ŠŒŒ‹‰†„}mZH7-1Jm³ÐÕ¢_>/1@Sg{Ž“”•–™­×¯{9bíú£á-•èóõøöî׊NÅöü…ý@üûøñçÃ…G#2W{Š’•–——–•“ƒlT>+%/Rx›¿ÖÖ¢€^;0/>_ƒ¦ÈØÙãƒa@/*1K³Îj(Àö¢ó~CÖñõ÷øúûûüûüü…ý€ü€û<üûöîâ½~A!5Wv‰” ¨¨Ÿb.>l~k;&TƒnWA02Hl²ÐÙÙʧT(‡Ã¿WHèú¡ ø¹!,¬íö÷øù€úûüƒýüü€û€ú<ùúøôéÚ²r4/PrŠŽL0”àëîíãœ6M¥®¯«¡ˆmT;15O{ØÝM!HtÁ¤w=‰êõû  ç;uæõ÷øù€úûûˆü€ûAúúù÷õö÷øöîáΚU &1†¿Øê÷ùøðÃ9d¹Âÿ¼¹´£ƒW-°Ä+TC+G¡ub!O âòúž ò}>Îñöøùúúû†üû$úúù÷õòñðòõöòåФ5!N~«ÃÔäñî¥.ŸÉ‚ÎÍËÁy8ʈ/¨¶¦~uj*%4†ãõ øÍ$#’ëõ÷øùúúƒû‚üû(úúùø÷öóñîìëíòõíÓ±4?oŸ¾ËËJfÌ×ØØ€ÙØÊShÌO&a˜¶º½»šT&>«ó î]KÙòö÷ùùúƒûü‚ûDúúùøöõóòðíêèæïôëϦ*&%"3b‰e3·ÒÞáâããà¿3™·%4d—ÙåÝÁ’M!.Ø ÷´!&›ëôöøùùúŠûEúúùø÷õôóñðîíêæäïóåÉ“ /.+'#t¨ºÊÛéìä“8¿„+0-& #ALOMH=&S°&UâøŸ ëW?Íïôõö÷øøùúùFøø÷öõôóòñðïîìëêèçäÞåòḭ̀4&279<;962.+''(J4 >)—}.:=AKTUSOC!Š>Sâø  ö¹!xåñôõõö÷÷„øF÷÷ööõôóòñðïîíëêéèæäâÜèðæÄ– #+/268;<:78IG 9Œ>3:>EVZZXQ6.w:Ú÷¢ î_-³êòóôõõö÷öDõõôóóòñðïîíìêéèçåäâàÚëïݽo#'*.158?Y“f#7=" $.BU[]ZQ%b+!¯ô£ ÷É)Q×îòóóôô„õDôôóóòññðïîíìëéèçæäãâáÝÛîíѵD"&).=0%I{[   6LUB h\í¥ ñŠ…ãïñòò†óCòòññðïîíììëêèçæåãâáàßÛÞïêÅŸ' %U”` $O !"G8¶õ¥ úæG-µçïð€ñƒòDññððïïîíìëêéèçæåäãâáàßÞÙãîâ»x d8*ls% (BE?5% %¸ñ¦ ö¾"JÐéîï†ðDïïîîíììëêéèçæåäãâáààßÞÜÖèíײJ `ˆ %*%Z`ba`^\N9  )¢ñ¦ ñsÛëíî„ï!îîííììëêééèçæåäãâáààßÞÝÜÛ×ìêÇž' €I~ HZ]_b€dcpZ-  &¡ò¥ úäK#›ßêì…í ììëëêêéèçææåäãâááàßÞÝÝÜÛØÛíåºw€%y€ 'N_cefmyva( (³ô¥ öÃ)2¹áêë‚ì€ë êêééèçæååäãââááàßÞÝÝÜÛÚÙÓâìÛ­F €" b .Ybdgv}{vP -Ìö¥ò™IÊãéƒê€é-èççæåãâáààááàßßÞÞÝÜÛÚÚØÖÐééÄ‹ ( €L`dj|~}yj% Jåù¤ ûîjeÒäèèé€è€ç.åãáßÝÜÛÚÙØØÙÛÞÞÝÝÜÛÛÚÙØÖÍØëÔ¥+    :^ds}~|s4 ñ¥øßBÕâ€æ4åãâáßÝÝÜÜÛÚÙØ××ÖÖÕÕØÜÜÛÛÚÙØØÖÌÌéÛ¨:$‡V%    )[g}€ |v8 (Ä÷¥öÌ.$™ÔÜ‚Ý1ÜÜÛÛÚÚÙØØ×ÖÖÕÔÓÓÒÓÙÚÙÙØØ×ÕÌËèÛ¨:;¡r8  ‚\y }u0Wî¦ó°#-©ÓÚ€ÛÚÙ€Ø ×ÖÖÕÔÔÓÒÑÑÐÐÔ€Ø×ÖÕÍÌéÙ¦6 :}©‹P + ƒay~€ |q  ·ö¦ñ‘9µÓÙØ/××ÖÖÕÕÔÓÓÒÑÐÐÏÎÍÒ××ÖÖÔÍÌéÕ¤-/-!6v¤šc-  ƒWx~~za Wí¦úîuE½Òƒ×ÖÖ€Õ,ÔÓÓÒÑÑÐÏÎÎÍÌËÑÖÕÕÔÍÍèÒ£'*/+ 0lŸŸk "ƒ Mx}~x: "Áø¦ùêYQÀÑ€ÖÕ/ÔÔÓÓÒÒÑÐÏÏÎÍÍÌËÊÊÏÔÔÓÍÎèÏ¡%4B!+,) .Š… „Ew||l~ñ§øàM]ÃÑ‚Ô1ÓÓÒÒÑÑÐÐÏÎÎÍÌËËÊÉÈÇÍÒÒÌÏèÌ #Q¯-#))%kj  $„>v|~x9 7ç¨öÓ=eÃЀÓÒÒ€Ñ.ÐÐÏÎÎÍÌÌËÊÊÉÈÇÆÅÊÏÉÏèÉ™ M°·§X$"qR"Y" „9v|~|` "Àù¨õË2kÄÎÑ2ÐÐÏÏÎÎÍÍÌËËÊÉÈÈÇÆÅÄÄÃÃÏçÇHš°¯Š7x=AŠ+ „7u|~tŽìö¨ õÄ0pÂÍÐÐÏÏ€Î/ÍÍÌËËÊÉÉÈÇÇÆÅÄÃÂÁÂÏçĉ>>+až©¢b~*_‘`  „7v}x6 5z¡Èåìñõ¤ôÀ/!sÂÌ€Î4ÍÍÌÌËËÊÊÉÈÇÇÆÅÅÄÃÂÁÀÁÏçÃ\°6/f›M"q#u’S !  ƒ9vxK  "Iq”¼âêðö ó¼'#sÀÊ€ÌQËËÊÊÉÉÈÇÇÆÆÅÄÄÃÂÁÀ¿ÀÎçÂzb¾Ã´r.1‹H)^%…/ $ ?tY   :h¯ö Pò®&$r¾ÈËËÊÊÉÉÈÇÇÆÆÅÄÄÃÂÂÁÀ¿¾¿ÍæÀucÁÊǾ’${=3J0Š{  0* H_  „jð¡Oò®+%q½ÇÉÉÈÈÇÇÆÆÅÄÄÃÃÂÁÀÀ¿¾½½ËåÀncÀÊÊÄŒ)…4:8B†X  !+*$  E†Mì¢ó·/&p»Å€ÇBÆÆÅÅÄÃÃÂÁÁÀ¿¾¾½¼½ÊåÀkbÀÊÊÂy2‰+@'S5  "! ! †Dë£ó¿0&o¹Ã€Å+ÄÄÃÃÂÁÁÀ¿¿¾½¼¼»»ÇåÁia¿ÊÊÀf=‡!AbvN#€€ †2é¤6ôÀ/&n·ÂÄÄÃÃÂÁÁÀÀ¿¾¾½¼»»º¹ÄäÄi^¿ÊʾUIz:pYUƒƒ  †$ç¥4ôÀ0&lµÀÂÂÁÁÀÀ¿¾¾½¼¼»º¹¹¸ÀäÇlS¿ÊÊ»BRo0%u9 <6  ‡à¦óÀ0%j³¾€À0¿¾¾½½¼»»º¹¸¸·½äËrJ½ÉÈ·.\b/4oU    ‡Ô§óÀ/%h²¼€¾+½½¼»»º¹¹¸·¶¶ºâÐuEºÆÃ§'fU$BV *D‚ € ˆ˨3óÀ0$e¯º½½¼»»ºº¹¸¸·¶µµ¸ÞÕz5µÂ½‘&pH#M9G&‰ˆ wâò§2óÀ0#c¬¸»»ºº¹¸¸·¶¶µ´³¶ÙÚ…,¯½·}(w<YM ‰ €ˆ /†Þò¦0ôÀ8"`ª·¹¹¸¸··¶µµ´³²µÓÞ ¦¹°h0x/P62‹ € Š (åô¥ôÈ< \§µ€·#¶µµ´³³²±³Ìßœ%•³ªV9u"$8J Œ€Ž2–èõ¤õÊ>Y¥³€µ!´´³²²±°±Äà¦2‚®¥DBm/ &=£@²î¤,öÒ>U¢±´´³²²±°°¯¯»à±Am§Ÿ4Kd0=%¥]Óó£+õÒ>QŸ¯²²±±°¯®®­³Þ¾RV –'SX&B § "žî£)õÒ?L›­°°¯¯®­¬¬°ÖËf?˜‡"[L ..ªxî£(õÓLG˜«®®­­¬««­ÌØ~'s!cA>«ʤöÖPA”©€¬«ª©«¿Ü˜'{`#h6  !3 ª {ì¥%÷àQ;¦«ª©©¨¨±Ü«@[N,e+ 0!§$mÌð§÷àQ5‹¤€¨§§ªÔ¿\;;4b!!4 ¥ Wžáðª"÷àV.…¡¦§¦¥¨ÄÔz*;]1-0&¦5Ëéõ­!øág*~ ¤¥¤¥²Ú˜4BV!-2¦ …ò¯ øêh%vœ¢££§Ñ³W GL1G  §#Ú°øêj mš  ¢½Îx LAC>©¨±ùíƒb—œŸ§Õ—C85!I#€ ©œ²úî…V’˜žÁ»i(><ª°³úðŸ#IŒ–¤Ñ†>F#€˜ …#â´ûñ¡$<…”¼³e+; ” 6^€¨Àšb ƒ vñ¶òµ,1}˜Ë‡G'  € (Nt™ÀÞêîòôíÒN +Ú¸ôÁ8(t©½l2" :aˆ«Òæíñö‡ß9€±ô¹)ôÎE&qº¥aL   .PxšÂáêîóïz ”ð»#öÜ](a´u6 +X…£D?bЬÕèíñõ’ó‚ œð½øçu/)$Hv¤Ìçíòî”}›Äâêïó™§F¿ñ¿úï›*€$¯êñö‚öóõžë›¶ëöÁ ûòÌS!,›ñô÷ì²zq’Õòúö÷óñöùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™t8mk@ H¥Ã¼±­¡Š€leVD7 dòùûüúúùùø÷÷÷õõôñÓo ãûýþþþþþþþþþþþþýýûö²œøýþþþþþþþþþþþþþþþþü÷ª ?òýþþþþþþþþþþþþþþþþþþýö{ »ùþþþþþþþþþþþþþþþþþþþþüï;@ôýþþþþþþþþþþþþþþþþþþþþþúÊ&¨ùþþþþþþþþþþþþþþþþþþþþþþý÷… 9êùüþþþþþþþþþþþþþþþþþþþþþþþüóˆU, €÷ýþþþþþþþþþþþþþþþþþþþþþþþþýüùöóåÁ™rK%  12 ~øþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýüüù÷õòá¿™sN) 7ÍòóéÄœrG uøþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýüûùøõóèÅ {W2 ÜúþþýüúøõïLJCM]N- uøþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýüúøöôíÍ©„_: VõýþþþþþþýýûøööõõóÓK~úþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýüúøöôïÔ" ‹ùþþþþþþþþþþþþþþþýûø÷öüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýýüøâL†öüþþþþþþþþþþþþþþþþþþýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýúìJéüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýúç4:óýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýúÖ 7óýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýøºñýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþý÷Œ ØýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýôS§úþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþúá#löýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýù­ +ñýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýö`  Ãúþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûð§K q÷ýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýüùõÏd ëüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýúöÂ5šùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýùÜ5òýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüï"­ùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþü÷•<òüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþü÷Ÿ ©ùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýø¢ .ðüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýùÆøþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüð6Þüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýø•`öüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýò, ²úþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþã-)èûþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýùÞ<jöüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýúæ? ®øýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýúä1 ÜúýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýùÒKóüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýø­ öüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþü÷j ±øýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüæ#ÔúþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþùŸ5éûþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüñ6Tòûþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþù¢töüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüî#“÷üþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþý÷v ¬øýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûÄ ½øýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýñ$Éùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþö\#ÓúþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþúŸ* (ØùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûÊVPNF8) ,ÚúýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüàYSSRQQPOJ<-0Üùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýë[SSSSSSRRRQ.1Ýùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýï`SSSSSSSSSR4/ÙùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýòcSSSSSSSSSR:,Øùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýï`SSSSSSSSSS?+×ùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýë[SSSSSSSSSSF(ÕúþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüûúûüýþþþþþþþþþþþþþýáZSSSSSSSSSSJ&Óùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýእ¼Ïßçëòõö÷ùûýþþþþüÏYSSSSSSSSSSM%Òúýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýú¤XVWXYZ]bdhz•ÙúþþþþúºWSSSSSSSSSSO $ÏùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýólTSSSSSTSSSTVfïýþþþùVSSSSSSSSSSQ!!ÌùýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûÆYSSSSSSSSSSSSXÆûýýýö{TSSSSSSSSSSRPA Êøýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýø‹USSSSSSSSSSSSUyÜõöô¼XSSSSSSSSSSSSRQPBÆøýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüæ^SSSSSSSSSSSSSSUY]_bXTSSSSSSSSSSSSSSRQP@Ãøýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþú¯XSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRQP;Àùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ÷tUSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRN.¼øýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþûÒZTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRQG·øüþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþýù–VSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRQN"²÷üþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüîcTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRQM «÷üþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþúºWSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRPK$¥÷üþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþý÷USSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRSQPF( ÷üþþþþþþþþþþþþþþþþþþþþþþþþþþþþþüÝ[SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRQJ2 ”öüþþþþþþþþþþþþþþþþþþþþþþþþþþþýù¢VSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSR: ‹öüþþþþþþþþþþþþþþþþþþþþþþþþþþþôjUSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSP  €õüþþþþþþþþþþþþþþþþþþþþþþþþþûÆYSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRHtóûýþþþþþþþþþþþþþþþþþþþþþþýøŠUSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRPhòûýþþþþþþþþþþþþþþþþþþþþþüå^SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSP[ïúýþþþþþþþþþþþþþþþþþþþþú®WSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRPLéúýþþþþþþþþþþþþþþþüûûüõtTSSSSSSSSSSSSSSSSSSSSSSSSSRRRRPPOPQRRSSSSSSSRF?âùýþþþþþþþþþþþþþýù¨Çê«XSSSSSSSSSSSSSSSSSSSSSSRQQPOI=/! .IQSSSSSSRP&2×ùýþþþþþþþþþþþþüâ_Y]WTSSSSSSSSSSSSSSSRRRQPOND6( 1PRSSSRQC%Éùýþþþþþþþþþþþù–WTTTSSSSSSSSSSSSSRQQOI<. =RSSSRL´øüþþþþþþþþþûÞ]TRSQPPQRSRSRQPOMC5' %QSRQM—öûýþþþþþþýøŽTPNB2!:PPOH;- PRQI pðúýþþþþýùÉ>(% MO8 EÙøüýþþúë6ñö÷öÞN4yadosage-2.12/doc/icon/favicon.ico000066400000000000000000000070661227056367200165410ustar00rootroot00000000000000h& ¨Ž( @ÿÿÿ:櫘7;Ljb•ÒëÏ–•”‹O}`T-@¦+öêÏî‰ch|+1:[O:(oŠi@g1騶YYaÿÿâJ !"K8C9ML  E4?%0:.@B*2G$I7&+5)3 =(- 1HA;'# D”^@2T„ÿýãÕ¾’êÚº$P*|ÑY_a|„‰2·¢RRI¥† Iz§‘g ZiaO ›2•›› B;++=‡#[…ŠK’ƒye(**(I¬ßË£1äËÁpj^‰yT/HÔµ£åƉiv~~kG)  >$7G341]dv¡’y5^zžðäÇûðØ$Ò¸†]j’utpÜ϶,S-ZFb¢@¶†öÁ<¸~T„JÞzø)w³ªnÆ÷¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿø?ÿÿðÿÿàÿÿÀÿÿ€ÿÿþüøðàÿàÿÀÿÀÿ€ÿ€ÿÿ€ÿüÿÿþÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿdosage-2.12/doc/icon/logo128x128.png000066400000000000000000000420601227056367200167350ustar00rootroot00000000000000‰PNG  IHDR€€Ã>aËC÷IDATxÚí]`TUÖþÞô>“Þ+„ t¤HSÀvp±×EQ‘µëZײºØ±"¨ ‚"  ½·`€@!½„Ô™L¯ÿ¹wfBqÕÝ_À%W/3™úæï~ç;÷Ü{ž€Îv^7ál@g;»­çyëÀyÞ:pž·Nœç­çyëÀyÞ:pž·Nœç­çyëÀyÞ:pž·Nœç­çyëÀyÞ:pž·NœçíŒÀår N§Sät8ÄnG$°øø¿‚ÀoEìVÄî‹ ‰|üy‘@O‹|ìo™Læ;Û'î¥Qá‹Å"]µòû1+–}{CcSc"€#2*ÚBFu“…Íj•ª144ÌE†¶*UÊú°°0‡Hé±FµFÝVÝ ×ër¹ÜM½ ÿE;£0ÒÞŸ{ÓÇ}8ÇjµÊ=n·ÈOð²ÑN÷8°‘Î@ÄØ@$b‡ée @@ð‰Å"Á`(Ÿ<åʧN›6/*:ƦT*=gûDþYÛ€Ífå:˜pÛ-7m¯¯¯öùȶb± Kˆà½ðÑüzÂÇþæ·þÁíó²[~Ÿÿ#‘H¼r¹Â•ž‘¾þõ7Þº599¥Q©RyÏöÉü3¶3€Æ†ùìY÷¿»aÃúé§SªPk„þã'C®TÃí°ÂçvÃå°Án±«pÀa·ÂA÷]N;=î niúÛ Û ŸÇëU(ä®~YYß¿ýîû·DFFZ;ÝÁïogD÷âÕ?¬þØ#}ßÒÒ¢ËäBæ…pñm³!–ÊàñxáõzØð‡@£_Lu ±¿ŒŽP!x x\Ø}´e%ÅÈÛ¸Æê2éÇÕW_ó'žúû«CˆólŸÐ?[;#¨­­UÞzó ßúdÁÅ)))F…BÑ©~cûC`2™¤4êSæüëÕçöíÝs…Ûí‘F'§áž7@A$/òÝ{Êð Ãû»ÿ> t¨¤bŒHEÁ‰l;x…»ÖãØÆe¤!ì>…Rá¼ì²Ëç<õô³/¨Õj'Š„NMð+íù}Ñ÷ß-üÂsÏ®hnnÖ;N±H" ¾x ®}ðï«u|ô·ùÞ¶†0€;zèõ %zGk±þÀq+)þ%(Ìwámã/ºøÝ¬þö§$'MëÚ­œÜƒK,{Å v¿S,žj§€Ãáàÿ7'ŠF¿ä…çŸ}òëÅ_=Bn@ƾKB‚oÀ¸Kð—G^€B££ÑîkCùžS÷ƒ#ßÝ– üϳc®@'aÅÎC(+ÌÇÞEoÂÞbô ¤"%©G&“z·¦¸øøB ó“’’Ž%%§KNN.ˆ«aZ¡-(ü¡åùŒv0›Í®åççucS¶))]Š#"#b±ÿDûo™Še“>€ÇÈø/Åü]z÷ôÙO#4&ôu|Óüé~÷1¾›Â¿À}&å"#RÃÐÜbÁš½¹(صÇ6-ç¡¢×ãΰI%—¾F쑌L¿Å£T*𢢢‹ãââ Ô“§¥¥‰­Òju.‰Dìõÿfö©€ñ?«)Z@á™dó†õýç¼ô¹csW;b±5<*úx|rr~|rJ^jZ×#=zމ‰iTÈå‰Tâ‰Ä>6aà ܖ^™ÿŸûîÛ|ðÞÜçÈå $&&ÂNq<3¼&$ ‘‰©ˆH삈¤T„ǧB¦RûÁ ˆü€ 1ÍAÌu„($BzàD]~Ê/Âñ{q²ø(š)<47ÖÃíbón^zé=LC° &øó<§ù> fl:HKxxD±ÄqÇqFaR»_æJ¤Ü•ƒÞã“JÿÜy 6G4ïHäݷܸ£‡Í˜x“‚ØÚåF±Ãã+sû|å>Á[.ˆ½Ít¢¼©S’ߣo¿Í#.½Þj·«ÈWè¤ãJºgt/Ñh5N2˜èóÏÌ|ëÍ7ž·˜Í2Y˜ûþˆ‹CQa!Ž?Ž’ÒR”R¯o¨#wmXÂâ“—Œ¸è£â b‘ÂK`ðrP°¨Ü zAj¨ j E“ÐÐÔbECs3NT”£º² õÕU0××ÀDÜ…‡ƒÈßÙç 0ëØ­,@·ƒA_šXœ„õ.i]KÃBÚTjµ‡å%þŒÑQ¿øóO\µà­ÍŸßM§H¤3!q¸àµÓɲ»ˆZ©Û\h @¸<Èõ Þ½b©§„N“F³‡87È:®tÀÀAë‡ ²>??¿ÿ¢…Ÿ?i·Û%¡¡ax—P[SƒÜ܃ÄIHíÒÄ&0[%%%%F“‰Dƒ šð(èc¡N‚ŽnU¡Qð‰$D%Quõ ©õ§woøñ¾9⤃ (h˜InP÷Ù \pS·ê¨?hq#‡F&ÏÄøçíÙ‰òJ¥RΡ×çv»eÔE¡¡¡xõ_¯cÁ¼P_VèÉd@¥Z‹¤ÔT¤¤¦!¥ ë]@ B«¸¸ˆÂÏe°Úm 9Ô‘±ÐDÄCMT”ºPæç``>ŸôNàŽÄ¹DD  `ÈÅPS(©’Š V;é3›PMÀ¨=Q‰æúZX›`iªçÀp»™+i –§h öƒÙï¤ßë&†;y÷Œ3n¸ñæ)qÑc ²Ù³xfó¦ xœv™A&MéŽ)$²RT2(iTHd*‡›ÁkwÂbvbNu –Úh jµ)UpqlVŸÇw»\ÌW3^mÕŽáïÏ<‹Þy —§ê0:-µf;Š-(n² ¼É†“VÙ¡aHf`Hí‚Tê‰IÉ+ä8QUÅQL½¨¸UU'`w8 V¨¡ ‹!`Ä0¨@X˜) XñuË"!`;2¤œ3†Zr%ZºUË<ô¸~‹Ñd殤¶ú£ &Ò6c#,ÆXÍ‚9Ȉ6ò¹Ç ŽaûÖ-Ø´~©Œ‚˜Áíâ`'8B§FϤhôNŽEׄ(¨är¢S‘?,ôùx^€)ÿf¢éòz#*©Ÿh4¡–€á$VRëPG áw]¨‡…‡s –‘Ë`,Á€Áz]C1¥j=”¤!0Ô ¡‘`sŒ%À×!ø +H[Ì}0Ãû]  "7bÐ(ù±§Æ†£ªºkwìCùálTÜMÌçðI%OdTtá~<©W¯^L žm#ÿÖ3„„G¿5}{d@Lšû@AÖ.[‚Ã[„×jF–A†+’ôXTÜ YF?¼ð¯w Ó‡BB£IJ´$üì‘ñ(а=ŸlhÄÆñÝ7‹QZTÈY…hÌ—Šé„u’ñeHKˆFÔtK‰‡N«f9æ_ù(åz!pÈêšZxøWSß„ÚF#šˆ®™[6„„p@0–`®#9%,ú065“Žð»Ž H#ù}ö¹2m†}˜ übb+΂Ðf¢„ûÎLpF´¸h@”¨ÁªM;P’³ÕùXªÚ'—É\i]»nýø“WÇÇ'˜Ïåy!{ßÞøþ2íHCCƒF&Üûüè>p(ÂÔ „H#À#%åØöýRü´a,ÍM’švËøË3 Uª¹ñ™á%5.†åûÕ,·Mä9‚Û·#{×6T•³Göƒˆ\…ñ1QH®i)0 ô}Rˆ%~‘lj:@ò¦ˆíÄ.u ä¯ëPWßHaeé+QttL+ ºPOLàŸG¿—G¥\S0¦(A³Ñ–vbL¡Ð‡ANà‡J½‡³ÿ±Òñ$G‡c\ÿØw$Ûvgãøžh,/„4E΋/žðî˯¼ú„F«už«‘P^V¦™2ùÒlX]•jð—™`è%WA¥RCFj™D!Ô"/ê-v”åíZó=òönÇC<ÂgõdD¡lfޱ73¼HŽÐó÷ oe“8V2V'¯ {vlEõªÒB8ÌÀfúØû™;`†ŠŽFZzW¤¤¥AkГTø¿)ÿÌex,ÁÀÄRÉ‹•\-N’&¨¯=‰&Š*ÜN'ý.—]»vãn£ }nTT?à††zŠ ë("`˜L-œ]˜¦Q´!§® ®Ô…ðs”I@Ò«+~ضGòŽ¢`û˜j˜Ûð±ÜÄ}÷Ϻéæ[oûŽâ9)  ò»ï¼mÁÎ;®bë³ÆN¾WÎx:½ŽS°„FŒ†6›{—SèÔ”:Ñw˜R ·XJJZÌ€ü€ñƒ´ÉŒïæÝ?yƒÀkÄ‚ßEmä"Zì†ãÈÙ¹¹»·¢º¤GÜM°¹¶¾ƒ>ž…‹½z",6–ÂG ädL1r®ö…SdÍÂ5¶ŠÈX_‡Ò#y¨?Q ;iÖÙâ3mH(Wï³…S¯ÓC¤ ÀÀX"…¾‡1;຺“\Sð9 î>JÐDîÃIâ–…¤zŠ8¦ß~7Râ°xõ&T”áØ¶UpZ[xš:<<¼ú¹ï0pP)ïœÓ›~ý_¯Þýɼÿép:å½úw¿ð6â鑠ᱺ gfh- w6ÉbçC[€B îË™aÙè­À]g‚Ë˺Ï?¢è…œ9è….‡õž w”À»k Žî݆“¥œXhÉB7ƒ„üsX\<"’SšDbލZCl¥Ò@J Ø÷PˆV¸}г³!W—š‰ÐPØ[P~üi‚:Ä÷ÎDÒà¡&й¶2¶ÝØÄAI.(ÍÏÔ“¡P(ùo3’«(.*·KÉ-æÆ€É7aòØá 7V¬#STPš½…"Ë#¸ûôí»ò£y ¦‡††ÚÏ5W 8vÑÖ-[ºß3ãî&£Q+Üüð34b4ÔJ%Ÿ}›ì ø¾€¯—05OÏÉÅþQÍ×îDSp0úZAàãlÀæðtë Á8J 9•b O«Ãj£…+ÿcùù‰lFþž­h:YÃ×2Šg€S2HzAmR õT¨"£!¦Ð²lËF4—V¢×èÛ‘9ìRD‡«¦C¯ðAâµ"{ÇZ|óŇDD¡Û•S!¦ßÉCQ¶.‘ÑR[ su¬tk?Y ¯Í 9}n1Of¯^¸þ/7ë“âo³@ÎÁÃOIGrF/L›rW`ï\”؉º’£üGÓÈw̸g挛n¾e1ELxÏ•‰"nªššjåu×\ýãñ‚cƒ)æ–L¼æz\s×,ôzî‹E"¡ÕçZʈZ€V×ðYÀß}­º€…yzÒáñgmËé©À†ýÝDº£´¡%µ 8v G¶¯Cé¡}°[̈÷¸0ž^[Cß\HнN,O£#4©a®«C×Q3Úk(âcЏ-"õ¤i؇rsñÒ³CÜ­'â.¹¢x^ @âI$ú _Ke)ZòÃG£ûùçžGXX8füõ.¤gtçEMm b‰™®šz=6í?Œj¤%{6‘›if®ÔCìaÉꟵzò”+ŽºpÌ–ƒÁAní¬Os°ÌÝœ×^½wÁüy/’uëÞC¸‡¢$ò‡ ébÑ)p‘ÇnD~Ú mfÙpJ²Æà ŸÝ÷ÀàñÁ@@p{ywyüë™[Ð’æÐËýúÂAÔZN@8P^‡Â£‡±õ³7a"ƒ¤ø\¸G+…Ž>«†üËaò3‡éýe¹.…!‰½—Þ]»÷FR|4£Ck@DˆÍV~X½¼÷ ¢n™eZ¶`á4§ÈÇÁÈX yÛHönÂ;o¾…Í›6â«Å_âáG£°U‡¦æfˇÕjƒÙjÁ¦Í›Éu¹`56²h…g Ù¹\®pGFEL™r廹ᆯBBBmê³8WÀMÆÜ@Þ‘#±×O½.§¹¹)D«7ˆ¦Ý}?Æ_9«fQb¾Q#0â úþàÈo9gÌÙšo;6¸ Òù;€Áå_üÁÔ½šP©–+ˆQO!é¦ü(%1¶ñ½ça>QŠnb/ˆTCϾÈÅ–»ÑH>樋Àà¡@$S&'ÝŒn½ú¢wß¾HHL‚H®É¼ÿÆÓ¨‹Ž‡îÚ;!{Ak»T‚§Åó¼WpaBî¸õv¼øçqôèQ »à 8˜>3‘‡l^ƒ‰d^®_÷#rsÁFn…‡¼äbXîœ^GŒ `ë³gÜ;söEOÈ9[³†­¿²±±‘­Û›ú&2Œ43k î}~"(Žfb0¸KG$ _8Eýÿ>ÿœ€­·ÜðMàñWþúo¹`$ãÛȘ¬³Å! :I%BRT›lX—W‰²ãG±åƒàh¬E¦B„û’B cKÉé=^ÒnƒÕáA}FžW„£‚Ͷ꣘Þá é¤î ±?ÿ'(g¾qT@ÃW*#àòø¯a.¡²ÂÃCwßÉ×5̾ÿ>xh´3m䥓>}ú¢ÿ€þ=z Ô ŸôªªªÄ¡ƒ±qý:>rV¶×ÁÅ#BžDŠŠŠ®øbñ’Áéé g6›M¼qㆬY÷ÍÜÐbnQBB…î}£.™ƒÔ+Â)©?8h:®1óµ1¼ÿÖo|Ÿ×¸ß>ÿO L'ØÈL'¤“˜SË$(¬3aÓÑJ”Ú‹] þŸÕ„!¡ ÜÝ56SGïK^Ñû| žÒvÐßåć½òÈÑT œtÄVúAî ÓàÍPŒ/è ÔCµŽ/^\vˆ7-CôúExýå—°kÇ|ôÁHTêèÝRXèó[ܸNˆebÒ1:t† ¿±±qˆˆŒäÌy0'Ï>ów”••ñÁ2§áá-Ÿ~¾°ÿ‹Ï*X£˜WqÇ­·,ÊÎÞw)¡Ÿ³ÀŒg^ó³€T³jB- ï?°‚ê€S·¾VÀ(3€ ñƒ®=ÇÁhc£Æ‡^Q:>é”]Þ€ý…'pdã ä®ZHFw`B¢×§G‚É92xìÖK·b„ ;Ô‘á úˆ¥°)µp$¦Ã‘ÞÎnýà ôaH<‹ bÜvÓÍxó98œƒe8§Nˆ¢& Å/ZÀ þŸ Àž@¤ÐL Pèè£#wïåz  ²»¿|9Û!#Å~}·LL …‚M³/w3 ÀÀ»Ÿ| ÔéÖI·Fr¹n?‘f`ÚÁ(’ÂÞµ/l#&ÁGñÌÂWñÀ-7ò¹Ç¡èÁJBUBÇAƒºÛÃ&ÍÔtNØ”µ—´€‹~­-¨Eïþ½qýôðø££©‘‹B2¾Ò5tØ_½úÚ¿f†GDZÏÖj¢Ÿ­ ®««SŠ¢¯Š¾CéŸ4#È%núZ3Œ¾bÜ=ó¯øaÕJ;vŒímô‘±ÉøÃ–üóµ×ÿi;›KÉ~¶oÇöm=ïq÷V 5r…R4éÚé¸êö™\؈óðBdŸ}b€‚BÐpíÀ ­ø¹.hˆÔnæœè®AŒ^êf+~8PŒòüCØ>ÿŸpµ*%è¢S@Nšä—Ánƒ``ÇÊæ¦;xHÄ©CàÌ„½»v£$÷ =æÉÈUNl+ób‹€={ãŠ+¯Â+¿Cñ±BD*bàrËI¨JrÕ!" ÿ2‰—ÈAßWU¨“.™ˆE‹²h€ ¿.iiû?þdÁ%ññ Fb‚³š)<í¾<<{ÖË?þ¸ö¯lIÓw<þ"2)Üa«{[Ý@+õÿ{hA[7à=•Äi˾ºÀÓ d<‹Ã‰Þ±!Ðʉ²«±=¯G6}‡Ü5‹itÛ"á©h-B•RfmAÐæV!Âcáʆ#¹¹8¸eßî½£‘ÖM†Uøf[ªL€„BIµZÅ×:x\^Â’‚~€’|7œ”}ln¶ýADÆ÷@%³ÑçÆø #°oïœö1ž|/î¡èÛo½y{C}}¢H,eŠÛžx‘mwƒK¬Zsæ­·>¬Õè™à”8-´qm|-›bº Blk~%—TbÇg¯ó¼¼ÌíÄT¥“ÕR­„\AÀðA0PH ½Þ § ‘ˆ`Ý’Ïa·Y(V¯BR˜šg¿Ï>†ü³âSˆìVLr:0Í톊†¯8ÈŒ ´$ÚÆM„-£'Ö½õňïŽaWA…YŽ7>þrSzé݈MNåi_6Ëçó:)‚°Âck†Çҥςp%KPùë„E„½µ2jP@¦ C]cSëŽ&@Õ»ï}8iÈСùzý¹±hôßne‚pÏžÝÝþzç훌FS¹q÷¾ýqã£/".&†G¢¶3f?Ñ׿¦£ÄéÝ€¯Ng|´cÒv'zĆBK´~¨ì$¶æÇî%ïãÄ¡½P9ì¸×bC@#0Û“~!5pù»e`ÍGïÃfn@¿Át鎅R|±ñ2•tMMFRæ@Èä²ëù—…ù·©ÐÁÛMкë 3WÀki sñcj° X_.ÇÞ)i–ýXu3whhXÕÃ>vãe—OÞs.€àWw³=~o¾1çŽùŸÌ{ÍfµÉä …hÌ”©˜pã݈1ðl¡XÙû3 Ðð ZÀoä_Âi_ëŠLd%Eð“¿þP>Š­ó^½á$ÄVBéµZú^°ñ…¬cÇÂ8p Ö¼÷.\2‡y`ˆ°ü À2új숈ˆ@B÷¾P‡„úÈ0&¡[ÿïöçBdäf#è“-'a­Ì…£¾ˆÄ¤“üãÍ",.Pâ„Y'@,‘zBBBêyì‰i“§\±ól¯úU°mcl†ðž¿Þµhvö$—Û-Õê Â5÷<Š~#Ç!T§öÏþ»0ÿÓž€Ó»¶ŒÐÞø8EÿÁ™¥àc<£èR"F¯øPžF^±ûn'Wðýgl† =Cíèé-j2¢.5¢«¯ÆÚ÷æÂm3"1‹\7½`u¡‚4Na BÊü¿˜º„ÂB´¡ˆHJ‡J =Æ2¥bB^-Gl…É‚î–:´킳¾”@ì‚ÉáÃù 䜔Âîñƒ€˜àäËÿ|õ²QŽÎ=›UM~S}æ r~ÚŸð×»ï\__W—HoGÆ%7<öRºf@«Vúwâ´A«ñƒ~?ðWhå8¶­ÖÑøm™Àÿ¡=@·f6?©Gi‚²“Mønç!ìúúTÞit »9ÐÒÛÔtŒZ¥ QS§býŠp’LOè€Õ HÜ^¤ª<$òÈeøÜþ€îK dr¹á‰]™Ö2…’¯–b‹DãÂõÐkä4 È=áíµh)ØFà2ÁNýº@Ž­U2ºO ÊÜÑQQÇ>üxþÄî=zÔœ­åb¿¹@[3°dñ—½òÒ‹_™Íf%+öÞ; W?ø,b£c RÈN €V´ÚÌwJ²ž`} ÀÈæ‹B[u…mB‰V0´¿µ”œ.2š• <¸K Œ‘صkv|þ:æˆéµä*è5z/›Ê%C šÍh®®‚BCßmg¡‘’”$Ü4*¸ì-­E«Ø<’D"pFÐB×s¹‡pÎj¥ ‰Q¡«ÁlCŒAîqa8R^‹5»bó‡/ÂXWEô/rÑ1ˆrz½šÞ'—Éa3!a#W)à„XA.‡4Šÿµj7¨Ûb"º÷ñD”Dìïœ rĤ÷EhbŠQ¡z„1ÀAÀ„¢Ûã±-0Wä@]øè‡ê%$ E^•ZãºùÖ[gßÿÀƒ Qø»JÄ8ìvQMMµêÎ;n[”wäÈÅ.—[Ê–’›z+M¼– &i Omèðïôqjµ®j£ÐJû§4BÐÀ|ç/ùa– ‚{û‚èjö] -Vžž‘Àw}»m?¶þêŠóŠ›o¹ äàu39z¾‘º—F§ØM=[WÂëÓÉ,s!F)‚Þ ç4îqÚ$>? ÀÙ@аÄ.ˆížòçH‰‹äÀ‰‚ e=\š ¶ÂTšƒz«¯ïWñ‘BoXXøÉ>úxD¿¬þ%gzjøwײ٬⼼¼Ø;o¿uumMM7–0Òè ¸ü®‡Ð¥ÿ4 " éƒ.DJRé;Ý ðòµˆM‡×ÁT™‡Ü“>|˜«‚ÕMï•+]#Güâ­wÞ»;44ôŒÎþGE¢Ì--’7ô{ä¡Ù+I„ÒH‡EFcò½O"*­Ÿ7ZW†¶×@{Cçëhøàë|í5Kñ²Ý¼J6¡ü­"Ò‡fŠØûF”×5cùv€7ÐPz±* É=”X<ˆˆKÀÈÑcÑ£g/@9vîØƒàµð=‰1±1¨©¬@ªÌƒp¹Zb76’YM6éÓªÚ¸–xŠ# 9êC4)Ÿ:æËæD~&ð9-¨Ý»-Õø*_‚'äpù¯N§·Æ5jÿ™„ÿq•06?ðù§ &¿ùÆœO˜(DbqlR\J ‰Iä@ƒœ 80l?âÏ´Õ§ó÷lÖ¿[Úª üà “ÕÁÿ¾ =Ç«ê±jW6Ï{Æj2¦V†™½c`ry±½ÖŒÃFB¢cqÁÈ Ñ33“ï&ÊÞ·ûöîE}}=ßI¤uYÑE'ƒž€¾×ii†Çaåz  0&ctbhµ¤ €Ðø¾ù”ÀÏL¸áj®Fõî¯Qm´c¹‚F±€LA,páâ·ß™{GèÌüÇð—~7KÿùòK÷-þêËçX!(ŠoE)Ý31ñîG¡¡xY$‘ž’ƒíômGU÷ÓmÔ~à96§!šÕÈÙž=¿8d `¶98(†tC^ÅI¬%¸ñÃ`®¯åõºèä¸*9ñ )n쨷 Çè†*4ƒ‡ GŸ¬þü{~ÚŸ¥_/ÏnCw%ø|€Î` 'Eó‰2êµÕ|vY„*ˆ¡T)2p "R{úWV‹E­ `z éØVÔÏÆ÷Ebl¨ÁI‚0$$Äôá¼ù \r¦ÖüWuY99b¶˜ôÕm[·ÜN‘”è_Ô=kÆÜ|?ß?'KÑ>tø»áÛºˆö® 5 <Ç ÍÀ:=nµ9¹V‹ƒ%ÕX¿ï6}ð¯øÁvø„èu¨(,À ½ãÂU¤ö]‚ »N¾!ôš¿L‡‹ÔúÜw߆ƒ\BŒÏXƒZ¿°µŠ"ÓÉ*î ‚z€ Œ€¢–rN9ÈI¯$õ‡lÚœÏ Šøx¬Í(ß¶U f¼‘CŸç| ¥Úyëí·?ýÀ¬Ù¯Ÿ©ˆà¿.IF‘TÍz`æ‚ìììKILI¥r…ÐwøX\pÝP¨u|o_ÛvÊ¿ÚLá4®  ÚégLÀ\kl€‚¬Ñ/%û +±9û06|ø<¦fŒ9ӧ߈\òõ+— ÁÔˆIá ôÒȸP«±»ñN¥£/›Â¿“Â]¾9Dn· AƶÆyM.ÀÆÝ/S˘€e—ÉèôQ­¹&ž}€ ¾× Ä÷BbOÆJAhÈÛ‚Úãû±ˆ´À::~±ÔC Ýóõ·ËÇGGÇØþ`EE…E†ûgÎXRPPpËå’²šÀFOÄÀ)7´´¯½_ø7àg·»œ3ð‘Ñ¥<d%hè¤÷%ì9V†Í{`óÇ/ñ a Ž[n½"<9ŒÍÍX½röíÜt©C¼Rùœj;&^uÊËË(\<€ÁCcËúu#ß‘× …R¢hb•“ w$/@R¡Õø’ (BPÐñ°‘±wÄWŽÆJ”l_‚ìn,-RÂæ|!!¡æ?þ¤ÿ¡C‹ÎÄÄÐÿ[©X¶ ôh^^ô=½ëÛªU}Ýn·DA <þrd]2× 2AëàÇ騧qíö³Áǃ³†l$Gé5è“y¥Ø¸k¶-x_{@®P¢‰½+®º‘‘‘\3?Žï—/Ece9ºÒ <ä‘ ¸ÂÃ|áËÅ'â‹Ï>…©¢ ‘$ü´äÛÕ$ò\¶êùÜ>š›J¶ .7ˆÛ3À_Û½/’ú$ª¡8­(Ýú%ªjjñ…„&—ˆm9·Ïš=ûοL¿q1Enê(þ_k3ÈÉI~à¾{—×ÔT§»=‰R¥ÆÐ W ó¢+!S¨Û0Akž°C8t ýNÇmÝBÛûl† .L‹ÞIÑØq¤¶îÀŽ…oÁa³AÙ‹Dp7`ØÐA9rÔä×Y‰í[6ctöY7Ür+–-ýý D¿~Y|Òhõ÷ßÁ`iA|d8EFhxXè…Ëjâu„T¤D^'XÙ#QhÃ1}‘œ5Šƒ€å jmĉ‚ÌÏ—£Ü,aÂÙ˜¼h̸ñ¯¦§§×SoIHH`×Gbë Ù®"¨Õêÿ7Pü¿‹fåf÷ïÏN›ýÀý )ìújC'^‰žc&Tþš<Áæë„64|þtó\Aðq¶>!>L‡^€í¹…X»n=ö~óv'âÒ' ¥ÏÕh©Í¡“¾2‘C‡Æ€yVoùü¼#GpùäÉXIŸ<åJÄ'$`çö-ضy|Â)4LHŒ‡¥±>+8!ƒ’ /ƒ‹@૆ĂÛnøS"QJLÀÒÐq=ÈôÁYÀX–‹Òý°²T@N½ nŸÀ²Û96‡ë528¯z®ÓéìSFF†‘4¿íÖ­ÌÖ°\¾ÿ”)þjál¢hïÞ=÷ͼçÛæ¦¦dÇ#Vë ° SÐýÂ˸;hAûucíâÏ4@ î·a€€älË=ŽÕkÖ {Ù|¸(äKí}z ™Naš>—Qrd=TJ1®¿þzlܸåee{vï 7ÝÌÁ~óÕgˆp¢ÆèCm3…ƒ¤3bcb ¤mkªƒÂm ,7ô/QÄþÜ‚ÇÁCE)¹i`ê˜1AbæÄ÷[c ·/Ƕ*¶×Êáà›’½•M&ócô{øf‘@õRÆÌ-1`†w¥¦¦š233_|ñ êÿ þ°rñ--&ÉÆ ë{=þè£ß477'x½^ [G0äâËÑ}Ô¥²’mmÜÁo5¼ÿißÏtBð1Îá:r€CXõÃ*üôÝçpS¨—Þïrôy#”J–ÂõPÜhCmÅal^ù¦N½Û¶nå»v¢¢£øúÿk§^“шoÍÃôõˆÕz1¿eä« PZÂpB\ ¢Â dh'̵ðPÄ TÉy.€¹6uÌö °hn;1—…i‚Ôò}ŠÇ6/ÁZÖW+açõ(½MFó^ŸÏþ §7X™•mÞõMŸ>½èÅ_Ì5 çXc%ã ½ŸþûSß4Ô×Ų%:C ÃK‘A hïNžÆðíòsÈD`L¨}ScÉÇŠeßâàš%½^Ž¡ãn€Z¥âÔ,¢V˜¿›˜‡oœŽåË—ñšGlý‹õG‡¼Ã‡·ûG<4¨‰˜hrXxHœÈ<>„à =WEHDt$´Z•? HnÁajâ D¤¬2)±‚œbEŸËæ__@ˈ6’ú Cå¡­Èkôb]µÊŸÏÔdl™E"µå×Î1£ýåóçÏÿ)<<üwOýá—Œa%hvlßÞó©']RSS“Ì„¡>$T`i䌗@ÚÁ´Óc·‚¡Ã á阀-L‰Ñ"+-»ÇÒo¾ÆÁµßðx¾ßË1zâШýðyìÈÞµ9{Öâºë®ÃÂ…Ÿ!<<µ5Õ}úÔ¯^½z{TTÔïN"±ë¢ƒôÙ³î'Ô¤r& }äDº tý;+hõP­ú'#§ ‹-ÂÁ†š„É×Ü Q28f,Zðì63úeõ׋"..••˜råU¨«­Åþm?bVFD¨ý…®6žP¡Â‘Ù(vlÛÊSÈ­Çó³“ë dEƒ)íSetØOUlÙ™'*ìRìmb À®ƒæù5 pê;è)2hÙ¶mÛ†˜˜˜sAüÄBÄY,­©­éêq{$º yŒWèðƒ £+h—B>í ¡(ì¤ëU ŒìÆ 6}º°æ^Ævð°Q¸éÖ{¡×©¹ °R<ÿ¯¾Ä©š…{Ë¿]Ê·s³ë\6y öíÜIíaÜÚ£€wóuÐ$ô†"v CVõÁò>^O+Hm+æöÄ30°YÄ(™:‰Ç,r2ŸÛí F¿)+˜˜˜hÛºuëÚäääs~0w°?uöƒ‚êên$ ¥Zƒ@0é# è¨ :¦ŽO—-ló6|aŸtUVã“Ï¿ÄþU_°bÎ8hfÞ?›@ÃëRˆŠÇŸx„ûzO³’.¡aa¼@å¡`ÝÊo1B]‹á‘NÊ6J°¤Â€þŒÆîÝ;y‰9Vò…ÂÜJ‹Í±X,ʼnĢ(‘H …‚‚ÛXäð×Ñ ì¦ó_ì€Ýòöl± K5{†$¯ÓåÉ5™-ÿ †¿Ö"##7n\Û«W¯ß]càŒ  RÈ,­©©Í Q$Ö‡„Y#Æ"}ø$ÈH¥™ ˆ„¶"°­òï˜!d•t›•êºF|ðé"ìûn!¿žPVÖÌžý4* ÂÃË·<÷â31jþ}{yê6>!‘3Áþípk\âT^°qþu•µÊ$„GÅâpîA¶”ŒaÑcµÙ—X펕|ÿš{T+ðʹ¬Œ_'ÅrAB(ùí8…\6ŽþHa׿hE»ÿ§¸íçf³Õ6Ýÿ-ç344Ô½víÚu´þ)ÐÉÎzàk†=ˆöø|÷ÞøfÕ×äï'óä»ö+ì4êÂ1È¡çz¡cBì\°miV ÛŽî}úñUCì"ÖŒý.÷>“Ùúöo¥ëS(¤Zê>…L:«²c‹õ.·ûN¨Ÿ5 ²$$ÎM:õ1˜ç·á¬€5‹?:ˆâñG—”•–f²•ÆNè;t$Y*©ɢÓiö.ÁÇfQÈ¢÷¯Àºù¯Ãb2 Áó¾ˆ0C)ÖnYƒÝv㢠ðãšÕ¼.0k ÆÝÛq…¶]TnØé4}zRmb:œ$ú*ÊËøn_ú:W‹Åú¶ÃéÚ ¼ßÓDR‰$M£VÞFš!Fàëšà%fÚÝbµ}Ì>û÷|«C‚/..Ît×]w¿öÚkOèõz¯F£ùE œu°Æ.Z•wäpì#ýmqQQa»xEß!ÃÑ}ä%ÓˆešàÔÃ?-ÌÖþwOŠART®Ü€åï½cÃIhˆQ™ñ8b9–®þ• 6|8–-]ʦ¯¡×Ž†â£¸Io‚’bö| ÑV[ôè5`8@,aãŨl4š¥ïµü‡? A^}7p_ìõy­ßÿŸfüxÖP©TúÒÒÒšo¿ýöü+¯¼ò¤V«õ.‹xN€5–JÎË;M øªðxÁ@¶¨„í9È4 =G]ÊA ê0YÔV¶‰¬x3~¯”x¬Ø´‹ß{ uåÅPȸkÚ ôLíů ôùŠùðȼèѳ'–QÈè?>>c3ìu¸Hmåäó½Yf} Q(˜7|øp# ÆvLu΀5¶±¨°0òûîý²°ðøP¾²H¡¢‘7½F_…FjyÙiý¿ÿ>SYa: †õûâÓ¹o "ÿ db)®-†öÆýþÒwÅ/*ñêïÙBWÿu*Ê0FjF¹ >1¾¶i‘”Ñ›×nnjô×özÉW[žr¹=eøýôÆ›(bùýîK.¹¤ôá‡f—Ïu2FàÏŸíìØ¬ÄEÅEá‚EÇ †³ÝÈr¹BèÑo 2Ç\…ÎÐ.‹øó%bþ¿•rÆ è‰#…¥øpî;(Ø·,!wá€1˜0tÏú½ûí›èѧ$úmÞ¸WðŒŒˆ„¥¾Ó±{Ü äÉBù&Ø£5fþùñ# ¹`(¯ÿÏòÿì4Z•Нÿ›$¶±bXêÕBŸÌ‡xMuý>v9D¢þyä6ã7†jçHãú€D¡‡DâabƒÒs¬ùAPlxò‰ÇæçüôÓxT"•‰ºöÌDÖø+ &%ߺÆð4SÄR‰™iIˆ Wðæ‚/°mÉ<¾Ä;Â…»'ý•uX¶÷[Œ;¥%ÅÈ=tHâõb€×†LrëÅ" vH´HéÞ‹^SDÂÑêõäãíÛ»÷R‡Ã!e›OR»uÇ€ WAÂ6Ÿ´YhÚfF Ƥ˜p‚ øbåz¬øøuëj¡”)qóè[QÕP…íÅÛ0füx<ðÈÝðuRŸÜ6„Ó˜ß$UÁÉ/1SY^Ê‚‰¿ß;SwŽ4~%uv3V•ä¶Ûn;6kÖ¬âs¬Q¸&ª­­Õ<ô·YïíݳçJ;€Ô­(!% ƒ&]-H$–"°µÌ=èÕJŒÊê‰M{rðÅGï¢âØ!Š·Ä×ó"˜-(n)Æ`r»wíDUe¥Ÿè3"ÉNJæ«‚Lð±O>ßd¶¾ìt¹óp‹¿@c—òˆö½ÌÿGDDX'NœX~ÓM7•Rˆè`+ˆÎy°Æêž¬­Uÿmö¬7öíÝs=X,ÅÄ'bÈ%×BÃ/åÿÉhƒlGïð¾=Ps²|<G·ÿÈ·§GuçáCá@¯>™Ø²yýÕÚÃÂÂàaz°Zø…ª"cãü£ßúyÝOY³ÉòwßoHÕžÍÆÂ@fôLj#j.¾øâʱcÇ6êõzOÛ¥c °òñG~eçη²Á;"*\z 1ñ)NU)õñ]8ÝSâfÀ[ó¿À®åŸÁN¢PMÔ®Sêaˆ AJ—Tl\¿28ËãE"&.Žœi v±–`£ŸÆ¿ÛJq¿ÍæXé;ÇÅÀ€jæÎ»?11ÑÅfÏ鉠ßÒØ6´º“µò—^|ñ¹5k~¸‡@!c)UCX†Mºá ©|3G <à Dà Z é¯×lĪE£®¼ˆWa)ß^½3ùªŸ­[6ÁÁ*†¨µÜxVDP8(!@±k»œŽ@=#¯µÉd~ØãñÖá§ÿ3µÿ裘9sfÙ/åþT`Àd2ÉŸ{æïü°jåCVŒ@¤Õ‡ð (Ñ©ÝÈp§ê±Ôðð~=p¼¤ .ı=ù¥è¤2ú÷À°wÏ.8݄ĥðë 7"8Áö3˜[LT)ý?g«±™À¤¤$ëêÕ«7tëÖí´³•:°Æ®qd4š¤/<÷ÌLÁÓ«UAF©ÔZaÈE—!>½7x^„®‰qˆ ÁÜÏ— {ÕW0³kRô0xÈ0¾ú'÷Ð!>œ#ÓûÂçv¡©ô(¼të¯Eà¯EBüÏ?o9œ®}8ÇG›æc“=ävL:µît/øS€5¶5Ýb1ËÞoî-Ÿ|üÑ?ÉJ2”ˆmJí?r+ÛÇØ ÿƆ `Þ´iÓFŠN Ü?=Xc'ÃjµJ¾ÿnù°¼ðüç&£)ŠmO—H¥B\jWô=ZY=3xxÑ·ß!ëXIàqµ¥Ö€f¢ý<ܳÛËèä'Q(Ù…"„-4ú—þQi_–¨ ìùC`0Kã:cbbÌ&¶n[¢££íÚÝ»wG¬Y³&®©©IA®PÄf0OÓØœ¿wÆŒyO<ñÄqŠÿÿ7¢€×X5ÓÛ·õ~ü±GÕÕÕ¥0è\J„°è8 w)¢cã0zH|ýÃzìÙ¾¹Ù¼°”š”¿Ïå€ÓlbWg›3O4·˜Ÿ"ƒ;™Ždª?÷ÿ7£¿uT7xM4m%•nb½k×®&¶˜F¬_Œ’^ ãÌf³ÀôOUU•ôÉ'Ÿì±aÆ$¿ˆŽ±]•f–fŸµlÙ²-ô¹¿è¶þ§8A’œŸö§<þè# ËËËúÒÉ’Í º0 )ˆ˜ÈpÌ[¼ …9;Ñ\S¹J G‹Ñîy½.6Ïo³;· ýdÏï2~€¾ƒ†fFôÊårgrrr 30ëlCÛÿÏÖï±Mðµ¿u—oII‰øú믖ÁX0ø8ûNòý;ÆŽÛ|Î/ ûÿn³YLTþÄãÌËËËg·Ù$‚ éCCqÁøK0qÂDTÕÖaùÚõh®¯ÃÉ‚ƒpYÍ ·ÇSÑl²»oúô鵿¶cø¬±Lbuõ õ Ï=ûÊ–Í›n¢Ä׿‡…‡ } Åä+®¢°p ömß‚æª"øØNOApÙ®¹¯o'‰]ÙÛiîÝ»w3õ&¢eã§Ÿ~š±oß¾Hö_í · 70c·tïÞÝB´înˆÿתÁÖÒÒ"ñ“}‰ùDŒEè{ídüýW_}uÝoÙ.þ? ÖØ¬ass“ü9sîúæë%ÏØlV‹èD ¡a¼ TKSj—OJ'¡x %5í¡Þ™™'333™Aãââ\ÌŸ2c²ÑÿÅ_$ÔÖÖÊ ÌÐ-ÌW³¤K°xÃ;ªoûòË/#ÿö·¿ fb0++«ö±ÇË¥[ë/‰¾Ží¬ùç ,Òo—~3ê­7^§©©1‰“(œÿ~¢p·^o0¾üÊ«— ½à‚ˆû—|11 ¿Øzƒ?bTÿÞFbW´|ùò(ªmàÀ&vL¿§RÈÿ<Xc‰tÓÑÿ|å¥gäüt5@Jáž õ+ìš{fÞÿÀÕ×\»ú\¸ŽÏ™lç‚ %«Õ"ݵsgï={v'P(’’SJÇŒ»*!1¡I!W¸¥D÷gû8Ïd;¯l äÏ)vö’Âg³lb¯Lvæ¯Öq.´óíTëÀyÞ:pž·Nœç­çyëÀyÞ:pž·Nœç­çyëÀyÞ:pž·Nœç­çyëÀyÞ:pž·Nœç­çyû?o3§‚£ÚIEND®B`‚dosage-2.12/doc/icon/logo16x16.png000066400000000000000000000012051227056367200165610ustar00rootroot00000000000000‰PNG  IHDRóÿaLIDATxÚcd 0RÕ€Ý;wº=}&€•áï‹êšºF¢ xñà¾üw&¦?ŠròOÝÃÔMaÿüñ õE-»ì-LWhéè\ÕÓ7¸ÌÅÍó«5É7´nœR’Áp~Ï6?Dõ,> È1üýÇÌðæéS†û1¨ ³oX0o^ † ]’ÊKý€œ?@|öÆS†9“{¤.e`Òwf8¢cÄ`ôé Ãï{·xÙ˜w÷õ'²°°ü…pêø1ýÓÏ>^ptqcøùó'Ã׿~3±3,*Mdh}xažˆ*ÃwïnŸùÇ`a¼{ñ‚EnX\\4Å>±8h2Ç/?Xÿ10³±2,´ÑcøÌõ•ÁDMŒ!8ОaÞ†};6ß‘äåúˆ AQGí3¬þýúÎðï÷/E †™Å± ’ü Ïœ`Ðã~Ëœ›Ä°ü&óÔöI³s°FcpDÔY˸"£¿?¾3ˆðq2l_²‘áï—[ |O÷2Üyð›AG‚‘áÔ³/·Ï¿üSüàÁƒ½ ß0RHxäIý€D3 A^†Kë—1œ=µAøÇK†Í×ß$íãggg7üùóǦààà'K—.=‡5%‡…Ñ÷‰¶>»r1Ëë'n}øzúý»÷-@) þ¥KJJ®àLÊQÑQÛþpð{n]µä××ÏŸAšo€4«¨¨|PUUýº}ûvù¿ÿ®Â›Z[[ZΙ›[ìþ«¨¨ø &wôèQAkkë÷ÔÍLäýré|K…žûIEND®B`‚dosage-2.12/doc/icon/logo32x32.png000066400000000000000000000035151227056367200165630ustar00rootroot00000000000000‰PNG  IHDR szzôIDATxÚí—yTT÷Ç¿ó›}`@v‘EdöN±Am0QhD‰4ÍžÚH–£¶¶VÒ6MÛœr"¨mH­1XM5‰5uIÜ…È:ÈÎ`d€fæ-¿×7FÏñô´=iš?rÏ›÷{3ïÝ{?÷Þßï7÷IðÉW_j€³Y7NÔë›’’’›“›“SôÍ*•ÆyGÏoÚ´ýëïUþ¸0Ø;. à÷¡±îéº:3,<¤(-’†ED‘Œ}”Qû’À=5MòD=CGgÇÄëÐU u¶`ÌÔ‰qS/š>¾/%iíî3¥"Ö|‘2ôõ[ªþQñ2ÔN¶—ÿáú]oTa÷®Ì=X’†¨ÄDô [áéå ^àŠ±^A³áá? mýŸÀ9²¾f@ÿå38sà͆ö®ÞÔi”®}ê•„¥¬ÏùV.ÔTÃR¢G^FˆXxÚ;ä oíÙM«*_'3µr𷔨9M6‹èxPIô£*û‘LXi÷O.64`A|Ä«;vî^ÿ™-ÍM±O•>ÛZuô}pSTR Äœ8ÅÓÃÁÁÞ*lÜþ+Ìßµ žÞè ŠƒnÙxÅlƒï#/ÀqéC­/U VúžAmmÍxSs»>"2²ï¿¸O) ±§¶UX0'2R „ˆ‘S Âò ko§mÍ¡(Ș‡Óž.p1a8‘AËàÄ[uP©e˜rjpÖ'í‚eÔ¢•J¥“·-ÁÍ‹çK×n3SÍ–g¶þŽI…"&€²”' ÃQ–ã ÏñT%'¤­»‹Y‘K^)èeC4¹0ÖBãf'‘9Éz:'PC|‡? [ª[ —XPóΡ«§à–èð™ÞözZŠá°OMÁq=jîz=<äxÊ¥`8A~:¬.XŠdo ÚêëÁ)ä„È!a\˜—•Šå~ZËE<ü£=¸osùÚgKŸÞ1-ŽãÔ~^š÷žÔyû€c*z'‚HÀññ"žùÁs(ùuí²âU+O À-ím1éi)-?ùË ©L©ϲT œ˜ 2 CR¢Ãè£+–‘{W¿F¦½dÂ:FƒÈ9ÿa+Õz/Pµ`!sc£hçGÊÞí­ÍÖDGGÛ***šòòòFn à–+FcLVFjó†?‘É?…€ fç€>:Ý¿ûþú¶o,ÊËK&öãÈDÎÍÐÉ/µ ž’))ì#5ŒË¹O4«(++kؼyó•Ïøty£2Òô-ë+êr±,ë¢˒̸(úà½9¤þ½÷iÉã%„¥ú=r‘”ÛÐÛÑö*O…q%{Èäò¤ZÏ"«u¢ÜÅ0ÇE“ÒÂÂÂþºººsÓpKowלԔäÖu¿yS¥ÔxBŒÉbÖ=ô¼ôøf¼¶¿ãýðU+qtpÊ1j±¬Õd7Ô™\–À±œQ¼æoø“Èd2GUUUãš5kLÓjÉLý}³’b[ŸüùOµ÷ øyiéßö”›š6Ovy-çé¹À(rupàí)‡ë Q…ÜøÐ[ÌÜüîÝ;£råÊ•mÓî ÅÖ,hnäìÖ‡6þÒÇ'8ìP/j~÷[(|=à²;ÀR¦«kÅ t膓ۉP]]}®¸¸xàŽšR«Ø¦‰›UË·y.8wÑ"ü°d9õ #£f8'm½ëä:ñ1Õ¿Ezëx3#’ÇŸÊÏÏ7ßqW,nHêˆÐà†œÅ1N— 'Ž„mäÚi'¼-Þî¼5zFcÏÊÊËÈÈÏÎζFí† ôn‡ÃqH­VswÝ–ÏÏL;*õÖ-i<[ϯ((4,^¼dðرcþiii¶ÌÌÌq·ãÿ¤—žžn¨¬¬lŸ³^OÅݸeÿ¾šü´ôŒ6ñÿ¾ënm|¹_L¾ø"ä_0Y8{¼ô„IEND®B`‚dosage-2.12/doc/icon/logo48x48.png000066400000000000000000000072571227056367200166100ustar00rootroot00000000000000‰PNG  IHDR00Wù‡vIDATxÚí™ xUå™Çÿçœ{ÎÝïMnrÉBö‚D0@–¶ˆÄ©ƒuì3TÅ©Ö-"PË"dQ@@Ù"¢,…Š,Ö"V¶% Z ‘ȲÜäæî÷žmÞsCa:óÚÁyÆïÉ—»Ÿóþ¾÷}ÿïûÃàÿø`î´ßÜi¾¸Óü¯ƒAN#œ$J,†þ° £r§èx^1 òw@3¾ªª²ó¢7¾ŽˆfƒÁ0 >“ÉäµZ­®Þ%%.-)ý«Ñdu:ú$‰iii1Œ;fkSHî—’ß“Er8F ©uWê÷ùS¡ &>9tذm‹EüNø¼^þ£¶ óúkùݽ-1… ‹2‚áÂ4‚¬n^·Zn©>T¿dÉ’YYÙAµÐÒëõÊ?@ŒDX)B±-‰¬ÆÃñ:™åt  ÒÀ—U•ÙOÿâ©ò˜”Œ¸G'ÍâX½‰ K BŠH`eœ¯Iݸø5ÉÆŠU½Š‹?ÍÈ̬Në”VÓ)­S­Ýnpt< -%š/mê·jŒ·Õ­_¿xá ác_ à“RkMé™UÙwýÕã¶Øí®Óµ§rŸ÷ë=KJ…„ôF,Œ­c'X pzÈ 6N…ïÊyµæp¹Ré‚ìiªW¤ O¡$ŽØ‹))©µ©))'“S’kSS;Õ¦¥¥Ÿr&&º F£Dy$}#€òÝ»ºm›9±üéÞhñ†áñEäP¶ª¼Êò|0Ö[s²ædñ!÷él²B¬“‘9*§C„ÐàSœö¸0 :0Šˆ°Ï«ú[liR½ÍJc}âs7˜_a ÊN§³füøçÝ»wÉ¿“¼a~1ꉵMõõ?I±„b¥‰éë0à-¯ CŸ¯665ª÷ïþ½»E™A9‰ÈKŠ#Ã9\hñ#@‡¬Ñ , ODAƒ'€«‡)6zk }¢ÂÈ‘ìB¦ä¨zp¨ü3"wÕÏŸ¿ðÇÝ «o×̨'_5ÅŒxtÔ3‚ëÂW8üÉVö*ÁÀ†Ã,ðÄ››Q¾g7ì/ÇÉ£ƒÙ C§¤D¤e¤#.1 Ë¡±Åƒú¦fªlôzñpP¨^¸¼A4¸½XF›±Ž8$ÇšÔ-kʤ8_ùöÒ僓’’Zn'é™9³g+?XñÊ3Óçêcbc³Ž‰ç`50òºè—‚"-V(‚`(Œ— RQ¾g«Àlµ ); é챸Rs<D}Ëeèa7‘h³Ò1 à9 uuh•8˜ìuÏæ5‘þ}JʦMÿýó‹5l4o© 2‡*æýfÜo==u–¥àN¢'wk.×]Y™ô"¢çé‰7H5@qñR>Úº>ù÷@Äe½îø<ôôzåâÌW5Øz¹†.=À ¤ÆzDöý}3S Rá¨8¤¸ë·‹‹‹·=òÓÇ—””¼•‚È47» “&N\#Øã‡ý˳ãt6»t­"Ij€Br*ÑŒ€(ÉÐê€æ‘yÄ „ñÞšõðlYÇÎh2_@^ ã¯÷‚¤AУ,·AÄ ví;ˆó§bR N¤&ádV.ܲGë›apÕ"Öf† C&u;ï5álÈF²–þpðÛ/LyqR\\\àfWÿ€6<P¶rÅè›>|yÔ”YBZFå­uÖ< \õ€,·C(ôžS-7DØu Þ]¶â¦UîiElrªSœàíµø¤FBaN:“’`µÛ`â"ˆ•ÐpáþtšQ΄bÝ/L9rȇî°X­7]Ø®ˆ¢í:Ÿ{n܆æ@d𿎑u8ŽB©ÍmŠ}ÍxíM­@©ª €ÑÂŒ ž¼9ml5UÈ‹ˆPS:¢Q4g҇Ÿ0föèÃê=Ã\¨¢±E¿ j;¨€ÃÄh˜ƒx6»»9j~ê…HB«ìAª‰nOï§ØXÄÛõÈ/gV>| gðþÇû%9µtÇ‚·V|uâ82¥”Æ–¿ËíO툻=|n@ó‚N´sk=ztÍm_ÖŠ±më–ç½>oyVa±%«°7'è&ÑfÂ’YÓA}Xª}MMMÚIá÷¶ÊÛû<¾À2 ŸÛ¹”¢’ŒŠË—/ßß¿·&¯ßèòz;ÄŽŸühÖ«¯¬ê˜Õ%¦K¯{¹”Äxfˆu¨;uœ¼E;6ƒMõ7\ B,JZr†#b%¾ŽïÿÖØ^3W”É“'WÒêŸÖùßÐ h[ªÛW^Þgú‹ÓÖØS:t+À±ªÄl\¹„²SeEVBž‰êÜ^ÿä’7j¨Û$—Ð:ЦLÅ”§N™¥s!&&Fž9sæ‘Q£FÓªò·vƒC;ÉçŸ.œ:yò:•7¤ýàáºÛ?f.ž9©†¼­a 0‡”ö i{£¶ÅÖTE3v_Ú£VmÅììlonn®'''G›^zí!#•íÛ·'N™2åò¶.??¿uÓ¦M{ÓÓÓÅv·|kCkÅOž¬É˜6eò{.wëÝùwéöíüTû½û:$:çvîÜ¥Q€ÚÚZ+õ8š±^ÍXÍèÔÔÔ m굂Y,–k!D9ÄŽ9²”õ ,8TXXèk/fßú-&í*Gý•+1 ¼ñҞݻJ•óÜ‹¿Ÿ1";;ç…È5ټޯ˼á˜Luuµ™¤3¤…ÐmUâ[áp˜¥˜Õ¹šm6›Ýo¶˜#F£ér êÿÏM¾ïêøàNïîôø€KH} eÓ÷IEND®B`‚dosage-2.12/doc/icon/logo64x64.png000066400000000000000000000135331227056367200165760ustar00rootroot00000000000000‰PNG  IHDR@@ªiqÞ"IDATxÚí›xTUÚÇÿ÷ÎÌÞ2é„@IHP0°ADP)ŠeY°âºî¢t è* ¡¨`APaW ŠJ ½H é™´éõÞû½g’ ø¹û­7û<ŸGŽw2æžó;ÿ·{àðÿ¼q­=€Ön¿hí´vû@k µÛ¯Z{­Ý®@0äDQäeYfß!sê2Ïó‘«J¥’[{r¿¯×«(//Þ¶uË­ô#¯Ñh:ÎEW·Á`p¦›Íf·X,>VAj퉶¶òv»Ý0~ìC__žWpÏdÐØI¤ I©TzÝ:èí¿<ñäáW«Õÿ•~6—Ë©zmÙ²‰+W½3;³ï­êv»! "ìóBôû€ õõ(øþ„ä³—…† »=÷©§g=KÊ”ÿ:³øY|>Ÿ¢°ð|̘ѣ©Ìѱ£f-T¨ôF„ÃÁ¡)$Œ$£ gÏbóÊ\É[uÁÿÜsÏ<ä¶o´dÿm¾áß …¸’’ QÏΚùÚÞ}û† ~x¢ªÇà»9N¥¡‰‡#ü4ù AÃadZ|±ev­þ›hÑkÏ͘>ã‘ÌÎOZ­V¯B¡” 3“HoMóàÂá0ðû”uv»1(¬6›‡S(E…Š N(óðäñ¹Í›¿ê?cê”O|€öÎG'ñ݃È)e„%AR‚?$¬04¼ŒXeþ}-ò·~&Ç@«¯ ÄDÇ%'·=Ó®}û3ÉÉíò“Ûµ+hß®ý9“Ùäc`T„o‚C÷þÅ£ G]¹eãç·nË÷†%йM–bEÛö§’Ò3¾Ë¼®ûNÁ`ð +!!¡òàý½¦M¼!5-]ÙùZÔ»½)5°$$#*)†Ø6y|!LP¢µ*è@Ñ÷Çp¡à4*JŠQW]!µ‰¥+Ø5eµ–&%µ-h›œœŸœœ\Àzû”çbccku:IÔâ/ƒ+/+Õ?5ññ5Q1(ÚëSj|!H¾°xÆ+Š‹¼´SÉÜ%“ÑTWpøðáÛ ”~cCrJ¨4ZZ}JËQípÃË 0Æ%CO@4–X²3jR„NÅCàd„‚xëípÚ+QU^‚ÊòRÙYk—ƒn‡IdÑD$Ñ10btttј1c^¼ûwÃ?Ñëõ¡–†ÀÍŸûò„5«WÎQHš©6þ–- ¾ê̪åÑéÖÛáôzåc‡É%ÅE`&“šÚ3¸ÿ·¨rqÁáE¹+¬ˆ"5$¶i N©„ËãEaq ª]^Z˜ˆ>:‚ÑL~‚Í5bÔ¨ $H"E¿³®újT–•Ê¥%h(/–rØ7iòäGï~ï‡f³9Ø¢þôÇG—oÛ¶uÌ”9sÕe…çpôë è£óÃ,pæÜ†A¿½Z€"¿ ›6~-›7ÁïqÃ$(‘ƒŽIqh×&‚Z M¬¢Áƒ²ÚÔ{‚Эh“Ü1±q”$ð°×Ô¢øAqRØt0Øâ¡³Æ@¥3‘|M<Á°è4èDß·g/}µ^TH!çÜWæßÕ¿ÿM»I á0cúÔç7nÜ4mÂì…Bçk:C)†·å+ >qÊttȼA“.yw¯Ï{m-vïÙƒ];wâô‘Õ‚G»¶ HéÐ Ém †&¢N¡€ÃåAUu ɽaŠV³:tDTt4å‘<ÊËÊÉ|JQÝàF˜|ŠÎbƒÖdƒÑlFÏÌ|úÅ&œÞ¹IŒ‰²žãÍ·¤vL« ¬Slï®^5ì•ù¯¬½ã¡Ç47 »‡S“=ë8¨• 4jºò€Ò<²¾ y{æäX¸c¶\]ïÀîÝ»±Ç6œ;F0¼¨H ÉéHÊè#­¼‚`Ð_Cé‰$é¨5ì¥0%%ÔŒôùD‚‘Ú¡(k¤ï%³*.F~~>D]zõîƒõ}$WžÚÊÉÉù47wÉ&³9Ðá“+È?cußÈãí3»ÄN˜ù"Oq²úü% 3ƒÀìY¤UÙäéÊjêìµÓDƒ€cÅøäå¨Ûý&eÙ¨"sò‡òöRêì âtÃ!QÉi¨Hï1ûzäì^‡®ñ1ØõÅ„ýjˆ”N±à ^Ä·7£×õ½ä7V¼.FÙl¥¹‹— ».;ûÔU`õ}Þν&Ožôe·>7éîŸ83˜L¤9i€»LLª"u© €Ø¤¦†P¸ñêö‡bQã›#ذt ¥ßcRªV=¥ð)E9Dé³_B•9»ŽD)üøNÔ¡WÏÞ()8‹@•„pÝ_£Þƒ´n:|ÿýqQ%u æíÕ³×½ÁpÅañâÚÖ×Õ©§NôÚ¾}F=4ýye·^}9V ¶ÃQÇEÈì?¼Ä HòJÙõ{_§Sâƒováۥϣw £©HÒ’:8=E“þì°•©ɦ›sbãÎsž‹8oH€;h$ÓJ$uEA§+EJ†òˆó±ãÆ?õè£-3[,W•]@¾€?sútÛqcÊÓš­ ÏÉåcãã¡¢€) ù£2"”š\b¢ØØ/BK!”EˆÕï­Á©uobºß‰TR€Ê¨…”ž†ÂŽ™8vp²‡Ãï®Gº²6[4ù ó'ôœ5~NÔ¨”m”•º)Gæ]kþña—N™YåF£1Ô"Xs:ª÷ß]=jé²¥¯vëw‹0â‘'x£ÉH¾@f#h492y©éÚ  ù*Ë©0©!Î(àû¢2¬Yô´ò0Æé@493MfN&µA­ý Tmyl9âGÎi”H¥RBEñW}ˆ’jÈN£¬Þ‡OÎ*QìÑ„Ò3»äýmñÒ‘T4ÕR(¼â¤è2l»Ëép¨'=9qåÁC‡ïºýá?«®8„ÓéuM*hòÍ“—› 7N\’CYÄQ4B`‹eÉV >Û²>ƒve¥èNáΦÓÁÝõZ”—‚Ý ÁåV V-àSÁ‡6é×ÂHyDœÕ€£î GP_xŸpòžjC¨óu¿ùvñÒ×îµEÇx¯8 þø f ……çã7ö§×—vßä9ÊNTú2/Üèت#"÷­n°)0¬ (rÛˆ`Ÿm†Â~V‰,Ë]„‚›`#±ô¶N¯Gmƒn½H$µ¨$4”Šêuj$ee#¡CR©.Ð ‚ª8~‰Êûk Á»†ß·`ꌧg[®ÐüäŽÇãQîØ¾=gú´)Ÿé­Ñ–á“æð1ñ‰”!ª"«j–»ÔhLòh¾­ŠRh’8•ûMÀ˜7÷#Õ¦Ãâ×–ãä×a`¿~ µá»#Gàs»)Þ‡ QÊèbUì”T ¡°©Õ(‘H®ëÝq1VL÷wW£dß'Xv@’ªD«gá²×öîÝç°VûóMáŸn‰9aíš÷G.Y²øÕøÔƒÆOåµF x…2bh’ºÜ¼Ò¥/G’,Z!’L1n*±“Í,yíunû}cµ¨ÒØ‘Ý{öî…ÛÑ€DщD‹ag-e¡aª8´Jd¥µG羃¡'Ÿ¤RHðÚ qpÛçxý„>Üí·7~ž»ôÕû¢¢lþÀüÇãæ¿2ï©õë×OIÍþ­pÃÈ ¼šÊVžœbsHÄ%¿“<XµêHõSª«WaÙ²e(Ü÷ ÒIê7ÇhPâô#Ÿ7 ³W|wô(ÊŽDŠ‘`dZî:¨i²f-‡“1 qÈìwù+%h"Ê|•Û‹ä|ŸÕ³üíÕ½»uÿÍÏNˆþå¦h à™SœùôŒÅ;væý¡óõT9wŒæ Ë—hÊ.ÁœQ‚š,D„U­À«¯.…ãÜqŒðAìüf3 ŧДñ1 eΚj˜v˜ jpad¿6L߈¢blÈê? FkŽJlÙø1>,4~ÿ¸Ùÿå‰ùä ~VXü?w…„»]GµÂ;‡¹íºþCTÙ·ÞÃ1Ç7N¼YhœôEÓhò ì&l×ÇFååÒÜ…¸pü(2©\¾õæþTWb׿0Ä·AÉ´Ž©Ø³ácÄ“ík( *ÂnXx?t*:ê‚`¶F 茜޶¹ûƒb\‡¬oŸxrÊýY;×êõúÈf*ëƒA¾*¬ù|>¾ººÚ4yÒï8yòænnW^{Ó0N¥ÖFRåLàùËò0ØïL4z›†Gîüy¨-­C×>cQ_²ƒVÖ‰ø¸hž?Ïž)">Ö†ÃÛ7€s†`¡ªTò¤ E"€†haè¤Õ…̾ƒP‘oí*•ŠÜªJ*Ä&QNàMIIq¥¥¥9333999µ={öt0(W   {hž2é‰÷Oçç÷ïÖ¨²ó·ñ*¶óC:¿B3€f°¼ÖD“‰Ö)°pîKðÖy1ðÎIÐQÕxpÇjD›€ªÊJÄÅÅ¢¡ºCLñuS€³ fG©rŠJ)ÊȤŽ0,f#tQÑøààùd-ç±×»þHwó±ítöŠr9;;»ž|ØÎ¨¨(éª4C¨¬¬0Ϙ6mõ±“'f÷¬ÌºaHî3hœ|3 ö¾–/ìzþ_gCôðû¦×/§p'¡¼¼ ]®ÉBÕw›pgrT*±×Õ•ÐJa(56ªUT @Š 'iBEì®ä3ÞG£|ăÆ|Õ1rFF†{ë֭߯ÇÇ‹W €5¶PUUeš>uòªcÇOÜÒõ†[TY}s•p™øA :A‰›sçˆ «Vàæn7£ÁU.„]»vÀÌÉŠzlSFÁ£5É5µ5¡—gQ0:L·h‘‡ …GŽMÝ»w¯]°`Á¡;%Æ Ô××i^˜={á–­[F'g\#dß4¬Bd[C”A‡°ßå}1ið…|ÐÅêp`ÿ$X±&“)!¾*%äá ;\Þy?ŠéWÛd£Ñ(;öììÙ³OÒë–‰ÿçñx„7V,ŸðÎÊ·_TëMꞃîâ£bb¹ ±xuñ4T”4>w0ÅÂ]Y$‹Á Ë(öûVøÁøyÎ/rH3²Ë±“z2;ÈÉ6c.ΑréÞ{ï-^´hÑw-ÿUc›*^‚°víš‘KçæòJA×càmŠ®]ºPì?ŒmŸý=²­¦1ZdOM…  ÉÝ).ÏÊéÝø'ίùD*+x¨ƒ•½ì5;Š—ššêbÕ`ii©~ÿþý1n·[Aãˆ$g‹Eš;wîÁQ£F•5W‡¿øYavºÌëõª¾ø|ày/¿ôf ¶ví;@Ñ3§V¯z5%Eú=¥Âç.÷ 6™ìE©±¡iU/›(]%rfîôôt'õHéË®”÷ûXØ|¨kïÞ½¦ñãÇ÷¶Ûíö~ïÞ½«Þÿý=qqqýÊä°4ƒ@Y£r×®¼³Ÿ{vuƒÃÑ6§ïJSL"÷ågëe_mUÀ,¢xy€ç¢É49r$Úår ä'lUÙd©²sQ:ë¤ »˜=7ÁˆÀa)ïïKç~øáœ]»vőǯ_±bÅ>RGò€‹Ÿýž'9*OŸþ¾ýsÏÎzãü¹s=Ùñ‘p((¥g¤o1bä£×vébOHH`ÕŽ=j±Ùl¾ÄÄÄ@Óîšdþoçt?î½÷ÞK®©©F]Hž_ü1¨ÿøqyVDQÖ¨{ÿÝUã80V$ÿÁ‡ÇÍmÛ6©Ú`0¶TÆwéý¸H9þ i¬1ç ¡pXÁb ‚ ¶ÖiÑ_ÿÁDk µÛ¯Z{­Ý~ÐÚhíö+€Ö@k·ÿþ_¹áßL€IEND®B`‚dosage-2.12/doc/icon/readme.txt000066400000000000000000000001411227056367200164010ustar00rootroot00000000000000Source: http://www.iconarchive.com/show/multiminimal-icons-by-multivitamin/my-pictures-icon.html dosage-2.12/doc/install.txt000066400000000000000000000006321227056367200156670ustar00rootroot00000000000000Installation ------------- The easy way with pip: ```shell pip install dosage ``` You can invoke Dosage directly from the source code as `./dosage`. Alternatively, you can install Dosage using python distutils by invoking setup.py in the root of the distribution. For example: ```shell python setup.py install ``` or if you do not have root permissions: ```shell python setup.py install --home=$HOME ``` dosage-2.12/dosage000077500000000000000000000432201227056367200141030ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012-2014 Bastian Kleineidam # ___ # / \___ ___ __ _ __ _ ___ # / /\ / _ \/ __|/ _` |/ _` |/ _ \ # / /_// (_) \__ \ (_| | (_| | __/ # /___,' \___/|___/\__,_|\__, |\___| # |___/ from __future__ import division, print_function import sys import os import argparse import pydoc import threading from io import StringIO try: from Queue import Queue, Empty except ImportError: from queue import Queue, Empty try: from urllib.parse import urlparse except ImportError: from urlparse import urlparse from dosagelib import events, scraper, configuration, singleton from dosagelib.output import out from dosagelib.util import internal_error, getDirname, strlimit, getLangName from dosagelib.ansicolor import get_columns class ArgumentParser(argparse.ArgumentParser): """Custom argument parser.""" def print_help(self, file=None): """Paginate help message on TTYs.""" msg = self.format_help() if file is None: file = sys.stdout if hasattr(file, "isatty") and file.isatty(): pydoc.pager(msg) else: print(msg, file=file) Examples = """\ EXAMPLES List available comics (ca. 3000 at the moment): dosage -l Get the latest comic of for example CalvinAndHobbes and save it in the "Comics" directory: dosage CalvinAndHobbes If you already have downloaded several comics and want to get the latest strips of all of them: dosage --continue @ """ def setupOptions(): """Construct option parser. @return: new option parser @rtype argparse.ArgumentParser """ kwargs = dict( description = "A comic downloader and archiver.", epilog = Examples, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser = ArgumentParser(**kwargs) parser.add_argument('-v', '--verbose', action='count', default=0, help='provides verbose output, use multiple times for more verbosity') parser.add_argument('-n', '--numstrips', action='store', type=int, default=0, help='traverse and retrieve the given number of comic strips; use --all to retrieve all comic strips') parser.add_argument('-a', '--all', action='store_true', help='traverse and retrieve all comic strips') parser.add_argument('-c', '--continue', action='store_true', dest='cont', help='traverse and retrieve comic strips until an existing one is found') parser.add_argument('-b', '--basepath', action='store', default='Comics', help='set the path to create invidivual comic directories in, default is Comics', metavar='PATH') parser.add_argument('--baseurl', action='store', help='the base URL of your comics directory (for RSS, HTML, etc.); this should correspond to --base-path', metavar='PATH') parser.add_argument('-l', '--list', action='store_true', help='list available comic modules') parser.add_argument('--singlelist', action='store_true', help='list available comic modules in a single list') parser.add_argument('--version', action='store_true', help='display the version number') parser.add_argument('--vote', action='store_true', help='vote for the selected comics') parser.add_argument('-m', '--modulehelp', action='store_true', help='display help for comic modules') parser.add_argument('-t', '--timestamps', action='store_true', help='print timestamps for all output at any info level') parser.add_argument('-o', '--output', action='append', dest='handler', choices=events.getHandlerNames(), help='sets output handlers for downloaded comics') parser.add_argument('--adult', action='store_true', help='confirms that you are old enough to view adult content') # used for development testing prev/next matching parser.add_argument('--dry-run', action='store_true', help=argparse.SUPPRESS) # multimatch is only used for development, eg. testing if all comics of a scripted plugin are working parser.add_argument('--multimatch', action='store_true', help=argparse.SUPPRESS) parser.add_argument('comic', nargs='*', help='comic module name (including case insensitive substrings)') try: import argcomplete argcomplete.autocomplete(parser) except ImportError: pass return parser def displayVersion(verbose): """Display application name, version, copyright and license.""" print(configuration.App) print(configuration.Copyright) print(configuration.Freeware) print("For support see", configuration.SupportUrl) if verbose: # search for updates from dosagelib.updater import check_update result, value = check_update() if result: if value: version, url = value if url is None: # current version is newer than online version text = ('Detected local or development version %(currentversion)s. ' 'Available version of %(app)s is %(version)s.') else: # display update link text = ('A new version %(version)s of %(app)s is ' 'available at %(url)s.') attrs = dict(version=version, app=configuration.AppName, url=url, currentversion=configuration.Version) print(text % attrs) else: if value is None: value = 'invalid update file syntax' text = ('An error occured while checking for an ' 'update of %(app)s: %(error)s.') attrs = dict(error=value, app=configuration.AppName) print(text % attrs) return 0 def setOutputInfo(options): """Set global output level and timestamp option.""" out.level = 0 out.level += options.verbose out.timestamps = options.timestamps # debug urllib3 #from requests.packages.urllib3 import add_stderr_logger #add_stderr_logger() def saveComicStrip(strip, basepath, dryrun): """Save a comic strip which can consist of multiple images.""" errors = 0 allskipped = True for image in strip.getImages(): try: if dryrun: filename, saved = "", False else: filename, saved = image.save(basepath) if saved: allskipped = False except Exception as msg: out.exception('Could not save image at %s to %s: %r' % (image.referrer, image.filename, msg)) errors += 1 return errors, allskipped def displayHelp(options): """Print help for comic strips.""" errors = 0 try: for scraperobj in getScrapers(options.comic, options.basepath): errors += displayComicHelp(scraperobj) except ValueError as msg: out.exception(msg) return 2 return errors def displayComicHelp(scraperobj): """Print description and help for a comic.""" orig_context = out.context out.context = getScraperName(scraperobj) try: out.info(u"URL: " + scraperobj.url) if scraperobj.description: out.info(u"Description: " + scraperobj.description) if scraperobj.lang: out.info(u"Language: " + getLangName(scraperobj.lang)) if scraperobj.genres: out.info(u"Genres: " + ", ".join(scraperobj.genres)) if scraperobj.help: for line in scraperobj.help.splitlines(): out.info(line) return 0 except ValueError as msg: out.exception(msg) return 1 finally: out.context = orig_context # the comic scraper job queue jobs = Queue() # ensure threads download only from one host at a time host_locks = {} def get_hostname(url): """Get hostname from URL.""" return list(urlparse(url))[1].lower() lock = threading.Lock() def get_host_lock(url): """Get lock object for given URL host.""" hostname = get_hostname(url) return host_locks.setdefault(hostname, threading.Lock()) comic_errors = 0 class ComicGetter(threading.Thread): """Get all strips of a comic in a thread.""" def __init__(self, options): """Store options.""" super(ComicGetter, self).__init__() self.options = options self.origname = self.getName() def run(self): """Process from queue until it is empty.""" global comic_errors while True: try: scraperobj = jobs.get(False) self.setName(scraperobj.getName()) with lock: host_lock = get_host_lock(scraperobj.url) with host_lock: errors = getStrips(scraperobj, self.options) with lock: comic_errors += errors jobs.task_done() self.setName(self.origname) except Empty: break def getComics(options): """Retrieve comics.""" if options.handler: for name in set(options.handler): events.addHandler(name, options.basepath, options.baseurl) events.getHandler().start() errors = 0 try: for scraperobj in getScrapers(options.comic, options.basepath, options.adult, options.multimatch): jobs.put(scraperobj) # start threads num_threads = max(1, min(10, jobs.qsize())) for i in range(num_threads): ComicGetter(options).start() # wait for threads to finish jobs.join() except ValueError as msg: out.exception(msg) errors += 1 finally: events.getHandler().end() return errors + comic_errors def voteComics(options): """Vote for comics.""" errors = 0 try: for scraperobj in getScrapers(options.comic, options.basepath, options.adult, options.multimatch): errors += voteComic(scraperobj) except ValueError as msg: out.exception(msg) errors += 1 return errors def voteComic(scraperobj): """Vote for given comic scraper.""" errors = 0 orig_context = out.context out.context = getScraperName(scraperobj) try: name = scraperobj.getName() answer = scraperobj.vote() out.debug(u'Vote answer %r' % answer) if answer == 'counted': url = configuration.Url + 'comics/%s.html' % name.replace('/', '_') out.info(u'Vote submitted. Votes are updated regularly at %s.' % url) elif answer == 'no': out.info(u'Vote not submitted - your vote has already been submitted before.') elif answer == 'noname': out.warn(u'The comic %s cannot be voted.' % name) else: out.warn(u'Error submitting vote parameters: %r' % answer) except Exception as msg: out.exception(msg) errors += 1 finally: out.context = orig_context return errors def getStrips(scraperobj, options): """Get all strips from a scraper.""" errors = 0 if options.all or options.cont: numstrips = None elif options.numstrips: numstrips = options.numstrips else: # get current strip numstrips = 1 try: if scraperobj.isComplete(options.basepath): out.info(u"All comics are already downloaded.") return 0 for strip in scraperobj.getStrips(numstrips): _errors, skipped = saveComicStrip(strip, options.basepath, options.dry_run) errors += _errors if skipped and options.cont: # stop when retrieval skipped an image for one comic strip out.info(u"Stop retrieval because image file already exists") break if options.all and not (errors or options.dry_run or options.cont or scraperobj.indexes): scraperobj.setComplete(options.basepath) except Exception as msg: out.exception(msg) errors += 1 return errors def run(options): """Execute comic commands.""" setOutputInfo(options) # ensure only one instance of dosage is running me = singleton.SingleInstance() if options.version: return displayVersion(options.verbose) if options.list: return doList() if options.singlelist: return doList(columnList=False, verbose=options.verbose) # after this a list of comic strips is needed if not options.comic: out.warn(u'No comics specified, bailing out!') return 1 if options.modulehelp: return displayHelp(options) if options.vote: return voteComics(options) return getComics(options) def doList(columnList=True, verbose=False): """List available comics.""" orig_context = out.context out.context = u'' try: page = hasattr(sys.stdout, "isatty") and sys.stdout.isatty() if page: fd = StringIO(u'') out.setStream(fd) out.info(u'Available comic scrapers:') out.info(u'Comics tagged with [%s] require age confirmation with the --adult option.' % TAG_ADULT) out.info(u'Non-english comics are tagged with [%s].' % TAG_LANG) scrapers = sorted(getScrapers(['@@']), key=lambda s: s.getName()) if columnList: num = doColumnList(scrapers) else: num = doSingleList(scrapers, verbose=verbose) out.info(u'%d supported comics.' % num) if page: pydoc.pager(fd.getvalue()) return 0 finally: out.context = orig_context def doSingleList(scrapers, verbose=False): """Get list of scraper names, one per line.""" for num, scraperobj in enumerate(scrapers): if verbose: displayComicHelp(scraperobj) else: out.info(getScraperName(scraperobj)) return num def doColumnList(scrapers): """Get list of scraper names with multiple names per line.""" screenWidth = get_columns(sys.stdout) # limit name length so at least two columns are there limit = (screenWidth // 2) - 8 names = [getScraperName(scraperobj, limit=limit) for scraperobj in scrapers] num = len(names) maxlen = max(len(name) for name in names) namesPerLine = max(screenWidth // (maxlen + 1), 1) while names: out.info(u''.join(name.ljust(maxlen) for name in names[:namesPerLine])) del names[:namesPerLine] return num TAG_ADULT = "adult" TAG_LANG = "lang" def getScraperName(scraperobj, limit=None): """Get comic scraper name.""" tags = [] if scraperobj.adult: tags.append(TAG_ADULT) if scraperobj.lang != "en": tags.append("%s:%s" % (TAG_LANG, scraperobj.lang)) if tags: suffix = " [" + ", ".join(tags) + "]" else: suffix = "" name = scraperobj.getName() if limit is not None: name = strlimit(name, limit) return name + suffix def getScrapers(comics, basepath=None, adult=True, multiple_allowed=False): """Get scraper objects for the given comics.""" if '@' in comics: # only scrapers whose directory already exists if len(comics) > 1: out.warn(u"using '@' as comic name ignores all other specified comics.") for scraperclass in scraper.get_scraperclasses(): dirname = getDirname(scraperclass.getName()) if os.path.isdir(os.path.join(basepath, dirname)): if not adult and scraperclass.adult: warn_adult(scraperclass) continue yield scraperclass() elif '@@' in comics: # all scrapers for scraperclass in scraper.get_scraperclasses(): if not adult and scraperclass.adult: warn_adult(scraperclass) continue yield scraperclass() else: # get only selected comic scrapers # store them in a set to eliminate duplicates scrapers = set() for comic in comics: # Helpful when using shell completion to pick comics to get comic.rstrip(os.path.sep) if basepath and comic.startswith(basepath): # make the following command work: # find Comics -type d | xargs -n1 -P10 dosage -b Comics comic = comic[len(basepath):].lstrip(os.sep) if ':' in comic: name, index = comic.split(':', 1) indexes = index.split(',') else: name = comic indexes = None scraperclasses = scraper.find_scraperclasses(name, multiple_allowed=multiple_allowed) for scraperclass in scraperclasses: if not adult and scraperclass.adult: warn_adult(scraperclass) continue scraperobj = scraperclass(indexes=indexes) if scraperobj not in scrapers: scrapers.add(scraperobj) yield scraperobj def warn_adult(scraperclass): """Print warning about adult content.""" out.warn(u"skipping adult comic %s; use the --adult option to confirm your age" % scraperclass.getName()) def main(): """Parse options and execute commands.""" try: options = setupOptions().parse_args() options.basepath = os.path.expanduser(options.basepath) res = run(options) except KeyboardInterrupt: print("Aborted.") res = 1 except Exception: internal_error() res = 2 return res def profile(): """Profile the loading of all scrapers.""" import cProfile cProfile.run("scraper.get_scraperclasses()", "dosage.prof") def viewprof(): """View profile stats.""" import pstats stats = pstats.Stats("dosage.prof") stats.strip_dirs().sort_stats("cumulative").print_stats(100) if __name__ == '__main__': sys.exit(main()) #profile() #viewprof() dosage-2.12/dosage.freecode000066400000000000000000000006241227056367200156540ustar00rootroot00000000000000Project: dosage Version: 2.12 Hide: N Website-URL: http://wummel.github.io/dosage/ Changelog-URL: https://github.com/wummel/dosage/blob/master/doc/changelog.txt Source-Package-URL: http://wummel.github.io/dosage/dist/dosage-${version}.tar.gz GIT-Tree-URL: https://github.com/wummel/dosage.git Windows-installer-URL: http://wummel.github.io/dosage/dist/dosage-${version}.exe Some comics have been fixed. dosage-2.12/dosagelib/000077500000000000000000000000001227056367200146435ustar00rootroot00000000000000dosage-2.12/dosagelib/__init__.py000066400000000000000000000016101227056367200167520ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012-2014 Bastian Kleineidam """ Automated comic downloader. Dosage traverses comic websites in order to download each strip of the comic. The intended use is for mirroring the strips locally for ease of viewing; redistribution of the downloaded strips may violate copyright, and is not advisable unless you have communicated with all of the relevant copyright holders, described your intentions, and received permission to distribute. The primary interface is the 'dosage' commandline script. Comic modules for each comic are located in L{dosagelib.plugins}. """ import sys if not (hasattr(sys, 'version_info') or sys.version_info < (2, 7, 0, 'final', 0)): raise SystemExit("This program requires Python 2.7 or later.") # PEP 396 from .configuration import Version as __version__ dosage-2.12/dosagelib/ansicolor.py000066400000000000000000000215211227056367200172070ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2000-2014 Bastian Kleineidam """ ANSI Color definitions and functions. For Windows systems, the colorama module uses ctypes and Windows DLLs to generate colored output. From Term::ANSIColor, applies also to this module: The codes output by this module are standard terminal control codes, complying with ECMA-48 and ISO 6429 (generally referred to as ``ANSI color'' for the color codes). The non-color control codes (bold, dark, italic, underline, and reverse) are part of the earlier ANSI X3.64 standard for control sequences for video terminals and peripherals. Note that not all displays are ISO 6429-compliant, or even X3.64-compliant (or are even attempting to be so). Jean Delvare provided the following table of different common terminal emulators and their support for the various attributes and others have helped me flesh it out:: clear bold dark under blink reverse conceal ------------------------------------------------------------------------ xterm yes yes no yes bold yes yes linux yes yes yes bold yes yes no rxvt yes yes no yes bold/black yes no dtterm yes yes yes yes reverse yes yes teraterm yes reverse no yes rev/red yes no aixterm kinda normal no yes no yes yes PuTTY yes color no yes no yes no Windows yes no no no no yes no Cygwin SSH yes yes no color color color yes SEE ALSO ECMA-048 is available on-line (at least at the time of this writing) at http://www.ecma-international.org/publications/standards/ECMA-048.HTM. ISO 6429 is available from ISO for a charge; the author of this module does not own a copy of it. Since the source material for ISO 6429 was ECMA-048 and the latter is available for free, there seems little reason to obtain the ISO standard. """ import os import logging import types from .fileutil import has_module, is_tty if os.name == 'nt': from . import colorama has_curses = has_module("curses") # Color constants # Escape for ANSI colors AnsiEsc = "\x1b[%sm" # Control constants bold = 'bold' light = 'light' underline = 'underline' blink = 'blink' invert = 'invert' concealed = 'concealed' # Control numbers AnsiControl = { None: '', bold: '1', light: '2', #italic: '3', # unsupported underline: '4', blink: '5', #rapidblink: '6', # unsupported invert: '7', concealed: '8', #strikethrough: '9', # unsupported } # Color constants default = 'default' black = 'black' red = 'red' green = 'green' yellow = 'yellow' blue = 'blue' purple = 'purple' cyan = 'cyan' white = 'white' # inverse colors Black = 'Black' Red = 'Red' Green = 'Green' Yellow = 'Yellow' Blue = 'Blue' Purple = 'Purple' Cyan = 'Cyna' White = 'White' InverseColors = (Black, Red, Green, Yellow, Blue, Purple, Cyan, White) # Ansi color numbers; capitalized colors are inverse AnsiColor = { None: '0', default: '0', black: '30', red: '31', green: '32', yellow: '33', blue: '34', purple: '35', cyan: '36', white: '37', Black: '40', Red: '41', Green: '42', Yellow: '43', Blue: '44', Purple: '45', Cyan: '46', White: '47', } if os.name == 'nt': # Windows color numbers; capitalized colors are used as background WinColor = { None: None, default: colorama.GREY, black: colorama.BLACK, red: colorama.RED, green: colorama.GREEN, yellow: colorama.YELLOW, blue: colorama.BLUE, purple: colorama.MAGENTA, cyan: colorama.CYAN, white: colorama.GREY, Black: colorama.BLACK, Red: colorama.RED, Green: colorama.GREEN, Yellow: colorama.YELLOW, Blue: colorama.BLUE, Purple: colorama.MAGENTA, Cyan: colorama.CYAN, White: colorama.GREY, } # pc speaker beep escape code Beep = "\007" def esc_ansicolor (color): """convert a named color definition to an escaped ANSI color""" control = '' if ";" in color: control, color = color.split(";", 1) control = AnsiControl.get(control, '')+";" cnum = AnsiColor.get(color, '0') return AnsiEsc % (control+cnum) AnsiReset = esc_ansicolor(default) def get_win_color(color): """Convert a named color definition to Windows console color foreground, background and style numbers.""" foreground = background = style = None control = '' if ";" in color: control, color = color.split(";", 1) if control == bold: style = colorama.BRIGHT if color in InverseColors: background = WinColor[color] else: foreground = WinColor.get(color) return foreground, background, style def has_colors (fp): """Test if given file is an ANSI color enabled tty.""" # The is_tty() function ensures that we do not colorize # redirected streams, as this is almost never what we want if not is_tty(fp): return False if os.name == 'nt': return True elif has_curses: import curses try: curses.setupterm(os.environ.get("TERM"), fp.fileno()) # More than 8 colors are good enough. return curses.tigetnum("colors") >= 8 except curses.error: return False return False def get_columns (fp): """Return number of columns for given file.""" if not is_tty(fp): return 80 if os.name == 'nt': return colorama.get_console_size().X if has_curses: import curses try: curses.setupterm(os.environ.get("TERM"), fp.fileno()) return curses.tigetnum("cols") except curses.error: pass return 80 def _write_color_colorama (fp, text, color): """Colorize text with given color.""" foreground, background, style = get_win_color(color) colorama.set_console(foreground=foreground, background=background, style=style) fp.write(text) colorama.reset_console() def _write_color_ansi (fp, text, color): """Colorize text with given color.""" fp.write(esc_ansicolor(color)) fp.write(text) fp.write(AnsiReset) if os.name == 'nt': write_color = _write_color_colorama colorama.init() else: write_color = _write_color_ansi class Colorizer (object): """Prints colored messages to streams.""" def __init__ (self, fp): """Initialize with given stream (file-like object).""" super(Colorizer, self).__init__() self.fp = fp if has_colors(fp): self.write = self._write_color else: self.write = self._write def _write (self, text, color=None): """Print text as-is.""" self.fp.write(text) def _write_color (self, text, color=None): """Print text with given color. If color is None, print text as-is.""" if color is None: self.fp.write(text) else: write_color(self.fp, text, color) def __getattr__ (self, name): """Delegate attribute access to the stored stream object.""" return getattr(self.fp, name) class ColoredStreamHandler (logging.StreamHandler, object): """Send colored log messages to streams (file-like objects).""" def __init__ (self, strm=None): """Log to given stream (a file-like object) or to stderr if strm is None. """ super(ColoredStreamHandler, self).__init__(strm) self.stream = Colorizer(self.stream) # standard log level colors (used by get_color) self.colors = { logging.WARN: 'bold;yellow', logging.ERROR: 'light;red', logging.CRITICAL: 'bold;red', logging.DEBUG: 'white', } def get_color (self, record): """Get appropriate color according to log level. """ return self.colors.get(record.levelno, 'default') def emit (self, record): """Emit a record. If a formatter is specified, it is used to format the record. The record is then written to the stream with a trailing newline [N.B. this may be removed depending on feedback]. """ color = self.get_color(record) msg = self.format(record) if not hasattr(types, "UnicodeType"): # no unicode support self.stream.write("%s" % msg, color=color) else: try: self.stream.write("%s" % msg, color=color) except UnicodeError: self.stream.write("%s" % msg.encode("UTF-8"), color=color) self.stream.write(os.linesep) self.flush() dosage-2.12/dosagelib/colorama.py000066400000000000000000000117451227056367200170220ustar00rootroot00000000000000# These functions are part of the python-colorama module # They have been adjusted slightly for dosage # # Copyright: (C) 2010 Jonathan Hartley # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name(s) of the copyright holders nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # from winbase.h STDOUT = -11 STDERR = -12 from ctypes import (windll, byref, Structure, c_char, c_short, c_uint32, c_ushort, ArgumentError, WinError) handles = { STDOUT: windll.kernel32.GetStdHandle(STDOUT), STDERR: windll.kernel32.GetStdHandle(STDERR), } SHORT = c_short WORD = c_ushort DWORD = c_uint32 TCHAR = c_char class COORD(Structure): """struct in wincon.h""" _fields_ = [ ('X', SHORT), ('Y', SHORT), ] class SMALL_RECT(Structure): """struct in wincon.h.""" _fields_ = [ ("Left", SHORT), ("Top", SHORT), ("Right", SHORT), ("Bottom", SHORT), ] class CONSOLE_SCREEN_BUFFER_INFO(Structure): """struct in wincon.h.""" _fields_ = [ ("dwSize", COORD), ("dwCursorPosition", COORD), ("wAttributes", WORD), ("srWindow", SMALL_RECT), ("dwMaximumWindowSize", COORD), ] def __str__(self): """Get string representation of console screen buffer info.""" return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( self.dwSize.Y, self.dwSize.X , self.dwCursorPosition.Y, self.dwCursorPosition.X , self.wAttributes , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X ) def GetConsoleScreenBufferInfo(stream_id=STDOUT): """Get console screen buffer info object.""" handle = handles[stream_id] csbi = CONSOLE_SCREEN_BUFFER_INFO() success = windll.kernel32.GetConsoleScreenBufferInfo( handle, byref(csbi)) if not success: raise WinError() return csbi def SetConsoleTextAttribute(stream_id, attrs): """Set a console text attribute.""" handle = handles[stream_id] return windll.kernel32.SetConsoleTextAttribute(handle, attrs) # from wincon.h BLACK = 0 BLUE = 1 GREEN = 2 CYAN = 3 RED = 4 MAGENTA = 5 YELLOW = 6 GREY = 7 # from wincon.h NORMAL = 0x00 # dim text, dim background BRIGHT = 0x08 # bright text, dim background _default_foreground = None _default_background = None _default_style = None def init(): """Initialize foreground and background attributes.""" global _default_foreground, _default_background, _default_style try: attrs = GetConsoleScreenBufferInfo().wAttributes except (ArgumentError, WindowsError): _default_foreground = GREY _default_background = BLACK _default_style = NORMAL else: _default_foreground = attrs & 7 _default_background = (attrs >> 4) & 7 _default_style = attrs & BRIGHT def get_attrs(foreground, background, style): """Get foreground and background attributes.""" return foreground + (background << 4) + style def set_console(stream=STDOUT, foreground=None, background=None, style=None): """Set console foreground and background attributes.""" if foreground is None: foreground = _default_foreground if background is None: background = _default_background if style is None: style = _default_style attrs = get_attrs(foreground, background, style) SetConsoleTextAttribute(stream, attrs) def reset_console(stream=STDOUT): """Reset the console.""" set_console(stream=stream) def get_console_size(): """Get the console size.""" return GetConsoleScreenBufferInfo().dwSize dosage-2.12/dosagelib/comic.py000066400000000000000000000075411227056367200163160ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012-2014 Bastian Kleineidam import os from .output import out from .util import getImageObject, normaliseURL, unquote, getDirname, getFilename, writeFile from .events import getHandler class ComicStrip(object): """A list of comic image URLs.""" def __init__(self, name, stripUrl, imageUrls, namer, session, text=None): """Store the image URL list.""" self.name = name self.stripUrl = stripUrl self.imageUrls = imageUrls self.namer = namer self.session = session self.text = text def getImages(self): """Get a list of image downloaders.""" for imageUrl in self.imageUrls: yield self.getDownloader(normaliseURL(imageUrl)) def getDownloader(self, url): """Get an image downloader.""" filename = self.namer(url, self.stripUrl) if filename is None: filename = url.rsplit('/', 1)[1] dirname = getDirname(self.name) return ComicImage(self.name, url, self.stripUrl, dirname, filename, self.session, text=self.text) class ComicImage(object): """A comic image downloader.""" ChunkBytes = 1024 * 100 # 100KB def __init__(self, name, url, referrer, dirname, filename, session, text=None): """Set URL and filename.""" self.name = name self.referrer = referrer self.url = url self.dirname = dirname filename = getFilename(filename) self.filename, self.ext = os.path.splitext(filename) self.session = session self.text = text def connect(self): """Connect to host and get meta information.""" self.urlobj = getImageObject(self.url, self.referrer, self.session) content_type = unquote(self.urlobj.headers.get('content-type', 'application/octet-stream')) content_type = content_type.split(';', 1)[0] if '/' in content_type: maintype, subtype = content_type.split('/', 1) else: maintype = content_type subtype = None if maintype != 'image' and content_type not in ('application/octet-stream', 'application/x-shockwave-flash'): raise IOError('content type %r is not an image at %s' % (content_type, self.url)) # Always use mime type for file extension if it is sane. if maintype == 'image': self.ext = '.' + subtype.replace('jpeg', 'jpg') self.contentLength = int(self.urlobj.headers.get('content-length', 0)) out.debug(u'... filename = %r, ext = %r, contentLength = %d' % (self.filename, self.ext, self.contentLength)) def save(self, basepath): """Save comic URL to filename on disk.""" out.info(u"Get image URL %s" % self.url, level=1) self.connect() filename = "%s%s" % (self.filename, self.ext) comicDir = os.path.join(basepath, self.dirname) if not os.path.isdir(comicDir): os.makedirs(comicDir) fn = os.path.join(comicDir, filename) # compare with >= since content length could be the compressed size if os.path.isfile(fn) and os.path.getsize(fn) >= self.contentLength: out.info(u'Skipping existing file "%s".' % fn) return fn, False content = self.urlobj.content if not content: out.warn(u"Empty content from %s, try again..." % self.url) self.connect() content = self.urlobj.content out.debug(u'Writing comic to file %s...' % fn) writeFile(fn, content) if self.text: fntext = os.path.join(comicDir, "%s.txt" % self.filename) out.debug(u'Writing comic text to file %s...' % fntext) writeFile(fntext, self.text, encoding='utf-8') getHandler().comicDownloaded(self, fn, text=self.text) return fn, True dosage-2.12/dosagelib/configuration.py000066400000000000000000000021031227056367200200600ustar00rootroot00000000000000# Copyright (C) 2012-2014 Bastian Kleineidam """ Define basic configuration data like version or application name. """ import _Dosage_configdata as configdata Version = configdata.version ReleaseDate = configdata.release_date AppName = configdata.name App = AppName+u" "+Version Author = configdata.author HtmlAuthor = Author.replace(u' ', u' ') Maintainer = configdata.maintainer HtmlMaintainer = Maintainer.replace(u' ', u' ') Copyright = u"Copyright (C) 2004-2008 " + \ (u",".join(Author.split(",")[:2]))+u" (C) 2012-2014 "+Maintainer HtmlCopyright = u"Copyright © 2004-2008 " + \ (u",".join(HtmlAuthor.split(",")[:2]))+u" © 2012-2014 "+HtmlMaintainer Url = configdata.url SupportUrl = Url + u"issues" Email = configdata.maintainer_email UserAgent = u"Mozilla/5.0 (compatible; %s/%s; +%s)" % (AppName, Version, Url) Freeware = AppName+u""" comes with ABSOLUTELY NO WARRANTY! This is free software, and you are welcome to redistribute it under certain conditions. Look at the file `COPYING' within this distribution.""" VoteUrl = "http://gaecounter.appspot.com/" dosage-2.12/dosagelib/decorators.py000066400000000000000000000020301227056367200173550ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2012-2014 Bastian Kleineidam class memoized (object): """Decorator that caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned, and not re-evaluated.""" def __init__(self, func): """Store function and initialize the cache.""" self.func = func self.cache = {} def __call__(self, *args, **kwargs): """Lookup and return cached result if found. Else call stored function with given arguments.""" try: return self.cache[args] except KeyError: self.cache[args] = value = self.func(*args, **kwargs) return value except TypeError: # uncachable -- for instance, passing a list as an argument. # Better to not cache than to blow up entirely. return self.func(*args, **kwargs) def __repr__(self): """Return the function's docstring.""" return self.func.__doc__ dosage-2.12/dosagelib/events.py000066400000000000000000000247041227056367200165300ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs import os import time try: from urllib.parse import quote as url_quote except ImportError: from urllib import quote as url_quote import codecs import json from . import rss, util, configuration from .output import out # Maximum width or height to display an image in exported pages. # Note that only the displayed size is adjusted, not the image itself. MaxImageSize = (800, 800) class EventHandler(object): """Base class for writing events to files. The currently defined events are start(), comicDownloaded() and end().""" def __init__(self, basepath, baseurl): """Initialize base path and url.""" self.basepath = basepath self.baseurl = baseurl or self.getBaseUrl() def getBaseUrl(self): '''Return a file: URL that probably points to the basedir. This is used as a halfway sane default when the base URL is not provided; not perfect, but should work in most cases.''' components = util.splitpath(os.path.abspath(self.basepath)) url = '/'.join([url_quote(component, '') for component in components]) return 'file:///' + url + '/' def getUrlFromFilename(self, filename): """Construct URL from filename.""" components = util.splitpath(util.getRelativePath(self.basepath, filename)) url = '/'.join([url_quote(component, '') for component in components]) return self.baseurl + url def start(self): """Emit a start event. Should be overridden in subclass.""" pass def comicDownloaded(self, comic, filename, text=None): """Emit a comic downloaded event. Should be overridden in subclass.""" pass def comicPageLink(self, comic, url, prevUrl): """Emit an event to inform the handler about links between comic pages. Should be overridden in subclass.""" pass def end(self): """Emit an end event. Should be overridden in subclass.""" pass class RSSEventHandler(EventHandler): """Output in RSS format.""" name = 'rss' def getFilename(self): """Return RSS filename.""" return os.path.abspath(os.path.join(self.basepath, 'dailydose.rss')) def start(self): """Log start event.""" today = time.time() yesterday = today - 86400 today = time.localtime(today) yesterday = time.localtime(yesterday) link = configuration.Url self.rssfn = self.getFilename() if os.path.exists(self.rssfn): self.newfile = False self.rss = rss.parseFeed(self.rssfn, yesterday) else: self.newfile = True self.rss = rss.Feed('Daily Dosage', link, 'Comics for %s' % time.strftime('%Y/%m/%d', today)) def comicDownloaded(self, comic, filename, text=None): """Write RSS entry for downloaded comic.""" imageUrl = self.getUrlFromFilename(filename) size = getDimensionForImage(filename, MaxImageSize) title = '%s - %s' % (comic.name, os.path.basename(filename)) pageUrl = comic.referrer description = ' maxsize: img.thumbnail(maxsize) out.info("Downscaled display size from %s to %s" % ((width, height), img.size)) return img.size class HtmlEventHandler(EventHandler): """Output in HTML format.""" name = 'html' encoding = 'utf-8' def fnFromDate(self, date): """Get filename from date.""" fn = time.strftime('comics-%Y%m%d.html', date) fn = os.path.join(self.basepath, 'html', fn) fn = os.path.abspath(fn) return fn def start(self): """Start HTML output.""" today = time.time() yesterday = today - 86400 tomorrow = today + 86400 today = time.localtime(today) yesterday = time.localtime(yesterday) tomorrow = time.localtime(tomorrow) fn = self.fnFromDate(today) if os.path.exists(fn): raise ValueError('output file %r already exists' % fn) d = os.path.dirname(fn) if not os.path.isdir(d): os.makedirs(d) yesterdayUrl = self.getUrlFromFilename(self.fnFromDate(yesterday)) tomorrowUrl = self.getUrlFromFilename(self.fnFromDate(tomorrow)) self.html = codecs.open(fn, 'w', self.encoding) self.html.write(u''' Comics for %s Previous Day | Next Day
    ''' % (self.encoding, configuration.App, time.strftime('%Y/%m/%d', today), yesterdayUrl, tomorrowUrl)) # last comic name (eg. CalvinAndHobbes) self.lastComic = None # last comic strip URL (eg. http://example.com/page42) self.lastUrl = None def comicDownloaded(self, comic, filename, text=None): """Write HTML entry for downloaded comic.""" if self.lastComic != comic.name: self.newComic(comic) size = getDimensionForImage(filename, MaxImageSize) imageUrl = self.getUrlFromFilename(filename) pageUrl = comic.referrer if pageUrl != self.lastUrl: self.html.write(u'
  • %s\n' % (pageUrl, pageUrl)) self.html.write(u'
    \n') if text: self.html.write(u'
    %s\n' % text) self.lastComic = comic.name self.lastUrl = pageUrl def newComic(self, comic): """Start new comic list in HTML.""" if self.lastUrl is not None: self.html.write(u'
  • \n') if self.lastComic is not None: self.html.write(u'
\n') self.html.write(u'
  • %s
  • \n' % comic.name) self.html.write(u'
      \n') def end(self): """End HTML output.""" if self.lastUrl is not None: self.html.write(u'\n') if self.lastComic is not None: self.html.write(u'
    \n') self.html.write(u''' ''') self.html.close() class JSONEventHandler(EventHandler): """Output metadata for comics in JSON format.""" name = 'json' encoding = 'utf-8' def start(self): """Start with empty data.""" self.data = {} def jsonFn(self, comic): """Get filename for the JSON file for a comic.""" fn = os.path.join(self.basepath, comic, 'dosage.json') fn = os.path.abspath(fn) return fn def getComicData(self, comic): """Return dictionary with comic info.""" if comic not in self.data: if os.path.exists(self.jsonFn(comic)): with codecs.open(self.jsonFn(comic), 'r', self.encoding) as f: self.data[comic] = json.load(f) else: self.data[comic] = {'pages':{}} return self.data[comic] def getPageInfo(self, comic, url): """Return dictionary with comic page info.""" comicData = self.getComicData(comic) if url not in comicData['pages']: comicData['pages'][url] = {'images':{}} return comicData['pages'][url] def comicDownloaded(self, comic, filename, text=None): """Add URL-to-filename mapping into JSON.""" pageInfo = self.getPageInfo(comic.name, comic.referrer) pageInfo['images'][comic.url] = os.path.basename(filename) def comicPageLink(self, comic, url, prevUrl): """Write previous link into JSON.""" pageInfo = self.getPageInfo(comic, url) pageInfo['prev'] = prevUrl def end(self): """Write all JSON data to files.""" for comic in self.data: with codecs.open(self.jsonFn(comic), 'w', self.encoding) as f: json.dump(self.data[comic], f, indent=2, separators=(',', ': '), sort_keys=True) _handler_classes = {} def addHandlerClass(clazz): """Register handler class.""" if not issubclass(clazz, EventHandler): raise ValueError("%s must be subclassed from %s" % (clazz, EventHandler)) _handler_classes[clazz.name] = clazz addHandlerClass(HtmlEventHandler) addHandlerClass(RSSEventHandler) addHandlerClass(JSONEventHandler) def getHandlerNames(): """Get sorted handler names.""" return sorted(_handler_classes.keys()) _handlers = [] def addHandler(name, basepath=None, baseurl=None): """Add an event handler with given name.""" if basepath is None: basepath = '.' _handlers.append(_handler_classes[name](basepath, baseurl)) class MultiHandler(object): """Encapsulate a list of handlers.""" def start(self): """Emit start events for handlers.""" for handler in _handlers: handler.start() def comicDownloaded(self, comic, filename, text=None): """Emit comic downloaded events for handlers.""" for handler in _handlers: handler.comicDownloaded(comic, filename, text=text) def comicPageLink(self, comic, url, prevUrl): """Emit an event to inform the handler about links between comic pages. Should be overridden in subclass.""" for handler in _handlers: handler.comicPageLink(comic, url, prevUrl) def end(self): """Emit end events for handlers.""" for handler in _handlers: handler.end() multihandler = MultiHandler() def getHandler(): """Get installed event handler.""" return multihandler dosage-2.12/dosagelib/fileutil.py000066400000000000000000000010471227056367200170340ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2012 Bastian Kleineidam """ File and path utilities. """ import importlib def has_module (name): """Test if given module can be imported. @return: flag if import is successful @rtype: bool """ try: importlib.import_module(name) return True except (OSError, ImportError): # some modules (for example HTMLtidy) raise OSError return False def is_tty (fp): """Check if a file object is a TTY.""" return (hasattr(fp, "isatty") and fp.isatty()) dosage-2.12/dosagelib/helpers.py000066400000000000000000000030231227056367200166550ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012-2014 Bastian Kleineidam from .util import fetchUrl, getPageContent, getQueryParams def queryNamer(paramName, usePageUrl=False): """Get name from URL query part.""" @classmethod def _namer(cls, imageUrl, pageUrl): """Get URL query part.""" url = pageUrl if usePageUrl else imageUrl return getQueryParams(url)[paramName][0] return _namer def regexNamer(regex, usePageUrl=False): """Get name from regular expression.""" @classmethod def _namer(cls, imageUrl, pageUrl): """Get first regular expression group.""" url = pageUrl if usePageUrl else imageUrl mo = regex.search(url) if mo: return mo.group(1) return _namer def bounceStarter(url, nextSearch): """Get start URL by "bouncing" back and forth one time.""" @classmethod def _starter(cls): """Get bounced start URL.""" data, baseUrl = getPageContent(url, cls.session) url1 = fetchUrl(url, data, baseUrl, cls.prevSearch) data, baseUrl = getPageContent(url1, cls.session) return fetchUrl(url1, data, baseUrl, nextSearch) return _starter def indirectStarter(url, latestSearch): """Get start URL by indirection.""" @classmethod def _starter(cls): """Get indirect start URL.""" data, baseUrl = getPageContent(url, cls.session) return fetchUrl(url, data, baseUrl, latestSearch) return _starter dosage-2.12/dosagelib/languages.py000066400000000000000000000114411227056367200171640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ISO 693-1 language codes from pycountry Iso2Language = { u'aa': u'Afar', u'ab': u'Abkhazian', u'af': u'Afrikaans', u'ak': u'Akan', u'sq': u'Albanian', u'am': u'Amharic', u'ar': u'Arabic', u'an': u'Aragonese', u'hy': u'Armenian', u'as': u'Assamese', u'av': u'Avaric', u'ae': u'Avestan', u'ay': u'Aymara', u'az': u'Azerbaijani', u'ba': u'Bashkir', u'bm': u'Bambara', u'eu': u'Basque', u'be': u'Belarusian', u'bn': u'Bengali', u'bh': u'Bihari languages', u'bi': u'Bislama', u'bs': u'Bosnian', u'br': u'Breton', u'bg': u'Bulgarian', u'my': u'Burmese', u'ca': u'Catalan; Valencian', u'ch': u'Chamorro', u'ce': u'Chechen', u'zh': u'Chinese', u'cu': u'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic', u'cv': u'Chuvash', u'kw': u'Cornish', u'co': u'Corsican', u'cr': u'Cree', u'cs': u'Czech', u'da': u'Danish', u'dv': u'Divehi; Dhivehi; Maldivian', u'nl': u'Dutch; Flemish', u'dz': u'Dzongkha', u'en': u'English', u'eo': u'Esperanto', u'et': u'Estonian', u'ee': u'Ewe', u'fo': u'Faroese', u'fj': u'Fijian', u'fi': u'Finnish', u'fr': u'French', u'fy': u'Western Frisian', u'ff': u'Fulah', u'ka': u'Georgian', u'de': u'German', u'gd': u'Gaelic; Scottish Gaelic', u'ga': u'Irish', u'gl': u'Galician', u'gv': u'Manx', u'el': u'Greek, Modern (1453-)', u'gn': u'Guarani', u'gu': u'Gujarati', u'ht': u'Haitian; Haitian Creole', u'ha': u'Hausa', u'he': u'Hebrew', u'hz': u'Herero', u'hi': u'Hindi', u'ho': u'Hiri Motu', u'hr': u'Croatian', u'hu': u'Hungarian', u'ig': u'Igbo', u'is': u'Icelandic', u'io': u'Ido', u'ii': u'Sichuan Yi; Nuosu', u'iu': u'Inuktitut', u'ie': u'Interlingue; Occidental', u'ia': u'Interlingua (International Auxiliary Language Association)', u'id': u'Indonesian', u'ik': u'Inupiaq', u'it': u'Italian', u'jv': u'Javanese', u'ja': u'Japanese', u'kl': u'Kalaallisut; Greenlandic', u'kn': u'Kannada', u'ks': u'Kashmiri', u'kr': u'Kanuri', u'kk': u'Kazakh', u'km': u'Central Khmer', u'ki': u'Kikuyu; Gikuyu', u'rw': u'Kinyarwanda', u'ky': u'Kirghiz; Kyrgyz', u'kv': u'Komi', u'kg': u'Kongo', u'ko': u'Korean', u'kj': u'Kuanyama; Kwanyama', u'ku': u'Kurdish', u'lo': u'Lao', u'la': u'Latin', u'lv': u'Latvian', u'li': u'Limburgan; Limburger; Limburgish', u'ln': u'Lingala', u'lt': u'Lithuanian', u'lb': u'Luxembourgish; Letzeburgesch', u'lu': u'Luba-Katanga', u'lg': u'Ganda', u'mk': u'Macedonian', u'mh': u'Marshallese', u'ml': u'Malayalam', u'mi': u'Maori', u'mr': u'Marathi', u'ms': u'Malay', u'mg': u'Malagasy', u'mt': u'Maltese', u'mo': u'Moldavian; Moldovan', u'mn': u'Mongolian', u'na': u'Nauru', u'nv': u'Navajo; Navaho', u'nr': u'Ndebele, South; South Ndebele', u'nd': u'Ndebele, North; North Ndebele', u'ng': u'Ndonga', u'ne': u'Nepali', u'nn': u'Norwegian Nynorsk; Nynorsk, Norwegian', u'nb': u'Bokm\xe5l, Norwegian; Norwegian Bokm\xe5l', u'no': u'Norwegian', u'ny': u'Chichewa; Chewa; Nyanja', u'oc': u'Occitan (post 1500)', u'oj': u'Ojibwa', u'or': u'Oriya', u'om': u'Oromo', u'os': u'Ossetian; Ossetic', u'pa': u'Panjabi; Punjabi', u'fa': u'Persian', u'pi': u'Pali', u'pl': u'Polish', u'pt': u'Portuguese', u'ps': u'Pushto; Pashto', u'qu': u'Quechua', u'rm': u'Romansh', u'ro': u'Romanian', u'rn': u'Rundi', u'ru': u'Russian', u'sg': u'Sango', u'sa': u'Sanskrit', u'si': u'Sinhala; Sinhalese', u'sk': u'Slovak', u'sl': u'Slovenian', u'se': u'Northern Sami', u'sm': u'Samoan', u'sn': u'Shona', u'sd': u'Sindhi', u'so': u'Somali', u'st': u'Sotho, Southern', u'es': u'Spanish; Castilian', u'sc': u'Sardinian', u'sr': u'Serbian', u'ss': u'Swati', u'su': u'Sundanese', u'sw': u'Swahili', u'sv': u'Swedish', u'ty': u'Tahitian', u'ta': u'Tamil', u'tt': u'Tatar', u'te': u'Telugu', u'tg': u'Tajik', u'tl': u'Tagalog', u'th': u'Thai', u'bo': u'Tibetan', u'ti': u'Tigrinya', u'to': u'Tonga (Tonga Islands)', u'tn': u'Tswana', u'ts': u'Tsonga', u'tk': u'Turkmen', u'tr': u'Turkish', u'tw': u'Twi', u'ug': u'Uighur; Uyghur', u'uk': u'Ukrainian', u'ur': u'Urdu', u'uz': u'Uzbek', u've': u'Venda', u'vi': u'Vietnamese', u'vo': u'Volap\xfck', u'cy': u'Welsh', u'wa': u'Walloon', u'wo': u'Wolof', u'xh': u'Xhosa', u'yi': u'Yiddish', u'yo': u'Yoruba', u'za': u'Zhuang; Chuang', u'zu': u'Zulu', } dosage-2.12/dosagelib/loader.py000066400000000000000000000055211227056367200164660ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2012-2014 Bastian Kleineidam """ Functions to load plugin modules. Example usage: modules = loader.get_modules('plugins') plugins = loader.get_plugins(modules, PluginClass) """ import os import sys import zipfile import importlib from .output import out def is_frozen (): """Return True if running inside a py2exe- or py2app-generated executable.""" return hasattr(sys, "frozen") def get_modules(folder): """Find all valid modules in the given folder which must be in in the same directory as this loader.py module. A valid module has a .py extension, and is importable. @return: all loaded valid modules @rtype: iterator of module """ if is_frozen(): # find modules in library.zip filename zipname = os.path.dirname(os.path.dirname(__file__)) parentmodule = os.path.basename(os.path.dirname(__file__)) with zipfile.ZipFile(zipname, 'r') as f: prefix = "%s/%s/" % (parentmodule, folder) modnames = [os.path.splitext(n[len(prefix):])[0] for n in f.namelist() if n.startswith(prefix) and "__init__" not in n] else: dirname = os.path.join(os.path.dirname(__file__), folder) modnames = get_importable_modules(dirname) for modname in modnames: try: name ="..%s.%s" % (folder, modname) yield importlib.import_module(name, __name__) except ImportError as msg: out.error("could not load module %s: %s" % (modname, msg)) def get_importable_modules(folder): """Find all module files in the given folder that end with '.py' and don't start with an underscore. @return module names @rtype: iterator of string """ for fname in os.listdir(folder): if fname.endswith('.py') and not fname.startswith('_'): yield fname[:-3] def get_plugins(modules, classobj): """Find all class objects in all modules. @param modules: the modules to search @ptype modules: iterator of modules @return: found classes @rytpe: iterator of class objects """ for module in modules: for plugin in get_module_plugins(module, classobj): yield plugin def get_module_plugins(module, classobj): """Return all subclasses of a class in the module. If the module defines __all__, only those entries will be searched, otherwise all objects not starting with '_' will be searched. """ try: names = module.__all__ except AttributeError: names = [x for x in vars(module) if not x.startswith('_')] for name in names: try: obj = getattr(module, name) except AttributeError: continue try: if issubclass(obj, classobj): yield obj except TypeError: continue dosage-2.12/dosagelib/output.py000066400000000000000000000061361227056367200165630ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012-2014 Bastian Kleineidam import time import sys import os import threading import traceback import codecs from .ansicolor import Colorizer lock = threading.Lock() def get_threadname(): """Return name of current thread.""" return threading.current_thread().getName() class Output(object): """Print output with context, indentation and optional timestamps.""" def __init__(self, stream=None): """Initialize context and indentation.""" self.context = None self.level = 0 self.timestamps = False if stream is None: if hasattr(sys.stdout, "encoding") and sys.stdout.encoding: self.encoding = sys.stdout.encoding else: self.encoding = 'utf-8' if sys.version_info[0] >= 3: stream = sys.stdout.buffer else: stream = sys.stdout stream = codecs.getwriter(self.encoding)(stream, 'replace') self.setStream(stream) def setStream(self, stream): """Initialize context and indentation.""" self.stream = Colorizer(stream) def info(self, s, level=0): """Write an informational message.""" self.write(s, level=level) def debug(self, s, level=2): """Write a debug message.""" self.write(s, level=level, color='white') def warn(self, s): """Write a warning message.""" self.write(u"WARN: %s" % s, color='bold;yellow') def error(self, s, tb=None): """Write an error message.""" self.write(u"ERROR: %s" % s, color='light;red') #if tb is not None: # self.write('Traceback (most recent call last):', 1) def exception(self, s): """Write error message with traceback info.""" self.error(s) type, value, tb = sys.exc_info() self.writelines(traceback.format_stack(), 1) self.writelines(traceback.format_tb(tb)[1:], 1) self.writelines(traceback.format_exception_only(type, value), 1) def write(self, s, level=0, color=None): """Write message with indentation, context and optional timestamp.""" if level > self.level: return if self.timestamps: timestamp = time.strftime(u'%H:%M:%S ') else: timestamp = u'' with lock: if self.context: self.stream.write(u'%s%s> ' % (timestamp, self.context)) elif self.context is None: self.stream.write(u'%s%s> ' % (timestamp, get_threadname())) self.stream.write(u'%s' % s, color=color) try: text_type = unicode except NameError: text_type = str self.stream.write(text_type(os.linesep)) self.stream.flush() def writelines(self, lines, level=0): """Write multiple messages.""" for line in lines: for line in line.rstrip(u'\n').split(u'\n'): self.write(line.rstrip(u'\n'), level=level) out = Output() dosage-2.12/dosagelib/plugins/000077500000000000000000000000001227056367200163245ustar00rootroot00000000000000dosage-2.12/dosagelib/plugins/__init__.py000066400000000000000000000000351227056367200204330ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- dosage-2.12/dosagelib/plugins/a.py000066400000000000000000000346351227056367200171310ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012-2014 Bastian Kleineidam from re import compile, escape, MULTILINE from ..util import tagre from ..scraper import _BasicScraper from ..helpers import regexNamer, bounceStarter, indirectStarter class AbleAndBaker(_BasicScraper): description = u"Able and Baker: Hatin' and Dictatin'" url = 'http://www.jimburgessdesign.com/comics/index.php' stripUrl = url + '?comic=%s' firstStripUrl = stripUrl % '1' imageSearch = compile(tagre('img', 'src', r'(comics/.+)')) prevSearch = compile(tagre('a', 'href', r'(.+\d+)') + '.+?previous.gif') help = 'Index format: nnn' class AbsurdNotions(_BasicScraper): baseUrl = 'http://www.absurdnotions.org/' url = baseUrl + 'page129.html' stripUrl = baseUrl + 'page%s.html' firstStripUrl = stripUrl % '1' imageSearch = compile(tagre('img', 'src', r'(an[^"]+)')) multipleImagesPerStrip = True prevSearch = compile(tagre('a', 'href', r'([^"]+)') + tagre('img', 'src', 'nprev\.gif')) help = 'Index format: n (unpadded)' class AbstruseGoose(_BasicScraper): url = 'http://abstrusegoose.com/' rurl = escape(url) starter = bounceStarter(url, compile(tagre('a', 'href', r'(%s\d+)' % rurl)+"Next »")) stripUrl = url + '%s' firstStripUrl = stripUrl % '1' imageSearch = compile(tagre('img', 'src', r'(http://abstrusegoose\.com/strips/[^<>"]+)')) prevSearch = compile(tagre('a', 'href', r'(%s\d+)' % rurl) + r'« Previous') help = 'Index format: n (unpadded)' textSearch = compile(tagre("img", "title", r'([^"]+)')) @classmethod def namer(cls, imageUrl, pageUrl): index = int(pageUrl.rstrip('/').split('/')[-1]) name = imageUrl.split('/')[-1].split('.')[0] return 'c%03d-%s' % (index, name) class AcademyVale(_BasicScraper): description = u'Academy Vale' url = 'http://www.imagerie.com/vale/' stripUrl = url + 'avarch.cgi?%s' firstStripUrl = stripUrl % '001' imageSearch = compile(tagre('img', 'src', r'(avale\d{4}-\d{2}\.gif)')) prevSearch = compile(tagre('a', 'href', r'(avarch[^">]+)', quote="") + tagre('img', 'src', 'AVNavBack\.gif')) help = 'Index format: nnn' class Achewood(_BasicScraper): url = 'http://www.achewood.com/' stripUrl = url + 'index.php?date=%s' firstStripUrl = stripUrl % '00000000' imageSearch = compile(tagre("img", "src", r'(/comic\.php\?date=\d+)')) prevSearch = compile(tagre("a", "href", r'(index\.php\?date=\d+)', after="Previous")) help = 'Index format: mmddyyyy' namer = regexNamer(compile(r'date=(\d+)')) class AfterStrife(_BasicScraper): baseUrl = 'http://afterstrife.com/' rurl = escape(baseUrl) stripUrl = baseUrl + '?p=%s' url = stripUrl % '262' firstStripUrl = stripUrl % '1' imageSearch = compile(r'[^>]+Back')) stripUrl = url + '1.%s.html' firstStripUrl = stripUrl % '1' imageSearch = compile(tagre("img", "src", r'(img/strip/[^"]+\.jpg)')) prevSearch = compile(r'[^>]+Back') help = 'Index format: nnn' class AhoiPolloi(_BasicScraper): description = u'ahoi polloi - ein f\xfcllhorn voller f\xfchlh\xf6rner' url = 'http://ahoipolloi.blogger.de/' stripUrl = url + '?day=%s' firstStripUrl = stripUrl % '20060306' multipleImagesPerStrip = True lang = 'de' imageSearch = compile(tagre('img', 'src', r'(/static/antville/ahoipolloi/images/[^"]+)')) prevSearch = compile(tagre('a', 'href', r'(http://ahoipolloi\.blogger\.de/\?day=\d+)')) help = 'Index format: yyyymmdd' @classmethod def namer(cls, imageUrl, pageUrl): return imageUrl.rsplit('/', 1)[1] class AirForceBlues(_BasicScraper): description = u'Air Force Blues | By A. May -' url = 'http://www.afblues.com/' stripUrl = url + 'wordpress/%s/' firstStripUrl = stripUrl % '1997/09/07/need-a-clue-do-ya' imageSearch = compile(tagre("img", "src", r'(http://www\.afblues\.com/wordpress/comics/[^"]+)')) prevSearch = compile(tagre("a", "href", r'([^"]+)', after='Previous')) help = 'Index format: yyyy/mm/dd/stripname' class ALessonIsLearned(_BasicScraper): description = u'A Lesson Is Learned But The Damage Is Irreversible' url = 'http://www.alessonislearned.com/' prevSearch = compile(tagre("a", "href", r"(index\.php\?comic=\d+)", quote="'")+r"[^>]+previous") starter = indirectStarter(url, prevSearch) stripUrl = url + 'index.php?comic=%s' firstStripUrl = stripUrl % '1' imageSearch = compile(tagre("img", "src", r"(cmx/lesson\d+\.[a-z]+)")) help = 'Index format: nnn' class Alice(_BasicScraper): description = u'The little webcomic with the BIG imagination' url = 'http://alice.alicecomics.com/' rurl = escape(url) stripUrl = url + '%s/' imageSearch = compile(tagre("img", "src", r'(%swp-content/uploads/\d+/\d+/\d+-\d+-\d+[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%salicecomics/[^"]+)' % rurl, after="previous")) help = 'Index format: name' class AlienLovesPredator(_BasicScraper): description = u'Abe (the Alien) and Preston (the Predator) represent in NYC' url = 'http://alienlovespredator.com/' stripUrl = url + '%s/' firstStripUrl = stripUrl % '2004/10/12/unavoidable-delay' imageSearch = compile(tagre("img", "src", r'([^"]+)', after='border="1" alt="" width="750"')) prevSearch = compile(tagre("a", "href", r'([^"]+)', after="prev")) help = 'Index format: yyyy/mm/dd/name' class AlienShores(_BasicScraper): description = u'A webcomic about four guys forming a band. They find that being a band is more than just playing the music.' baseUrl = 'http://alienshores.com/' rurl = escape(baseUrl) url = baseUrl + 'alienshores_band/' stripUrl = url + '%s' imageSearch = compile(tagre("img", "src", r'(%salienshores_band/wp-content/uploads/[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%s[^"]+)' % rurl, after="prev")) help = 'Index format: yyyy/mm/dd/p/' class AllTheGrowingThings(_BasicScraper): description = u'All The Growing Things - A Tale of Gardens, monsters, and old ladies' url = 'http://growingthings.typodmary.com/' rurl = escape(url) stripUrl = url + '%s/' firstStripUrl = stripUrl % '2009/04/21/all-the-growing-things' imageSearch = compile(tagre("img", "src", r'(%sfiles/[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%s[^"]+)' % rurl, after="prev")) help = 'Index format: yyyy/mm/dd/strip-name' class AlphaLuna(_BasicScraper): description = u'Luna, a young girl discovers what lies in her soul: a werewolf beast and a destiny. An adventure manga story for werecreatures fans.' url = 'http://www.alphaluna.net/' stripUrl = url + 'issue-%s/' firstStripUrl = stripUrl % '1/cover' imageSearch = compile(tagre("a", "href", r'[^"]*/(?:issue-|support/upcoming)[^"]+') + tagre("img", "src", r'([^"]*/PAGINAS/[^"]+)')) prevSearch = compile(tagre("a", "href", r'([^"]+)') + tagre("img", "alt", "Prev")) help = 'Index format: issue/page (e.g. 4/05)' class AlphaLunaSpanish(AlphaLuna): name = 'AlphaLuna/Spanish' lang = 'es' url = 'http://alphaluna.net/spanish/' stripUrl = url + 'issue-%s/' firstStripUrl = stripUrl % '1/portada' class AlsoBagels(_BasicScraper): description = u'Also, Bagels - A Comic of Inept Redundancy' url = 'http://alsobagels.com/' rurl = escape(url) stripUrl = url + 'index.php/comic/%s/' imageSearch = compile(tagre("img", "src", r'(%scomics/[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%sindex\.php/comic/[^"]+)' % rurl, after="Previous")) help = 'Index format: strip-name' class Altermeta(_BasicScraper): url = 'http://altermeta.net/' rurl = escape(url) stripUrl = url + 'archive.php?comic=%s' firstStripUrl = stripUrl % '0' imageSearch = compile(r'') prevSearch = compile(r'Back') class AmazingSuperPowers(_BasicScraper): url = 'http://www.amazingsuperpowers.com/' rurl = escape(url) stripUrl = url + '%s/' firstStripUrl = stripUrl % '2007/09/heredity' imageSearch = compile(tagre("img", "src", r'(%scomics/[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%s[^"]+)' % rurl, after="prev")) help = 'Index format: yyyy/mm/name' def shouldSkipUrl(self, url): """Skip pages without images.""" return url in ( # video self.stripUrl % '2013/05/orbital-deathray-kickstarter', ) class Amya(_BasicScraper): description = u'A Graphic Novel' url = 'http://www.amyachronicles.com/' rurl = escape(url) stripUrl = url + 'archives/%s' firstStripUrl = stripUrl % '117' imageSearch = compile(tagre("img", "src", r'(%scomics/[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%sarchives/\d+)' % rurl, after="Previous")) help = 'Index format: n' class Angband(_BasicScraper): description = u'Angband - Tales From The Pit' url = 'http://angband.calamarain.net/' stripUrl = url + 'view.php?date=%s' firstStripUrl = stripUrl % '2005-12-30' imageSearch = compile(tagre("img", "src", r'(comics/Scroll[^"]+)')) prevSearch = compile(tagre("a", "href", r'(view\.php\?date\=[^"]+)')+"Previous") help = 'Index format: yyyy-mm-dd' class Angels2200(_BasicScraper): description = u'Angels 2200' url = 'http://www.janahoffmann.com/angels/' stripUrl = url + '%s' imageSearch = compile(tagre("img", "src", r"(http://www\.janahoffmann\.com/angels/comics/[^']+)", quote="'")) prevSearch = compile(tagre("a", "href", r'([^"]+)')+"« Previous") help = 'Index format: yyyy/mm/dd/part--comic-' class Annyseed(_BasicScraper): baseUrl = 'http://www.colourofivy.com/' rurl = escape(baseUrl) url = baseUrl + 'annyseed_webcomic_latest.htm' stripUrl = baseUrl + 'annyseed_webcomic%s.htm' imageSearch = compile(tagre("img", "src", r'(Annyseed[^"]+)')) prevSearch = compile(r'Previous Comic\s*

    ', MULTILINE) help = 'Index format: n (unpadded)' class ARedTailsDream(_BasicScraper): description = u"Finnish mythology meets teen boy and his dog" baseUrl = 'http://www.minnasundberg.fi/' stripUrl = baseUrl + 'comic/page%s.php' firstStripUrl = stripUrl % '00' url = baseUrl + 'comic/recent.php' imageSearch = compile(tagre('img', 'src', r'(chapter.+?/eng[^"]*)')) prevSearch = compile(tagre('a', 'href', r'(page\d+\.php)') + tagre("img", "src", r'.*?aprev.*?')) help = 'Index format: nn' class ASofterWorld(_BasicScraper): url = 'http://www.asofterworld.com/' stripUrl = url + 'index.php?id=%s' firstStripUrl = stripUrl % '1' imageSearch = compile(tagre("p", "id", "thecomic") + r'\s*' + tagre("img", "src", r'(http://www\.asofterworld\.com/clean/[^"]+)')) prevSearch = compile(tagre("a", "href", "(index\.php\?id=\d+)")+'< back') help = 'Index format: n (unpadded)' class AstronomyPOTD(_BasicScraper): description = u'A different astronomy and space science related image is featured each day, along with a brief explanation.' baseUrl = 'http://antwrp.gsfc.nasa.gov/apod/' url = baseUrl + 'astropix.html' starter = bounceStarter(url, compile(tagre("a", "href", r'(ap\d{6}\.html)') + ">")) stripUrl = baseUrl + 'ap%s.html' firstStripUrl = stripUrl % '061012' imageSearch = compile(tagre("a", "href", r'(image/\d{4}/[^"]+)')) multipleImagesPerStrip = True prevSearch = compile(tagre("a", "href", r'(ap\d{6}\.html)') + "<") help = 'Index format: yymmdd' def shouldSkipUrl(self, url): """Skip pages without images.""" return url in ( self.stripUrl % '130217', # video self.stripUrl % '130218', # video self.stripUrl % '130226', # video self.stripUrl % '130424', # video ) @classmethod def namer(cls, imageUrl, pageUrl): return '%s-%s' % (pageUrl.split('/')[-1].split('.')[0][2:], imageUrl.split('/')[-1].split('.')[0]) class ASkeweredParadise(_BasicScraper): url = 'http://aspcomics.net/' stripUrl = url + 'comic/%s' firstStripUrl = stripUrl % '001' imageSearch = compile(tagre("img", "src", r'(http://aspcomics\.net/sites/default/files[^"]*/asp\d+\.jpg)[^"]+')) prevSearch = compile(tagre("a", "href", "(/comic/\d+)")+r"[^>]+Previous") help = 'Index format: nnn' class AxeCop(_BasicScraper): description = u'Axe Cop' url = 'http://axecop.com/' rurl = escape(url) starter = bounceStarter(url, ( compile(tagre("a", "href", r'(%scomic/page-\d+-[^"]+/)' % rurl, after="navi-next")), compile(tagre("a", "href", r'(%scomic/[^"]+/)' % rurl, after="navi-next")), ) ) stripUrl = url + 'comic/%s/' firstStripUrl = stripUrl % '0' imageSearch = compile(tagre("img", "src", r'(http://mainsite\.axecop\.wpengine\.com/wp-content/uploads/sites/\d+/\d+/\d+/[^"]+)')) prevSearch = compile(tagre("a", "href", r'(%scomic/[^"]+/)' % rurl, after="navi-prev")) help = 'Index format: usually stripname' dosage-2.12/dosagelib/plugins/arcamax.py000066400000000000000000000110011227056367200203030ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2013-2014 Bastian Kleineidam """ Arcamax comic strips """ from re import compile from ..scraper import make_scraper from ..util import tagre _imageSearch = compile(tagre("a", "href", r'(/newspics/[^"]+)', after='zoom')) _prevSearch = compile(tagre("a", "href", r'(/[^"]+)', before='prev')) def add(name, shortname): url = 'http://www.arcamax.com%s' % shortname classname = 'Arcamax_%s' % name globals()[classname] = make_scraper(classname, name='Arcamax/' + name, url = url, stripUrl = url + '%s', imageSearch = _imageSearch, prevSearch = _prevSearch, help = 'Index format: none', ) # do not edit anything below since these entries are generated from scripts/update.sh # DO NOT REMOVE #add('9ChickweedLane', '/thefunnies/ninechickweedlane/') #add('Agnes', '/thefunnies/agnes/') #add('AndyCapp', '/thefunnies/andycapp/') #add('Archie', '/thefunnies/archie/') add('ArcticCircle', '/thefunnies/arcticcircle/') #add('AskShagg', '/thefunnies/askshagg/') #add('BC', '/thefunnies/bc/') add('BabyBlues', '/thefunnies/babyblues/') #add('BallardStreet', '/thefunnies/ballardstreet/') #add('BarneyAndClyde', '/thefunnies/barneyandclyde/') add('BarneyGoogleAndSnuffySmith', '/thefunnies/barneygoogle/') add('BeetleBailey', '/thefunnies/beetlebailey/') add('Bizarro', '/thefunnies/bizarro/') add('BleekerTheRechargeableDog', '/thefunnies/bleekertherechargeabledog/') add('Blondie', '/thefunnies/blondie/') add('Boondocks', '/thefunnies/boondocks/') add('BrilliantMindofEdisonLee', '/thefunnies/brilliantmindofedisonlee/') #add('CafConLeche', '/thefunnies/cafeconleche/') #add('Candorville', '/thefunnies/candorville/') #add('Cathy', '/thefunnies/cathy/') #add('ChuckleBros', '/thefunnies/chucklebros/') add('Crankshaft', '/thefunnies/crankshaft/') #add('CuldeSac', '/thefunnies/culdesac/') add('Curtis', '/thefunnies/curtis/') #add('DaddysHome', '/thefunnies/daddyshome/') add('DeFlocked', '/thefunnies/deflocked/') add('DennistheMenace', '/thefunnies/dennisthemenace/') #add('DiamondLil', '/thefunnies/diamondlil/') #add('Dilbert', '/thefunnies/dilbert/') add('DinetteSet', '/thefunnies/thedinetteset/') #add('DogEatDoug', '/thefunnies/dogeatdoug/') #add('DogsofCKennel', '/thefunnies/dogsofckennel/') #add('Doonesbury', '/thefunnies/doonesbury/') add('Dustin', '/thefunnies/dustin/') add('FamilyCircus', '/thefunnies/familycircus/') #add('FloAndFriends', '/thefunnies/floandfriends/') #add('ForHeavensSake', '/thefunnies/forheavenssake/') #add('FortKnox', '/thefunnies/fortknox/') #add('FreeRange', '/thefunnies/freerange/') #add('Garfield', '/thefunnies/garfield/') #add('GetFuzzy', '/thefunnies/getfuzzy/') #add('Heathcliff', '/thefunnies/heathcliff/') #add('HerbandJamaal', '/thefunnies/herbandjamaal/') add('HiandLois', '/thefunnies/hiandlois/') #add('HomeAndAway', '/thefunnies/homeandaway/') add('JerryKingCartoons', '/thefunnies/humorcartoon/') #add('LittleDogLost', '/thefunnies/littledoglost/') #add('Luann', '/thefunnies/luann/') add('MallardFillmore', '/thefunnies/mallardfillmore/') add('Marvin', '/thefunnies/marvin/') add('MeaningofLila', '/thefunnies/meaningoflila/') #add('Momma', '/thefunnies/momma/') add('MotherGooseAndGrimm', '/thefunnies/mothergooseandgrimm/') add('Mutts', '/thefunnies/mutts/') #add('NestHeads', '/thefunnies/nestheads/') #add('NonSequitur', '/thefunnies/nonsequitur/') #add('OnaClaireDay', '/thefunnies/onaclaireday/') #add('OneBigHappy', '/thefunnies/onebighappy/') #add('Peanuts', '/thefunnies/peanuts/') #add('PearlsBeforeSwine', '/thefunnies/pearlsbeforeswine/') #add('Pickles', '/thefunnies/pickles/') #add('RedandRover', '/thefunnies/redandrover/') #add('ReplyAll', '/thefunnies/replyall/') add('RhymeswithOrange', '/thefunnies/rhymeswithorange/') #add('Rubes', '/thefunnies/rubes/') #add('Rugrats', '/thefunnies/rugrats/') #add('ScaryGary', '/thefunnies/scarygary/') #add('SpeedBump', '/thefunnies/speedbump/') #add('StrangeBrew', '/thefunnies/strangebrew/') add('TakeItFromTheTinkersons', '/thefunnies/takeitfromthetinkersons/') #add('TheBarn', '/thefunnies/thebarn/') add('TheLockhorns', '/thefunnies/thelockhorns/') #add('TheOtherCoast', '/thefunnies/theothercoast/') #add('ThinLines', '/thefunnies/thinlines/') add('TinasGroove', '/thefunnies/tinasgroove/') #add('WatchYourHead', '/thefunnies/watchyourhead/') #add('WeePals', '/thefunnies/weepals/') #add('WizardofId', '/thefunnies/wizardofid/') #add('WorkingitOut', '/thefunnies/workingitout/') #add('Wumo', '/thefunnies/wumo/') #add('ZackHill', '/thefunnies/zackhill/') add('Zits', '/thefunnies/zits/') dosage-2.12/dosagelib/plugins/b.py000066400000000000000000000337071227056367200171310ustar00rootroot00000000000000# -*- coding: iso-8859-1 -*- # Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs # Copyright (C) 2012-2014 Bastian Kleineidam from re import compile, escape from ..util import tagre from ..scraper import _BasicScraper from ..helpers import indirectStarter class BackwaterPlanet(_BasicScraper): url = 'http://www.backwaterplanet.com/current.htm' stripUrl = 'http://www.backwaterplanet.com/archive/bwp%s.htm' imageSearch = compile(r'') prevSearch = compile(r']+)', quote="")) prevSearch = compile(tagre("a", "href", r'([^"]+)') + '« Previous') help = 'Index format: yyyy/mm/' class BetweenFailures(_BasicScraper): description = u'Between Failures' url = 'http://betweenfailures.com/' rurl = escape(url) stripUrl = url + 'comics1/%s' imageSearch = compile(tagre("img", "src", r'(%swp-content/uploads/\d+/\d+/\d+-\d+-\d+[^"]+)' % rurl)) prevSearch = compile(tagre("a", "href", r'(%scomics1/[^"]+)' % rurl, after="previous")) help = 'Index format: stripname' class BigFatWhale(_BasicScraper): description = u'A weekly comic strip for those who are not dumb.' url = 'http://www.bigfatwhale.com/' stripUrl = url + 'archives/bfw_%s.htm' imageSearch = compile(tagre("img", "src", r'(archives/bfw_[^"]+|bfw_[^"]+)')) prevSearch = compile(r' HREF="(.+?)" TARGET="_top" TITLE="Previous Cartoon"') help = 'Index format: nnn' class BiggerThanCheeses(_BasicScraper): description = u'Bigger Than Cheeses - My webcomic will knife fight your webcomic' url = 'http://www.biggercheese.com/' stripUrl = url + 'index.php?comic=%s' firstStripUrl = stripUrl % '1' imageSearch = compile(r'src="(comics/.+?)" alt') prevSearch = compile(r'"(index.php\?comic=.+?)".+?_back') help = 'Index format: n (unpadded)' class BillyTheDunce(_BasicScraper): description = u"Billy the Dunce: A webcomic about some genius kids, some supernatural creatures, and one dumb kid who's stuck with them. Like Goonies, but with more Lovecraft." url = 'http://www.duncepress.com/' rurl = escape(url) stripUrl = url + '%s/' firstStripUrl = stripUrl % '2009/06/an-introduction-of-sorts' imageSearch = compile(tagre("img", "src", r'(%scomics/[^"]+)' % rurl)) prevSearch = compile(r'