pax_global_header00006660000000000000000000000064146144250160014515gustar00rootroot0000000000000052 comment=bc1753b00c5f342d28b1af73cd4f662b206c1f37 paste-3.10.1/000077500000000000000000000000001461442501600127135ustar00rootroot00000000000000paste-3.10.1/.coveragerc000066400000000000000000000002311461442501600150300ustar00rootroot00000000000000[run] branch = 1 # NOTE: cannot use package easily, without chdir (https://github.com/nedbat/coveragepy/issues/268). source = paste/,tests/ parallel = 1 paste-3.10.1/.github/000077500000000000000000000000001461442501600142535ustar00rootroot00000000000000paste-3.10.1/.github/workflows/000077500000000000000000000000001461442501600163105ustar00rootroot00000000000000paste-3.10.1/.github/workflows/tests.yaml000066400000000000000000000017401461442501600203400ustar00rootroot00000000000000name: tests on: - push - workflow_dispatch - pull_request jobs: build: runs-on: ubuntu-latest strategy: matrix: include: - python: "3.8" env: py38 - python: "3.9" env: py39 - python: "3.10" env: py310 - python: "3.11" env: py311 - python: "3.11" env: py311-namespace - python: "3.12" env: py312 - python: pypy-3.10 env: pypy3 name: ${{ matrix.env }} on Python ${{ matrix.python }} steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - run: pip install tox - run: tox env: TOXENV: ${{ matrix.env }} paste-3.10.1/.gitignore000066400000000000000000000001431461442501600147010ustar00rootroot00000000000000*.log *.pyc .coverage .idea .pytest_cache .tox .venv .vscode *.egg-info build dist docs/_build/ paste-3.10.1/.hgignore000066400000000000000000000001601461442501600145130ustar00rootroot00000000000000syntax: glob .project .pydevproject .settings *.pyc *.pyo *.log *.tmp *.egg-info dist/ build/ docs/_build .tox paste-3.10.1/.readthedocs.yaml000066400000000000000000000011011461442501600161330ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html # python: # install: # - requirements: docs/requirements.txt paste-3.10.1/LICENSE000077700000000000000000000000001461442501600170262docs/license.txtustar00rootroot00000000000000paste-3.10.1/MANIFEST.in000066400000000000000000000005231461442501600144510ustar00rootroot00000000000000recursive-include docs *.txt *.css *.js include docs/_templates/*.html include docs/conf.py include docs/test_server.ini include regen-docs include tox.ini include LICENSE recursive-exclude docs/_build/_sources * recursive-include docs/_build *.html recursive-include tests *.txt *.py *.cgi *.html recursive-include paste *.js *.jpg *.png paste-3.10.1/Makefile000066400000000000000000000015501461442501600143540ustar00rootroot00000000000000# simple Makefile for some common tasks .PHONY: clean test dist release pypi tagv paste-version := $(shell python setup.py --version) clean: find . -name "*.pyc" |xargs rm || true rm -r dist || true rm -r build || true rm -rf .tox || true rm -r cover .coverage || true rm -r .eggs || true rm -r paste.egg-info || true tagv: git tag -s -m ${paste-version} ${paste-version} git push origin master --tags cleanagain: find . -name "*.pyc" |xargs rm || true rm -r dist || true rm -r build || true rm -r .tox || true rm -r cover .coverage || true rm -r .eggs || true rm -r paste.egg-info || true test: tox --skip-missing-interpreters dist: test python3 setup.py sdist bdist_wheel release: clean test cleanagain tagv pypi gh pypi: python3 setup.py sdist bdist_wheel twine upload dist/* gh: gh release create ${paste-version} --generate-notes dist/* paste-3.10.1/README.rst000066400000000000000000000073331461442501600144100ustar00rootroot00000000000000 *Paste is in maintenance mode and recently moved from bitbucket to github. Patches are accepted to keep it on life support, but for the most part, please consider using other options.* **As of release 3.7.0 Paste no longer supports Python 2. If you are required to continue using Python 2 please pin an earlier version of Paste.** **With version 3.10.0 Paste development moves to the pasteorg GitHub organization and will be going deeper into maintenance mode unless more active maintainers step forward to take over. "Deeper" in this case means that releases will be much less frequent and patches will only be accepted for security issues or major problems. Current consumers of Paste should prepare to migrate away to more modern solutions.** Paste provides several pieces of "middleware" (or filters) that can be nested to build web applications. Each piece of middleware uses the WSGI (`PEP 333`_) interface, and should be compatible with other middleware based on those interfaces. .. _PEP 333: http://www.python.org/dev/peps/pep-0333/ * `Paste project at GitHub (source code, bug tracker) `_ * `Paste on the Python Cheeseshop (PyPI) `_ * `Paste on Read the Docs `_ See also: * `WebOb `_ Includes these features... Testing ------- * A fixture for testing WSGI applications conveniently and in-process, in ``paste.fixture`` * A fixture for testing command-line applications, also in ``paste.fixture`` * Check components for WSGI-compliance in ``paste.lint`` Dispatching ----------- * Chain and cascade WSGI applications (returning the first non-error response) in ``paste.cascade`` * Dispatch to several WSGI applications based on URL prefixes, in ``paste.urlmap`` * Allow applications to make subrequests and forward requests internally, in ``paste.recursive`` Web Application --------------- * Run CGI programs as WSGI applications in ``paste.cgiapp`` * Traverse files and load WSGI applications from ``.py`` files (or static files), in ``paste.urlparser`` * Serve static directories of files, also in ``paste.urlparser``; also in that module serving from Egg resources using ``pkg_resources``. Tools ----- * Catch HTTP-related exceptions (e.g., ``HTTPNotFound``) and turn them into proper responses in ``paste.httpexceptions`` * Several authentication techniques, including HTTP (Basic and Digest), signed cookies, and CAS single-signon, in the ``paste.auth`` package. * Create sessions in ``paste.session`` and ``paste.flup_session`` * Gzip responses in ``paste.gzip`` * A wide variety of routines for manipulating WSGI requests and producing responses, in ``paste.request``, ``paste.response`` and ``paste.wsgilib`` Debugging Filters ----------------- * Catch (optionally email) errors with extended tracebacks (using Zope/ZPT conventions) in ``paste.exceptions`` * Catch errors presenting traceback in ``paste.cgitb_catcher``. * Profile each request and append profiling information to the HTML, in ``paste.debug.profile`` * Capture ``print`` output and present it in the browser for debugging, in ``paste.debug.prints`` * Validate all HTML output from applications using the `WDG Validator `_, appending any errors or warnings to the page, in ``paste.debug.wdg_validator`` Other Tools ----------- * A file monitor to allow restarting the server when files have been updated (for automatic restarting when editing code) in ``paste.reloader`` * A class for generating and traversing URLs, and creating associated HTML code, in ``paste.url`` The official development repo is at https://github.com/pasteorg/paste. paste-3.10.1/docs/000077500000000000000000000000001461442501600136435ustar00rootroot00000000000000paste-3.10.1/docs/DeveloperGuidelines.txt000066400000000000000000000110001461442501600203320ustar00rootroot00000000000000++++++++++++++++++++++++++++ Python Paste Developer Guide ++++++++++++++++++++++++++++ Hi. Welcome to Paste. I hope you enjoy your stay here. I hope to bring together multiple efforts here, for Paste to support multiple frameworks and directions, while presenting a fairly integrated frontend to users. How to do that? That's an open question, and this code is in some ways an exploration. There's some basic principles: * Keep stuff decoupled. * Must be testable. Of course tested is even better than testable. * Use WSGI standards for communication between decoupled libraries. * When possible, use HTTP semantics for communicating between libraries (e.g., indicate cachability using the appropriate HTTP headers). * When possible, use WSGI as a wrapper around web-neutral libraries. For instance, the configuration is a simple library, but the WSGI middleware that puts the configuration in place is really really simple. If it could be used outside of a web context, then having both a library and middleware form is good. * Entry into frameworks should be easy, but exit should also be easy. Heterogeneous frameworks and applications are the ambition. But we have to get some messiness into Paste before we can try to resolve that messiness. * When all is said and done, users should be able to ignore much of what we've done and focus on writing their applications, and Stuff Just Works. Documentation is good; stuff that works without user intervention is better. Developer Info ============== Mostly, if there's a problem we can discuss it and work it out, no one is going to bite your head off for committing something. * Framework-like things should go in subpackages, or perhaps in separate distributions entirely (Paste WebKit and Wareweb were extracted for this reason). * Integrating external servers and frameworks is also interesting, but it's best to introduce that as a requirement instead of including the work here. Paste Script contains several wrappers for external projects (servers in particular). * Tests are good. We use pytest_, because it is simple. I want to use doctests too, but the infrastructure isn't really there now -- but please feel free to use those too. ``unittest`` is kind of annoying, and pytest is both more powerful and easier to write for. Tests should go in the ``tests/`` directory. ``paste.fixture`` contains some convenience functions for testing WSGI applications and middleware. Pay particular attention to ``TestApp``. .. _pytest: https://docs.pytest.org/en/latest/ * If you move something around that someone may be using, keep their imports working and introduce a warning, like:: def backward_compat_function(*args, **kw): import warnings # Deprecated on 2005 Mar 5 warnings.warn('Moved to foo.function', DeprecationWarning, 2) return foo.function(*args, **kw) * If something is really experimental, put it in your home directory, or make a branch in your home directory. You can make a home directory for yourself, in ``http://svn.w4py.org/home/username``. * Not everything in the repository or even in the trunk will necessarily go into the release. The release should contain stuff that is tested, documented, and useful. Each module or feature also needs a champion -- someone who will stand by the code, answer questions, etc. It doesn't have to be the original developer, but there has to be *someone*. So when a release is cut, if some modules don't fulfill that they may be left out. * Try to keep to the `Style Guidelines`_. But if you are bringing in outside work, don't stress out too much about it. Still, if you have a choice, follow that. Those guidelines are meant to represent conventional Python style guides, there's nothing out of the normal there. .. _Style Guidelines: StyleGuide.html * Write your docstrings in `restructured text `_. As time goes on, I want to rely on docstrings more for documentation, with shorter narrative documentation pointing into the documentation generated from docstrings. The generation is done with `Pudge `_. To try generating the documentation, this should work:: $ easy_install svn://lesscode.org/buildutils/trunk \ svn://lesscode.org/pudge/trunk $ cd Paste $ python setup.py pudge This will install Pudge and `buildutils `_, and then generate the documentation into ``Paste/docs/html/``. paste-3.10.1/docs/StyleGuide.txt000066400000000000000000000061321461442501600164640ustar00rootroot00000000000000+++++++++++++++++++ Paste Style Guide +++++++++++++++++++ Generally you should follow the recommendations in `PEP 8`_, the Python Style Guide. Some things to take particular note of: .. _PEP 8: http://www.python.org/peps/pep-0008.html * **No tabs**. Not anywhere. Always indent with 4 spaces. * I don't stress too much on line length. But try to break lines up by grouping with parenthesis instead of with backslashes (if you can). Do asserts like:: assert some_condition(a, b), ( "Some condition failed, %r isn't right!" % a) * But if you are having problems with line length, maybe you should just break the expression up into multiple statements. * Blank lines between methods, unless they are very small and closely bound to each other. * Don't use the form ``condition and trueValue or falseValue``. Break it out and use a variable. * I (Ian Bicking) am very picky about whitespace. There's one and only one right way to do it. Good examples:: short = 3 longerVar = 4 if x == 4: do stuff func(arg1='a', arg2='b') func((a + b)*10) **Bad** examples:: short =3 longerVar=4 if x==4: do stuff func(arg1 = 'a', arg2 = 'b') func(a,b) func( a, b ) [ 1, 2, 3 ] If the whitespace isn't right, it'll annoy me and I'll feel a need to fix it. Really, this whitespace stuff shouldn't be that controversial should it? Some particular points that I feel strongly about: * No space after a function name (bad: ``func (arg)``). * No space after or before a parenthesis (bad: ``func( arg )``). * Always one space after a comma (bad: ``func(a,b)``). * Use ``@@`` to mark something that is suboptimal, or where you have a concern that it's not right. Try to also date it and put your username there. * Docstrings are good. They should look like:: class AClass: """ doc string... """ Don't use single quotes (''') -- they tend to cause problems in Emacs. Don't bother trying make the string less vertically compact. * Comments go right before the thing they are commenting on. * Methods never, ever, ever start with capital letters. Generally only classes are capitalized. But definitely never methods. * Use ``cls`` to refer to a class. Use ``meta`` to refer to a metaclass (which also happens to be a class, but calling a metaclass ``cls`` will be confusing). * Use ``isinstance`` instead of comparing types. E.g.:: if isinstance(var, str): ... # Bad: if type(var) is StringType: ... * Never, ever use two leading underscores. This is annoyingly private. If name clashes are a concern, use explicit name mangling instead (e.g., ``_MyThing_blahblah``). This is essentially the same thing as double-underscore, only it's transparent where double underscore obscures. * Module names should be unique in the package. Subpackages shouldn't share module names with sibling or parent packages. Sadly this isn't possible for ``__init__.py``, but it's otherwise easy enough. * Module names should be all lower case, and probably have no underscores (smushedwords). paste-3.10.1/docs/_static/000077500000000000000000000000001461442501600152715ustar00rootroot00000000000000paste-3.10.1/docs/_static/default.css000066400000000000000000000135441461442501600174360ustar00rootroot00000000000000/* :Author: David Goodger, Ian Bicking :Contact: ianb@colorstudy.com :date: $Date: 2003/11/01 20:35:45 $ :version: $Revision: 1.3 $ :copyright: This stylesheet has been placed in the public domain. A modification of the default cascading style sheet (v.1.3) for the HTML output of Docutils. */ body { font-family: Arial, sans-serif; background-color: #fff; } em, i { /* Typically serif fonts have much nicer italics */ font-family: Times New Roman, Times, serif; } li { list-style-type: circle; } a.target { color: blue; } a.toc-backref { text-decoration: none; color: black; } a.toc-backref:hover { background-color: inherit; } a:hover { background-color: #ccc; } h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6:hover { background-color: inherit; } cite { font-style: normal; font-family: monospace; font-weight: bold; } dd { margin-bottom: 0.5em; } div.abstract { margin: 2em 5em; } div.abstract p.topic-title { font-weight: bold; text-align: center; } div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { background-color: #ccc; width: 40%; border: medium outset; padding: 3px; float: right } div.attention p.admonition-title, div.caution p.admonition-title, div.danger p.admonition-title, div.error p.admonition-title, div.warning p.admonition-title { color: #c00; font-weight: bold; font-family: sans-serif; text-align: center; background-color: #999; display: block; margin: 0; } div.hint p.admonition-title, div.important p.admonition-title, div.note p.admonition-title, div.tip p.admonition-title { font-weight: bold; font-family: sans-serif; text-align: center; background-color: #999; display: block; margin: 0; } div.dedication { margin: 2em 5em; text-align: center; font-style: italic; } div.dedication p.topic-title { font-weight: bold; font-style: normal; } div.figure { margin-left: 2em; } div.footer, div.header { font-size: smaller; } div.system-messages { margin: 5em; } div.system-messages h1 { color: red; } div.system-message { border: medium outset; padding: 1em; } div.system-message p.system-message-title { color: red; font-weight: bold; } div.topic { margin: 2em; } h1, h2, h3, h4, h5, h6 { font-family: Helvetica, Arial, sans-serif; border: thin solid black; /* This makes the borders rounded on Mozilla, which pleases me */ -moz-border-radius: 8px; padding: 4px; } h1 { background-color: #449; color: #fff; border: medium solid black; } h1 a.toc-backref, h2 a.toc-backref { color: #fff; } h2 { background-color: #666; color: #fff; border: medium solid black; } h3, h4, h5, h6 { background-color: #ccc; color: #000; } h3 a.toc-backref, h4 a.toc-backref, h5 a.toc-backref, h6 a.toc-backref { color: #000; } h1.title { text-align: center; background-color: #449; color: #fff; border: thick solid black; -moz-border-radius: 20px; } h2.subtitle { text-align: center; } hr { width: 75%; } ol.simple, ul.simple { margin-bottom: 1em; } ol.arabic { list-style: decimal; } ol.loweralpha { list-style: lower-alpha; } ol.upperalpha { list-style: upper-alpha; } ol.lowerroman { list-style: lower-roman; } ol.upperroman { list-style: upper-roman; } p.caption { font-style: italic; } p.credits { font-style: italic; font-size: smaller; } p.first { margin-top: 0; } p.label { white-space: nowrap; } p.topic-title { font-weight: bold; } pre.address { margin-bottom: 0; margin-top: 0; font-family: serif; font-size: 100%; } pre.line-block { font-family: serif; font-size: 100%; } pre.literal-block, pre.doctest-block { margin-left: 2em; margin-right: 2em; background-color: #eee; border: thin black solid; padding: 5px; } span.classifier { font-family: sans-serif; font-style: oblique; } span.classifier-delimiter { font-family: sans-serif; font-weight: bold; } span.interpreted { font-family: sans-serif; } span.option-argument { font-style: italic; } span.pre { white-space: pre; } span.problematic { color: red; } table { margin-top: 0.5em; margin-bottom: 0.5em; } table.citation { border-left: solid thin gray; padding-left: 0.5ex } table.docinfo { margin: 2em 4em; } table.footnote { border-left: solid thin black; padding-left: 0.5ex; } td, th { padding-left: 0.5em; padding-right: 0.5em; vertical-align: top; } td > p:first-child, th > p:first-child { margin-top: 0em; } th.docinfo-name, th.field-name { font-weight: bold; text-align: left; white-space: nowrap; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { font-size: 100%; } code, tt { color: #006; } ul.auto-toc { list-style-type: none; } /***************************************** * Doctest embedded examples *****************************************/ span.doctest-url { background-color: #eee; border-top: 2px outset #666; border-left: 2px outset #666; border-right: 2px outset #666; padding: 0.25em; } div.doctest-example { border: outset 5px #666; background-color: #eee; font-family: default; padding: 0.5em; } div.doctest-example h1 { background-color: inherit; border: none; color: inherit; font-family: default; } div.doctest-example tt { color: inherit; } div.doctest-status { background-color: #060; color: #fff; } span.doctest-header { background-color: #ccc; font-family: monospace; } pre.doctest-errors { border: none; background-color: #333; color: #600; } div.source-code { background-color: #000; border: inset #999 3px; overflow: auto; } pre.source-code { background-color: #000; border: inset #999 3px; overflow: auto; font-family: monospace; color: #fff; } span.source-filename { background-color: #000; border-top: 2px outset #999; border-left: 2px outset #999; border-right: 2px outset #999; padding: 0.25em; color: #fff } paste-3.10.1/docs/_static/paste.css000066400000000000000000000003021461442501600171120ustar00rootroot00000000000000a.invisible-link { color: #fff; text-decoration: none; } a.invisible-link:visited { color: #fff; text-decoration: none; } a.invisible:link { color: #fff; text-decoration: none; } paste-3.10.1/docs/community/000077500000000000000000000000001461442501600156675ustar00rootroot00000000000000paste-3.10.1/docs/community/index.txt000066400000000000000000000010731461442501600175400ustar00rootroot00000000000000Community ========= **Much of the Paste community has moved on to other things. These links are left for reference, but do not expect to find much activity there.** Much of the communication goes on in the `mailing lists `_; see that page for information on the lists. For live IRC discussion, try the ``#pythonpaste`` channel on `Freenode `_. If you find bugs in the code or documentation, please `submit a ticket `_. You can also `view tickets `_. paste-3.10.1/docs/community/mailing-list.txt000066400000000000000000000013671461442501600210300ustar00rootroot00000000000000Mailing Lists ============= **Much of the Paste community has moved on to other things. These links are left for reference, but do not expect to find much activity there.** General discussion and questions should go to: `paste-users@googlegroups.org `_: New posts are `on Google Groups `_ `old posts are in their own archive `_ More abstract discussion of Python web programming should go to: `web-sig@python.org `_: `Subscribe `__, `Archives `__ paste-3.10.1/docs/community/repository.txt000066400000000000000000000002201461442501600206410ustar00rootroot00000000000000Repository ========== Paste is kept in a Git repository at http://github.com/pasteorg/paste Go there to make pull requests and report issues. paste-3.10.1/docs/conf.py000066400000000000000000000074571461442501600151570ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Paste documentation build configuration file, created by # sphinx-quickstart on Tue Apr 22 22:08:49 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. # If your extensions are in another directory, add it here. #import sys #sys.path.append('some/directory') # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.txt' # The master toctree document. master_doc = 'index' # General substitutions. project = 'Paste' copyright = '2008, Ian Bicking' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '' # The full version, including alpha/beta/rc tags. release = '' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. unused_docs = ['include/contact.txt', 'include/reference_header.txt'] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- html_theme = 'nature' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Content template for the index page. #html_index = '' # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # Output file base name for HTML help builder. htmlhelp_basename = 'Pastedoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). #latex_documents = [] # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True paste-3.10.1/docs/developer-features.txt000066400000000000000000000105101461442501600202020ustar00rootroot00000000000000Features ======== Testing ------- * A fixture for testing WSGI applications conveniently and in-process, in :class:`paste.fixture.TestApp` * A fixture for testing command-line applications, also in :class:`paste.fixture.TestFileEnvironment` * Check components for WSGI-compliance in :mod:`paste.lint` * Check filesystem changes, with :mod:`paste.debug.fsdiff` Server ------ * A threaded HTTP server in :mod:`paste.httpserver` * A tool for seeing and killing errant threads in the HTTP server, in :mod:`paste.debug.watchthreads` Dispatching ----------- * Chain and cascade WSGI applications (returning the first non-error response) in :mod:`paste.cascade` * Dispatch to several WSGI applications based on URL prefixes, in :mod:`paste.urlmap` * Allow applications to make subrequests and forward requests internally, in :mod:`paste.recursive` * Redirect error pages (e.g., 404 Not Found) to custom error pages, in :mod:`paste.errordocument`. Web Application --------------- * Easily deal with incoming requests and sending a response in :mod:`paste.wsgiwrappers` * Work directly with the WSGI environment in :mod:`paste.request` * Run CGI programs as WSGI applications in :mod:`paste.cgiapp` * Traverse files and load WSGI applications from ``.py`` files (or static files), in :mod:`paste.urlparser` * Serve static directories of files, also in :mod:`paste.urlparser`; also serve using the Setuptools ``pkg_resources`` resource API. * Proxy to other servers, treating external HTTP servers as WSGI applications, in :mod:`paste.proxy`. * Serve files (with support for ``If-Modified-Since``, etc) in :mod:`paste.fileapp` Tools ----- * Catch HTTP-related exceptions (e.g., ``HTTPNotFound``) and turn them into proper responses in :mod:`paste.httpexceptions` * Manage HTTP header fields with :mod:`paste.httpheaders` * Handle authentication/identification of requests in :mod:`paste.auth` * Create sessions in :mod:`paste.session` and :mod:`paste.flup_session` * Gzip responses in :mod:`paste.gzipper` * A wide variety of routines for manipulating WSGI requests and producing responses, in :mod:`paste.request`, :mod:`paste.response` and :mod:`paste.wsgilib`. * Create Apache-style logs in :mod:`paste.translogger` * Handy request and response wrappers in :mod:`paste.wsgiwrappers` * Handling of request-local module globals sanely in :mod:`paste.registry` Authentication -------------- * Authentication using cookies in :mod:`paste.auth.cookie` and :mod:`paste.auth.auth_tkt`; login form in :mod:`paste.auth.form` * Authentication using `OpenID `_ in :mod:`paste.auth.open_id`, using `CAS `_ in :mod:`paste.auth.cas` * HTTP authentication in :mod:`paste.auth.basic` and :mod:`paste.auth.digest` * Dispatch to different authentication methods based on User-Agent, in :mod:`paste.auth.multi` * Grant roles based on IP addresses, in :mod:`paste.auth.grantip` Debugging Filters ----------------- * Catch (optionally email) errors with extended tracebacks (using Zope/ZPT conventions) in :mod:`paste.exceptions` * During debugging, show tracebacks with information about each stack frame, including an interactive prompt that runs in the individual stack frames, in :mod:`paste.evalexception`. * Catch errors presenting traceback in ``paste.cgitb_catcher``. * Profile each request and append profiling information to the HTML, in :mod:`paste.debug.profile` * Capture ``print`` output and present it in the browser for debugging, in :mod:`paste.debug.prints` * Validate all HTML output from applications using the `WDG Validator `_, appending any errors or warnings to the page, in :mod:`paste.debug.wdg_validator` Other Tools ----------- * A file monitor to allow restarting the server when files have been updated (for automatic restarting when editing code) in :mod:`paste.reloader` * A class for generating and traversing URLs, and creating associated HTML code, in :mod:`paste.url` * A small templating language (for internal use) in :mod:`paste.util.template` * A class to help with loops in templates, in :mod:`paste.util.looper` * Import modules and objects given a string, in :mod:`paste.util.import_string` * Ordered dictionary that can have multiple values with the same key, in :mod:`paste.util.multidict` paste-3.10.1/docs/do-it-yourself-framework.txt000066400000000000000000000452461461442501600212740ustar00rootroot00000000000000A Do-It-Yourself Framework ++++++++++++++++++++++++++ :author: Ian Bicking :revision: $Rev$ :date: $LastChangedDate$ This tutorial has been translated `into Portuguese `_. A newer version of this article is available `using WebOb `_. .. contents:: .. comments: Explain SCRIPT_NAME/PATH_INFO better Introduction and Audience ========================= This short tutorial is meant to teach you a little about WSGI, and as an example a bit about the architecture that Paste has enabled and encourages. This isn't an introduction to all the parts of Paste -- in fact, we'll only use a few, and explain each part. This isn't to encourage everyone to go off and make their own framework (though honestly I wouldn't mind). The goal is that when you have finished reading this you feel more comfortable with some of the frameworks built using this architecture, and a little more secure that you will understand the internals if you look under the hood. What is WSGI? ============= At its simplest WSGI is an interface between web servers and web applications. We'll explain the mechanics of WSGI below, but a higher level view is to say that WSGI lets code pass around web requests in a fairly formal way. But there's more! WSGI is more than just HTTP. It might seem like it is just *barely* more than HTTP, but that little bit is important: * You pass around a CGI-like environment, which means data like ``REMOTE_USER`` (the logged-in username) can be securely passed about. * A CGI-like environment can be passed around with more context -- specifically instead of just one path you two: ``SCRIPT_NAME`` (how we got here) and ``PATH_INFO`` (what we have left). * You can -- and often should -- put your own extensions into the WSGI environment. This allows for callbacks, extra information, arbitrary Python objects, or whatever you want. These are things you can't put in custom HTTP headers. This means that WSGI can be used not just between a web server an an application, but can be used at all levels for communication. This allows web applications to become more like libraries -- well encapsulated and reusable, but still with rich reusable functionality. Writing a WSGI Application ========================== The first part is about how to use `WSGI `_ at its most basic. You can read the spec, but I'll do a very brief summary: * You will be writing a *WSGI application*. That's an object that responds to requests. An application is just a callable object (like a function) that takes two arguments: ``environ`` and ``start_response``. * The environment looks a lot like a CGI environment, with keys like ``REQUEST_METHOD``, ``HTTP_HOST``, etc. * The environment also has some special keys like ``wsgi.input`` (the input stream, like the body of a POST request). * ``start_response`` is a function that starts the response -- you give the status and headers here. * Lastly the application returns an iterator with the body response (commonly this is just a list of strings, or just a list containing one string that is the entire body.) So, here's a simple application:: def app(environ, start_response): start_response('200 OK', [('content-type', 'text/html')]) return ['Hello world!'] Well... that's unsatisfying. Sure, you can imagine what it does, but you can't exactly point your web browser at it. There's other cleaner ways to do this, but this tutorial isn't about *clean* it's about *easy-to-understand*. So just add this to the bottom of your file:: if __name__ == '__main__': from paste import httpserver httpserver.serve(app, host='127.0.0.1', port='8080') Now visit http://localhost:8080 and you should see your new app. If you want to understand how a WSGI server works, I'd recommend looking at the `CGI WSGI server `_ in the WSGI spec. An Interactive App ------------------ That last app wasn't very interesting. Let's at least make it interactive. To do that we'll give a form, and then parse the form fields:: from paste.request import parse_formvars def app(environ, start_response): fields = parse_formvars(environ) if environ['REQUEST_METHOD'] == 'POST': start_response('200 OK', [('content-type', 'text/html')]) return ['Hello, ', fields['name'], '!'] else: start_response('200 OK', [('content-type', 'text/html')]) return ['
Name:
'] The ``parse_formvars`` function just takes the WSGI environment and turns that into a MultiDict which may contain ``FieldStorage`` instances. Now For a Framework =================== Now, this probably feels a bit crude. After all, we're testing for things like REQUEST_METHOD to handle more than one thing, and it's unclear how you can have more than one page. We want to build a framework, which is just a kind of generic application. In this tutorial we'll implement an *object publisher*, which is something you may have seen in Zope, Quixote, or CherryPy. Object Publishing ----------------- In a typical Python object publisher you translate ``/`` to ``.``. So ``/articles/view?id=5`` turns into ``root.articles.view(id=5)``. We have to start with some root object, of course, which we'll pass in... :: class ObjectPublisher: def __init__(self, root): self.root = root def __call__(self, environ, start_response): ... app = ObjectPublisher(my_root_object) We override ``__call__`` to make instances of ``ObjectPublisher`` callable objects, just like a function, and just like WSGI applications. Now all we have to do is translate that ``environ`` into the thing we are publishing, then call that thing, then turn the response into what WSGI wants. The Path -------- WSGI puts the requested path into two variables: ``SCRIPT_NAME`` and ``PATH_INFO``. ``SCRIPT_NAME`` is everything that was used up *getting here*. ``PATH_INFO`` is everything left over -- it's the part the framework should be using to find the object. If you put the two back together, you get the full path used to get to where we are right now; this is very useful for generating correct URLs, and we'll make sure we preserve this. So here's how we might implement ``__call__``:: def __call__(self, environ, start_response): fields = parse_formvars(environ) obj = self.find_object(self.root, environ) response_body = obj(**fields.mixed()) start_response('200 OK', [('content-type', 'text/html')]) return [response_body] def find_object(self, obj, environ): path_info = environ.get('PATH_INFO', '') if not path_info or path_info == '/': # We've arrived! return obj # PATH_INFO always starts with a /, so we'll get rid of it: path_info = path_info.lstrip('/') # Then split the path into the "next" chunk, and everything # after it ("rest"): parts = path_info.split('/', 1) next = parts[0] if len(parts) == 1: rest = '' else: rest = '/' + parts[1] # Hide private methods/attributes: assert not next.startswith('_') # Now we get the attribute; getattr(a, 'b') is equivalent # to a.b... next_obj = getattr(obj, next) # Now fix up SCRIPT_NAME and PATH_INFO... environ['SCRIPT_NAME'] += '/' + next environ['PATH_INFO'] = rest # and now parse the remaining part of the URL... return self.find_object(next_obj, environ) And that's it, we've got a framework. Taking It For a Ride -------------------- Now, let's write a little application. Put that ``ObjectPublisher`` class into a module ``objectpub``:: from objectpub import ObjectPublisher class Root: # The "index" method: def __call__(self): return '''
Name:
''' def welcome(self, name): return 'Hello %s!' % name app = ObjectPublisher(Root()) if __name__ == '__main__': from paste import httpserver httpserver.serve(app, host='127.0.0.1', port='8080') Alright, done! Oh, wait. There's still some big missing features, like how do you set headers? And instead of giving ``404 Not Found`` responses in some places, you'll just get an attribute error. We'll fix those up in a later installment... Give Me More! ------------- You'll notice some things are missing here. Most specifically, there's no way to set the output headers, and the information on the request is a little slim. :: # This is just a dictionary-like object that has case- # insensitive keys: from paste.response import HeaderDict class Request: def __init__(self, environ): self.environ = environ self.fields = parse_formvars(environ) class Response: def __init__(self): self.headers = HeaderDict( {'content-type': 'text/html'}) Now I'll teach you a little trick. We don't want to change the signature of the methods. But we can't put the request and response objects in normal global variables, because we want to be thread-friendly, and all threads see the same global variables (even if they are processing different requests). But Python 2.4 introduced a concept of "thread-local values". That's a value that just this one thread can see. This is in the `threading.local `_ object. When you create an instance of ``local`` any attributes you set on that object can only be seen by the thread you set them in. So we'll attach the request and response objects here. So, let's remind ourselves of what the ``__call__`` function looked like:: class ObjectPublisher: ... def __call__(self, environ, start_response): fields = parse_formvars(environ) obj = self.find_object(self.root, environ) response_body = obj(**fields.mixed()) start_response('200 OK', [('content-type', 'text/html')]) return [response_body] Lets's update that:: import threading webinfo = threading.local() class ObjectPublisher: ... def __call__(self, environ, start_response): webinfo.request = Request(environ) webinfo.response = Response() obj = self.find_object(self.root, environ) response_body = obj(**dict(webinfo.request.fields)) start_response('200 OK', webinfo.response.headers.items()) return [response_body] Now in our method we might do:: class Root: def rss(self): webinfo.response.headers['content-type'] = 'text/xml' ... If we were being fancier we would do things like handle `cookies `_ in these objects. But we aren't going to do that now. You have a framework, be happy! WSGI Middleware =============== `Middleware `_ is where people get a little intimidated by WSGI and Paste. What is middleware? Middleware is software that serves as an intermediary. So lets write one. We'll write an authentication middleware, so that you can keep your greeting from being seen by just anyone. Let's use HTTP authentication, which also can mystify people a bit. HTTP authentication is fairly simple: * When authentication is requires, we give a ``401 Authentication Required`` status with a ``WWW-Authenticate: Basic realm="This Realm"`` header * The client then sends back a header ``Authorization: Basic encoded_info`` * The "encoded_info" is a base-64 encoded version of ``username:password`` So how does this work? Well, we're writing "middleware", which means we'll typically pass the request on to another application. We could change the request, or change the response, but in this case sometimes we *won't* pass the request on (like, when we need to give that 401 response). To give an example of a really really simple middleware, here's one that capitalizes the response:: class Capitalizer: # We generally pass in the application to be wrapped to # the middleware constructor: def __init__(self, wrap_app): self.wrap_app = wrap_app def __call__(self, environ, start_response): # We call the application we are wrapping with the # same arguments we get... response_iter = self.wrap_app(environ, start_response) # then change the response... response_string = ''.join(response_iter) return [response_string.upper()] Techically this isn't quite right, because there there's two ways to return the response body, but we're skimming bits. `paste.wsgilib.intercept_output `_ is a somewhat more thorough implementation of this. .. note:: This, like a lot of parts of this (now fairly old) tutorial is better, more thorough, and easier using `WebOb `_. This particular example looks like:: from webob import Request class Capitalizer: def __init__(self, app): self.app = app def __call__(self, environ, start_response): req = Request(environ) resp = req.get_response(self.app) resp.body = resp.body.upper() return resp(environ, start_response) So here's some code that does something useful, authentication:: class AuthMiddleware: def __init__(self, wrap_app): self.wrap_app = wrap_app def __call__(self, environ, start_response): if not self.authorized(environ.get('HTTP_AUTHORIZATION')): # Essentially self.auth_required is a WSGI application # that only knows how to respond with 401... return self.auth_required(environ, start_response) # But if everything is okay, then pass everything through # to the application we are wrapping... return self.wrap_app(environ, start_response) def authorized(self, auth_header): if not auth_header: # If they didn't give a header, they better login... return False # .split(None, 1) means split in two parts on whitespace: auth_type, encoded_info = auth_header.split(None, 1) assert auth_type.lower() == 'basic' unencoded_info = encoded_info.decode('base64') username, password = unencoded_info.split(':', 1) return self.check_password(username, password) def check_password(self, username, password): # Not very high security authentication... return username == password def auth_required(self, environ, start_response): start_response('401 Authentication Required', [('Content-type', 'text/html'), ('WWW-Authenticate', 'Basic realm="this realm"')]) return [""" Authentication Required

Authentication Required

If you can't get in, then stay out. """] .. note:: Again, here's the same thing with WebOb:: from webob import Request, Response class AuthMiddleware: def __init__(self, app): self.app = app def __call__(self, environ, start_response): req = Request(environ) if not self.authorized(req.headers['authorization']): resp = self.auth_required(req) else: resp = self.app return resp(environ, start_response) def authorized(self, header): if not header: return False auth_type, encoded = header.split(None, 1) if not auth_type.lower() == 'basic': return False username, password = encoded.decode('base64').split(':', 1) return self.check_password(username, password) def check_password(self, username, password): return username == password def auth_required(self, req): return Response(status=401, headers={'WWW-Authenticate': 'Basic realm="this realm"'}, body="""\ Authentication Required

Authentication Required

If you can't get in, then stay out. """) So, how do we use this? :: app = ObjectPublisher(Root()) wrapped_app = AuthMiddleware(app) if __name__ == '__main__': from paste import httpserver httpserver.serve(wrapped_app, host='127.0.0.1', port='8080') Now you have middleware! Hurrah! Give Me More Middleware! ------------------------ It's even easier to use other people's middleware than to make your own, because then you don't have to program. If you've been following along, you've probably encountered a few exceptions, and have to look at the console to see the exception reports. Let's make that a little easier, and show the exceptions in the browser... :: app = ObjectPublisher(Root()) wrapped_app = AuthMiddleware(app) from paste.exceptions.errormiddleware import ErrorMiddleware exc_wrapped_app = ErrorMiddleware(wrapped_app) Easy! But let's make it *more* fancy... :: app = ObjectPublisher(Root()) wrapped_app = AuthMiddleware(app) from paste.evalexception import EvalException exc_wrapped_app = EvalException(wrapped_app) So go make an error now. And hit the little +'s. And type stuff in to the boxes. Conclusion ========== Now that you've created your framework and application (I'm sure it's much nicer than the one I've given so far). You might keep writing it (many people have so far), but even if you don't you should be able to recognize these components in other frameworks now, and you'll have a better understanding how they probably work under the covers. Also check out the version of this tutorial written `using WebOb `_. That tutorial includes things like **testing** and **pattern-matching dispatch** (instead of object publishing). paste-3.10.1/docs/download/000077500000000000000000000000001461442501600154525ustar00rootroot00000000000000paste-3.10.1/docs/download/index.txt000066400000000000000000000025031461442501600173220ustar00rootroot00000000000000Downloads ========= Each of these packages is available in several formats. The source distribution is a complete set of documentation, tests, and the source files themselves. There are also two "Egg" files: these are files `easy_install `_ can install directly into your ``site-packages/`` directory, and are Python-version specific. The download files for the latest version are always located on the Cheese Shop pages (listed below). * `Paste `_ * `Paste Script `_ * `Paste Deploy `_ * `Paste WebKit `_ * `Wareweb `_ (deprecated, use `Pylons `_ instead) All the packages are available in some repositories: * https://github.com/pasteorg/paste * https://github.com/pasteorg/pastescript * https://github.com/Pylons/pastedeploy/ * https://github.com/Pylons/webob * ... and others ..note:: Several of these repositories and projects are fairly stale and insufficiently maintained. Work is in progress to get them on life support, but it is unlikely they will ever be active projects again. WebOb is still actively maintained. paste-3.10.1/docs/future.txt000066400000000000000000000061601461442501600157210ustar00rootroot00000000000000The Future Of Paste =================== Introduction ------------ Paste has been under development for a while, and has lots of code in it. Too much code! The code is largely decoupled except for some core functions shared by many parts of the code. Those core functions are largely replaced in `WebOb `_, and replaced with better implementations. The future of these pieces is to split them into independent packages, and refactor the internal Paste dependencies to rely instead on WebOb. Already Extracted ----------------- paste.fixture: WebTest ScriptTest paste.lint: wsgiref.validate paste.exceptions and paste.evalexception: WebError paste.util.template: Tempita To Be Separated --------------- paste.httpserver and paste.debug.watchthreads: Not sure what to call this. paste.cascade and paste.errordocuments: Not sure; Ben has an implementation of errordocuments in ``pylons.middleware.StatusCodeRedirect`` paste.urlmap, paste.deploy.config.PrefixMiddleware: In... some routing thing? Together with the previous package? paste.proxy: WSGIProxy (needs lots of cleanup though) paste.fileapp, paste.urlparser.StaticURLParser, paste.urlparser.PkgResourcesParser: In some new file-serving package. paste.cgiapp, wphp.fcgi_app: Some proxyish app... maybe WSGIProxy? paste.translogger, paste.debug.prints, paste.util.threadedprint, wsgifilter.proxyapp.DebugHeaders: Some... other place. Something loggy. paste.registry, paste.config: Not sure. Alberto Valverde expressed interest in splitting out paste.registry. paste.cgitb_catcher: Move to WebError? Not sure if it matters. For some reason people use this, though. To Deprecate ------------ (In that, I won't extract these anywhere; I'm not going to do any big deletes anytime soon, though) paste.recursive Better to do it manually (with webob.Request.get_response) paste.wsgiwrappers, paste.request, paste.response, paste.wsgilib, paste.httpheaders, paste.httpexceptions: All the functionality is already in WebOb. paste.urlparser.URLParser: Really this is tied to paste.webkit more than anything. paste.auth.*: Well, these all need to be refactored, and replacements exist in AuthKit and repoze.who. Some pieces might still have utility. paste.debug.profile: I think repoze.profile supersedes this. paste.debug.wdg_validator: It could get reimplemented with more options for validators, but I'm not really that interested at the moment. The code is nothing fancy. paste.transaction: More general in repoze.tm paste.url: No one uses this Undecided --------- paste.debug.fsdiff: Maybe ScriptTest? paste.session: It's an okay naive session system. But maybe Beaker makes it irrelevant (Beaker does seem slightly more complex to setup). But then, this can just live here indefinitely. paste.gzipper: I'm a little uncomfortable with this in concept. It's largely in WebOb right now, but not as middleware. paste.reloader: Maybe this should be moved to paste.script (i.e., paster serve) paste.debug.debugapp, paste.script.testapp: Alongside other debugging tools, I guess paste.progress: Not sure this works. paste-3.10.1/docs/include/000077500000000000000000000000001461442501600152665ustar00rootroot00000000000000paste-3.10.1/docs/include/contact.txt000066400000000000000000000005221461442501600174610ustar00rootroot00000000000000If you have questions about this document, please contact the `paste mailing list `_ or try IRC (``#pythonpaste`` on freenode.net). If there's something that confused you and you want to give feedback, please `submit an issue `_. paste-3.10.1/docs/include/reference_header.txt000066400000000000000000000001021461442501600212660ustar00rootroot00000000000000Paste Reference Document @@@@@@@@@@@@@@@@@@@@@@@@ .. contents:: paste-3.10.1/docs/index.txt000066400000000000000000000045441461442501600155220ustar00rootroot00000000000000Python Paste ============ This is a new copy of the documentation for Paste. The project is now being maintained on limited life-support from https://github.com/pasteorg/paste **Please consider other options.** **With version 3.10.0 Paste development moves to the pasteorg GitHub organization and will be going deeper into maintenance mode unless more active maintainers step forward to take over. "Deeper" in this case means that releases will be much less frequent and patches will only be accepted for security issues or major problems. Current consumers of Paste should prepare to migrate away to more modern solutions.** Many of the links you will find in these docs will be out of date. If you know of a better destination for a link please submit an issue or pull request at the GitHub repository above. Contents: .. toctree:: :maxdepth: 1 news future testing-applications url-parsing-with-wsgi do-it-yourself-framework paste-httpserver-threadpool developer-features DeveloperGuidelines StyleGuide paste-httpserver-threadpool testing-applications url-parsing-with-wsgi community/index.txt community/mailing-list.txt community/repository.txt download/index.txt license Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` .. comment: I want to put these somewhere sometime, but no place for them now... Python Paste -- 50% tastier than Elmer's! Paste: making the web sticky. Fix broken websites by applying Paste liberally. Paste: paper over your inconsistencies. Paste: a soft mixture of malleable consistency. Paste: a tasty mixture to be spread on bread or crackers. Paste: glue that won't hurt you if you eat it. Python Paste: the web extruded into the form of a snake. Paste: the vinegar eel. Paste: you bring the cut. Paste: a doughy substance from which to make metaphorical web cupcakes. LAMP? LAMPP! Putting the P in Wep 2.0! Frankenweb crush tiny humans! DSL? DSF! Paste: Comfort for the framework-scarred Other Components ================ * `Paste Deploy <./deploy/>`_ * `Paste Script <./script/>`_ * `WebOb `_ * `WSGI specification (PEP 333) `_ License ======= Paste is distributed under the `MIT license `_. paste-3.10.1/docs/license.txt000066400000000000000000000020651461442501600160310ustar00rootroot00000000000000Copyright (c) 2006-2007 Ian Bicking and Contributors 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. paste-3.10.1/docs/modules/000077500000000000000000000000001461442501600153135ustar00rootroot00000000000000paste-3.10.1/docs/modules/auth.auth_tkt.txt000066400000000000000000000004701461442501600206400ustar00rootroot00000000000000:mod:`paste.auth.auth_tkt` -- auth_tkt cookie parsing ===================================================== .. automodule:: paste.auth.auth_tkt Module Contents --------------- .. autoclass:: AuthTKTMiddleware .. autofunction:: make_auth_tkt_middleware .. autoclass:: AuthTicket .. autoexception:: BadTicket paste-3.10.1/docs/modules/auth.basic.txt000066400000000000000000000004211461442501600200720ustar00rootroot00000000000000:mod:`paste.auth.basic` -- Basic HTTP authentication ==================================================== .. automodule:: paste.auth.basic Module Contents --------------- .. autoclass:: AuthBasicAuthenticator .. autoclass:: AuthBasicHandler .. autofunction:: make_basic paste-3.10.1/docs/modules/auth.cas.txt000066400000000000000000000002721461442501600175630ustar00rootroot00000000000000:mod:`paste.auth.cas` -- CAS authentication =========================================== .. automodule:: paste.auth.cas Module Contents --------------- .. autoclass:: AuthCASHandler paste-3.10.1/docs/modules/auth.cookie.txt000066400000000000000000000004721461442501600202700ustar00rootroot00000000000000:mod:`paste.auth.cookie` -- Cookie-based authentication ======================================================= .. automodule:: paste.auth.cookie Module Contents --------------- .. autoclass:: AuthCookieSigner .. autoclass:: AuthCookieHandler .. autoclass:: AuthCookieEnviron .. autofunction:: make_auth_cookie paste-3.10.1/docs/modules/auth.digest.txt000066400000000000000000000004511461442501600202730ustar00rootroot00000000000000:mod:`paste.auth.digest` -- HTTP Digest login ============================================= .. automodule:: paste.auth.digest Module Contents --------------- .. autoclass:: AuthDigestAuthenticator .. autoclass:: AuthDigestHandler .. autofunction:: digest_password .. autofunction:: make_digest paste-3.10.1/docs/modules/auth.form.txt000066400000000000000000000003621461442501600177600ustar00rootroot00000000000000:mod:`paste.auth.form` -- HTML form/cookie authentication ========================================================= .. automodule:: paste.auth.form Module Contents --------------- .. autoclass:: AuthFormHandler .. autofunction:: make_form paste-3.10.1/docs/modules/auth.grantip.txt000066400000000000000000000004201461442501600204540ustar00rootroot00000000000000:mod:`paste.auth.grantip` -- Set user and groups based on IP address ==================================================================== .. automodule:: paste.auth.grantip Module Contents --------------- .. autoclass:: GrantIPMiddleware .. autofunction:: make_grantip paste-3.10.1/docs/modules/auth.multi.txt000066400000000000000000000003561461442501600201520ustar00rootroot00000000000000:mod:`paste.auth.multi` -- Authentication via one of multiple methods ===================================================================== .. automodule:: paste.auth.multi Module Contents --------------- .. autoclass:: MultiHandler paste-3.10.1/docs/modules/cascade.txt000066400000000000000000000004211461442501600174340ustar00rootroot00000000000000:mod:`paste.cascade` -- send requests to multiple applications until success ============================================================================ .. automodule:: paste.cascade Module Contents --------------- .. autoclass:: Cascade .. autofunction:: make_cascade paste-3.10.1/docs/modules/cgiapp.txt000066400000000000000000000004311461442501600173150ustar00rootroot00000000000000:mod:`paste.cgiapp` -- run CGI scripts as WSGI applications =========================================================== .. automodule:: paste.cgiapp Module Contents --------------- .. autoclass:: CGIApplication .. autoexception:: CGIError .. autofunction:: make_cgi_application paste-3.10.1/docs/modules/cgitb_catcher.txt000066400000000000000000000004041461442501600206330ustar00rootroot00000000000000:mod:`paste.cgitb_catcher` -- catch exceptions using cgitb ========================================================== .. automodule:: paste.cgitb_catcher Module Contents --------------- .. autoclass:: CgitbMiddleware .. autofunction:: make_cgitb_middleware paste-3.10.1/docs/modules/debug.debugapp.txt000066400000000000000000000004301461442501600207250ustar00rootroot00000000000000:mod:`paste.debug.debugapp` -- debug app ======================================== .. automodule:: paste.debug.debugapp Module Contents --------------- .. autoclass:: SimpleApplication .. autoclass:: SlowConsumer .. autofunction:: make_test_app .. autofunction:: make_slow_app paste-3.10.1/docs/modules/debug.fsdiff.txt000066400000000000000000000005421461442501600204030ustar00rootroot00000000000000:mod:`paste.debug.fsdiff` -- Show differences between directories ================================================================= .. automodule:: paste.debug.fsdiff Module Contents --------------- .. autoclass:: Diff .. autoclass:: Snapshot .. autoclass:: File .. autoclass:: Dir .. autofunction:: report_expected_diffs .. autofunction:: show_diff paste-3.10.1/docs/modules/debug.prints.txt000066400000000000000000000003171461442501600204610ustar00rootroot00000000000000:mod:`paste.debug.prints` -- capture print output ================================================= .. automodule:: paste.debug.prints Module Contents --------------- .. autoclass:: PrintDebugMiddleware paste-3.10.1/docs/modules/debug.profile.txt000066400000000000000000000004701461442501600206020ustar00rootroot00000000000000:mod:`paste.debug.profile` -- profile applications and requests =============================================================== .. automodule:: paste.debug.profile Module Contents --------------- .. autoclass:: ProfileMiddleware .. autofunction:: make_profile_middleware .. autofunction:: profile_decorator paste-3.10.1/docs/modules/debug.watchthreads.txt000066400000000000000000000005051461442501600216220ustar00rootroot00000000000000:mod:`paste.debug.watchthreads` -- watch thread workers in paste.httpserver =========================================================================== .. automodule:: paste.debug.watchthreads Module Contents --------------- .. autoclass:: WatchThreads .. autofunction:: make_watch_threads .. autofunction:: make_bad_app paste-3.10.1/docs/modules/debug.wdg_validate.txt000066400000000000000000000003631461442501600215750ustar00rootroot00000000000000:mod:`paste.debug.debugapp` -- debug app ======================================== .. automodule:: paste.debug.wdg_validate Module Contents --------------- .. autoclass:: WDGValidateMiddleware .. autofunction:: make_wdg_validate_middleware paste-3.10.1/docs/modules/errordocument.txt000066400000000000000000000004401461442501600207420ustar00rootroot00000000000000:mod:`paste.errordocument` -- Do internal redirects for error responses ======================================================================= .. automodule:: paste.errordocument Module Contents --------------- .. autoclass:: StatusBasedForward .. autofunction:: make_errordocument paste-3.10.1/docs/modules/evalexception.txt000066400000000000000000000003421461442501600207210ustar00rootroot00000000000000:mod:`paste.evalexception` -- Interactive debugging for errors ============================================================== .. automodule:: paste.evalexception Module Contents --------------- .. autoclass:: EvalException paste-3.10.1/docs/modules/exceptions.txt000066400000000000000000000023531461442501600202400ustar00rootroot00000000000000:mod:`paste.exceptions` -- Catch, display, and notify for exceptions ==================================================================== .. automodule:: paste.exceptions.errormiddleware Module Contents --------------- .. autoclass:: ErrorMiddleware .. autofunction:: handle_exception .. autofunction:: make_error_middleware :mod:`paste.exceptions.collector` -- Collection information from exceptions =========================================================================== .. automodule:: paste.exceptions.collector Module Contents --------------- .. autoclass:: ExceptionCollector .. autofunction:: collect_exception :mod:`paste.exceptions.formatter` -- Format exception information ================================================================= .. automodule:: paste.exceptions.formatter Module Contents --------------- .. autoclass:: TextFormatter .. autoclass:: HTMLFormatter .. autofunction:: format_html .. autofunction:: format_text :mod:`paste.exceptions.reporter` -- Report exceptions ===================================================== .. automodule:: paste.exceptions.reporter Module Contents --------------- .. autoclass:: EmailReporter .. autoclass:: LogReporter .. autoclass:: FileReporter .. autoclass:: WSGIAppReporter paste-3.10.1/docs/modules/fileapp.txt000066400000000000000000000003701461442501600174740ustar00rootroot00000000000000:mod:`paste.fileapp` -- Serve files =================================== .. automodule:: paste.fileapp Module Contents --------------- .. autoclass:: FileApp .. autoclass:: DirectoryApp .. autofunction:: DataApp .. autofunction:: ArchiveStore paste-3.10.1/docs/modules/fixture.txt000066400000000000000000000011711461442501600175420ustar00rootroot00000000000000:mod:`paste.fixture` -- Test applications ========================================= .. contents:: .. automodule:: paste.fixture Module Contents --------------- .. autoclass:: TestApp :members: .. autoclass:: TestRequest Forms ----- .. autoclass:: Form :members: .. autoclass:: Field :members: .. autoclass:: Select .. autoclass:: Radio .. autoclass:: Checkbox .. autoclass:: Text .. autoclass:: Textarea .. autoclass:: Hidden .. autoclass:: Submit Script Testing -------------- .. autoclass:: TestFileEnvironment :members: .. autoclass:: ProcResult :members: .. autoclass:: FoundFile .. autoclass:: FoundDir paste-3.10.1/docs/modules/gzipper.txt000066400000000000000000000003421461442501600175330ustar00rootroot00000000000000:mod:`paste.gzipper` -- Gzip-compress responses =============================================== .. automodule:: paste.gzipper Module Contents --------------- .. autoclass:: middleware .. autofunction:: make_gzip_middleware paste-3.10.1/docs/modules/httpexceptions.txt000066400000000000000000000031041461442501600211330ustar00rootroot00000000000000:mod:`paste.httpexceptions` -- Easily product HTTP errors ========================================================= .. automodule:: paste.httpexceptions Module Contents --------------- .. autoclass:: HTTPExceptionHandler .. autofunction:: make_middleware Exceptions ---------- .. autoexception:: HTTPException .. autoexception:: HTTPError .. autoexception:: HTTPRedirection .. autoexception:: HTTPMultipleChoices .. autoexception:: HTTPMovedPermanently .. autoexception:: HTTPFound .. autoexception:: HTTPNotModified .. autoexception:: HTTPUseProxy .. autoexception:: HTTPTemporaryRedirect .. autoexception:: HTTPClientError .. autoexception:: HTTPBadRequest .. autoexception:: HTTPUnauthorized .. autoexception:: HTTPPaymentRequired .. autoexception:: HTTPForbidden .. autoexception:: HTTPNotFound .. autoexception:: HTTPMethodNotAllowed .. autoexception:: HTTPNotAcceptable .. autoexception:: HTTPProxyAuthenticationRequired .. autoexception:: HTTPRequestTimeout .. autoexception:: HTTPConflict .. autoexception:: HTTPGone .. autoexception:: HTTPLengthRequired .. autoexception:: HTTPPreconditionFailed .. autoexception:: HTTPRequestEntityTooLarge .. autoexception:: HTTPRequestURITooLong .. autoexception:: HTTPUnsupportedMediaType .. autoexception:: HTTPRequestRangeNotSatisfiable .. autoexception:: HTTPExpectationFailed .. autoexception:: HTTPServerError .. autoexception:: HTTPInternalServerError .. autoexception:: HTTPNotImplemented .. autoexception:: HTTPBadGateway .. autoexception:: HTTPServiceUnavailable .. autoexception:: HTTPGatewayTimeout .. autoexception:: HTTPVersionNotSupported paste-3.10.1/docs/modules/httpheaders.txt000066400000000000000000000003301461442501600203630ustar00rootroot00000000000000:mod:`paste.httpheaders` -- Manipulate HTTP Headers =================================================== .. comment: I just don't feel like documenting the items... .. automodule:: paste.httpheaders :members: paste-3.10.1/docs/modules/httpserver.txt000066400000000000000000000003121461442501600202560ustar00rootroot00000000000000:mod:`paste.httpserver` -- HTTP server ====================================== .. automodule:: paste.httpserver Module Contents --------------- .. autofunction:: serve .. autofunction:: server_runner paste-3.10.1/docs/modules/lint.txt000066400000000000000000000004201461442501600170160ustar00rootroot00000000000000:mod:`paste.lint` -- Check for the validity of WSGI requests and responses ========================================================================== .. automodule:: paste.lint Module Contents --------------- .. autofunction:: middleware .. autoexception:: WSGIWarning paste-3.10.1/docs/modules/pony.txt000066400000000000000000000003501461442501600170370ustar00rootroot00000000000000:mod:`paste.pony` -- Add pony power to your application ======================================================= .. automodule:: paste.pony Module Contents --------------- .. autoclass:: PonyMiddleware .. autofunction:: make_pony paste-3.10.1/docs/modules/progress.txt000066400000000000000000000003661461442501600177250ustar00rootroot00000000000000:mod:`paste.progress` -- Track progress of uploads ================================================== .. automodule:: paste.progress Module Contents --------------- .. autoclass:: UploadProgressMonitor .. autoclass:: UploadProgressReporter paste-3.10.1/docs/modules/proxy.txt000066400000000000000000000004621461442501600172370ustar00rootroot00000000000000:mod:`paste.proxy` -- Proxy WSGI requests to HTTP requests ========================================================== .. automodule:: paste.proxy Module Contents --------------- .. autoclass:: Proxy .. autofunction:: make_proxy .. autoclass:: TransparentProxy .. autofunction:: make_transparent_proxy paste-3.10.1/docs/modules/recursive.txt000066400000000000000000000003501461442501600200610ustar00rootroot00000000000000:mod:`paste.recursive` -- internal requests =========================================== .. automodule:: paste.recursive Module Contents --------------- .. autoclass:: RecursiveMiddleware .. autofunction:: ForwardRequestException paste-3.10.1/docs/modules/registry.txt000066400000000000000000000005641461442501600177310ustar00rootroot00000000000000:mod:`paste.registry` -- Manage thread-local request-specific objects ===================================================================== .. automodule:: paste.registry Module Contents --------------- .. autoclass:: StackedObjectProxy .. autoclass:: Registry .. autoclass:: RegistryManager .. autoclass:: StackedObjectRestorer .. autofunction:: make_registry_manager paste-3.10.1/docs/modules/reloader.txt000066400000000000000000000004451461442501600176540ustar00rootroot00000000000000:mod:`paste.reloader` -- Monitor for file changes to restart the process ======================================================================== .. automodule:: paste.reloader Module Contents --------------- .. autofunction:: install .. autoclass:: Monitor .. autofunction:: watch_file paste-3.10.1/docs/modules/request.txt000066400000000000000000000007651461442501600175540ustar00rootroot00000000000000:mod:`paste.request` -- Utility functions for the WSGI environment ================================================================== .. automodule:: paste.request Module Contents --------------- .. autofunction:: get_cookies .. autofunction:: get_cookie_dict .. autofunction:: parse_querystring .. autofunction:: parse_formvars .. autofunction:: construct_url .. autofunction:: path_info_split .. autofunction:: path_info_pop .. autofunction:: resolve_relative_url .. autoclass:: EnvironHeaders paste-3.10.1/docs/modules/response.txt000066400000000000000000000005411461442501600177120ustar00rootroot00000000000000:mod:`paste.response` -- Utility functions for producing responses ================================================================== .. automodule:: paste.response Module Contents --------------- .. autoclass:: HeaderDict .. autofunction:: has_header .. autofunction:: header_value .. autofunction:: remove_header .. autofunction:: replace_header paste-3.10.1/docs/modules/session.txt000066400000000000000000000003631461442501600175410ustar00rootroot00000000000000:mod:`paste.session` -- Simple file-based sessions ================================================== .. automodule:: paste.session Module Contents --------------- .. autoclass:: SessionMiddleware .. autofunction:: make_session_middleware paste-3.10.1/docs/modules/transaction.txt000066400000000000000000000004341461442501600204020ustar00rootroot00000000000000:mod:`paste.transaction` -- DB-API transactions =============================================== .. automodule:: paste.transaction Module Contents --------------- .. autoclass:: TransactionManagerMiddleware .. autoclass:: ConnectionFactory .. autofunction:: BasicTransactionHandler paste-3.10.1/docs/modules/translogger.txt000066400000000000000000000003201461442501600203760ustar00rootroot00000000000000:mod:`paste.translogger` -- Log requests ======================================== .. automodule:: paste.translogger Module Contents --------------- .. autoclass:: TransLogger .. autofunction:: make_filter paste-3.10.1/docs/modules/url.txt000066400000000000000000000002711461442501600166560ustar00rootroot00000000000000:mod:`paste.url` -- URL convenience class ========================================= .. automodule:: paste.url Module Contents --------------- .. autoclass:: URL .. autoclass:: Image paste-3.10.1/docs/modules/urlmap.txt000066400000000000000000000003401461442501600173510ustar00rootroot00000000000000:mod:`paste.urlmap` -- Map URL paths ==================================== .. automodule:: paste.urlmap Module Contents --------------- .. autoclass:: URLMap .. autofunction:: urlmap_factory .. autoclass:: PathProxyURLMap paste-3.10.1/docs/modules/urlparser.txt000066400000000000000000000006101461442501600200700ustar00rootroot00000000000000:mod:`paste.urlparser` -- Handle URL paths and server static files ================================================================== .. automodule:: paste.urlparser Module Contents --------------- .. autoclass:: StaticURLParser .. autofunction:: make_static .. autoclass:: PkgResourcesParser .. autofunction:: make_pkg_resources .. autoclass:: URLParser .. autofunction:: make_url_parser paste-3.10.1/docs/modules/util.import_string.txt000066400000000000000000000005141461442501600217300ustar00rootroot00000000000000:mod:`paste.util.import_string` -- Import objects from strings ============================================================== .. automodule:: paste.util.import_string Module Contents --------------- .. autofunction:: eval_import .. autofunction:: simple_import .. autofunction:: import_module .. autofunction:: try_import_module paste-3.10.1/docs/modules/util.multidict.txt000066400000000000000000000004041461442501600210240ustar00rootroot00000000000000:mod:`paste.util.multidict` -- Dictionaries with multiple values ================================================================ .. automodule:: paste.util.multidict Module Contents --------------- .. autoclass:: MultiDict .. autoclass:: UnicodeMultiDict paste-3.10.1/docs/modules/wsgilib.txt000066400000000000000000000010101461442501600175040ustar00rootroot00000000000000:mod:`paste.wsgilib` -- Miscellaneous WSGI utility functions ============================================================ .. automodule:: paste.wsgilib Module Contents --------------- .. autofunction:: add_close .. autofunction:: add_start_close .. autofunction:: chained_app_iters .. autoclass:: encode_unicode_app_iter .. autofunction:: catch_errors .. autofunction:: catch_errors_app .. autofunction:: raw_interactive .. autofunction:: interactive .. autofunction:: dump_environ .. autofunction:: intercept_output paste-3.10.1/docs/modules/wsgiwrappers.txt000066400000000000000000000004271461442501600206140ustar00rootroot00000000000000:mod:`paste.wsgiwrappers` -- Wrapper functions for WSGI request and response ============================================================================ .. automodule:: paste.wsgiwrappers Module Contents --------------- .. autoclass:: WSGIRequest .. autoclass:: WSGIResponse paste-3.10.1/docs/news.txt000066400000000000000000001220261461442501600153630ustar00rootroot00000000000000News ==== .. contents:: 3.10.1 ------ * Correct packaging and testing when not in a clean virtualenv. 3.10.0 ------ * Move development to https://github.com/pasteorg/paste * Vendor cgi.FieldStorage and cgitb.Hook * More cleaning of Python 2 style code. With version 3.10.0 Paste development moves to the pasteorg GitHub organization and will be going deeper into maintenance mode unless more active maintainers step forward to take over. "Deeper" in this case means that releases will be much less frequent and patches will only be accepted for security issues or major problems. Current consumers of Paste should prepare to migrate away to more modern solutions. 3.9.0 ----- * Remove dead format_environ code in watchthread.py app. 3.8.0 ----- * Remove remainder of Python 2 code. Thanks a-detiste. 3.7.0 ----- * End Python 2 support. * Remove use of distutils. * Fix double query processing in parse_formvars. 3.6.1 ----- * Tiny release to confirm release automation. 3.6.0 ----- * Provide kwarg for timestamp format in Translogger. 3.5.3 ----- * Use importlib instead of imp with Python 3. 3.5.2 ----- * Additional fixes to next in iterators. Thanks to cjwatson. 3.5.1 ----- * Replace deprecated threading.currentThread, getName and setDaemon with threading.current_thread, name and daemon. Thanks to hugovk. 3.5.0 ----- * Python 3 fixes to auth and wsgi.errors handling; notably making wsgi.errors text. Thanks to brondsem. 3.4.6 ----- * Explicit pkg_resource dependency to easy packaging. Thanks to mgorny. 3.4.5 ----- * Remove deprecated dependencies paste/fixture.py. Thanks to jhance. 3.4.4 ----- * Update setup.py to work with setuptools 50.1.0+ Thanks to hrnciar for the patch. 3.4.3 ----- * Patch auth ticket to be python3 compatible. Thanks to TilmanSchaefer for the patch. 3.4.2 ----- * Correct sorting of items() in EvalHTMLFormatter. Thanks to amitmarkel for the patch. 3.4.1 ----- * Fix next in iterators in wsgilib.py. 3.4.0 ----- * Allow binding IPv6 address when starting a server. Thanks to Lekinho for this patch. 3.3.0 ----- * Use six.BytesIO when reading wsgi.input. * Remove use of pytest-runner. 3.2.7 ----- * Python 3 updates for use of StringIO and auth_tkt.py. 3.2.6 ----- * Correctly handle HEAD requests (to send empty body) when gzip encoding requested. 3.2.4 ----- * Use is_alive instead of isAlive for Python 3.9 compatibility. * Use encodebytes instead of deprecated encodestring. * Fix Python 2 and 3 compatibility for base64. Thanks to tirkarthi for these fixes. 3.2.3 ----- * Correct ``100 Continue`` in Python 3 3.2.2 ----- * Avoid some reference cycles through tracebacks in httpserver.py 3.2.1 ----- * Handle io.UnsupportedOperation from socket.tell() 3.2.0 ----- * Ensure unicode URLs work in TestApp. * Make LimitedLengthFile file return empty bytes. * Protect against accidental close in FieldStorage. Thanks to benjaminp for many recent contributions. 3.1.1 ----- * TestApp.encode_multipart handles bytes filenames and params. 3.1.0 ----- * Allow anything that can read() for a file-like response, not just a ``file`` instance. 3.0.8 ----- * Fix quoting of bytestrings (#23, via jelmer). 3.0.7 ----- * Send bytestrings when writing chunks from ``paste/httpserver.py``. 3.0.6 ----- * Revert the changes in the 3.0.5, which introduced unexpected errors in ``paste/httpserver.py`` when using SSL. 3.0.5 ----- * Quiet a deprecation warning in OpenSSL. 3.0.4 ----- * Python 3 fix for deleting items from a dict. 3.0.3 ----- * Ensure pytest requirements set properly. 3.0.2 ----- * Encoding fixes in ``paste.fixture``. 3.0.1 ----- * Remove use of future for sake of html.escape and use own. Using future was causing installation loops. See: https://github.com/pasteorg/paste/issues/6 3.0.0 ----- * Fixes for use with Python 3.7, mostly to do with ``StopIteration``. * Moving to https://github.com/cdent/paste to keep things maintained. 2.0.3 ----- * #26: Change six requirement to >=1.4.0 from [Linus Heckemann](https://bitbucket.org/sphalerite/) https://bitbucket.org/ianb/paste/pull-requests/26/change-six-requirement-to-140/diff * #28: Py3k fixes from [Nils Philippsen](https://bitbucket.org/nilsph/) https://bitbucket.org/ianb/paste/pull-requests/28/py3k-fixes/diff * #29: paste.wsgilib.add_close: Add __next__ method to support using `add_close` objects as iterators on Python 3. fixes https://bitbucket.org/ianb/pastedeploy/issues/18/py3-test_config_middleware-failed from [Marc Abramowitz](https://bitbucket.org/msabramo/) https://bitbucket.org/ianb/paste/pull-requests/29/pastewsgilibadd_close-add-__next__-method/diff * #30: tox.ini: Add py35 to envlist from [Marc Abramowitz](https://bitbucket.org/msabramo/) https://bitbucket.org/ianb/paste/pull-requests/30/toxini-add-py35-to-envlist/diff * #31: Enable testing with pypy from [Marc Abramowitz](https://bitbucket.org/msabramo/) https://bitbucket.org/ianb/paste/pull-requests/31/enable-testing-with-pypy/diff * #33: tox.ini: Measure test coveraage from [Marc Abramowitz](https://bitbucket.org/msabramo/) https://bitbucket.org/ianb/paste/pull-requests/33/toxini-measure-test-coverage/diff 2.0.2 ----- * #22: Fix improper commas in request headers in wsgi_environ (https://bitbucket.org/ianb/paste/pull-request/22/fix-improper-commas-in-request-headers-in) Fixes issue #4 ("WSGI environ totally borked") (https://bitbucket.org/ianb/paste/issue/4/wsgi-environ-totally-borked) * #24: test_wsgirequest_charset: Use UTF-8 instead of iso-8859-1 (https://bitbucket.org/ianb/paste/pull-request/24/test_wsgirequest_charset-use-utf-8-instead) Fixes issue #7 ("Python 3 test failure") (https://bitbucket.org/ianb/paste/issue/7/python-3-test-failure) * #23: Replace cgi.parse_qsl w/ six.moves.urllib.parse.parse_qsl (https://bitbucket.org/ianb/paste/pull-request/23/replace-cgiparse_qsl-w) Fixes issue #8 ("cgi.parse_qsl is pending deprecation") (https://bitbucket.org/ianb/paste/issue/8/cgiparse_qsl-is-pending-deprecation) * #20: Escape CGI environment variables in HTTP 404 responses (https://bitbucket.org/ianb/paste/pull-request/20/escape-cgi-environment-variables-in-http) * #6: Add HTTP exception for new code 429 "Too Many Requests" (https://bitbucket.org/ianb/paste/pull-request/6/add-http-exception-for-new-code-429-too) * #25: replace ``has_key`` method to ``in`` operator #9 (https://bitbucket.org/ianb/paste/pull-request/25/replace-has_key-method-to-in-operator-9) Fixes #9 ("used methods removed from py3") (https://bitbucket.org/ianb/paste/issue/9/used-methods-removed-from-py3) * #5: Invalid error message when the socket is already in use (https://bitbucket.org/ianb/paste/issue/5/invalid-error-message-when-the-socket-is) 2.0.1 ----- * Fix setup.py for six dependency: move the six dependency from extras_require to install_requires. * Port paste.proxy to Python 3. * Fix paste.exceptions.serial_number_generator.hash_identifier() on Python 3. * Fix paste.util.threadedprint.uninstall(). Rename duplicated uninstall() function to uninstall_stdin() and fix typo in variable name (_oldstin => _oldstdin). * Add README.rst file. 2.0 --- * Experimental Python 3 support. * paste now requires the six module. * Drop support of Python 2.5 and older. * Fixed ``egg:Paste#cgi`` * In ``paste.httpserver``: give a 100 Continue response even when the server has been configured as an HTTP/1.0 server (clients may send ``Expect: 100-Continue`` before they know the version), and wrap 100 Continue ``environ['wsgi.input']`` files with LimitedLengthFile just like normal request bodies are wrapped, keeping WSGI applications from over-reading from the socket. * Fixed parsing of paths beginning with multiple forward slashes. * Add tox.ini to run tests with tox on Python 2.6, 2.7 and 3.4. 1.7.5.1 ------- * Fix bug introduced in :mod:`paste.auth.auth_tkt` (with ``url_unquote``) 1.7.5 ----- * Won't install ``tests/`` directory (also caused installation problems on some Mac systems). * Fixed problem with gzip middleware and zero-length responses. * Use ``X-Forwarded-For`` header in :mod:`paste.translogger` * Fixed problems with mimeparse code * Fixed some corner cases with CGI scripts * :mod:`paste.auth.auth_tkt` will URL-quote usernames, avoiding some errors with usernames with ``!`` in them. * Improve handling of errors in fetching error pages in :mod:`paste.errordocument`. 1.7.4 ----- * Fix XSS bug (security issue) with not found handlers for :class:`paste.urlparser.StaticURLParser` and :class:`paste.urlmap.URLMap`. If you ask for a path with ``/--> ''' resources = '''\ ''' def insert_head(body, text): end_head = re.search(r'', body, re.I) if end_head: return body[:end_head.start()] + text + body[end_head.end():] else: return text + body def insert_body(body, text): end_body = re.search(r'', body, re.I) if end_body: return body[:end_body.start()] + text + body[end_body.end():] else: return body + text def make_cowbell(global_conf, app): return MoreCowbell(app) if __name__ == '__main__': from paste.debug.debugapp import SimpleApplication app = MoreCowbell(SimpleApplication()) from paste.httpserver import serve serve(app) paste-3.10.1/paste/cowbell/bell-ascending.png000066400000000000000000004036011461442501600210370ustar00rootroot00000000000000PNG  IHDRrgAMA asRGB cHRMz&u0`:pQ<bKGD pHYs   vpAgJ|IDATxgTU{#T $ $A* T̂ QP$(HlQPI" "98cU~my֚O0^g;8{U|4nQiU`O|#êL[= ;󶺏F6L+\r[*>Z}-ɋ"EɚfmV*`ݰ=V O[ ; ?r>e`D1leɯk56zC &H5ƾ7Jʔor| m{=~G(z]ɵSj&beE9*dg%+O=g;׎_?X1OeUΝqHM8x .JG2R:@ց/T*8wRAΙʐ,c:IbU;yEaxm̦uDU@nyY:V>kf}fSeh 6z՞\#oE5żle: `i1;W&n{q~(** Ur[#f?}=7ݜ_^ +x+t}ga[(X5O\W yK/@ຩ(x9i_z:{g.O%1_::֭~|Zme?36ad^ߪzxBYMΜid4[pȮ%.%Fwo"#'\qVu~N#Bk{ټHNHB-&7SWIjTPI /&{Awa`j'~Zo2id-!ǎTT?=b9<;+*PM'viZ'(l6F?gZ!DUX˙&w1@pސ=]lOXV˱2$MCq.x-U5/@tH&C30$z?{Ng̐HMtE <{f-!6ϩ2wȻvDZS Fe^4l,x2X-?n) $RK1F,.hռ 玶{5Rnn nu $_F SAZ"L9ZYn:KeUDbm&1+x3@xt$x0aVoN<]8]J+z;- 7zK5~2PCJ;j8]].o~9 ypMCuG:rp.kžĩἆ56ݽ"r't{D$^hNfߢS խAߐi=T>ONΡ:‰ubn,FgyCjR |U @c(AN"7t9nr`F?L;gKWlȓ/<,S-D7y 7AQ7~ #xdoU#o릨noHn͝2ϬS$:zo 7)? V[*ئx;1q`-jɿb ő2C6³n[ ֳaQwb }&h+-rB*kէ {8me8+xkmAgZdnpK`-SVGCy*;i=]VJ~dFIؙe 0dytct C¶O͋(/c@ -!'ۆ?Xƹ6}@V!#5{E:!a6笱j.PHogA|i.ՕcFUNA k)(Rĉ& 7n |U1Ybb:#Wt?A͍α@l} Ա%ϋ#bxWhu|忁dgXZG )kuOoiKsHޮ:wBwP se!z- Γb8w.NM0ke1A{XzGyV.=eJx;FX˘`'v"`*h&S~jiu*<\wQQcS̩ ny AVoɹ7{`)o*, .OQr̓9XnEKt!PePiE[ ̝b.9< ߙ_v!֑ u]FDf%zXW,6{MП [SV|g=88bց@lNBHe?=@|K9 />HB~`͂Eqy3OzEgD/y^6xv[{4s3, 7ʘA"T_|'˩`GbȔz QUݫn"Z#OYtVwkUc }{u^Sp~Zo{~>yL/DĞ1C)^?: ɷ#zw+z@ lcs*_UWȷm}aF`NT^W 3 x;L]YZC$;z0ͭE6.FAV[w=7GA5,*'^OC97A֏TV/|@Wwg[ZJ+&"pv0!YA7m:CdVeSLmqw~IN46ySUA7E@P~TO7 URjEMP.ޑ"Qvc=vS{H|EJh`qYx,rX@ܐ"cVD7zc^vTqVA76Z r^48g@MRkgf*Cq'lɃx0;yWP b>U%X߉$n~N^՟ouGS&?7>=݅thkVXorC&W,5fׁRrbL5J_)xxQ34?^\9 ?I +Z Oi љoMX~ (S{XԯϽҭN/#nd}"πjVκ.H)c=vV$g.SIT<35Qi{ ܻlaHR:7AN ".A "h}f;gf._쯙 ac:Aeh lЎ V` ̝<| vmp>' 97*®$2#*0Vmpz*Wܼ*ƂywsXUf<Ⱦ:ÞVP%l MbTtyQzBn /!Q'ˉyQpg"A-=!oJם ? x "hPKHiW ԭSZC|{^gbYk`5AEVԝ>r8ix]_n%3g'^l-:0nuz@֫^i8W-ć kՖۛ]`O#@v/RW:fok=/]6O<7l~z385s7z/@|7xws!?L~փJu a=uP c dį "nOi-G E7Kf2 ͼ8YPE7dJete6Ac5 Rbس%`x4PT48 V&Ŗ9.hJKrUiO PES;pR.n@–.%| B\ &7o]D4`$? 6> ~;+EZ'3Y@hzGH[?vd^ADO$MHĨ3.d:/߿2nSk(x 2A=qD:|a & "/>$ȁp`X 1)tH553wA8A|f*V]] 'y99MAc}o 2V!o/ <d*@]מ\ dqwigz1(g@U!',z)H~Puu)]M ~. }fU&9Uԯv&XȈm9po/k))_a_~*2N@x{0/Gת+Nju 9 ;31ȘBVٴ F/h|qNH)ccfqx7fEOsΓN+H,X!gG't9PHxnvPZ͒.?+z:VzJQT? g x]Mc>5 { $O@j,9p.YN+pwQ@!JU !1BjyU Pqpm;fȼ—<f-*N9+qgC]XH=WNL՟OɜE&Xz|<Qz>6ȧT3Mỵb"3n!ͬW\짂'ʭ`%\9y74@|j+qAfjv3$󂃺 ,qÝVI>U1^n% :F6^84u>{AO~3`3+w'? a Z }8ujavʭ6!V_6p&A};_>6FmÊh=:zvbp8P赏j7g0 ga]񼐬T=vOku"KT)^uS@R ϲݾGףLeL18~ ̊;]T͍c7w Īm`v0`1%zW+I :\A/jcj{PA[8f 1zU\.::0 )SF- b WX,@UEsʃ~q B>_$x1H]3T?QD@T& sI9BCYe+g-p$ϛHM&cN'!5_N'0MosɪfOs2'[eS^_ =|`TWn|0mxsSrv۵@c4PS`7w=o@HNic]s) m,Z]`ޠׯ@M pL jӐHo9B)cgBdHzy  :SayIWs2Gi vu$uzY X'S``&g'e'~ɠ0Xի7xr +ZG:ݿeN0"l)*輫JM3@ Y 0=WټIq]&ۘY,>k.seșvg 8ԏDV~(17qr2{+R8"G imڠJȪ~.'@orŷznMuWrBkTvS\{ɽ~b:sZڂ SFmj&߯- tL,-}hR(IU^/ ݩx觚x+ Ȟ > ڃi%R!`QZ'+.>d (|(߃pn0rdΣPpZdup0:rL{څk@皡lQI9΁W[u[PDuJۂ9` L%qbg哲"v3NL0Ywn.֫h}jJ I}MFV {7OC<V-!=`-H|A}s!5M.R5O 0ȁxJ7m_@rjH^Pk"ޭoFա촭NrqsrnlJ`O ٠q[Np٪_:F Mgsvٯ= p -@E0E i< {MeR f{c`P@`+Dڂa67 X΀VA4wu ?n^ < FSfZxab8"\|}?~AxdmuQWSu\0cޤ/>^ ȂƓMScLpr4s y5 :Cq1.wpt'5sv<0=PVso:?VZa6V߱oʟ ː&MMƃSE|:yA{ ɩdj=8"I.:!1,f?f >IKlvs惻˺?7m[-յ+ V<|zs{)lcT-6ڬc_28z߇}`rIZQ`.K3M#7@g֐T#}a4 0)z\UL]:wYWm?lntd88ka5n9IiY#d;wr`؃,hxdFNu.߃%J@]uOvƊ8MA[M ԕ wY\~ngNy LOe Ջ23bj=ݺ Ny( Eۦ F W.HWj1T S=<Q2U!{^BУ>j 'r1L}-\@VT50!ZR.(?s&Be91WmoWc9"@5{`a /:Nӂy"znJzs8RX7-t2 0q`# zdMr#e؝ߎiuDe^Qm+&W^Mqp` hɕDQpH"=FgsCI^S*UρO>}KBd09pڛnP(Uղ@Xjnl q@ԕ5F`YT>%WA쐝4QSٓ٥Wa+8O:pKiy$,'H^WWtU`-NuLp궁5$ # s}!K=z0崜+}Yqx: @eM:?nSSvH}4'bhi, -rt/s/j}ª KG@mk}G+$qf _DKq66CB,euj,k'vD́,g HN4Qy<vj>Wy p~ }1DU>(|x bQ  :G3 q#h,W\DZY]uƹbց1^=y/|Qy/GOOmi /?AyT >J5|hգ >]p>.!/:{AV6*);Uuwv**.OgkroE#(2M{ wܜ6oEW{tyDQ$o { f}8r .Wx{r? pY׭(dŮ{";LQ &y2zbCbV@VFם@g~ s FƂ8pC 2xwqkS w8,7'wc‘^QP]U/L>sk dj/|cp7ث v: Γ;L?9r VENH5߫_X> wbA&X2"sni@,]3CTq@PA}v}VW#E#X?<%ClPh )"PdXtm !8;(A"@OL?ePXeK9z;rf>r9M~g bP;" `}db/侗\N0Yb,榿nƮpN yCdjd5bms g웠N%u('XUˮfvVsPOb"o^n2Dyఈ%΃ne1~ν`MN"!qC|!X(䇖],\jǦ2B|x&r8d?UiXk)Xy}'MmpʷM?PZK#]DYUX+=%YFKH>}S/zuI^'Ц )$/iԭ]P[G |ܙvN_6Q{wygOl(T4ƒ@ 83@+e!ԩ#+) (o@v7A:o@W 1eS"'rXŸ ?R^Jo b?ErlP2/^z&xS;;yԫQbU[=6*BDTS8]3wP<reN}gn{BJ׀#Dߋ:RIiQ8h1D'F>> SLVޜ,bM&`޷ր5HVx NSU_ПsVsQ]h hb)6o:@5P*"MwX k)Hg>Un`';E[@BoVekdY+i+R- AH ~/: `<Y"cb۠@"cY x̕M yd~?^[7 :CJ%#.,HذFeKj kFGDf./*R ,v DqY/JԇUcv v06Eֈ0Bj<%|0)[oW琬?wĜ?W ?3&|ߞ7sL=dž"b+Aa}k묮b`eY@35]V񯊍֗~骖B {\RT:'H-r|Wmެe-3m//|3W#M~w}ȝ"WWIwGXG {kjabR/A ~Vi!V*QG_=(к )"x}& Ds>l Ahth Δ@g[ 8X3Ŧ9&a? 1\AYa|d_KU<+kp+:@tzZ< ~r AWŰ!$>J.q!`/WeTkTPRVρ?r;Zfӿ̈7{eVVVO~A$@-CM { bZ~S38d]^Ap@ 9p9ew98=#]zA5]r:%[+3ķ ]6ƺKܗ.%[є2e*b[ 9c8:^d ސ(>#dwz CC^XW[}[pbP+߿OP s| E[Y[־t(ImO)H݅w@%m ñk[.)p1,rdg#9#yI:f@Py tY28J*}f95H1tE;-:};Ōy!pLiH&6g<|쇤q,N9Yg!vӟ᳾oAׁĆC>6?T _A/ ݭ`-wno;mW=`S) +;?}ܻBZ{8e\㌎tLͬ'Ñ=tM7C~ I=>5vAkd@wN9ëUyDbU٫͋ 7畅?t0(RHK.B\Vx6h_> Y)/R{@3?uz@" S GSp$X-"<([y^ͽ@Ms rR 1M^ACP>"e g^^@J>_ @KBkdbb\5O&Ӑ@r!DXm/hVm|"w/|#\~ 䟥$CgUXn9ۇu2n_5, IMz P V'^A0Geó`WO8XPX, =c0_s@o HsQ ު. VT-PO# d1kWKNuhȸ$=a=uS k.e/zJkHYnF!}]@Y?? ^u׽R;9Pi]!=Tawۘ- jhAh1^ru@<(O'?ٯKG1 A91\^QvVp4॰-yD#Uڮ w9 ^^wm- D+x~=~cD?O[C@ k"w28}[᱇Y߷:WXG p>($UÐqf?_H'Ǖ >,cq&v; sIw^>i}#2Ƚ%$Sr g!Q(fc%''$(?k5Jpaj$Dz0tfp)Q? da^9RT!_t1?,߀I7s[g;Y:xzʎqM ivt k=*r8n\sVf=[/3?!.> YKQ`_;= "%" m ~[q '2wBDའ. \Xsja{|N}/ї WAe;@/Z ?|T8įA(r'|.lP_ǍF8zlύ6?Y)U¼[_GkTRL>/']꯭ug nR|[OR&7}#9TR:C^Eoir  9:P Gy#@{ _tm n$˃ɖF [OP bj#8e7h 992)CvF^'˛VCP,p^߂ߞ*ۗGU15*~ݭWx0.ȉv-^.l:B]/\@ޒSyv+1[ ; FhHLL~k< ˀLWP V:p~жw՜6 A~ r[0 AxNLM ܞ\aY( y?>]B,_F-&Q݋?EwczMԔEx~ ;9GKD`_b);khzESS[%sܛ{ӞVW +ԫՓۜW﮹٬RtO;5hI1^{t=MOMD>fi  'R )N#Pӽ} V Ы b[!o?vSSLQOti'U'zDO<5 ܲH:PWsvܢ$@#! w6Sn>l~Tk+i`W }=YP/XJPyᚅ${nsM#DWjiR~9Fjπqz_|lReH\9+@vy ?dA|oU1o"XfЕ`* .Fhg7mg(lD%g2X:d$Fv'XY]2M)M3R?ry#7w?tJ55;i_ @$-nց}rg!JN62eEy䍔n%}7xg[A5byVPFO:DXEPbs26v_07 {xHv ݽb\2͝Naw'mӐ7S=~Cq:EI϶BF<Փ7"}QЯu$brMI9*`mȴD)0jUu, WtoAtɀ2cwl-Gf_Fi[-]_sHP~͒s, \,VGg\3|W~67Mɽ5_ 0OdqW%-mE0:jQr<7[3GV҅d?`{|~ H*W~l mzEA![|쿛b_r}DNf1:w'KCt^48Y9XKT`OMAe'Ms3/ۛ`~h_뚧!|jڀYjڮVEC,5v(mD?܌}\t0앢.ݖuJ[() ̓hGu8m ϻS 3!뼷>v.-r<=r(8]u Խ~ ~#a8 #]4w⫠3Mn皿G^E6/^WT!(Rp$C3̴G#aFx?5iGC|Vn_kCy6pz*ZfΕFիݑ4=՚Od}HNOvˆSHUۆ +Ϭ1K^7]Gvi0;TڿAg׌f)6x]tMzY- 2G< __ 37 3W 7ޞƠPbػ &B Q V5R3EOҠU=-8&D e tAEeh j&/71\̕K %#%-fG[aybox_$ML)g,<V,Avs (kp3O: !g8ST(KqU|f1}Pb,X޷yqB(`XeSD\hP[%PbAEʧW.'g5_34,|k6)ϑoSuz)ysSVa{5GOyzvU2 p.zeA8w/ %Xo-${M]D5jY M- DGvW.TkyF5M@d  L-W!(lr\2`?Mς`w0 l&-*KIfuVzn eg| 4E!7yt_2`  ܲ)!83|Ew| ;N@1E{hB֋vyWoBN<mn&ȭn}L(3z*F;?Z vqпj\yPTܝ7=jtK{& /*P:ͣE*ə;7YV<f-!$I:Sf v|UB/i;6`=39/)ϓoA>#/]A*3&#hcZ}=h`twOVMX5+)B]ɅZFͱ6&E3yAf=˺&~&XL=% syN{{^4:)c:Y @9j^ 9k=y5l(5 dr&X#uE.B:`o慩1猪95U[‘ `? d W90n+UUt Zd;$7Y-R: LI6ApU[D cK2pAQ)"j/-!w\!=ZSPl"]r_z`-p> p>cbdzAd. \nj3. ~ N_DK 7!$ %~//L{aQVXE0%DmAA*X;u#t 3*4CAMK;͜\'y]:R7x1n;)eҢ蟏sZET^S-z`w= PUew$[8ɥN2O~_8Je|P]UD 4c[gy!eghg4SMX-j'ʤ}N]LA%^#%]Q-UO /bP0ZI17M c+Be TG0z`opϰ tq][V;pg$T uxY L: `U0ۣ(J Sac[D3"SulN;OUre֫`v)c(~y-_w|J.皩 uI x,/7ui*D=SeIuB>߉SQ6)6>߲,bwmlz0.o//|]f5{` 5Z?]KW2 =d0S"P4u>dĘRA,õ\"oy_ ג]yo&@ pVDۃSl؆H[Qd&S xY_3@T] .D&9WÉVU^Oe|pZ;zT秔ww˝!DV/뵳?-/_S/_Z~j :Wr.޶h|]kȿ2"hhɵŻi@w%>-[;;Ar_pCd[?RÜirn,sbh> guHD~AF7Xi[*,>6R sCr.zUoă] <?2/HQDo4w/$J8 eP03<#|eq;`0O6wq%P#598{9fDY0EPYU6XvW{ÃG恵>sP=Zc~a;n F g5fɳvگ=jӡhf1QԆ*KX TIS 0!MS>p5;kxJtYR)c%- uK`|,W~ 蚪T /֝} MՖɫlYNU؈jQV @fg@^ LjU1@p<08b0N AnҜ^rU2 Dpp&T9ПIy@<W{izmUH/Ĝׂx暮 ̰b#FO.p?^P~TOOJ_[G syZLx@}n2f~wb`_"6i;#/Pr ^p$!S*ZN!,G@4A_GR R~ twB} ͹!x?s" T}27*/ⷂٟ̑&RԂKf"Pp_,i}ULJ6)?^/Ѻy~6;LM4^?ŒҗyZ6#" t=H=1]Cp`nSςs3 *CUJxNV6S@]es{T#oHN?A =,/SڪA碻~ c^O . ^yj>pM bb};x$9J13cE<[اxF;7uVvNmA[+~l<u'>PI n!< jQ@ks eȹAC^a=ΐ,);?CtB:۠ZWέ>^* Yf6Rگ')ìz-r}t1^V _Mɼ ~ĩIM &"SUxrϮw)?X_寰_aa [j}^P/R]DQUw1G I&AZh>veƢEkDk`7Az*:)uXZ §7}즠+A 45ypzZ/@r :]?RA*UҎCd,:Mkan0ot.~^ ɅӐ˟e_1 ~, oA&;r/f*}\#f`woGi.z[,[=s寰_zM;?bE%=gƏG^=˽N(bt*.wN=^ 6C蜔 Niskp XuŻaW镑 NZr kTrܔMdh{@dS,ePlWOb60^LNHMFŠ.K  eޕAy*k=v@iP?L<䁟7f R=StJKv H ˆ@$buPp [FT0oA) PFqYZδ[8+hW @ȻwB䅔%Po0[uZbEmxݜWZsb ǙURdj&(Lww=.sT9)op* 3z>t,L~: =ٛ7A] F Cޅx"«)o=^eVq{wfיB:寰_Ѻ{e ;F \E:bd.-P<)Nrt#0ߥ!<ገ4Rl9B:_'C7Q)ڣM{&}0$> ʀQ Ds" ~RiHXd$ș;-]֯?tX%@oS¾j)XJ"0B.SZT:CJ'dpRr Q^d+ O%7mSR4QK!N c?$R}jzPxۗ: Ua/ LG{6>cx̔`V|u7xcz2^9-PBJ Uo  O%N!RڟFP!Fww:\:tf::ːq=eUY~YށV\++`v |_27t@A~+_p3uZ̝~KqQM˖O?j,Nt\-_5S=NY jzU+^gςئHV8L)C"B-Dbn@ׇ:ir@7`$&+b$ZXY&^'(6]@tw z{Pt3~9裊%H?]Syyiͭ a/f9`gꧬ/ H8a ,ȡ7f08!}oJLT9 7A͈ !VDH)䁨n!N*r8D{Yf"@)GVҏJ6م!}ݙTRM;+rtAQ }zt/wGE MFA~ n4Q. 0CʺP1sd'#[2ke3ZK]xǷ'6ܔSGH)Ju7;iJPʮT+7!nf5Yz{JVskb2$k a٠:=c mZ60[ptsM5њ;f kW@aIƠ_` 7gMC\9]½A' L5 XO!g|S ';xZ'DPL9V#ÅJ)+0hUV-Wv;kAP[T ʫ'*&DWI f[^ MŸ!W8O } #fhJXB'?` ?Z[PzWZ 'n~px 0Ҝpc͇Ihw܌7o_ hS!Q 0>EAuV}s%=Td_˗M/@mOY|anǜ=fst`~ҿC <D븅~,Y& }r"0`=/*EyP[4Lbgů<r!`2n]MѿYknn 7Ԩ!GYm@P˧A< JKQ1H 0d/5?~jH.О@Ju"ɷjN1.l+c{J h rL!~E;Mgm.er#D8S50"D;=a3sG 6V( fِ@{9 n)(0_`=cU]T-ݚt0DT=w4 SL&X""S\@} HޗPR`k˹ rx4sS K6W O 8Oof+=`(kH=] r̖!Mq΂wQk-bq>SwuCwܟrd>tFYSMr]P^3@ P9zgtGУUVd\iJsd: `&` Zv3 8l~HSڳ`mԥ\Q`ɅpyxWKlme] b u`&AE;m?@do_"BW** Ś}ʴ(qo' <^Һ8ǵi'@bmyu|q3 =1?hGzTvD, γ{yS)U# h)5,5 dws|5 1=yֻ̆!e'ԝ@TBE,B|7͙>#V7{D3b;ѧ&>+L寃|7]a^&RzI V֯}3R T/kn. :aa6,FGI%앐l$VzwUfApߟ<"d(gPw^+AɹStx^ʔ[= H5b&[Ld? %{[u#C5_I]pG`VvO#+5*.L+s )M Sa?Uu9KJ$DI" (`QHQ$* "HDr$9U{V?G=FotןPc|欹֬UKě`m]V@vVj/@H.Elp27̵݄b6nj;k{C(P> Q3`.xî$h/G!2nq@.x? h8 u^r^ O";v8Q7+aف: G %Ҷd?p@ѳ qmOЏ1Dd[WVyPc+zwʶb 9gg@8ތBXɷ!uծ9رr"أcvEl *ZE!2{HoD"CAJ!>gA-m >߄DHkfT-XL *{SϭrWR-׮se@{|Eo9 v$,P5ŞcH]x>쏪c=]0-A]7Jpsi0Bt\րtVߥyR W3wW?u١<Kc`wcox&$S[ܟUA4',s y+[ b D y1Y9diG2!exǶQMUWX%xlh8)r r:=H1mW?&pq^6%yR8 |DޔFk+kj$b<L} R+wDyWS <A~^ jA1'!+Ngsq+: l_ق/,OAAr gP; ~Tgx n lhS|'*ʻYߧ~P{{*W\g 3J6g(ϨGouwnȽ/VY`䶲.}c w቙2Ң8E5GxzHAދ@^=_4;Au vH ]+PDs%pP뮲M:ĝ֎Y׽| zL_ˇH1;D; @|l6<K?W 7D6{B,'6R;?쇉#ֻM+P[zG٨ (Kgs#h0HO{+vw w |\ z5Oݔ$CcY6wPL.72 IK0y/;S3n_WE3ZMzf :.pwF`@/RFrwpLҎMUU+*pm^7pmn,!~6DYc/< my3ûׂ}/w$\-t=ziʾ=0GSg?߭r[pR}8KZj-X}lOވdrg|pmt ~LRA4Lgs  bX"/gAKk#W W. TY~b'(ؿ s\9s fD ){)zDV@33W#Xkvu@wxZmwDMۀ+^ ߃b*c!tJVA"J{"qHApgdU/u{wDHf*Cb Za`~ L-~JI_ ΂W3ԴoU{9eꊂzGt= F;iѝyyGA4cnGj Ah#-R ؅"$nJq1[ 'bMT0JY6$GANs;z s#,i;Q4vj852< nb.?juW?<9G^3rmWZT\n\݂vz@nhݮyKÞ7Θ pxUh'Tp]T}Iߩy=+h~k /hb:,zEz~ƫ ׻[0ݲ|l!q3V rgcR]]"B@08a*.<'A7tEb~չrH^W tJ<~Y<`wEw!|u A=n}4MF؇Ӛ=+pO I>6lq1Ee=b**GM@N+@rsЯȸZ b3U7[ ? g%r+xyB;7OD_a> xWB]Mf;@*g!`j_TkYȋp' Bɤ1OB|>]r j K()YtO,qX|5_F!9<徆r5N}›R .ɲa50|V[d򛜥Ӌi]{lQoX،v-MѲA5t2"%zc3f7Q{ Dh}Ӕ%!diz9QTP-@b' -~s@MqzP^ ʫtp;wI6a*ؾȺ񢀾=@Q@^#^Uw[V [yC )(~B0uW!|&zYd Ґ_I}=9/CZ{F #+@| 3L}+m|7$dP*\)ccneV2wAm57ALfW#»*xK:5? 4ʁ(0^TgU]SM,g DDrԯA[߫:@nza"?X%@)-$ 򱺠 #58g]J70N"#@B l?CTJtЀp=yndn=켺?2s8GSsw.=Y[_v#Ru^ (D>?z9| Z+ZJޱ@["5E zZmʁi;A  ʷp5,r(3k *K5_xբ jxMY7?ZbN΁j<`:;c@&H1Pɒ 3DH 1}Xmh(ǼmC0!zW k12|Ebɧ@i14Xpσ,|l$4N >?v: Zaro!ѵڀک@ՔAuG\`4ELҍt@vExymӴS%Zf&{84/RE{!y=7rɉTsCs[.ڛgS^?_ueࡥOu-oϭ Z,bޠ$.b)s( _;.}S $Ƚ_xEc/6m} mSH}= @٭x!C ޵{̌5y_@'dQ،T .Eg[~K_.>ՇhH\>0{tI|~%?%&@T0|v172B3\ B)j;}KT:ܥB= bzbD~W'pQ;FE Y.@-3)Hõ*[\Y[2Zs@~LTgH48eE[/%-JЧn.B>n4%v ͍; }e`iTpρ6P~b<#P UM%Sr晻>yta/&ɛ\ VmVvzmVX$Ϛ7'Jxd~Y7c6ɩ`ZJlπW}qY^%s`&fABQ@箇ڨsZqE{>QRj |RȻB59`惩^??@)C~[< _ &ykNlWDu[.43fm8TY;15R$m>{/3 D-?RABY3vsuU@.Aģ@-ܿ^鎤F\k [AګZ*k ;RKhNk{Ŀz^z+ b39;Is!:ob@"؇U,\=su6.mGDwȍL[|bOP4DҜ1:'O c sTO{ Q6 2ݽ>y¼(Bp[!"vzd<_zReqպe3jW N-Zoǭr =dyp5Sq{$b;W_}k:W7ȖqA|u!~Wt_l rq}4gs g\s# U4,%Ӝ ߀~Z)M+Ak7 1B86p!ZɎj^U~r\ XQ\GU;ᲆ`Il$v,d%1ZX/zǖqGPWx,XP̍XA*e|PH]9qiRDj$2u1Pĵt4'@}D\geP3ھog@Kw +CO= rN{?WAsD/&?_5!im63\J~W>@f@҃@?8e==^7bk(怹] 7=<πDv}A cA8*;<̷o AYP;@7Ċ賜Ia7^b~59ow`mν}.߹diq.wҗ ltj&[U5L ?}iKMŎKZ1C"4j9 }&|tsr;"_Fd^Qmwlo}z9bK$Dqj{d[mp}]mHQ]`Ak?Ns5M%?g*n60.ZG60Φ}AQO="E)2$ ^`&w.<&nf 6nh=ToQ7| ΆY$'dLsr@` ) _U.o!p' Y@8&ʁ[^ %aC.dn zy4Zh3`.3 ? MJn; + Kb&wfvu]T8nB/<Tb#mG"2B-q4 Vʟ`gsL9*ŐV*O<=To4Y>%!mbZL[w/CP4{GoμBnoPrsk.-?oǭK[ TO7rk)8p - itY\-EP`gCdyaA7tqEdALW!z yWseB-rM節/9!졓9MAl圧EP L )BMU, U-O 1̝9lWNU3DJxIt5LY7bsA'f0U^ -gPR5X q׀WNvWt~6DN5xO>ɓ_<)xLXğWAR@` )JPvvDPT5f]evCT^*cF ډa*7d]JjC0",y鱫[Nrg2oŭ߁/NV\ȕBm&zl:ytց-$oW^~ i'BToX-+2 򰕸P8 ~|Alyjw)M|7c%r3—+\e aFXb"{WSt\v1B"z=vA"V@oHGUKV[4͎5 x!DL o%ǙsN@۝3YA$)Tq[_11+3:@Ƣ^2_@^º L%_ wy }C< Agw?!hd61U7/ `}8v^s)=[ hoU^`OT[дF]!}f{ȦOR$6# $&*$z%f;䍆]71/ pNnAtӌ^L hH=Sx׉cR,b pM.4fRXUm*z@>xL<+B"/A]oTwzv'N;,`+ GkAytisŤ@hTJY [fٞ`Jۂz3L}yԫf5{ ¦0Ȃi_&J0Et|37oC߲rЯi~Aʕ4q-Eh8c S n1 Iwr@Ul?R[lW7P[۴c+/Nyg=&Bu q1G7H]`GSREf]^#qt2sŞ 3tj|@髽j? j*ݣGt 9~~'^fb y[zo09@8ޠj"Ж},V1 ѕzษ'vGMsvN&XU #ѣAEs# z:EX.=F :Im|GmRЛ)1̃ @t6 82*$J U9s.D_:~': vEMw&O!S 6L \NVjONS Īn)u@`2W`vg 62A;?2G ߞcAmM6.4K6.dدf>Z`=P3\VۧL)U'`[yow `B*.OUiD@ztј1?w5S!uvrzC`wE"; nOtH'ɽ=n5EлSw_!O0t. < fF8A$䜼Yfv>^} wk_;FܱbϞDK2*{Uޔo=MZ`*jANȧAW_ `oȝ`ϲCeeA6' ?'$6G%`>p]pUTok0=jzǴU@Rmd=E؝{T) l/;WŁ^_Sl n^'>=~d=yy%\XQ7^UB#sCؼ^N6@lq̰\pQ1R-A|o;@:L*'qRH-r+!+/D@πWS;:DoBi .B ތl4#<$l@ >r<}lE==_!'y2`351{|.`& vP( _DY t_uF+HOM`q-ؾzbh6ͺ۩skW_Z0gD#^Db(S+V"u 71wNuUO^ &4p=m΁n .hVcl, PK_uCprWXH>%Šnh 6E@u"ܯPww!DHۡ>%.ۡ ,D]&mIwGjr2')O c]wC?},nN _`0:ڮ9!ƀm#|%]3q2}[ zV@[1vJ0\.qEj+TS _S- !{{+9{bXpdR^{OtT2A=N_J![GwOAM  =UgyC}apxrYP]9}OWЯǎ˽v:.B.6^R]ވ4vU=PaO |vMdSË<zk ]6~păv Q9[1dwe\w\K/oL *F}.m=k/vz:_&@YHZ7Ԙ=l?Sz];Ē@|!~}}jh[yʶ2s<ɫFzQ,~0/@.]06`b!巎v|{H -= V5E% Zp5~ M|f&pV;kSaiHKed湇v3 H< 9;R-4$gBVP + L\nC~ks5Tl_1Htj0Q c@̱u_22퐞/U }`Gxr$YT#5TV[֠(8徊^u@<.[mLU3 @׏A+Y+ ^my|,Fȁ@uPEphLO }.vN:N<)g RMu@oo>l{/ă{ >7d&;<Π̋^Ac!U"ue[xO?$ 2n0$ Gʐ|ܵ/JQRJn7-vjsj K43'!6$6to(R5:#rsճKw諫2o`b&c8iSrF^t W; nQ/_5#gSۚT7|d{pZ݌Wby@ Gu ?˭ޅ4SbF_-YTpGE+pOs=dm|E5o@OK 5>T^TVìOɺ9BPscߦeM` EEP-TiL:sy"P[)~eID ptDaQWx VՎg\6 9n] |̯bb+e7Ybq5 6z)btc0랦S@WuW 43, g@CϚ7HYKKAnz_A!ʀOy%A)jn䍲t[L `gsy7A_xgҎ7&j6eE6Zj. -^uw7Qk рt7U7集~[N[kK4M3`zEXƥ>)J{%vayqyOo|Yh7b?Q0i ̓ζ Ȕx-@Zn69D_4 [+^wuzNzjkN}9Ɲp$ʢY_B$7s-sµrbWj,h[긽"q{AtM>p#]x3WhB,;~Lci= }2C"u05?H,C´ !3?vuz&|+6Y&x^chA/o![hyLUٌ&X6UTXXy?zJ+~*R -b+rPE_q j2/ȁ%`  W@cFn&B<hᯍ&lnU2j6]/hUBT*:`2ѣn:ȩbb3w{z>07? tSyq;b8Fɇ`oF[dIGl jgQQ׊nj!d*,n/\2XU- K&>6-45wtwprC n/&r뫻7U/ʤ`CfNX5(^@/tYⲀ &z6+@U99g?Dś{; v'by}xRm@lwQ|חmA6RքCQnd5ܣWA3Ҳ]q7f3N9 B^+A~b-6-b(]=;=\L?o2Ii@W+!yQ&} 9AqX#'y; \,DOpRU3ݓkL)w-u7V:z(yGj￝hU6A?'peG L>hNǁ2u¢33 {7rN&΁x38OKpd( jMfAjwHgW4 X"ٕ!1^+Oʛσhٕ\u%R[0j}ss jzNM;rWXwdMĽE bކTCw;ff:xץƃ:/f=+fBHuDjMNxMtp$Q+ \c@6wu f14We-;uYPUEy*#] W K\ā]rb`83 FOw K$Pͽ]!k mj>`nѲ xeh-ȵv5MDcGS/Rdlaϩ ;Mb- EbDI;"&P GХY(v>^-)VTY 2R^3Msq/?0Ț%:.~( R %51[H j!d{4&kZA5O%?~f*xܢŋ#=-y$"V@IqI] ?'$""SN[t0a *~ Qi [pU]⭣ ა/˪{m}` bJkx2 AҖvwk6q< ;#w}WV{% /碧+d]]VTkEV'u<1K5L4Ks9yܵEM/@̐mwj>ECNek|sb&ƖaݻB?@ _'BF~2oxx\N|?"?xdpp^ȹ.!blu LWIFsD2AOһk S m {` puH^K b7嗱@\b]I1i&`^5޽GUgD7X[_o߄ؠz<%:'uX`OU 7Ns=wH<Rfj`%_b;ov@=(d}c:@Nn)J?orC8Nm-Q{ɗA}4Э#W+ȹ$:7nNP>fH \W|:5o\T~H;kuDN @IYP7 nPuM #B&] `XaVx>wl `[Db!A"UH%jA5荐xƻ$cMaPimk!@SYE.aMޓcu7@iqؼRt{ujKs!,+pn$j5+@ʹDg@pūܡoxЃcW)i>s. IS^=[O"f=^.yXGl vsW?Tpo(eC^Q0s}ٻtCEqp7Ҕ u^.bX><]T%%S#˿[b5n$:A4TUM¢ NeYS'_vwƈ&vc> rPKcZCy2&_T;i0'ժ+kb8Rڠ ΠvI $|.? sw.- py~f~~*jR8rALpǵ:Sxt?p] 2L ɱ kGo>gK+$m`$fxh !B79݃XX>`(nyŔE^aZO!P״Ӂm0S :zуP7@g_˻"$w_@͖"[Q뫾R߃QҶb[0YK~{- D dnMzҋ'{A%mz.&?lk@Pl(+r#W}ߎ?ƞW}nK,MuAjUNMʹ3LauU]H72EgJ= T]EQTU} T cbs@7z=o\<"{r%hK85VP( mHֳm;Wo9Vmv}:֜[ăƫKrHx.ZAlXCD.U b-]9~7wC<7"උ\6JNwH 6A 7K>~}j_E$W4`]olj@w+ Ћv8&4U[ cy5<a/O E\&v--{L AxB'r 8 }A> '6:-~N\/hl[sas?GOx<,-cX{u/D S{ 8=HORKx6+-.?DG! F1w jm/MҠ%N,P?`琘Keu5< fb iz !=/@Mfʥ \!+SJtnu: D إj6_p]13`|N? /b #KU@ܝjf^8Fg)* rրX-VvSk 5S͆dqSެ9D6{;#J¯GA^T4ki%ڹ5`{ˤ<z]=b)ac5gmoT|k7ź jݼNV/׺hW_8pq^n?9Z=?Bp- p@7vK.ə'Ƶ- m(/'eݐ*OD wJ;mP#v׶7Aݏ8xY~u:Ǧzk˗Dzspun!_9@|!*T$9 tiS_( ]󀿁;`G.zJoU>v AS䇮\Gܛ1U>:fOAKMI n,:!eG.J@̲!˕ |p誶0Oo5kuH{T5!Qǖf,媣 /+k0Wc_J F{@-7v Ǥ03?ۂ聓Yby.K,Qs7g0agj>DxCneXPozDu~ԭԟC7MAoI:a_@$ ۸^k)3M;3zɞ*;Op庨U8w`vw<%>7ֶ3Ad'`'6CM%D[03`Z:U]{]$ //?!5uڔ_)[fvMMoA][r \ʂ\Kzr^V틠s";PBs1S+zs.Anɶ]K=O\*7̔@jꕺ':aFxڸ˔\g6pjCz*7y8<: <66]hyL^~:d;p{y`^D~d٬ w-=>!> 7MUƎs@{7 ZfVd7ĭa'm1Lv)lpwSB0goTfI[ ?xK6OX:d6ղ׿]ϻ@K2o㼕JN*.힁\'2f0H!Kvp({? w P2TEf@|.~nf@k/YH<m{;hD;`M&X|D%B P %7A(E bI헯c?mu!#gMdQp==;.Vk>5ısx=9'RƚGL{GS Du&_d̩nh. t,K{H<ֈP b DCUp\أJ 56po\Xҟ -@- `j Fяmplt/qV7WQ=^1sv{DƋ2\&D5΢0@Vc+q^|5L1]A؝ZA׳U{%Ĝ`^g@gX6̂p oY[>Rk VܥZ@17ց NJ ;2d9dcP z5]ڤZATj?C>-Uͻ`GC.F/D/qCYXN,9R[Ìfjrl ^;/1v=:/eWwd̾=&g*{1UX)cfߓf㧃+!#d:A7?"a^bIo5ٲgC:EMf [?@CwRjjB4\ CtJ~e2=],$?*כw~ -M~ ~+? !NzR7s%c7k`YJn9akFأםm0אL~%.wی;@|YX7dOc.DmlA7T d3;ԵZzyH;镎Cz @aqEXaƧ6g/]!_gjoHUl.xL׹z8)f)ѧ̳@|>AMUT[yXl*C 9opL3 # lNGVM5Ue/9>Ц,L!:T+F7?+x,v%*xK˧_'g qB| x0to$(~;ثOkoYv7BP7@ՒCb!g2W]]Nn+>yGUOޮG,pp ۢE 3lb ?F4V WAM+)&Cl,O7iНTpbGL3nḊހ\?fGÓ9'7qQtQO3-r4Rd4s2b*TI͓?J5n D_Q]#UOBU+y{ 5T]_P)H֑ ۋl < ق@g[̞ n:L K R673|` +'`Ute@ΐKv9n(W n [I Mb_l1Epn@s?/ Dt< d&yATbA |Ah ը p_alJY%7( OBlIt7`4} p"`UK/_$9뇛a7/_Ȓ.z LS <DMُ:d '>ouW/^S;!9a )uLPԃGtCxdw7G!3D YwӺޡ3d ~\J92b2yfJ1or2ωoA<.s;Ī;?ݻ Ip!:m@;U8HB=y!>bS;y[ )-d?fz63j/^+z.a!r[ wGÃT`5SqQ0 -=]!<?]Be}vSmQH|;@MQLXWt2ڣ)&!gel%V }mJ:| g#).yĞަ zm~%^hL؏\ֻ'oN=2w ƾreA%m >:AV!E)U5 rwC}|as |ڢ_5=~Z?yElIy9bE@Oɗ@oN`Y"v8!p ]T3HL@Kfgh,'~teA,TkPd>Q=y/Dǣn,hy * 5t路'y֐jŎN:W=ZpM^oWsn5\d#`<{Ԇ{Cyp.f!f/͊̈́4;wKbA4;JwHyTU0ޱ|fP_–Ner_-NP5UgQxڶP, xFX l(u!/b.O| y _%eAWWPԟ@n*쇌^ Z*vdxbG5H6J]TKuO;yÛvLqM7~vySlLw3Rc ;m2i<6 @k$ ] `f<DϛaH/b z+D> vh*=^}Rĵ7l{ H>m6Bt֔Mnj?39$>H,}ځI5@kܷxدCf#Cf'GyjDe6֕m $^s@T,*)T wVIT /cB݄:{Wet9Vz@Qay 쀰dN0q)84BM ~5&}+ҿ(B[ kLmbؿ]-ϹuUjzS"C̾ [GS.൑i ZQV>rl s~'Ü><.WZ`S= !C f aNB{A\ aku@~+rx0*  BeiG]/Cw<:d֞$SzF6~ ɇL Q2* "ڂٔ:,9bs^.&hxº w2E"}-1,pkadqPꥱ'O1?m_i<3 媢̀ G(]\U]XYq؞ fRg2;PO˷d03ׂ`F7ߐ^-u%ψLn&Ud)g^m=1L4Y*v6u2@ l\6$IyHClTT=rVv%Τ11 >ڛkDY-yvV Vߜeۯ^mw_tf!xe3`65 *?NCXDNqQ_@c"robÑٷCY@#8 9ߚPd\\KQ,OmH֖$ [6Ad9Q> 7ȕQ5 5PxA#o Y]} d~Vi@s|#n{)JHy1 DT{]ܫdNWp\TkЇt+%ؤ͌nr& ОM`( nzX]LMȊT JT`*}D`%]dyE]1#ޅ/wƮyTgwE1+d LO͔=!&pQ01+*$y__\]5Ϲ@C5\e٠7obVW0zN/Ŋs{A/s}x]ŋj;v5jGwP<i e= 2Fo١S &-a% QB0)Xx">X(yL` i&*X5C?(uɝPʝM]_|TKU_A:`6+ ^`G)H @ \?z`މt5.ׄ@`B6@ ޜԫ`~#CT1%A.+BwnaqAP- r sc X`Έ maq`P* d5oFo_\Mޡ0fv) UKh߫Rbx em^}/ UAЙcPj!:6>ȡfMtDP 3z͕)Г kWy:?ٱZsyL*'u)0es ken@UF}VLQ)}C [D:vw# j.rF0-|/䘨vdN8 pwA>g(i鮀ն+ ,F(َ ˂kOrqR#,VŃų❓+1߮U>Um \-Y䶏obm,y9IK@MY }m>_Y?3` X0y;s7ݖwSײ$ D'TyQӢh a_wxABXP5]{laaW!^0δR I7Wmyv/˦ @%%꺜 jJY86 妨[*@uK-Zmrr "0 nyԾ*\x7@BMͿ۫(n0c.č~-b˃w7 J, :Ȫk& #R~@?kB4M!\I s nSũAr H]F4PL_ yXħi'+^=Uv,ۀ\[Al*_$:I)nnpTq@ۉy]A$1zC: ^RC#^Yj 뱽~%pչ^|xWn=FomAL>= J*IT wAĢe67U x;MaAaK1W[' ӇྱKj쭄rKL9e^A6?Qߖu̓ߣW-*&0d /9W=Mw-kv/bR闫"CFmHmHc\~%ϊ+* f h6D~L @ꕨOuž.&)nh\p; `3PQZ/ǃ,#W Oϱ*r ;7++ 8d;BN,9Lmf 1+D`dN< rˌY)+WiY @tt$6C'Gه + an?A"l簙9AGKnו)\@Q]fV#/Wy`l a`:9*/@آN*xfBb(TH|g +R׌Mu-W zOzGL'߄?EJ5Kve "Z J,r\v5@oAgM e]{9kz=9'[;<4F\d#jsZS yDg7k<ȁm- ; ՞^ M&L߁JğXx>5t'ʼa! /_ALۧ+oI4#t&@)?EV $E hl$;|єH g]ሬzyzwoXپfujԕ7J kw,QxcNc3 -VKi}J,K#nT`@&Vg};4z5~0xuF[pc=9;ڼj}m/L=ئљj{9ZA$ JQ$ T`@T *"(" I$)QIk9gU1AӾn5BUM(%Jz]N0i6qRVh 1]X u4W\&Yf~b{5VV/='=iE [I0CA9`{nDB<'Rb=zCTZ>g&0L Rc OZrOݸ?y/svQd5_S M 6/ʆRo+_ݦ@E >5\o'=TD~n돀]bP_%@r0a̖HIzyddmT u(H"ϠbM LLW={\ElCjf1+myD9o2F4OQ\T(,3l-fӉ^ v/˅Q+d D#!v^ҥ@>Q`^i Z6AVF_xv2RJn9trNtU{ &^Z[.}Ԏt}au}z=̟k`rڽ jjį~y ̨a?usJL*a@ 6)+Z7 sHoPsM?CHq H[ĥ  W\Ű4t'4y›Atc A6h ޑ 20T=W^2T#涐 A#Sbwdkʫ'!,b[h%lP;&ЃmI<0|@q)ԃgAU ggb|rqƐlJ&=),{e}F _6t~bۧƀO,W\'W }^ѻ^=΁.-{3rXs NSQ^R!l@ȏA|N- >*y) ݱc I5˘ 1} $Zۖ-AqSg``nb,m頷~yA, -"r~x*!Ho9" @o!d%;0 5UlFA" 7߃}iS2Yː X㍗u ٫`N,Jsδw\ Y?_\s*0bA rv*7|\wެ|*2_F߈ {\iPc@l;b;v1WJVc@_2K`lryTD|2or>@$+l|+ PVS ,of0`6]`y3.cY @NvA'+F@Rt`<}cdM9knyư"ym_Wч~=EF.^M߽w~7#{?c3QLyv҂)q #6Eb67?ߛf7EnVVp_tLmH/Ι6\oV,s!z?Ӎ=3~Z^>c.@fB`/?@75SAS VKQg~{bUf.Ħw![+{:P\fQsq$]?AvA_wnت2;hHo {f'Ps*{|H;j> nM h/y]\BvHk+ 苉:`_EgR~6l`So 4R1O! MQ.D |(WL :694m@Q[*h5b_i`zRr[2`]}[!Ny`6=͛DG t%{Ǔj>`{}p QNɟh$<ȻK#Gn^7eK`G)^N+l`(WR~4kUejͭ|V?tWӕ?,ߣaA AK봶xMSebbVt7#r>MG!N1! mX7P> )9GA19$@~xjQ~fP[6*zF!2p[}lEȦA5PU wwB>r8"%} Ė#пSSU}cjx8Էq@Q] *џ * n¯a~ވ^р9sf@+@/y!%Ipؑ 2fk;yXl^QUw:\{?ghP')rHlp VZ;=9w,JrnTy]!VC-!jk>^[ClKW&*@2>u?? p%.Hw;#ݡ[.ڇRs ҹ65=SB8IkI gY9,$C1oŠDڠ.֮=^%ƶNd~A|w&bA0fm f y| nkkZUfZ.7g0cd7 "[—d?`6?>qݫ ^|~“=0D<j :  ډq7xB; zu_&s+"Y̕FWyʿ؅5ypӵ5HjS19a*85Ɵ3e^5įyQ;?Gp{U{dkN ԌK#,o$Ʈ)|IVaky# tgBlk v> y!ng^P%}=x( ~͛`Ϛ_voDIӘ y+!~[^̴7A@CZ@W4*Wk?W7@ƙ' V9σH4pどjK^iuI@[Sy//Cᾅ1W>yZt\=-A3DD1`Rbhh' }j(xDA/5rr J[n:)C=nK;#]MP%ͯI]!>PО(*7rD:p 4-@K?o{3S:OR]B_`.w㟮7k]fjyIzx̻G[ԎFi s"VEspoMU@223Ax- x235?5=CV9_tyA֑3/B bϘ@,r V>(΁;ur@k=%JH׆!PսcB@D:4;F~@ 1QTWxAtۀiv-٩ZklJ}z+hg$_A𚸚@ }U ,N 5{&;&5!VT-N^I6t | ?vK= ) WM_)GE }Z9n*UzrDZ&Ai]b䵑q^CCޭڀZWظd}=t=c2?x 9"N7> %Du[^!y3Pao`6߹g"xԽrUG@t]~!{Ww׈c&jP]mߘhTr;7Wf8x8$&-C2[ KD CwirQ70 700ȣjh_~ ȱCqncNdM|)[Ck5IpeK3 xQ&`:ħk{ ݔ]MpC4l53 xz&nOyMj.FgA<| z giJ@A t DӢݤvvgɞ| Qv%|jp\:CxG`wF&BVG!Zd)XwΔ5"1 dzyGNAm6`srp[\l0&pjAfw8C72ǧ-?LS15,èD7=Q *}۟ӕb7{^?;2XTL {]V𮨴Y5UƂ&ȫ6tsfA`pCj-{jzGnk K{ AjDW_s==-C]t*f(KNPxlWy} qD/CT6>JL[* r߈  ʂCv ۨf#HU.H~u?g_)z-|ں`?]|MKyAq#ju4WJlMT.FǔCQj(bnʀ충0<koTx9 D"j!D L(Ò5ҵNۀbrz~'VypmKKw ȑ(fhI1Ȝvhzm (lq Ȱ InLg*ۼKA1룅?"?[ OXCa&$'p7ϩ>:j6I7u K*bB ]6[ (]Q*C5%h?z؝*ޱZVVZ4|>N_aJr%1ޓCfѬIA r&[>h~;ICDys@l,Axz!rsb:ː}< њ{?RV]nםgR9}O8T@oW˹cpҭ~Q~\|&:s Xa<^k >0`S}t.&ZDOG[@TkbA 牓nz.{c3k* fǃzW+&BY찕  T P =4h5 DI=,[lk4,RFzFK{ٚ=ϓ}Sez usWl'}G> r<>znw8.?GDYypuL;\7~o&GO +ϖ+ 0I1_1 -$b@+[?A5 WM7sKқlO0EcrBﴋY@Z~LlPpAv֩yY#߯+@tnWL^E ΖʟD/mE>̙r2e ,u*g~C/SlrKdWHL*# A :PkymnUHBM]5P_E@z\@x Oը!? tW ۯ눶mswB,,[@ƭPWcMndhMRE9B\y S147wҫkŝ3U5y ң[r0&Sj1 `ib7YVEAAUM3ȱ nq@wEƃg\^ܻT]D*Zj~\󰼜nrT*&/B*ʂ{9m":`APئ[?SL> 7 !u'â9knb WGAs 'ȟ?gD"}7O5{Di~ '`fD5JdAP3s-ȹrb 88|nwE4vY֭{ˢ߈o5yhu韮W ]v>PhKv߿7y}~S~Jk=5Ax]UXJ*V Bi/ N VT92!HHL~x=rHM zA0ƤMYƖCj&oۥێuE޺*X7dVWRo cZ| F5Hܕxo^nU{]`s)$zU4QTswc1d#DBO`~-lѮJ'8no3nVBn&DWR`Aܗ3@$_u@LdMeC>u'$$Ʃ]f/u7="PME@1ANVy@.JB2JV2s|@(UJ_* vG' SqB+oV q?\&Ad9r$1{ > ˄@Dܗ!ʓz;y9{o|OW?Ͽ+"yooJ梌+}|&ߎ<nuAzs-d\%+X.ys@5IWdq|-OH=KƺA+ϊb0fvm7gkymU  t( \]tpF->/Bmp{kfn$.$ہvY<#M"Xpσ9d!onCf('FU!Jm{l EU9PqDpF~ԑTx߄TPF;QPܭZ|| ɉ)P/ ,*Bpkzȱ2xiv0$N&D{r .c! ށZs2d| n ]/6Ɇf$F~6)x (h`lQ5د 2ua5HO;^#/r,MԘa^xߞ\v^#oP;4x/MTWm*D=?[w:e{iD[oEA ߋH7Vp֝N{T j7!5 VX?x`/}QAस&ʀ.c лtOQ @6HfOU D^1ekkς>No$"|[< Wӭ. Ȓ fk&^ u;>_ ܅h%Dt-4:r@qc\sApKJgүfXe'mE`ፂH^{I妫A6'ԛJy@ )pd{ [}vBy #*~'W߆!Yѐa*WA=og@5c?Atia}9? Gd^2*G'y@sW<\7=:>N= H qm!fRvߨ^n%Ȇ >[]_V!<ZbOe 4=f#ޏQWpcG ̎A -!v-T`d?gC3H-̞=XAU`?^NB+l KE g # pث.'!j{п;,jm~dW!6]SsW4΅FWn `EW ay0 eh9DC.%/ۃwh>B3|( 3`{b{!hn>CZ)!;S 2nvR< X^5XXzd}q/tr}ErG9f.T'` }x)YqUAceEo O~ F# q).JV7ςn)nz5Tz+^lt~ammc믫b-"zd+a#^`xԷQ?Ӌ@WSٙOP/"AU%C: g=Xy!D{7\03 ^^ 9.`&s SUo&+].Ttk|bBWug!]!1-hq]B:a ;@,o?j#q2֥}yp;d<,Ouy Fb> @7@V +FQs& 1 nRIw3(N&AN{*fW4DHNId/-Pb2A`#}fg v kL0s@>I{GG & vJT ee,Y[ w be9-n+f$U!$^nui5b/=6'Z@Ƒmi#JdVf:ĮS@>Hڒ.(l'@}VQľ"RㆀX7 (Atul |q\ @gJ+OIЛDnGuqk=Db0q\sGBN5 uc^hF{O^Hsk!zܙa#V(,f+߄P֏r ;j fBuѨD~zRtܻQatrp>mtjXcmש9Tk-dިx%w'~]խA=RTB͗]u+A~^׏Ȫ^'m(Y3n APD*wEȪ ȑ_#k`-@3/!om׀Z,vx[ H@=; dFjvd_뼎Y`z ̀`hMt{P-7AfV&+L إ 0-uHޜS2 }6zE/)drQ6_ g[N>U UVh X$|2pu=rQe7g@  f`ʻlUrڃ[as@~$ |$*jv۬Io0=3DZu4 YubZMb#xCmW(ѓkt(z8 ,yM5,Ӓn ׵cATWG\huUV [V*b@gI1l 7$Xȓј|×r$?ռEt֍o?S'@4XZeǔ]|dݢyOyJ 7Z ȟEX]p+Ӣ(^C</@ei0l {z*AO6$DE) d |̼h4}6iT7+lH橊T OCE<b)b Br 8fշ-Mh|<SPp3!'݃rx;巠o,LOgd0.n󪺨mW&C섿 \ @4-MLGM.bܐa.Bj  iCcD6V 201S!3v5Яȇ Mejh]BfPG d2 B:Y IT;; b{-T2 "ynͮT/Amd} p%_ToY. a;<4Hꆏ6AljF, .[]hwBM'쨍yu0e@p:rlP)] >ǹC}1 %ܧ- W@{R`G[{&|YWTv1a7M"At̆9W$ʫ j*ۂo%]P_ѮhvJ{CZI'uEpe m =Ծ vx=@(%!||[>|ƵwOc*?liKs7ݗzm7ʎ7=j{2M<_wFSVAQ9z/2_G9k[-|d&AG kxT]Ē!2*um6nr^Qh[iҬs!:1 (+GZo/`FgI9lwݕHoւId)ңgu ]3u@uS_!ٵKA Y,y`#s*a=NdG֜_nu3AOOsAT]? v!F7!{)D5LK#11C^d}':E ~䎂ko*%u3f`[ч`cL~up+Ϋ+ח4wĬ . S~Aȭlҙ0\{9ۥBjPj|9UAPʱetx0`xYg#!qx#U}f s;T︚.EbMDxZG;LF٧!gt1Z>>A5M~!"B4>)D}#8_6EHMOO@&܃2؄(v*z+\+OC0-M>{*HG9@mqʆxGz;tp̭aWiJ{x,k'wW+. ftoU? nEbO޿_Բ7'e_t^?>{^:[Хhkގy,8x٢{ 2x=&ddCuxg=-\Ӯx=~ *D9s–p"۵Dl<'8Rl6A)JUwĮzȫnz fzp/Ro'?"Wp NsB!ph$/b.$~9`Iӽ̫`_^2 ˍ RρnSDMM>Qw24A?덌 D%U"V'\{ :{nҩ^?]qEUrP o_E뮈E~D5T/9t;$Vzpx =$83BXF-RBbw + r?! {} pc jf\Iq8j/C͠ v#!kAtax!Ejlk;LV4 K^y`/@*|vy_.)o7eQMI@9N9<\yAgΊ 81W{ğBdXw]{8 }Z`2h37`ahN@}!R=eFv1߿O5! ~{Y7nftۢ| RGU(:P3c]ƫOdk`;]܄ &ANj%Kp!J.PX?~Z ɷT=я<Ė_S;[ C'ς+lkkC6=Ay;!ZCDSݬĴDܭ;P5QEP;3&0JTM-]p5NO3v<,O[ *ŨCྉ8eYxJ`7)3ȕp`9Ioe1&8Ə~Gb*k/ ~;/Gc>Ìaz j>Kk+g膇sеqW>=N [alp2]AA tJ+SUCA8AMH?ݵ]sˆeVXeOW'uEvvc~nzkV#Rs]G dGCgrb.잆؝*+V}9 +r -VA|UXUvj#_CFB7Yg 80{ Ƶ%o ^>YBp&` 풙0e`7D_;{\~zYz2ְqS|ׯpIDATwJ7|l N^&Wi j1)QSv;kc95}H;-=:<*NN@(/R;'gƮDt}YS瀜Qܙt@>.W~Dl 3n$ LW@~%zy2\]_*nu@WVgAtkdb}==) M^w쎨vwD Vϙ8NW? TW!x v ˾yoP!y{} .,r›aA( LU.*Ζ?bκ`oslP1 I?P(viF]]MWy>u\=J؇@f)IXAws4:r wAuÛ@6~ L{w祊1}Q^o,QLW@lT [!ޖa6\+4r=Tk x͸XDdڜ7а='V=P˧{1/aW<0xbeא*fU **VC5]&CIB^j4Z;㲾. 7zDftN֤]2<:R@}]dfBf^.񦧘.M=\{?1|Qs['V3 ޱ?J;pԑس^< {左<+@<\Stb'M;!=/{x Ef ȵ;՝vEoK SsKp] ܗ5V^=;1HUQk@y1]O.|`E@T}/An`4 Q0  F?BxFųw Y[4 +K}: ~A^&AT8}&qɌn +7|dg5p5S%BEދy*ZgBzU~!91nZnWfDv_Y*Uc";̧Am`+]Nj| n5Yn wR{!qיAT!Е\]gt3OD3p-S.^ā~qvm-dy1=VS#Ϲd@t(Bt3)*2?ulOg4U^~yK6W}-|X)k.ײn# 0W`Do/A]RcDy[!P5f=FTj}x0O믍 76-dkmz9'P1lp7w~\~r [!xST ή ߅peC1NubQt(1씟C%:AnrUߪ'pHt9G4wxMi^P{|6&p/ᄼ S`۫v)x8?`t Inׁ;9S[; liA~psxl$<+X~[!5!jnk A~ѝ@~9 &*f0}z.` Qpbgl"/oTI_PSs~2K)PgD52[^cP1o4$Nٶ`:{TOeru_Seo7f3< ؋L`d~W+z/`7䛢>(ĎS]f>/y"nظn=?T#q(dPOyc@*F45B.'U M\wq'n[čt뾅b[[n56"z~hqbt'KwpC¨5*xo2AMP7uIៃx 7[~QMʸ!W12\=Oll-;ҿޒXyKTEݭW4X;U̻Q+U`:QA (p,gqsÍR.r^b?Ċ艮$z'o}bmYuI譞mQ{$^ZAo25L~XQaMZ3A-U=Aj+)jF>|O 96?w􄨾CWp_La,] b .!~~DNU& <`~AdRnb^ⶁ'+J!* 龠JҖya?PA*/@T<|T]n}PIsXזw(d'Eǣճ~&P],y^mB&/B G{duqMZ,-OzEc]&q.7pwqrɗ YO­yjѼcT?Xa]賗MR]{чv=eEYn1ԝJl=zYQp3@'r/θ~P G- oW↜ ̜W{3 v[* N _E?7+@5zXjCsrHCdZ pU[uD,v*\CmC`z tGpL NDA_U[N'cA) *k"c 紛LP fXspgrHx.42g9_R! U'3PWxG=cAbnkU kA k=y?AWr]y/3yځܾ"ЋboMO<‹b2MKb.䖕?Zw@R*jGʵa Hk&г;tKN_E ؆3'N&&B!]>ryLaU=.zȆ_kiЭ#.avys:12^X%}:IQUUxⲹ 7^#[\QtP<bT>S%CHTMUNtYȫ &C4.8c@4vI]Z5Ы_#ݔ`:D6RE!(jxpw+w_A+vb˼{+=P wj5Cl>9Rl G>+o#sP甋Bc;5خb,މ[hjIG 8](v?ys[ lT G+ a^ y>4'̨+;CDR Z>mfxT8m2+@"^)fusS !6?`Rx* DY^#>v*ǽۣa aBAn8ݶpR |tY.s2teO!"\>1A/UDd5o-;|e[bAQ$L~ba4 Y$.eOBz[VX! $n~Eț wDK7Ղ@SFxSO A4N_Bض9(3 A7^ZbF˧Z ,׋%&&Țnwn ë#$՟}!^Q/=DQ.#G!xbg@G:%I:jOUE<h+/ΐUA3*pdp<l DKu33AiwfW*+ebRrTm= :rޯAى6brA0 vy?S%3DȻ$12񉌁,(fm~RLKxǟx/\kl\w9W 2&v/=KzF%@>h*ڥ@?C\TU*2 UU7ΌVr``/M5A~wAϕf:c[l̖!hȘ+(A{@^g( 1/ff%d*{@ *PO XUG ,ƆCA}(up!³H:Y}}eʹrܘk4RrX ⶦҗ+ {F;/Kf7^"TK^?0=Cn ćsH?-ƀ̛%ׁj~Py#R^M.d6'}aWD!Z :RAgbs.Cqh !"pW@[*ިR:|8Yc`j] ^WOgA2O壠rw\~Ft]2 \nQ+d[8A1Z:)^+{JA\0 猩ܢրT ނm\`-,s2gLq (lބO^5M2}la"*?ɤTWdE@`)mUĆ 2bHk*m.$)qo)s _2Ft pO:z, ;= XKFt]ldn9=>t7I|I^l ޷{VxjiFgY"ک d^KPl5s`Ff=*~lތ?OV 7z ns2Qďwߠxکt?FƂlbb?ȃ8 fK) 7ʀ,JocX~FmNg;lQ3_l.yK( +wwkfJmZ]]{#y{3wKO{`h/BzI68m% _POW]Rcmyqes- l Kxώ-)^% Qi bctޏ < -ځjm6' ^VӃ D0Î1o 45UwؖE|Olv-+'ρ-Y[]ػ-sMnOii(~d~ni eڻU r^}h|HU7· |$}QAb% 0XK:3PMg ͂^쵨Kd&1BxT+xبˀ=71A| v0-nqjlf4UT(hOpAlQm:6^vql:(vD3Rv&3@pΠ7(|@W|.pʫq3LRe5Pz6Ȏ[bU0Q7} ߅h ]Qh}kԁM~E)E9Ubc z+( c^7ZN xfAţ;v=te($[-!=wsrZGٚr]{:;f?]ku \.s;esXo`_ d+$-l{3@+ -W9 {mt8~)dȦ]k aT{Ml`?9CC뎃)-^d$ >3*1mыܭ @}㿠?x)*+⥽ s($Ne~(Į&~c?뭕 3]*<aI :>* txw~5@ߔĽ>ZkCPeG4 eDKĥtAkq dmzAFV{{4 5y >!VJ>*ei5Y @JAwW+MsȢn'_ǜv ko{Udp'v$OM@.PTYB}ȑrATp< &n?uA}N]2o2r\mxTOp]@0a<<hoy_fُ\whQ< RmtK`U&p07] .ǣ R}AFJAT9]DDSwF;D=uLhd,j_ Txt ʲfT GVV)He^: ˠ";43߅DX, :=nM ˀx֫n*.0ůr?$uLHҷX䯝'd~s<L.o K'=oo`: slHq3j:8,_W>gobJN S>|Ō[ν/9Km#ڤv $¶N(g2ػQ*Uv}? `ߎV Xe[@Exg4 _B_%%օOCx,t#!:"ƻ\ ׳5 dHO|} 3tQ]}Q1y'Įà'dnl ť!߳@r渝U?Te9/  @& |+G;@MtM `f[iWq7d{-V [U`_T< f%?ǂK0r nW8gt1YXq,CSv"mpDت`Svwo rq,xZW z3Y|4uw~LſZkӕ@(쥆`mtc|qL=Ioc8㞰@EhjW:کb%@9QixAN_:*BdpM5Cy2Pσ-OX;TB0."<Ո6q#Dbr ߗ_Py CyOA6_d 'u^H6E!{מIqi.ˉ Sм t߹R@uflA3 '@a9]_3`yzw9W$$A@2HIA((ɀ" *f@"*A%HA DPrXk9}=G=>zkOUEMнul~0 b@h#\j+Z waS(zʦ-sM@rN1RyhuHm`,A7d/.ÁlT@t):f>ԇr]0bnO3ݫ[ڭn8!yܗb,}ߦ{ x+rmoF3Ȟ} 7hXu%7yOP-4ko ս>PO[Ҧ L΋UU;Ad7J H _w{>vOAԚ\W˖b,s|x \/$*ChyLSz:].׋'n俠q)kb3rV!ZPl⚭*F>Vy"  n[@Dʿ 7.=ASaP߉b /)-~7U5F &Y7FAqϘ&,qR|J|‹e@>M/yf'!W5+oRʭ"=9!֝5uj^hW!oϨw vg#2 t VV{ 7xrľP θ3mCh<&5w٭&,Z!A.ߔ?v,'6giV4n`8x/;xpmcW~;vۻKZDUP[]o>}&!v90 OA/0v"䜑lQȹ5!k__"/{h:A48|UK'aH- T]U\"o[Q,}sFh-b,*@zy^Ue!֑W'q'%e9o١iH>x6<cpmÍ !l^ :B6yNSki,EK<"2A׷c6{+㌇A+i9 tL o g?M@9p0;o@v ^Q`O$fK'kõ.\i>ív+c@g j+5ȸMY MggXEԃ^X&x;b7AtR~~ďE>@O[ ʈ8PzBΈC5I n:)Zfat !6ӕ3o^a .[e99ßBʎt yOy+bFS@?TG}`TH .rT|>݄p(=)k(?!hQu\W-Ml-cw3(CM U$t/dUzEP-TK-x': o9OCa]mz3b_v Awpjz/I-T&GMqb~Z+q b5/cL\A,g z;x<_=8@~əvHM=ESCc̼ =SYtݣQ5R!hGK HASG@]SN So{\CEC}:'A즆[-.o>{o ES12OYJB-N0NȞ:e5|P5Ws2Nw0߆ dCP!xCTZAp#w'}w h/v)* M}1n46\RU7h%R`S0ȏ‚Q0nOi7vVg%O{TYmA-EӪ"a3}>٦zo rSQP0lw/x{e-Ujw]~Ox+CJwXEtg=a=d۲ Qrx_~hސ@?A{e5P^U82O.읕 !!6k g/ˀ<_|d״^`CjaނIso Bd`"꺸}ևcݡA~/3 a1H 9~gh^Y ͍d3HI.5A2@WHM/Ȩb[TC@ jŐlU ?[ C^[.x]3 2_vi2bR;ҟhtcOyGWWb y2j",Wbz1ˇ o叐&Mݐq4kn۽ ?yKC Pj@433ľdž]v;: +Z d׽ ^FxNn%L<b4 ME J_^ y/~Zww,% ] C QNs2>k"ʂWo4 "SDQ 2|ouOe VbD *1rZ-P{DaLQlςyI@IU?z ]mAe|h-mh 2 [ +ɂ 7O; EA?>K% Kz饏Bx;ঀ]r[^N΢iAtS]*upsrArcͿn]J&R?.8?^cC;(H{Rvթ^r3brQ~]QKmQ/{&u@$cߪF`F_V/àzNmuGۺ>{aҢ,S!o8AƇK:Pw'ClzF^*m7C &OԳ2QPyizcnA' ٢gdm*DmL܂Rc! SH~N4x@vYz)HO+PV-kw`*A)1:&A4ψ91 \ հ[ Oe X?c~`fnmbgj?IdoP'~_j?2_Q%; f,jVKjNQb*_֭UsW'8gWNL[oBF=vraytw>ҫYA5ԯ ֻC< j=M7C)4z}5TXJ]i24;KԱ9`Cwj ei-)}\U&DA'>P=q_3Mꆵ@W'Aܦ"1RYJ0 t< |k )pmF]Md*2j7L u;tq ɟ$,y L%׿ -O?2c[7D\k;v`Is%>Q.d^ κdr'Ħg% >n(V 2Hd[ۗ 6Γ]0#Yǃ!1HvAd9K2ͼm΁}WC o9p-t@Գ`_Ղn' mOz+H+ uONkMؗ;z^ VEC+o1 i/Aߍ "ʍ74K|~Y.c rߢɐÔN˧C^yGȯ4cy^ʨRGa_.~5+2D[:HwҲdQ_)Oʶn^=xp,gG@%QM^c@z;/d\پ XdVnȅr&db0Zl7B[] PY)Bni*.Yin*M_utE!c3WlDV$~1lYrro_-Ok?0 du|%Rou'f|6Ԉ4CQ ?P[o-U~tuCvY~!^7A3U1%Z b=e PD[<jw8 TqR! k>@K+ ( *a KGGtEwڂ/W36:`?(#Z ~E]..i)\+1VBf@Y>1mA-o蠴K_v'De D##{Z0UalE 1\N; ga讐lrh.rReAψyAvU3L6EYBo@QT F/_Xl/DIC3dZ(xkȮv7-߀ۂqϯUc58fϋ9pfG=\+t{T3z~!Q"X&n'H^E/:yi" ew$r]!qWy@u/w>FUA{P~ zGTsN zeon9@P_Ƨ⿡A4k}PE]y.گ8e4Ͻ(Yk{OSRǢS#gw Dc5`ӇǺ;]0e^7*x-˂.yn3ep M),-zv,e^Hɞ`[u<gf޳cy b~UFB0< +b=Y y*L7CUҿAX9\ f㪂h\ 70\q]@o:MNܨi?hiٱ sƥW2+bܤkL;Hw<\6ia2 z!dCA'Sɧ!jq)+zKQ_.2&3 ޟ}f WA>=5hd*l۩m<|Qw{  4&tI$' # @ē0CE6I..6݁ {LfJ*W ;/[߫׀<)UU!ᄁRr '~d2Ly R"l|*aʵ %A,p r!%nIB}A}jUs6IG)?2x^e8㏺NbHbRSqb/C篃{w?$gQ9NJ^MPIBƖz,v1W"AB+؝~m:ȇ!^4H0E)2U7//z߂z٭gA ֊ ڔ/ɪ?&;'L E/~xKU]yՏ>f[XҀX$^8)f|ۃj0e!{zfe{3{UL_gud:)㠊a02b;t?i n[N xusAe %[9Pto;dU=DMyQ4q{Pu87U j0ʅ(,-6[lM09"*Tq| [D v[yl'_?^Yozi7ּ=ј#7h!z+>'*OnQI::m+فͯ1 ˄q&oCö8/ Ls]τT{yo@7!zݴs Q̀  EL_S̥LI )bzCs oH) $lT"W&K!:iM惠3,f:r964Ju Fۅ5Sb1gu dϰAFa}U6' tiX{o:doRU mx/ ϒI?R;bk]_%n{߮;Bnvr4Dd9+-f{MWO%b .b:ou+J` I;@Ҁ|#8 vX84³rµ=ytWöy[>]uqR&A*1>Z0)\ @K#@zr9؛"zrwG!t d~z3/6eK|TEs@CH r@68*F=R!W/w$+RWr=j6j҅?AZ" u~ٌ)`-Ĵ `LMA.WϺVn=7Wx0+IySThDT+Obfr v71+du zZN>,j8xod Ep۶FcGjG>Pn]a އsDMU”3= re_P\fdAkG_@$DO-訝~ :Ay2`Jʤ&﬩#Q=Z %oq&gʍEƟ=ko{5:jtu_Lp3S#H|`OMW\, jHuTg!c,8.gp4vEb6ƷCދv>Lm/9MYD`W?F1 EwBZۛ ,%Ryy y\=vKBM rYoXX]@zD@x, .Ai7=:yw|crE+x]1vbLt"bB3U\y0ۙ} 2^U ho7#c2{2"l=UjH;WݭAk&ntP?A0m{@ND4dTG[R}Ksm.ۍ@y5wNw.I4o}։v[ Y/z'VDZ0xDCSpfN0/x%y>*5W. fu#4_v3,ҥ Vۯ 7_9r `؝8 4wiB43^ 05; j*顯u{5@)uP K85{ݮU޲R_'+O&'?ju) d|O;*6ow.'Orb(|ԅ x; LA{ = ` mmo. |;N$D?N3br y#@g>+:f?~ nKSX;!x?1]6r/5? ֓KĽ`$xu#Ȯif lv rd: d RFM\ȓr 틠ħ=. ^15v2* @4VPݧѳ`'p0)[2b=!,kɉv[.?@ +"&[]/ kFPSf3{5ܜp|d?\ؽA9Э\SqNTX/&@Upʐnl 䔍TSe{&60ʙZ=`wG?oy.vO!r@~f2ns!Uv9%gM._5Q wyIlSt}쥬O!>Q3h f-Y~U$LsOBy]; igyG5; \9 ޳ .eQ5V5G܆ԣ`r; z^EpO~A)MUʊj:7л Ѳ&M Edbw[ v+>>,*ߜ _pU8jZ`GHm M^~ x?E@Yza$Q=_trmD3nNe5$x՗g?^ %K-}!G*4.?xԬJUVxc{sIO#/zg ѣ v7u}DsTQ~ ϻFl fl*Ag@V^B0K*1`W'|+yCFE6ܧz% nE C2bWS_L0Uy6rTn%?@= >lS*X?ÝZ{zf'eD)J (?R*Uv(*@uR+P oWREW&z+!zI ̵X$٩`'kǮ2DHwꝠxƬ(~TiVtz?S_g!E $?\FA8IJ0nR }n'/C?DV(]gqz%Qւ"{OBt4uP zrnkj [d@b[a߆;3vCzAڿҒ E}ZWU7sH~vԛa(S5U՟[Z7'/rVGS XgO~0&ɢ`^l |X ڂ+$Yǎ[@oRm1Ur ЏנmZi"b>}S(H[&ܺ=1o.gcd>}RU] 1%K!}z7GǤ9;B4HࢬNg($= \ܧߏ5T?BqJw0 =];]pW%5V-ȉ ZεT3o;!+=G2;=J)0܋:.U^%Ow!Y{M4p=%[=\GhAta8 U! ]L ofǖ_ݐ v Ci|^eI/4&U Uu@7b? !)7XsUۭGz-v60)2䯐U8jX`CG޾;PT@T}5[yrOLgQn_?u HKP2<hy8d .ܾʺ'(M{/ThdC%部?!dD l8QT< #\.q4^)Cs&n@3*n@l mAX–0ND_OvD.]?2&@ ?%sD% P_f' "A|u}L`; `eb=Rbom`1/ 9wGwxk &@|fZ D%mR]Ua;9z^Y&&v Rw`:1h?ɷ[eAV%WB4-⏂kg#r!a4m!qKs~)CY+yj-o]/%F͛2_|iYS'X y!{v@#\>} ^PPI>"tw7Gi?ap~4:.\ +5}=,l9W$U#6ɶ 6N*܏ p;9 A,<{FU6WR[ _sӪ`+%-[,fp⛼3{PW$(K5QMfYO3r $x_ֳ]]{E/kUmY\c;% |eǘXWtf W d^6z|zxH=K:1 b?5@rVrkH=a ,<ׁh:"3D%O9|+<z; oܧgk?jLgKgB-z,;vȀ!Ud1nfzl!7 =e&Q  rIFR G(-x%/a>i%;%qnA`! H[ wYX~AJ 5w( VUT#@mo&{g "; >ho4l/u`Rgj6\wᢡX!Q~>\@o}F [b" cob[V-2+-րh0{T% T}gRoՐ-x]XG2h"5mo'@8$X t:gMad^0W1/ȋ fe)^R^h/NCꞨn6o9GX̃=$lVQƺ,H4u sށx-0 `%p@G3%ubJq ; x MV|3btM}ӻ@$o9w'Q&;!||jĭJ9 U18h a1q=b+؋-A3rg6R {ٲPTh繋/!w gni@yI&͋^wB8o} /dK;*`jD+X M׾6; lt/GcQީ4"Cl7 jl 愩fwE|ڂ" xxZds>RBs UHnZ*B%Xbb/w*~Q31&j~ {H n +yPE{Fz;]1+諲rvʀĆad^6hбDUoU~}cY6k_3 L' S!kn^b:Ptko1 WH}.{'jmJUAl^Wu? [rAue= \"jOmN}c^0%Lzj'6&sbiv6v$icW3 1x5]6͝X;:yxR7u ׁ)fx27i*í%m a-.O;0)mAFrOboW%R 80ȧ!0+ms|jҽq̌p=[ 8%njwqINPˍQ $1%QdK/A|_`w0`~z F^E9ϙNr S@ WN?7z<6:8UMsRY/⟌O!p )HfvLB4%.nTҟ@l&>2@`NdcOlʁKzbuK v7v`}\S(}һ ‘W<W0Cd4# xT>턴 *cۙ@ͱ:m#ةvVUЊ ca ?R翎 E^.V@A⭣=D봅uooJM-U)J u}eok pćr+,O`w.:#*ː@'[J`%"?SWA&vɋTN~qn^7^?S[,`5Ѵ𫃼'ڂ yAE!9=&y@w#{(mǐ~q5:, 6n(kfDBT%@)n̰ԂhD&ִhW *[nSr u@Bz~"Ay̎|U tx 4tey!.N6}f\A1H{z - /A{AT #:A4ͽ$rQ+)*+b7q1׾ & ^RARN`pBl0b n):ݽxä=b \n;nGVΓr\3)y0oX~7/o_/ޙ]WnsGqYSp1^ϊ n5ڝ70,}C?<tpEp )Y\ P CHugn8r>/v0D7m+聠:z)Clrq쯴? g>@ j f,ysU/*W4{|dE%ț,'N@@jv 9e g.|U^b#`Y\Gh3X0L͖T >?T= ~_P8fYϞTK jUpؤl꥗< \ # ,ԓ]TfQ*h)@r| ?YH^qte}RA&1Qe4)~R#1MS}\_|V#AQ׶7\0ؗHxl_=]{ŔGز cQ0 KƔh/_;ږw:M_كJ |W9y>:#Κq˔̭pxY΁te==E7!qգAb3$+tk >_y3[`RRׇq7* ݪ \G .< s*V|s |wWY$@4.՛lݗw/`7P)H?"h R0y"Z 1_/vq#-gR&n.NRn}1!߂.K #țr a11be12 Tg}XV(~}֜bQ ^PSm miρݣuC㿀jO;I J{'c&dpc׏Aw uRX z淐w5HdHs(LE5]̇;+y2fXtG4oG-@HMٱ%ūo Nٕwɒ1!D[@r]4 9QGwԽ[Ni 0/-Z0eAy7ؒj0Y꨿8z(wQ x/!}Tsˉ!wٜy3RCoYa˃˵?jRJvjbOno<ǸI*+vȝk%<=܋8|`c| A\슂czY2 ܟ{KyV`Rafd?1_@ԭA!ŏdW }T9 s g,<׌16B= \ A E eL2@͍Eוw+z"09Ꞛ}w~]aWf m.Ԋu?$^%XOU,^׀@! ^229rA8 ]VE`B K[!$% EGiK~\Zh8f[Nk ^ 2=<><:Kl+-{{֍{'hkPz< Q&~VLYE =OGϙo@=PTw] uy: a~S-"UƥzF>@vShzD`sm^7Rb!D/TT.Hm QlBv0ǽ+`65.p<.J69(ZYz:|T/>_P5}&uި4s vނdd /2Q =`ـ`5!RBTǫ.B(iAzoE5 Bޮ.8]QP36,sm䭋V%~n߅@y[|$'G'n@rRXJ-=z{P+PlH=Bח^:޸P`:^(w{!S;j>piY ;&v@Vg}0 ֱn9 (ic[!M[}rCm 7Wjk|"frMHRgxg?̞t(;^{=`W8.=  %‹6Ӥh=y :ǎQH1,</5 PGt+.ئE/\aܴ-qۣԵWdp E+y6P4 ĝlNA05h10Y %bCTK@6ķv-CE[w۬~&] _o$QA`85 uͰ%A5OD[t?w2*)mdw vN@C>okL*0 iWQ?ܿ *b1Divj0םObx6`'L2!ΎA =᎘?!xM@MH #T\Hy(l2LA20hnNBv&7lo[~'_]n޳!7,#>Y}M.)؟fa; '1l!7k`y_ Yu0tQ;m7?A"'Jэ-5̻2j2LYyydڻ[ n J0 D:h!D\8ԫA-?߂?F]H $JV(e$VhpWP/> [pbbͣj55}gv=_Vބ+z@f+ rz:V1³PnWiGn_Da ߎϲx9N?^&{;}'T?+}Չp?YA;RwjQ80E>u(0,dpzw.6l:dJ_ '8<n[.@EK֚­`Eܧ j8]ڀw}3g.h 0efHcFlH?=6`L+9I< _v:5l*?3Ǯ W-0 +ꓱwf^,D!g=l;|s@yA Gb|Z Ɓ0]ApuTkyOOExW#fi$Cl>[gv{&J S,H9z''feJs9?# ~x n<}syRʥ tv?3=,.ۖ SUI/WgD&mTUZ0\p$ nfDIΉjq뀝kt T@"pS\Gα%]؃g. C46n,W`@pkdl.p'̻ac+v1$>`&Q6,_t2ALO :n7ۢ&A%-?Em b+ @o3mU-J-J ,A(/B+a@x3AI_@~߃`?)o ,Qۭw ʋy ~ƀP7R!`V b AGx`RA0+[ G]܄-#/␪ |9,)9rA; 2n/MoPJO 2' 8w(عAVءt؏!W~Api F$wA^/B7O~i :v>w*w ɧ~99.>еIu˛ 퐨z@ RwEEwyd)T8n俲31 .˩v3(EN6PzBP?_Gg$Ml 8Bc;_"gAff0 t{@<0VpE8~{/z]Ɖga* rGT/fb[=fFσ{3ܽ4izXer!@wj \{ݎh_ěn:? X“,\g"GT҇fOʗc\\c}.Nd nXgTNӮ#njk-5_tA}>`׺<$#ABm{+ȸRƁkm}[۾:db]U@8eA21]3GOqիYD'e4lsAR3 D`?}$pKܗHOУu1 !q !>ټ.Xe`F #́6(L`:z01g];!u<8g+CbrAXgS<Fص/A*߀Xf}ox.gF3;i~yƀ.,'N'`.ˮ렎j. fo}\=/kA=>H~}flQDRr̋ZxPoW*o8<ޯ_UAƶXT3a#wٟo_5U{A-/,xB|~ SO6U>f ! .k .S|R|)enb} 4+"p볰e;1tY(M`G4À #!ȣL4) v6wq5" I RA,J QSQbsn.JoO Nk!:C~òjMwTUp;{/8?{۪oVNņ)F1Z^7}_|!w: j9+/g-{J=AN+uwP O9)wy ? Oئk&&c f,8t/x HQoG hwLMݽ;tQ1WS jn@:b(C0ЍP?A7Txw@GH qMw]$jMeص(14jW{p)9lp {A|hW(cS_$x A*UU -OB݌Wo7E01siKU8Or}Jw mY}viqu GV @6i?ߛLU hcװlxmtu<(6@~#n Jٱ [,ALѓDW܋!p/)Z7MM@0GL*Al=;kj lx B~jjՂ[D e+`Qc2t4Q lS=1; QCfuPz`E%fGlHͲaW@R!{զ-~lqm vii0'OAa Sϐlڒx'3p}_X5U2H5*Pr ibJ'CsJ*tfR!i; ja nz`j mȫ g Y}DSs89b!*%^vGr]cSܷlfT;Õd$aIDATDqn`gJrҶ\T9 Yp]'nJÅ"7 e=1 hƓ Ӑ>DA ^ d.KgTڒϾ1oQRgB 'mͫܛ aw =ώ w)d}fcPR/bQb<n6#\=-6+L3!m^:kS ^n#ȱY5c&ܤɏ9 n)b6|8evxV =Pdg@9NuπI{Dq @na{,y>%lV߮p?ߩ\7].ȹ6Vqďg7]f惬)0 } Fe|#P'$U `]܏r<9xε}dI6@G+KW^?+ר_߄koNQ3f E)V Zц0+p2:UpJ^.Yw.0YJVK̅k&䕂k!qo@9EH c2`v#tܲnrƮ,tE}fl,^7u  >fA$@g ta@Dp8NصVͮ9Dj/@rkoeN~pmh޿ҺgW7sAhns/ЏsiZV|z}ڙ?ޞ 9PeҿH rW:`;>.Pbb# eD4E.bNw(!"e5 W-ԏMM=iQ/hkJ]gD ~lkʂ|_^n7nIC)z \d_R  4{ӶyIsr< 򂔮5h0duku8fg8/Gρ)-{@autP r?KY 2]BY8e[mr+0,p;Mink` 8-c$lY733=Sߧnt2= !2 <V[zdĩH~Y鉢)>}Q}`TCz jRd베"!p idAe<0H/ngw0N܃8ܳ[cG;UT~ lOle߉frs V;܍vj5V"~3ݛc3mifH1,k7}5O[aE`L,QLTֹ+. jOW 8[M~`2 ~2#`tƀZZZ5_5/b!0[eQJqPMsCa+ɜ5@\6U0P|S,YcmD?y􇦜8r^xt;|29Ed/B1/@ ?Az MI Y_X OV (ZnO/h/9ӺR,me/e/k9_s@7 7)ToأaRܫ@2Kˠ7{ zow ~* +r{-"ִGO^k&07ݞwD8+ 6j,@P|@6@i^e{5MyK 7 ֫ӠqNM]B_=h!Mw3f^[g(~o0b xrXb|WyLNa|==A*TLqלrOțVUZ&X-` 'i*,4.9Zee_ O?< jک½@,e8#@x}辘K֯V=$x|$2 TnA0Wa _[=}XwU[F |9/ΰ5}WG&vnf@'F) MZ/μ 3+ ,V 99 5?] {%r/]Rx0iՍoȋ? ˛i-{o ,k ;y1PE{fLn=Hd(8{;sop>vZLgLxy^>H-&<:A5y"so%asZd FR\BdOjtF1_LP 0Xͯ 7w@&_o`\3G$:63z*t~]zCE QDŽ@1h+f2\qH$qSLoHsأ@05%oSS߼n:{0yMB~!oAN.SOǁ|S"k;-젚Y r) ΁ߌ>#*@x{س! yx2Wh-ٞv=} j>!wZ|]Zu"+n T;/%ׇ@Jڋ!<1_5"CG;JF ]-%3<e^҄-huPnjCz: bn]}nBxhnZ H- }_AOe8_֯ۅ:ݲNUJ}@D PTk>=d,xc@' ;`nuPEit ±`@1tYBbsLc$σc2dPMj<ȗ ю@[)"s1n5ă oPk GN-mDZOu;uX 1G]/md|9ݦs: ddsWSXu}`rH~bY*&k>3r^"Fā,EM35@~jTyN.c3nΐMXD,YL z1@.0% V[l}lV,6A5H?lTԼ4vY!~żxgS ;(>*q9PN7/Zo.`LQ@>cG A1f2F(I .y D=`P `}B#f"T7#A߉o@K,byAu2x}d ^p~?S`J%KwR-^Y p-sfk 'f,mS-x r5I>-&&u TB 3k$OC>nfXH1 : vȡ PS4УH2ċ| H0Mkhgr^Jq]xWe ޜ׀c x3sě p\) 1]zU<,,ڋ~"Q(OS>H籑p?rO iNY̋`ocܹ#;AȱP*PeG #u]fE?*Kwhj*g5 t/n|P,GCG8oUm>3k _aN W"ڒ"mr> Tt'`U]wE=bn.\`˒qneNS1:.Bz_ n@\\^/o>k.s"ehQѠi{ U]XcbY|l"f pZ>&b `Π_^'=Udo]Z4P XL`"eЮ9detgsn c8E?ئ7]g>%bX'oD)yHzμ1jDA|h^7@~w" DY3 %.x}G3d7@.["Xӈz P| g8%@7 xx1<=eDL7. ܜМ5U@op(#Θ@WS@qU (b0h0#s`(˂h3I ʺ`y@uX,AEkSl5B]QG=4Hy)T{ȳ4yEPy(~89{8BUfDz^qT+&ў\]"|it2_xչm ]]Iꃹ &e}یsPSfgxSqLU+` Eei g/7:[&i l~4@zAo.sQD10D-k(4Ps3K?M芈&@[=v+XMa78 sL)lw@2!{XU߫+b 6ڪ r6G;&<{M=h곡v>hա&HE+8dyZ-Em/T^LVk]P?9{sA4Cyfܢ{]`$x3!|69nKQm+2/y{ *7X\|n]]8Md ЉdP*Ty@תWAO6Oc..z@e`SN ֊Ut5mL4yLO0R4Km>bb +fjHL=ꚠRO=Y ׼y}@H=Wfl1`ҙ)6Pt, q\O0ߊ?|3L}A10,m& QŁ,OGtGH$[ftϥ^( RtIěu`nqQ1t!館 y%Mٳb ?vU3UdiH(8$s,Gb'Ğ_vq6M5ٽ@U[쀬Aས_q뺭 zCK{wȫ>t!{Cnҹ@1k;(h^u&,EO s Zn3*k*)|ɫTF0>e ;}?,$ƈ\0uUl_V=y\ jXC]^ի78@v K=W z^wʃoCw~eIT0B_vj7ʔҟoX}X7[Otz^Ń||PIUuuJ=ڨg Ī<3j;& |w.Vǂ#jeU X :DƇ{Ew&lHmoM:"^2-37\` (QlG}3U_ܧ.kD[ir&n U ?_6I@KT0{(td;0TK| &iF6֬k3b(-*Ut4;~Q[~N7,- 3h`^yu"죍hˇG,ۦ tzA5CM:bDΠ3_I AѧW&3I9Lڎސ@{N)S< ds^ChmsmjBFlIc`Qw= Sɺ>=y ⁔^) :-5<A2e֍)ʛG0#9B,V`ƛ Cq׬?6Љi*Y fXFS])2yPjI)/sۧAq zWR} ^ sMNARN,7īj@lP8Q%y*G $eDy5 w .X˼`7 T⟂^?_ 'nT/]xr\^;k3WoO0<b̲y4wG!Z?:!\U; H^p;mZЯi+^6tnL3?e r2 -7|9dN6OBd'k6ہ^+]doo X9L\lWl^g3Xw2!&`1)S08D6u@wi.F Igz T6k?b匔@|[@^u,ELo09gxE*;^k;f8 1ĀI4y]3/l/.͗cPem)JVQHL|hf3S@Gd,߸Tr!,@b8|_5a4GA ڊ`/ʂb?~q ^5Aʪ ^2xc >'Lz `.WAbz=)1sc@WAᜅn ?Y57I3 ްf|#c aPE>2<d.MOPz~$w] 3xT΂V4`~AE()W:z D\VK!. *pCFTr:" TU@w0ׁS#d)7 ozQZ u,rcL1(}/wV<˔`rs"nh!<ygWZ}k ~V ʋ,YPVIm@b{ >2zv^ c9PM |4c` ߈9hsY)/~=/ IoweM` Ai)c=蠓 n!@5@璨 #k Ps2˜FoA5WPE!S9 b=DF]DNPnvML/,Hv E.VJ^Dۻpdy;Bu7_yrHCsL'9_?10=[fr<nrO_ݽiS%鬬g:y׊?JPVw/%< A/dS7AL!, k&x@63AqpC] g\jm!dsEUK1Gu~Eeu( 칲?V]st1KZA&MFWJso7w"Hq2b[X_^s"]͸H,t{OU5V?@DoJܿ^#zU0x沋hě`V Go<":j.qA?ry_X_L P}cxds\Iu q")Cx z.S` , %T ;# ހe?)5V/:`c`uUlܙ 5Dl1(K[k!':- [noaeY2A\/Cjh~xVF(ÁBO!Z *<0JײBAӍ`{L<wl|m&_֦i3y N4O@Mh(xWǚ<"36ǯHOBf`| }8??,h=' a9~&~Y{vSLU/ SCWS@7ScySkex۪-IbmQtP)@S-5zb1w;B^P5;tYu7}@{!0xuz Q,Qe;&W@_/ bnȝfs/9s@fofs_jB1]= w-j8IMuԜrxzk(BwDX~{lo|[r5J) 7dr\Cg5Nxs{H 2D"> L x{)\,h肌aALb7IWVOdY g9a{ ^~΂ :8HL=m炸|Z;T'rDq5n!SbW1ǙA/ӵk6X+/Mv[Ar@#$.WWx4 ܿW]VaHئ=/GȃCzMpD7{LPaΙ<-tW`/Ua+A,F;߃|LPq!* u 89?Gx=ҐyHOs/}_3W-喢 XEco oߵj p d{]_ȹh^"zO?W"}.>1Y7`^wJ[w092'- r;siqDA4Xl2ZT,3 { Xa}(V jr0||J%eL t| {̄lp_Q orjm?M68tUۃH53 s9=TzLk萸d[ ɫƁ`}>bM^wW3`z:ќb0H7}&냠OG6AhiVA|ڂm_uK8: VE/ěn|v֚oS3?~ #~E/gBB"#3sP.::l͂»VB# ]YBNHbO%!.ap>`/7 e[琺/\6'~fD;BR~~S!n}|`><\j=ټq).7npҁ: /N%H{'3Utw+@(Sр ZW!+q#U kv4<]Ղg\\w] jq N^YG`_+{k%Y!b;g0?吪U9D {Ǣ nH /QjyseNQ≉Btr߂Hs f6׊^^ud9 ]Lѫx"^/j}E@1 38gde:9q PfJv˗ tt'8A3I4~]s/.CprcO3_SFvi:iJVKp5U[0֠E?y z5ܽis;ցT1 Yg2~>>?u 'e*_2az{<2,50*+M?[~*朘c:-KDQ5J!ppxyJdy/X0qD :()o_)l[!XK>3 KᏕK 2O cxeV&2j*fsV*|m13ˣA직\USfl_ğy!GO gX*7UpV[xjt 5I S >w;үF7Yz|] zYvW5N߾A1kU/Pj pG&ZOA,44@kXG}Ag {e MNɳ&YNEc@me4^I.^}ޤ;(z׶NӪ+KE'5Tw LßȧK O*.9J҃@co;]-|m?N]y&q9!rܪt }] k!\W3E}p_ R!4Eψ߽JW3V]S[U2RLwUjULB9yEpk!f굄}VCNqAB.ŕgpt3Y 9kׇhH5- |D9& <>SA|!HcFVxe a[DUSvHLb$<لg;G;nZ?f_6gFkgB {6 Y-|/lz(?ؼx `bR k0fi"" ~1НffEDqH%/7_cVQ9R=qhn]X+Mn}!m\v^gNoC9Yǝc9 ,%Ac l] \uꡓ@|dM59X|P<A]8ܟDy5QV})AL#}ֹf=fM sLlfpD8\M#l]R[ݻcߣ吖k^qY񙣹1Eusu kՂb4<:ɂ9/3@FyTȾ[Bz@k>jCLvep8[bp˸5ፅl=(5M@] m wU,A 7L+/suXXb0@KݛzI1]aZoXoi*|g5xL,d K$>qZ@%=Ҿx1nq{=gÓOF>}--ӞR0kgւx(:n`ΈzӦVA3`ʋ4.VU0yDUL9}5ȑ6ÁҬS1`^F0]5-B-8rl~HX6rT_$JbScChW 7@V vCp檐>"_.=sNQ+˼~'A ߓE"`7W@r % f+Y7A635۵W(ОzW+}Β P Y&DPTX-ړD:@0ϩμ,_#S%^DٓV4ȟlU1ڳV;ۻ~ oVI+犞NJ_rnc +[KE[dhc}2z 8Зqfz i8ɼn+;7 Z5=2Dx.XhWb1;  NW:BxWChRpf+~v{ Ak#d0 Xe_Ƃہ?S!Ј}@opSOy3[޲ZVO4H d_]>j"}[ Fʀ=Q_0Aѩ:^Vg[sFCRAV0ZN~Jphs uU!=8BF8f&Vpow{An洵 e.ܿ‡!ۛ!ƒ)M(VVD sJC%q}_\ 7#o?\dsh?;d  O++ĝ) 1|VB/SW~ {jUOϺ 1K?Z"[Aq&d` KRw:CV6dGp~ 8n ZFHΙ*;錅.EZw7~6k.`r1< KiZL@2E?)*&Δϼ`CA.-२7ศ&qLwA!AdqMzZAy2W菋M!iGb&[-,]'2u~img5 WP6dfs.ɭ ?Gf7r:}C*@9@v`5oo5 4{38]o,ᔄ\(!3ly+{jW>en Ĝ/WS@<޺odYB k`Z nfD^1oʿA&k!Wo1 1fp"@B&_@`ٸmaY-K_DMsXdJD5>A4{QF@!(aꉂbSU)JmӀHSx@ѐ++]쭔Ž\y=!Sy }8EwxO63AE>?[) sS D6U w렖 :At8";·Am #A,ٽ|ཀྵ9@oV[Qb?*'{`ϰ?8Χn'^5 E3K9=b":+\tc@EO`&.7`{D{i{] ɵ(w.E;ixҿKG@(*6j-|ɓu#.Ag*KSy7.~'sCÍ8f+vieRyS]+|Νr:5!!BގjVw n'u6d֓gLQR~/~M?'h6_ >^lY%2/<蕹7X>Tt?ħrҞ,wrQGrf+w@׷ 9[@8V-0ay31/bcLsۛǠ M'*ڭ¿“@VV&RJ|k\j{E2 $əh8ռi?w)b-BpwKH]pJʟD1(JZ(ܐEX"942U Op;Ch5D+SA߁l} Jva1۹U;EOT,b-W5zDtlk NzQyV ςʀYDA-?!v=CWt~8D55ZXs/|:]\u̿Nm2$"pcp> (e*dqVL|!M;of@<(fqKKgOܓ/"7AD& 1^&rZ '@OSD7n;Zȷ#ܷ q,g(2=om\a~nkx@qC 0oZyH4yxpnՇdU.L.1 x.ΪavGeI(>(*y /Z$rHu?pϋ"o 2uv΃NuRAԙ2`y'*ƃLYv^j0.W|!f`6@ցy&++GWxYm4m=3n<ɕ2xzIj13^e2/y̿͵#ϣSG(ueűOK2 0 Ҏ:,W'e |e < ) ۄdLeڥ#˨&'$/^W F׃=0 f7d,Ώ5 tWrjjUi]2pDNN-'!;|z9$~w ,OgWZ!唰ný~D*}__L"ʃȝ.6^/#5SSQ̛b;3|* V]_?u/ 6. gdKg_B/=1k $g=L&9V(/ 잦WG5 \$k!L'!c6Г,sc1&` X \f20Ug}%fX@njoI:.%HiCxwPe^'ٹCB7|oBkN<ć.!|/:!Ew:ĕ2e-Cd>QgggSC8*ZKS>ӓA"o}`J0_8C0џg ƈVz>52A !|D[U y@'j` @|HSZXXKw5% ޽) )2egs= ?x B#D߅ ^*c?`?? } 2.Ńݶ7hio '^Hq.7MWP_>$0EGtvp]Ӏbe Ebxa O^(LQEAeP넔@4gigbl tZ;&f`{Cq@~JCg=no VXѲoS #koNh_9ȺEqOo[agqklX bTn\$^{^_K ϜG2v LLwWLhRbfݬW[ufǼ)m *#ݭM_=In@]I[c P\ }W R2}@.,Apv//e jDBl1Y }[=c~pၵP6 e\n ˺6pR N]u}/O0SmM5Ӭn>f6{ay` ٠7|\S7 ]9ƺ߅+A0>b*<^y fw:bbSd `a ~]f8M[vWZ!zXŦӅ7+<{ 0nni|* P>]YOX!:XBu ;7 Kcm7oh:)` D7gGn( L2X]<w\O3,i=_K|jRj˟_0Dq`b#zEnv#`yͤWl6u>lZp `UaĘ + w?l #c V|JΘ"S"$E˷h[zxk4e7"/a\ д !*"oN9RBi߾nP@=ndodb 2ָ^ ̊Zo> &YI)$V+^l[] r8mfx(l\5$ 0` v: u+1!Dʑ].G Aڮ 5r8wAOx鵬MyŔ>lx,mfDH˜1%!19o%gS(S2 bsȻ('./dnrC]؄Xt{DWpY)@Ɂ٣;|PqbF@r\r =|5>dH; P!$D , Uz\pIp|CLak%]uܩfYi`P o`d.k_ >kN5Cco=! yWh >6H \wcM ̢b*/i Gʛ`fKP^=f8l\`1k01^XdDu , q PsDXgƂ\ &7(d {滑o"? "ɟ1TX> (dua(^ɌF~ P7댾kv":[ٓ/|+dQpnz 8k:l^L!2_/W/| }{ y\PF=ӽWUg;Bӌ:Z*.fqlP]=%<7kь ?ik|?O y'nY8o(kyPz‹k_ON;(dxY٩M[n[7w2U@SH}-CcK"HksNgT:Bi3n<Zfl^t9OBڐ$e1DWz+8tk;'{uUH@S3O pUV>6{N7:+ƙ4sE` ^mbڊ]`߱ >4@l0|".1߂{Û@dk@ì-zVd6luPNjځZo:-`ho~QԑAM4{@sjJjD0_z+kF)@[( b_ vf|b-@^7 !  ] >1]`Mm88A[sMai"я)7 먩Y&"9 r TлuaG.";駠_នT] kqw4q-(Pt j:bo r% ZA.*բ?{ RMtZƩ3`Qox,9G`FU r7Q77:(Lgďj?\b oot)9L祬j~ZU{MW_[|uZn@cce4$vS7OF%[_j=vSeqk?wvKtz-T0R29e xo^ M{ TRU+Iru,3x_[/A? N /<{hUkm_߂{_l^XA|y@rZ=3 |bS|]UM#!3G`vpYY?:MX]ƒf .RXDz:%$F_˸_/#Q`A _Gā5*~Z(mt8bN89ZӠcY}͗> T'/3W:>N qgo(ib:E`} Dbr4xMc7Eq?xA~ϩgwC6Яd#@>uNTESQwgok{^nҝ4HZfg6 su*o5^QnosW@nPգ!'[w0SFB"@f+mQ/ɑ/"uALZ?= C<`" hǽ"[&2yD ;aځ7p_|O[uu* ۀ/=\rTL <[|گn e^Pxs⋪~ Λ^U"|}սFG 5e:֖?E 4;XBBB:E$ؓc$ӫ<; )RdmXQ#ntNo紀r'އzzxǠ^wΉ-V 93/Ah{p`5;/ói}RKE:e _v`kl \5/BY[Ns@O37:Yitl:4QYe/ܜf",@fnE`< 1=`zSNћ8Ȟ"`rT0m3DOёz 1Q{K.BC%q !O4lBr +&Gp8mUy7]u )$pC}X=S߁+W&_@պ}l5~(8uھ^^kNl_u:)uwOA 3gL ] =Rś_!z3!'n'>V`LpzJ|3wf*sִþFT`?wB;O7m_?.5|U'V_-7vsؤHhQqnb&^?]9Wy2$Tt'/xI&9YѲY=ѕbցzӹ^Q58TZ| D W+gu_W1 c1A~ߌ3Mw@^y /= m+:'uRËi3Ձ:Nn΁idCp%\+kV^%^ػGW]=X of[\}3O.HpqybvA-4pۣ=jW5J| @"S!?oE«,\ OEHpƅdw :#%l[`"ΛYoH/ܡ5b2D.Yd0={ lh#ƂM:1)|*"sV%8a]v{)<+R"WAe-fxoq"Tƴ ɣ5R"4@%%ܙ&1@7܂ {Pg5`&5pl}UFnB9}j9LsLY.oW,+|.l5PX5xT+=y? K$o,b (a'A\7K̷e4w.>ۅ2;Das@ IV.\ 9I&?:૾z""CMy8?ON|^HT #:noybWl?8K@`rdY Wf|`6PT>>MVP}M lfyTB]C;M;SC^Ptoqρ_ PR 4f A q6[4j;M.|൱x:cF+hUT~ )+@daxҋE7S- [1+oL3O}"Wi΋nv;[_O2e+KGdo]ϽV9^Upj=6"Ս5> X?j (SFv#k'deFCHZA-i|LMN7 {U]U)~X~`P/"Nw5)Vod刼/) o}7p]XJxg@ߑgT@l5akShL*ilS }s1kß?."\|(uU-{D"5' ͤ 4Z~УD5c^ 23fBVRxZVkڛO%,|Yj8do0%p-(H\'Tt9S{WCYAbh%?؛^)E1yuOovsҵ%l[/Qsf9A<Ň^6+*NDk+0hfo[rås( T炉SOȶw@z3N^䲉FUib"x]60rM ؿj?xZk@mk>&x `,7*.d 9Qۊ7 ޝ\9("E4 y*$cT$(!IEӕ1S#sDf8wuo6q7}Z~|6Z";nM D>*7^jr@yr*}p;Fgys7P7i)?DȿW#`ZCm:sMÅ:7S MXi>V_[Srw8<|` ^ ;"(T) {d_ECREڮ6Br o٠;:jW8k^dcʁmb9@taXz8@`6yu[tWL t5Y_˞<kO} RK nfI$9Re*pL/\9E$hjn@-&0b U-HG4m{{JՊ 2@M>cc3Cւik/*v}lGZ̲N|1ƺ'HH~ zỹȔ"B섂UO7=͋UK5b_ ]Uź@98y%}Åm ,ߨ9+cEf0Ska>1En_S;A0("HJ}a?&FV9ߞVP}nY(ڠ{ Qjz<`-'DΘ m9֑b8$7mD V ؋6i!v̯ r ~njT}eoU~S=bI4km,}X*Y"<4iIʙV?D] \,A?h^̗nMO:\e*8z:~DYT:Ŧ ZV+> sz+! xWPw>;)u=lFہ .*$sh\ n{*eMPV7qμM#hQQNƘil1V&?] 5#!Y!NcYV#zx OgGuC3 ^m|!w3ayg98gfV@q; d<+.ĻgƻW v`wr@(E`-ר|V> @RY~ }uXˣ+,?>/[pbnޏ\^oN9׻p 4䵖CoXS/ | *]{\tͮܬR1lܫ⑉u^lK,JdfrR;@!g!NzLfIp(`Ψ_ l`-έ.!~07_ RuR M7p2Af{: ɀ=D 7MOj$z7f}[d9j|cm1$.Z-kdGy?0:Y<V# %kΩ^}z!? O VJrSz~doz2Y,إV6z΁T YV!ͦy.y.1@]YhN(kf!0\aKgF߳9"%+ 2FAG&{Xuk]7͹&>N?A feƖږۼ!<ˡ36 ւȼp`lF37FNC'\Rbv䗤S9WN 2靛 jq 夨<62NpD7<+f_ ٩tk+?-~Fu=]+WX\[# i]AHX v .(:fʉ X\_-ۚy.H 얎@j7v+[Ȕ7%C-WD $~hDX;1E?ꔇh 0!qd;JPZ+Õ JÅYKo%) `8;Z:}ܷQ\϶A9:/):FؽNAZmWdQvOAIj·MNiW5YBQ)j$^n*oe]3 27ڞ; h[0J| b`]ڸUAbzԞAV|zQ[!Tp$!S!x?M;U@}+X ׀YZȌoKP0o,7*=F"g`>^SS3o۵@9iUs@嶄/JBJψAkYO~6Ưeeq١$e_3dS6˟{^6mzd+aGujb.nuzbu<,kl QMغJjHƹTTY9|_V*:7$5zӚ_28*DE/n<-os1F 7(5"4?!{=hog!F; ^.ck,[1$ݐjQd PON%pc-H5wʼ anw {yҹ1p+.q@4 j_@^u>h7XYOb:  is+f} s!6nvoD!Sk.ݍp?I;(R :~AF|bxN( AC'q1ܤ@fH=k r-;y}/'97ڢ<yj$FڅPx΀ ]d0T?DhDs5Jd0rôq*_Qt^2qP/裐ب3gF($uHEk:t-S#f`:2@|)׋l%jL'$pTUnc~:0oS2K9աt̏Pk˲EWC @ōYNH{c)LϏHT(h;O7]_ sk91gQϳ9&"b΁y] sv?reϭqN>_kmGߦʨ^é}rqԂqE}Jv#ݏ 632q^rUo߃#2Rad\?G8x`>t _y!A]0nƀ?K< {raZg ^-6&@zKf$. F$CqPrD"Kf +[Qc-{PD!~sodUܱ4IpTY%c+wgg*Ί:*rY n1s=XekS_wV){9?(p] `T-LCw!JE`O'z_V`W`t0Ґj)_TSBx}ğF1 Up7PpsQnqq1ܡ$ޜ1@$"4SWVA׫Y ~{kGjυqFCO*(馛k jCp @z95i X,glKUuts/ ۬k@:nU*̄E^|}ޥDžׁG3iY&e+qf5^$|f{ $>A%5MTNT'4Swnv<,bMCLkZUqQeWwGe,-s]t{ʽkLHʔ\}he?Hi6=2:DY$h.&$F*A34a/]AZpk0CA< @\; EhB|Ȃµ 8 Nq=8˭P zk'DS6>aZ^dG1QqN OM4D~5}A 2@ד{T?ͪX* 'Wmj:X E?|3pDn~shLZnȳ&kEK~DO3֌j%x.He]΃6+xHaCviv^oԛcj1g,DaA`qyAa9` z#~^FCx2z ;PMt{7ǜ9`ś:?b3RkDE`SV}RP7xt| eУL)S f_Wsۂ~Yg/69sTZXcr VֽR %+w,WёW:ܪc{_ 'Pؼp偅%;4ZG1Q}-G|-eS5IsҪ\*In䈠i`aTz1\;~Ś\o֔9Uw,?ϵκ~*#}7\pۤv1 U[i` :}A./>I"{/RBW`';ŷ҂^(ꃚN,,kW:Xʹu@iIz Zk%ݟJGBX:-jVB,Vzԃ7ɱ^Az$n6Kh:pmgV"zȆTuȳ^]*z@\j= ;}L!A}E )x| T **CV5]i QZd׃Dsv;pCRLvD9d5g=/Uv¡}oŮZwD@v/A/JTzf?Y[Xλ3/5XW8ԳU[~yG65M8B<(;|!/Ҷ8) ^ˌ*>67[nx]ERGdWǻz5QXg-:55uMk*ۭGΌvJsgdiex~<(zYfxpx?Ub-!.1?"!qJ/HU7`ڪlq8(rZ[XW\(=w}'NfT,nMf NAAPG MMU2ވN]TS\EKts;yj $.)h{ږ.i5Tun \~dA3g"-O=w ΛW` p+'zs#{FΏ, :$%&[O L$;il߬jXD 1\HkVg0d >׽ސ/L d!> Z&'sy::@ϼ NۯΛovB7B*+XkBZy&7e䥄{;!^ FW8^n9mmЃz۷*u+%54l: {5hb>vn[m+F P b8{tHv,q;D ;[C*|;4;)cDoHƛnm.rDU_$ [^0s!ۓj*gzS]*kI]熈`xǙfjhdatP(AP3bzTh)8®_qJ:^]-uP +p%t?(_C*:4Wծv|S˹FԶPpzƖ vtr?^攉dz2#>jyS5L>FY1 3[24 jQ*G qZ5>F罽)Cc'A#MDX Y ඔ8cM†Tbsc+dvp/L%8g5|ʔC> xIF@=.)&ʦiP{^ip`$][&T |>R0BaJubMquT:yAT.Lp:(x/ [쀸c,QKLKu;E(ƃH@ )ca%sP6oG*"ghͯ˱Fuɻ.ijyOy[AoSϜ`L 8{r+fhˋ> Pi!;PUYB πf\bY1Q(R'dml%$Z.6bA'#UZt||mo^t2a b~8^"nn~`:zMq !ܯ+΂wS^5(rɭBVf|pz5E+./8 L:]+ Rт)wC)Rρ{<>RN: ]N;6H[ "SG@]L0A6@o~.s2m YTh/A7} 0'R~S!=>hzqv`{Eb14A8,,l=վ§`wO ޗw+©Nܶk3e?k탕_[Q>J .;]LUڇ7cpK]=]p㋋j$CA@:XjЏw&$IrMvCHٷ |.!?K7r9 ú. loΦ֋^9,Hȑsb/'m_U*ϴ1-e>؍ S<=z{P( i-1(P9Ð΂"+ƞvqm̒I2GVAVwGvA씻.;l\z,2iڲ+~[A״)%nuZk5]P|[GoMu31L/cR:}}%G|Klρ~O_ EA7'@C- lre Yk[ZlZq&,oï<i >Q 1n 9!(2jhZzt*Tw+@4T1DԌ|Na`cƃ{ƭ;Y`1"32ldNCd_'ԠOp^p#vFeL .Q ]^>oI,>.AhF Ͷ <;_Rzc1L~"J9vuerYɧds[a$8 ~> r?E賐=BMs7Ax8|TyQVApz:@u&v7yX/Qe6*[_yc|9z;z,|Þ#o@_ʞ~{zhR6xo}y\̏o@-RݠaKP#Y Zؑ:KN tMYV]`&+THH?E /qncw.-mK- :΋b$DyR?=@Z* !7A<&rR:i E:.8Mp6pAT#DžF igJ6c/ؾ7L+SN= OawUQǐZv n!LZ@”.5 0|ezD SA S$AՌvr:*K~ aO[d+;*zq3<9"^U(r~6 . Яx{R\[MwTMv./ALN}PK%bTO<ڸkq=XqNy3y\TYRf*)_L}05䑴|ߤ%9#-y!r{(Zˢ 8Y`M06?H~uw/ ׁ/C,7/yJkoνss3 lt -C%O`=;9| %4:lF?`v#DRjC愫/ BrLĮoGՂ6tmv+Q?{n  dba~L>oBvgdk W,~r_اş:tUSG흠2(0x#Qu?DtoFʀ[N! c w-J)!MN!+_\+s  %GB;x^8*4QяS@Io _Α,-ȊHE8_8o_R<6ƹLԫw$kD^Ƅ3x|x@&*5Ni~ J8>ai~[X涄0S| SY-Ϊ ا;Bn 4>E!z$.Ppb 6V F.)΂Ȣs9pnt7Vݘ  H?(X*l|mc}8ɴ}AN7OUԫt cƐX@]+n?ώfz֤T{nr-i߄k@ Nh3pFEvI۷os3jS 1Y~bxo1eXAK:Gs̯^Q] V%g?<4 '&B%؂ K _9(̪ n35{ɭnƆwS嶼qb7wU7 #˜{_E qHD-K&2U sA`$3/ ~mF\༤H[t2!`xT+K<-&/k6N{Jހ~ SnHKQOAD ėv=G!-=eSnui6n Ue2@: ^pGrւ=|C ޔ"Dw+`W1 2156Mm3Uyx.}p>9%p[r )UHԅjNzYHޗ$~r'gɬa Jk&3י`:eܮv~qi< _pg}F+O2Rw!mfǛuAVwb{+I0c_& 3gtp ׯ6#7=99mrk8Ѻh+غ@|96l?@a{Esrr2 (#΁, jAigs7E~oaܥwCZ=| liwfP)Cn 0^tOyepsy#l9m֑/g 90sM&\1G zC-5q,7/g@TsbAvU}UsY% [_hXjp ?FeMA#7/ g`_:{h#ui_@UAfr})I={۽2 ޗ .9< )'Sf8YR=<yX7zy$\`xtZlrZS|5"M\tBؑ4H!S;BrCnNH>sz.w@lM鐶 !*v(gg@jvĝz$H-A^Yr';8:Tjf4Os vm@ܦJ>&;ϦhɾttآNW+&NOݖ:֝iw .2d:y+y˲_YSwEoVW97΍ ͑>}=`oם*vTg{YisMڀ)@IH9tN:;|ZG#A'sfw?Tp*CnVo, ^_ًu@mu/L1LLAɎ҂tqNz0rApHV߁}H `Gۛ9zWj@ $]vU wMx[<I0 x?Y'y%#v9m6!;qu쵢}63T6mHY;[+\hGsP`jEAz 0u;%<_@}-9M1.i$V J/U:D/F_ZPWP}dh+#ًP&Ym~07f8WT=_ 36L*-=5#/}'ν;7vV>% +*GV=.??pG eW&e&9/cvrʼN{ 7JL9 +2Ju?uLs0a/k;4aTpFAuՊ;Y}@85[O .4DK)ς `IvI&ys}f7Lx;L|O{l9W)/ %ꐈj 2fyͮaGsҀ)M@f뽼My4Hir0P&hũAfEDNqݱzFnR9K]>s>mO@| cK*] )ZP hYx@wfɢ J8efm~@] ? p#'y@wNLJuP/qrm:.>MA~m;I:-7A׏̥}s3gvwWz[~OK\&<-[ttvs,1NWS DQL 8łt!\Ⱥ? au ?^ЃRSUlBtBg > NGP>*f0ʎ(Ŷ0S*8N sN_BrNֲHDYY컱I2&\pޢ :b>< 0l@Ӄ`[L7@n/~qp'[-o84G\!rgPrgW,^q=m=yS*o@sN!0 M[ܝ{"wJ&_riw]]V{#owFg-"[@8:ZKY* @xN/ '!+c^7O{ڠ;u"p7ܧ~y`*[ʪr`0[o~ ~*Ma@ D Cl(L{!wυD (Uzi' ,g g^STz_S:< 3O\ulK0 #! ) vٯ z9X?>݉}9'Mf,$O8x7Ep-~y/8 ñws/<m /$*''CpA9^&napJ+Ca0+&K똲8~ ct3Ew;r(utꮥ.R^H?nu<-WTSY>O*9#tb{%ܭwb]lo x#mݜ,DRaa53 ny=bjIgk FG'OdCЫ[à$W(< "z'~C`. h.mN}R?/.zD1^0_g #|~fqwςkE-R f =n%5 w] $Ïg d8s vX!\go q6 fS^ej}vB%s bu?x+&8Kߩr>wv7~qH>|m(vu"Y#!^(1TO*7k@O[i +܎;U|gɸ~Cd^;qjgT*WXy[o{c+зA]@*͞4/;Fƈ^: ό ±7ob CEf,央.@O`{M 8l%?UΦTW.l';_:~u?Ě / ΦhTǝஒ"m@],S#o2K;l 3ΕM]YD?)5rNJpDxm?A"PT a5Im<n?a-TA0 %-vy̭j,bh2CZ-{:̧s@.O_dt)st^n }66p8wK>GLrT?f})=U9z7Mo'Z͙:$e,Dw#BlOUGGf1W]k{gPe?6yW]y\x,[Peg H'_hѳ\(zmU0̬ x5mD~5^_-MAd]ZLCT0_/r K)KJTm 6`ȻܹP`SeBɰrpG(ԭxB7e b |,ċ39{ gY`ۙ߼yYjO\Z3w04m\)k.9Ao%P< q{ gl- M(ׁv1wZ"@ySK)6cwVVX*`:,v9 >(v4 & 5] _; $@mA,cA_i}==rb@"16~L|/^pDSg(b+*); AE{ ))\t;-&(7<(GB 97-/՜~ꤗq'CzmWvխN}3yWXyG*̬~O)|ץA չIo <ZW brk@??H0kP[ PV(c@OfBjVj erqԋn!DQZw1cl 0uKUmvrL$?ص^cYcU_r?$"|Eyxw{<tO F.(#H S(&^ O_e>0Lk{ 0cq*_I]ղEMG=tAu!7QVA*&ΗWU'PsUO;wc] =ꃔ&vGAf@^C">P" T+QNm`\f@tfy<f' f2Z̔u0q7Eٹ]}eQ|!TlZ??Mxg;=pf!ow$ԼX|ywyV6#"pl9QP<fZ/Жi 0# ,.Ul/s<T  {`[Uu/wLe[Dlm ~bOin_&(+ةb L_?RIIt ?>|]ѱúUq}oϫPWMmȓw#Z ܝo_g>Gŭ_4wU![QК ))o4vs.!(Bp_n!HiR2 R ){^V`-/ .ݥ(ݻ*%f90Ϙ owywzZpYc Vٝf8Н 0-= *3b[|O6 ùi ")- b[ƛμ `F_~U}b'nC,؉ 7 >-N3ɲSu7f,Z8m 3LhʹT4P Xc!8@(:@ ֚ $@+ʉ8EK`&sTAg 6@^qGDO1dSu$1N)S؛RgrMh~)u@ D@ڹз^q†6g&r3bG{Vr#H-@@sW{%#/!޼/"J6 *u> p>s "Og ΰ!lV+盟 VA &_ N[ݘ+wob/h TPB_1te YjqR n:"[9 Wp]P̀*~3n+T謏\i` nFh&:ްd瓹 ŧ' ).4U!_h7h#1jj/`38+E3+.=8A٠(AP&`@.xe_*\cxS͟`4=gf 򆬫 *Lw,!9~X^n{3K}"W԰sSp- $W_wn񲽇b/9l>3B|{ ҳKΥ-5:AغW& ;;|fBs3|F^q/.?+vf;7䕡hyFnͭl 8 |᫢-'cT]>ܲ_J&l8xr\ GGLoA BW{{!R6<8SQ?CxS7ᾘr7])5@!y@-6Oyl;:U5Gr&m(M#`d^$6V`[ޠ+V1ǎ7-¶y޷oK#-ak8?j7]Y~0+XM7Usy,rMGwXASpOr%umT7[wk*`Wi >hln`Z0SnnpgR/)LO3R{ʶ٠R*VHfJVZl_*Ov#hׄU ʸcH͜[ݒ\yZk2g[J˅_~2KܸkosKPYfbw$G ҦG_tBD!PjϪ&`;2XDL}Vm퐘=(q 9Y2?/pa&\s̕ќ Z$=l,DϧN ZYO}`Z{)m8 aj8cF-֯hpF*::x]%9~ur3mV PÞ z 詺={9X 5r3?>`&7}\1/f l! +{ԣwXjrNv jxHE}` p7LJ@LO$APDq+ǁlKf ~52y3+u~tShJxÎAW˃;v%:>ʷܓ A%wh 쵂𵰢n GT16rЩ)Ga8 G1ʣ+Ϲϓw2}t ""KSNm| ~齢l\GFSv>/1aZb6PU|63ټ ;ʋykA}oG;@U%[LU['\ԈVxH{<_Cw+M][& d)Q\1H.rܽ 7UJ !y?'AjJ7V` 6zDynRIZjDcd_oL`}YcW`~'@6{e/Ӥ+ʠjO[.mgAms+ʗeֶ勧DgZ(@w$ +sA _8i 0vз`bS9s0uhcfBx"ܞ BHlmg~sHU-dWE@Az^FL*p5+P`f%)f`[X<?uǷ:4ÿ<:/32Ho6$>n}Eĸ-)KOق7DOi'&9D^yVTAbo]VB@Ȫ}Ɣ΍91 bGslyp9)GMkk 61tA [7^2]TgCX8U^MC=S090A?3d1 fT0e2Q oY`۷%i 54M lYa3Dw;DցO@K\ T؂:$!ZJOT=ܼzI "\є6 ^c<''-.ZTA"fA|jt; ˯fH#A[C2YX8 d7yATр/Biekd$rdQ6T +,Y ;a:q5A ~w)>WHu9E3+w8_\3~.n>ީZJ[ݦV VJð%sG٣k,xL$χÂ`bLLp#(^!࿗!{gBds}:*oHO4FNsy W ^3^Rȹ?zKXWdD^S_,%"$fw OCbA!L b|(09r̙hT|=xcͳƄfpsw_knľ9A@N׎mAxǬ`kA?<nw_ G5rAZV@rNN {s7@Q=:9 Ƨagk2AБgG h -zQp Lq%,d4oud;;%>e/~`E&dn\z<$į$B*oPtQ~?`tpS}r҃`jVyȺ}{e{nn/pշ~UUPyUjrx׹``vxO8Q/doO [IN1OCzHo QnAtNmϒqL{YdR~I@u#pJ;C| pr=p+'Er'?MI3/#fWT/$o 5MЀ.[ Yw7ԩ|+"I`~Xl{7nEL<6\Nrl[;1" ԅխDď Ʀ4^qHyq{,{.vkxS00 iٓ a)Ps^$s{XdfpOf+;ȗ rh .LG'_BV&A|7 VxfG92W*/ۈɄ_pwxĎqRs*O1W[+"Y ;~ַ٘V)սɊIUVQR\"CB !Y!A̴UuVK@5Ax9^_ [?QANsS`F P5]V^W=AuHb'w;@,'UBA75؅`-,n9\\%91~s.,`82T|&.8YOndOXs 2ә(j_d')Ap1a@xЯ3< b$lE{;YisyL; 5kl~A)g`ڦWwo 0+0 2G_=,n>^T~\pܙ'_JeWv[}k.pj pxT}-H %H1"W43O9A bY v).Y&h f`@ҟ?Amp!V ƀ|Ӝ[@~fca 6+J@x1Gr` K ~Mjpn L(|d^J]3 Ϫ:PX#;?WHzo9hO S!/ VʩSp'A$NBr1y+H.$K~ R >(Ȯb|%جuO}ب}]TI ^=˫zx7mOPIy w|yאָ~VrK]lӢou~j=Uswͬyޯn7E=1Ŕ̑ -\5!Vϕn 4SKwsS,p*/&7]Bn7-z)!, 1npR5 :}^uo0Rl؆i ȪO~vg,Jݫ].̻YnW@febWyXbb%p -]}}BVl¬ie+~Ԭ7_s77}Z"ٷm^ya޾rf›u=>忓Iz"m?B%_e~[}˿Jnfd؋7A䔊AXnKr3)q@vcD DC1R5 .$_M<KAiQavL0# AaFX?`A1tPUR_M#?Dn1':\88[`>ˀ=PpSJ\n&N _4UNӣAr. h)năA 0̠>*>; 0* gµLqz99DjUe^+e=yV2Hro6d_On A~}!vM4wׂ7[f ;AMpϊ""* "+ 073< 2K?ϞOmr˃Ifj3e=Py![=MsѾav,؟o{*ժh^F9W彁9WH3U`{P殜 X*6Sr=Xf|X`J k~_!| 5NdS4 t` 5x;`O+k枑/IR|[t6x=^x䰜]b.< hM~*j nw{X<%]{& ݢf{05fi0Op?#wlk1D6+: C5uL 1]zbx5 HMMs::by@ UVx s}J>7<N>Y=;fcg@Zrbc")q'Ł &@_5U>v^v;TwrC AdQpdd,q*:q3j9Y?;Mvd'w`/av6؟2NL5_;ZJ Q_jPO~LwJʷ8>sP!y?^7?r_B~=?>SrzV(uHVY}>y_ !>//NY/6d5SM*yԩ)| q}/%? :raAK$ی' 1=~[4Ht]AsZ@[]7 )ub'/s]s >]LհY !ʅ/^I?#Kу15|~6մj^^Y8l@  3ƱDNV`?N-tr^N S99'@?/zdI9U!H Vǯ %q6[7n ї5SPǃ1}a x _<<)_> ?JeP攇 _!^s[/D=tL)MAg6K0S$O<x=l9pEzW ^[uׅ/(/IWBg`J$~{||3d_:<;o T`A|lr@T{d10W3QN) exQlv5F"~Y}P3vUm!0݃8.>L3 &^xtw/ kv> @ =‚ Q_JAT `(Le㪹 UdeЩvH2h6k禋:ڹ@K nɴVlWf.̗8~K99]7(xM=7?ӑNvzslJVV,{Vo{@oMtVi&骣 ~_7rrVKOμ-{[uOcDU:bg:\~qDD0]TQy985 <b j|c{l.0".n]n9AuOEb@&R40LNӿ9+w׃QئyD)sܮ{F@ {925I^ z)x B6EH{_eƲfr?!8vՙWOࢵ U)WȻFD] wjR`rn!pz.DvN0%(WЯq)C>v D;k ,~u , p:{@r_#E{Zp<{x*{v{\W[@m/S`O~:Z<f{Nx`u/dXJwZG_@+yЩ·O٬{WiP,^Xq7 R}_|b-W,=Ό,gwAeC zdN2kV5n DUFѓ-GB0';@ xSL[LyB=E;@O_A}lIGQ,압A<#H=jCS HX T5>:ryG1S+ꤓjXXf.\}EqU' 8'>׃TLvv-8@`adl>}*Ļ NN@)^e-&;o:d|0Z DʺIONO:wA~l15-rXP[psJKu= zF]Bܐ5+*٢`aE9> 4"`oFKS#!4e{F)tЩȬda0 vK^3,ȝ(9~o|E2n`K95;5[6?q?V9P2Wnu;=ȶW!}| f9?cM YU`nUX !Cx]V_7_C6j3 ,P->8AX$*]g[V~d>]^ sAT2{۠'H.Tl TxX3_| ԡ'6q0_go c#oqgbd9 R="j* `ȅ6C얟K~oÇSAtNOAݺTW]G/ap^sO E~+M{BExZJp$Ju9Wt.DYFLnH ƟCó:U"BKp_vC`[/l ?I}tdJ)} 2)ȓr}lE `N${'zBA8 X ur2-Ÿ$ w~'! ^OBy/3*3~>m^hxڎ̗@&7W4N0%q.zAtUy8du`510r3;?N-t5FM߁yUFDle  I4AO2 Ȳ `8'`F:d9sGi/l Fb:1: >ϻ;-ozt~o%Tu/{ yG0!Y›vfz}_Yd?Z*F{ ZZ vw1H+CBVgų`Sz8c s\sp3Aud_;)`3r@M3T?(VXSK`} "o2]{B` "3O\u@*ꋥpA_Sh7 D9Il HmWAW ׫!^fa %x,;oꇉ [qN< Bf~ SX/s )ؖl' >2:{&~*$㶜9*gOT>hsV'o@eVT1Ky+,.jZмaSSX]q8|\\S6W@]rF[韢A ^K5~#daA`Vܮj|Yp5b .Roz ? *f'ȍ pQ 2iڃ s#oֺ H]죺)O#N 8dg9(~uby~2jRWcjؙI<>'A]C X2.'d# ON ajb-D9ħz9H{_{2/Kݗ=nwdœ@lK03b!'~r=9/qKeb@+orl AduX^cvzO%+FߥCXCv]tuS8UEMu?b u/\WBA];`|YX'oSdn: H>=k)j~mL*/I 9 &!j(8\[28V`NP+[yh{@D73uEs*Cou"9SAyȨ]-[^a(mgj Q78DZ(N4z#T |d^ {r9SA'zA$} &C#>00G]=Odʯ95te0.YfaPʀ*!S#r;rS !ޔIvŸM;A<'KI)l]6 >`p\9 [~kl7K. nyuתvMvf9Y -ivB0_#i,$˄~սjDI]&q2m/ʂD?}\sAJ5MBk13`Jꨚ#%2a8%\hʶ3 B>lއ^Wy;Dfr +T l\76E@>]初U&Ě9] 2[; J~k} Vc6+$+lW0 t=vOޤ;) dZ <-c>}=s(ou[/_bhu;ߎh|x"s+*6q/Y:ػs^AtLpGntW 3pxɎs0TDm=S%ۃsL; 9f-EE[Dx 8Wuٖ-/s0mm= F90i.qd9U|NA+@[j [8ϛ`~'Aqq,$Áa .o$_n0@ m`R5! ğJ:{ρr!4d@8p&;ݠԝT$ 3 T`{~?{gýG6S ځֵI GfD.EVrwgA EtR^;fӹbxz /t=?`};s5<%ᗶ7 Nv=^ z~M..P7ra[\~QY+u< 35MiP˜61Q8 aX >/iR z5<½r;0.f(nRol{[g3vOb)%NO=kOцNsYOW<,ޏnT@& Ⱥjxc"GE[rYYS @$ )X"aY :m|ؤ4PV΂8ezhKEx`&iʑ #)mЧWs8%Zks5F $&qԫ5[tP+AvTD ˂;/@nYPq@Q㤚@6qۏA~hց-loܞv#y@ZsA+|?x—N*Xe ֲ/yO'My'b?(w]U=9H$I H  Q%(`B@DAT(I$I$A9K{LwW{/5k}kjX`+A`ϊΏ o9z}ݝ=o=еL LuV%@Luzx@|.?qfXNVkA={X  HErN).yUL\) |"Bp2<f=Lu#y6A۾ DsNۤAm,a`m2NwnY˂fi&'m=orUjX3sĆpr--K죆ق0B~0O`y:/s528cH}]Qm&S`MY\Rsũdqr; l]?|_?.ΦA0$(h 4zmsAC݄Z_NmSO!X"s G&2d/׼ 6.PX',CdN1o({uMH,ЍmsSt B+9u!ƀld y~jO+u ؎ j7* ̲MEn`D{%!i6eL Na Jc" 1`~u,Ԧ8߀}\ \P S4Ts(Ƈ@ȥཨi)⠷f.ȢֺneKۛT +< -%3LwLJ4" |k\zDqT "MVMgЃf)&Q"z3+-P?Fjf]$.zs,}<sG xW 1oEL_~3W} ʲ9 /7OY+T1yRƘaUP[. slZj!Wø]=(0y̳_)z=m,6N~QCxTUMp_xy\6An~ANA޳@gV`M(GDDVӇ8br3 v3!x ;uQk%?7IW?X73@OAO X%] ğ ~Ld;JKD 嘧U[_h>lz[s,h+扦T./yZG CLVt6`nb)S.,Y̌Ε-@ϘoV圷@*jLSʹv'[#ܑ[6٧ɀ0>/Fd{U,\(nDA{ix^s A4~h# z&Mif]O?֓TM?)LsnNo |@Rߔ[.B3rZJz⣎`h >c)@Η;JU3Z']FyT? g=l^$uJ;˜*A/}WF""ȍC sk56AXlW6hwAffr)k`VӴcTNE8[`ښf/xv$nYo~AUIt;G{цeJNvoØ 2*|Ɖ#宥K;Us\gyp;v!UCb<3g07^&A`@9:LJ: Jn~YdH0mXwL3K=Y.WA0e%g"Ka  k@þ feV* ^tfFA,6`†n:ߙA L`ۉp8]iBN0n͉$p ռN|`iA(-+9"x 23 LBy<8=yܿG0V6㜖m@&xZ lY:'yWs!a+SN/oD&mygn;·`V_J ܦzxRCW/ik? tu/d`oA :PlOp"\|^g@h]X7WC#' Ta П$fr}+cf.*g\% y>;)t5B XR ?tLj{ ؒbo;%X ^`^*W-ȅxQ Y HhԦ-Me33`Us؜Z B!{_ٿT]>huΫ5!,Jl>PMX_zĝm#td tfv\ idrz[aˠOYN?[(6<v} 8+@Զu}FV|4 ,K=`5/NˑI{>r1r=+^1Yؙz'z2-R=k_R g D@ [@0}kŖX]7&[;xE/܂^1 Y_mD8Ǿ g ܼ!8on?V6@-2-c"=GعL,DuUoCX PL0K<0EWL¹'ۃJ;wA z,ڙ1AE;\NE4$'IΎL[CL`SWPOo!gGJYO. AX1ڜ&v~ z c!8FDg40m+0"IMNY}2}A)usНi#-8Y ü͉&+Gfp"XjG67)/T #NʜҼk\WTWf^_1ٮNO 8P_ uqB1fV|6rAP#f DTSTyƾD䔞E$~bPzHTiN[PgI-&' y.!6l;6Y;[H;L8Lֿ{ fρif <-:'`Ef VmA, @Omn ژ ?wx*G@Nd[py4xepl0wb27 Ba4 Kclo7 O=zCXa7 <\T!sPp(c|Oby6 .cLb+B)SNF_ Z%}8.$vNvO;&U9K\ہi {O sbs*X[- eܪ`GF &$%ց*oAL#;N]ApJBؕU(i&6T\=-Xp@`(854"% ųdBQSvs~ {,_D; #$l}񋱹QQq1}%s-uY pf*8e^w"ݭzFFa3P##@Щ#ij_IqSa]pz]7 f0#RZNRn~{aPSӗdl35O-]'?ffqp7D|e;->eQA J- wBL_FN%ڧ3C} =l+NpS_mKG$We_4?mD|8g{"Kp9-jS"%Ⱥ2KY&Y ^&KAOxq?ir>nW]A 4&| {AxH[ ]lIh nW܆yN4u782r"eY4سeЕJÙ|M/A{Zw[X/sSgs`2*8op Q}b- QXp^1;m-|!f- EǠ7l&^fjf#ZLlQÀ;Eߜ K6Y ~"916m.`Z݊9\A tw=& @|gNauT9d7Yb28{dEVˢAﲗG3g_J+j{ cƱuv`({ 5 EmPU6 @Eb=J")+>W/< |d[,xߦ\΁\oڀ!5Դπ`P$(qW\'rȜ{ݣ7 vOzN(+'D&_ZwO/8.7֩K`3z\=]t\ÝRr?ަzٓh x;<?s56QAxMwkC-π6J< N)hj@ R[uPSEG]e Fi7١  :zevu,xEw  jsݍ 0GIBc  A؇b %fػz)rX7KQb9)E,@ s@]QP\j$6[D5'ٹ~q]7!3ʃ>BPPWKs]oӯ)j NzQ=ƁBR!f?+HI[#,?A-N u, ҳJp܁:~ ܄aƂt } D.-^0`# h%|<"ʾ xL#.@45p ) 7jPvƇ@#|?Lg18%D^'_uHͭn?cqgz _գs,k&+>WaV9ÔLE۠[Y|S1V+@9=i|j^Z]"$sa7<g L`9 NEW?b, ̲,X6^ t/ Cv-r,S: ?y3L(tҜ ΨNCbzVQ/y.' ` Os/ćQRv:H`<ם Nv^ePg,ffAns Ɛa7?ٰW7_YI ǁjOSuHѽg|YqQ?v@0$xDYkோL$b{8Ķ ᠫkw7v7Ձ~dT07oIup#}pl&^YUAY泙[@&ɩA6yPRIDATqHmb7s' Z)sԡ@a7tCcчc KDl qa%";[ K: VKoWryj E/&ŀO+_O~fylG}?Cg@A$YCzAUDVo0M;DI;9S5A)%%la1&Qҿ|)jy@Kx "o d]KvFLĚDQ{6uQ W^O&AĘ!N^Pv U8] Ary8)v~>\'NƬA50Vp;9p:%[dA0M)aa{V;-]/4?87 ASY/,9m]= Lb|<+/$?ƋnW.#nf -F@0&~x ng1: +]3H<Cu^8<AO[A0+$s9!sH48#2APY}(R` ^ pפ^)0xac9I`33OGCQC^r'X3N8`ӰoCūu YN& G&ގ-Y@7DjXR}KL>8-78WFLF8JO4L%ߙ]yC}Y-o뙾 E>R{A:rlf ;I-)AW`뎶oEv1fsl31Ё$Ȋrt l{4h# M3=E%pV8~i;&d'i+.N2xM-pY f4`R`{P `.8AD=d,yUG wJrt1 Xf.AķaVHϟAƞK 76|A7@6Iz;7ɠn:^[ ?#dQ+ie8nV@/4g!,o!xʖ >/'`ר=Fl-9=qL=SY/NN]oyMQP}HTp;/DžiGAAt}XGC 7_* @|s`ٜ aځmL-sE dzy9H_/C[17xn4jƥ ?C?b݂b-nWhus5|mrwl6 \v_nOr6ceFN?Zwl%Bly:KS4I߻[`&sڛtru`]/ j f=ng>O[/z+T,Lpf]ƴ;l1Oz[Ooe a&Ka~V6f59/GlSz0?2@i 8bڦXl { h龍c `N&r6N]!,'cD g,#"Ӣ3Y0TE7,C!(e q]o@a$I ʜ94bB;y(wE-~.zC<@Ym9)U/\"S ?Ik,/[I)GA"SP` AVn7ٜH A1e0&jxl85m_LL_Mo `։4}UW!DГl}5 j߁H,~owҌhS A=!l3bXpDLiLd]^iB+Mu7;7 ̅2%-ᴔ!ihDK.*.\b3)=ȿĜHOnEszWMTAx̟i8obd9E`)r;LoWcOA#I ,VYTvvF6ZCqL~sqP-֪{>8MNiyb3j,81od3 &3Adⓠy{,P-ѝ`ئ`:ECSj6 `n{`  S:g¹ n8Y[/G:Mʿ{=+/,zuY[&܇?6*xgr" )B~<&"x[#Ef3-@f5]S+Dg]n{`syA@f13Qd[*} ?{Lp΃[|M|ҘAb:nTu08yuʂ\UuL}.?'n>%06: ߼W mH{7( f^sVUAdл6!aBIdů{Ad;.Bؔد0 yCl1IgGAutrFU؛i%nY "j'HS҅7rg^&O)f Nu?Sd+r;0EAϛlAP/Ey<5RpKn7Ȯr#kx/xg`qK69Mͣ`Ɂ" 4a'A'xb)$  ky;8_8%~\zmh^ ,Ԋ8QX`gj"ȝ ,0E< 7Ռ& _ mN <@1٣C_`fڷq%e M`Xev, Ϋ Ok >KnɬGCp+iAxRVPϋ]YAU01’ 7-@/=gvLe8ꔁpW7ySuLpցjM&.fs~S0*/r @YTQ0\.AMyt[nbsX Hd1" z7>6_L\K< g&Up~gǝV+#}@߈Ӽ@l4q',tw_|*ecJv,x Հςgui49ʡI Ƅ& @!u{OAd}ҡ|LP 2Wks%K0m{D6{_ 8({VATw'm0_CtSX#c5_]}ެԗe۽+d|n ИqwsO 8;ܨ4P wɷes-2m@ǁʿy8Nü2 ;'A4nXu-&O!উ2  ŧ qpk=`[0z01 X ^Fr3Ukcl:M3iw7;{lvU/>oAmZ7Ua" @tA?POe@o jiEZ\YQ] ("E_BRe"8vQTZaf d0Q` }1T#AWǿQaQEeutEsop4fmL8FS=v'XpV@X, [1Y@vn: dٻ3Hvjج  ~eSӾ 6[8ƒudW;d9'x=e| ߺA*r?^ؕ^)O{v09 ʨg>C>GA"V8O_3D2d<|#{ikk@0O^b%D{6}qԎ:`׫5Nus>Ѷi-ϨqwW 0'km[}hV]v_f:735A|!>GA23c# d\ Է|P՝83^YP9+ʀce.vrq\@>-C+Q4e?;X^C{< .mb,W"oz?2 ]頗ȧAo}h * v#STw$2G`T9+W .Gc?g94j67c =lE[_#&/RDGLr.ӟ-<1SM{)Q&++ PM,;Ks+F`Nۙ~ d[*CXF &x֬Wx7&VF-l/Nv=?0VyPN ݮ 'kܷ͝3-n&ފ^c'd D^vtV@* MD[{ae?TSU=<@;)!RA GA 1yb ~5E&eT3wBrg@+'>ji;B8'nNeP߆[L]c ~ནUjM-x b:@WW@'M/稳Qt'>w ^@S竇NЛh,OAl]ʬv.ׯ%)Df+:QTOU5 j$ k_Wj+"Yo?oZ ퟍͅ#,H ?Y~5y(mSS|(Nfw<{t06}?mk깦8j3Tq>-fs8dU.S0@|b.P #< E57 V]JQ]iA/ {;Ip.c UAaxnPo6v ND̉v<Faσ^((TʐTMX@U&] ɢ+%Ur/; : R'gjr RK. { 4:23 L>M9 8`'Nҵ~Xc$&%yl# "3 C`9u5lj{ z e""z  L/#tbj⥂#y[̫f7 &z_k[Y&b(؆IsD6> 0r2zcM0`DsSOn'BQOD΃ pGw@h; (h[9D0bEA~`z@,7\ ̏THZ ?X-=1C~Y5@1 '5\,} [K_x J7f-rcb1;˟>*?e{axx綄#-$NV GDIEK"#G奋ci:Z wa,CuJ9`s<|3q-r"</NjO"6'hs@*- Af2L0 hBk3 궿h#F8v8Ӏ}3W?Aq. MKҫwCx{S }n^0ܖ)˜ n3=0'E%};?IBJLr\Ls+^K y[T jwyx;r>Hhn3H5 ¥eF|M'"TFz x45ѼnW5`uv2c)La=fn&;ӟpnpVw]$Z3iD&?:$A MN-GtTj28)n<f,]+)ئ ˪*;XdӜ~m)l櫰* \ :~>5f ٺsdhW N#?]OQ_6G6iln`WEㅂu@B( t| JGI0d5)rP/=0%ǂͩP 1yx!iu i`j_3M{þv;lw]c}\GASS݌o|VKF 9@-y DfI S?-'BxY ?L@N7Q [(ĺ'>SGW͛pմ Б%`-7b0_"I2/q#ȣx9rDփ O\b DY˼,gēL_otV~㢗m^#aȓN1'y7 s9(?5Du )j 6n rQ!KT$~K7U:_q&RvijwP=)wK:  U%AosY9?hQY[ٗlmGNptD(`V@pLɾF9Sİ4EOi3B6uAdujiЅ5} L6D|f[@C^rAG@#&<`)&6٭ ?Tݫ0M=DU?},Dfx]ݢ`s7v)ЀM"/(vS$Sv0ݲ xY 4JoRMb@dQ0/9eb/inoj1:_헴8_7mxѰTdhc1fi_\tdث؀ /9.?p5T=XݩՃ=?=ʿ:wFCaլrj6$ݕ< jn=2KMq5l|FD\DZXl ~;SdTR=3ʏ v58Eno0m6,`6߆-T5}0MN8r G`;@Wq^_ʧD.a;9OwtnW{ē zn#?o6ao][`;naHU,*<d+C-:lmAƋ6!pF6_a7d>V?I |F^Ϥ`׊M^&'B0cFS D \7~PUv[xZK@s7e{ooYg|gnޤgq  bBamXl<`=5Oçu,UC vC$؍r 0d#Ai y#d7O&t@p^ amTQo'߃ 50)a oǫ' Az#`.0?=ijC@ vBї@w19 &]dncߋx匼`Vbs~7C&:! &^[N`wH{Bu[tlu^ 3~"[bBsPg7}0>"뀽aO' .v } ׋G }*C|j.!:: }=#_ItH09;^ zH6pkq_.73 ce?23EL*wzYB>O\AkG70rk*y8 v(o$;BT(/>rZ'I {Ž^CK~s`R"; R*/uсъ܈N6 &ȹ*YsRN Y D_~ t+Eu0M łmL<<w:3A]0.?yO| U, NeQҹ7-%`8pH^3*Q'7B0)v9w0mӉM`KY16УAݶC( |PLRFJ+PL[j$OW oBw?7XjxX]WƜo&؟Bͥ?5]= 7AoPV3J4 ˂Z!J:Sv |/ӘI p~1dvM[RC9I$N S䂦>!-'3Xꜙ/&~ˉ]LjY,pLII#eE٧,W7?1A i6(fv]̂)ek@6M3HK}_m{Wf~aAt^`JCHn??q 5O@M5 u`N94k;3K߂~ T9X "KT@_4sd!7W/F{^Kz,9KUyjjjg,kGATeM}N@ƣ/H6@=|fb) ${xLR&0ȃcH!ni[`}"܎ -`RM~ľƘ-6-q]c ߹aD9[ )>j nwU1WeE8"=|>;QqLcliq[N)H~.l$^YlY}2jLK͆䗓.?2V$Cbz~[J=;k^ߝ#@;v/50٘ė7 b ^;/j;+NJ~[Z Un6V7LPlH_ ~׌L%Mw i@ uK,S] rZ~*O 쳪9AQA/pna١+ڊ+3̤*U!R>{*yb2{AsA9f* SE]q TOkw:dZhg"{êA.HKz89nA/Y&@ t:XWӧ@31D;Bn؆(BS҈`ZNNu~]Z73% v,]\Of *-r郠˦0Xl !NX=3&|b{<#@ijA81(sMۆ ni`o?@r2yqqWh 6&jG[  ;W!fjx™yJ?a`y`CaVS=qZ`rGnL5 \oAqi^Xߴ*`x㙣W W@:b^y=Džg,-';_||;M~vlGkއ/ysB2>xRWRy\#QRTQ4ӧě89*^]m\9U'/($d"L9)n6g7U6 P9'gywAO1O/[++4wX#J$OE'(3Dg7^\s9iNrC5O2 4EaZV}ӵ:8kTi5(AMQN5GV4Se@U ~Dpz ?w9[4GƃMA>Ï"))fƠ;YA*_2U+6k%[mhc 4PT^Q6t0* ]`P1s Ԟ*cxG`-lYQ}3F4!la_6]@tg=5Jz^%]0m64@ć eM+` `ڼ C~v:"9"GAl=#,|u8~`8_nWŊ<l] cG\/u~Lz3zUC<#NA *3 h@2m_`QM| lb9 2ح A n3 {R^+2˒ >'īR5/@g } ^##@-hSka7l|ƩI` `r6_ v-}{ y!ȧhpWGWr ?Iiɹ!]r}G$gV=lZ/s"|DWQ և@pH/. Dr/mok | a9<DDLz;̶¯ Mݴ,.]_;,ٝvX8}fvpdD|78]4"iC&8D.qL 6u{ ×ƠGNHaO{:zDI9=R;ߓmvΜ Ysf%;LgJc]]lK|jHBpE DN. t?3#7#?Uvy"PP( S+z?%~gc6զȤ&FSd%郼,A,aAdݏ@nLQ9#2BIN`;r-ꀓjAw)j 4^]*cM&S^H&YV1 8_n~g@W̯Ԍtw_hd`\3 sA TE48Ȝ=2%eos?L<,{`IJA|P$"PYg2/c.Y .C"O\W(?6\O?l_pF;y%8Y(`Kj+D~u`1: }*Q=j!ι@gsZ ȍw'x83#mI6y&++WΣN9#TMǜc8ɓ^t6|\(8 ?+Mj 1`7Kqg>F{b%s\C=!?1!'"e!9ӫ_@w@`?1 ×H0/@t{b#pJ^=Pŝ1u; yAy6S^~w찇 r\ص9L>SxII ?{BrEиkW V+GmNrS,(G޻v֚6<Γf)]fŧAxL/CPl :enK5((mS倬nOJQdA<BP8ln^U4`- /{Yj~l 9{3DI09fs\J1;*41Ǵs+0NU5A~߁Dqf!K8烩ll+qDj-r|+hX@-u]*^aLXikTܦf$@E (#mYHݜ|bva-rW7_BtSd ?cTs=v|x,|)D/&`C$$ PMGRdjx(OqF j*VA~KUUa9IR,AI$($ QQDPQd HN$bu9cs޿vG\߮гf7[ c|Ycy> l'YQN_Z~" ]ewΐs)),jq.ׁLyDGžY {+vy|\as`#QFL }8S (fo@;.1qL6Ub,k}vL̫ǓA_G& 8K=dyT=(5@r9QS3֝yrci3bbA>R 6y A.o?;n' 2߻+]tzSݢ OW߿șNV}uCoeQ6V_Z㶹F| ݰ@d5/F@nR%ܻȗ~0oܮ%K` gbݭJ5'C~g؆|+BR) |("D0DfCZpW7r1PST3pak%dU0,;ڃxyq@'wc- BefLpsF(+N@l(Wz@tPw XD`%j i x+%^ML[ mQ= P]Snk0(9, |(D1 g󜂨m+w'ZMr-L1l+9Oo`e  { r5WOρtd7 $.A4dXmGa껻~`oH,z ;Eb S%:1\p[t6pbb(^  7SN2ţ&kVTśD51 nr>﵄oRKBiɏTq wm͛`K˧dm]wl!3 9l}̤D$x' TY;"L2J<2+NB8.*693@1/:D @ &E`JKOm(t ?x$G0q⟂7~׏Oݓ-ʝ:qdM^fcbT,M>[v?x5;z\o:-n˼`_` pGN9U{V@&bCqT)U7۾b_%  %lHPXF[f6ٯcBЛpU1I+wIQP#Dx0z@hmqE/\;E[e+_z6 ˁ{A>~'ښ΁~HqEP%:k{9PM`a5 z7d}po=p$t>!\A{­ I53l ږfH'A{DYp1^D)z`";2{u-0C= &$ )>aw6"L\ d[&lYQqL i O3^m-1HAQtFK$ƺ!EKb:r:$>h''(.blm5)Tlpܖ=~[/'U,)jgD*v esဋKM=AK~o Q!,jAH ^3V?eg}AD}սz\.EM.?C^ VƮ9;?:s)yZ]b6׹σ CVA_Q@KR k Zze3s`j#PXXy~zAp% \w*G:QùU2'A }8%J{'uȏZ'e^A1g5}R pX 3 0Y7O`Iׁ\#6 r8et 8nVAXIQ' 7r g{b%g9 V6 _^:ho x5nb< AQnyK|$΂[^JB4/lYӻ^K65!3-}#@TF}̀ԧ2\oivx]Z`Q2195@ݖE?@WGׁ+aH~'YWUdl#VAxV?@YL˻ ᠨ|P29A0GbyD aoyUU)~>˭YoV*{/BhM݌|چ*@WAMp=|FtX+;qDf1$vvEv(!iAC80kNQ F) 2؉nLX6~9q? NF9T0 NcmO1#-Tp@t6stL+czsLLML ntu{?ك쀍PA K]uWUSW}ju$bQ8ebS@xJʬ}@EڤTwČ> =vDk/qˍ@s)GxGo :oc]iU7E?FJ.Gg l$g US@yWl  <,S}M7y@ 3H;NA,ţ gq ?QZU.Av={iiځr2Y)AA7D%l7 ~Glc:E?Y8q_N.Q~7sBr~D'E LA PV}T`b..FrԛbC20u{uDǀiG)Ѝj=,o6kIv+ 9*c{r2>4WA\ԏOa~ >{s)n]fg@&e\~6ICXPJAX8,c炭ꋊ}".$$ݲ~{R. wD3ijNB=s{ALmkjEx!Θ`@/Dq :ڥj##! XpccvȓnzL7P~k_3u7nGk.L'o\S^ 󷼣.l^Ǯt};@9O;ٱewe^4@o)O@lN&xu+..@6k.z]jp_io&p= ;;aMmOKsՓ jVӱM" ,wY!HtbJwf pUGms@wAM7Gr7=8/g@V.ۍYSg 4>9MVπ]!ɜ էU-p}JSbz_o*_OO^},Hs{ϥOj&Wh e!9<5xߒ-E$yHf!n$ RΚ]}++wLh>fۨ_cv< lӧH߶B4ҋS1T?ar ~L"vc  rXwһj8.2"`=vyMgl n$,9o/Eg!p 3 *%h- Ȧ5^oߺ CH7UpmߤdjdRAF^AsI 5qsARM`G ߅5VMJQl;8:ե%]sϋ0qOoo ,3-x8Fy]ޅpHp|ZokU'<@!./}A0'Գj39ڨp2pkdz9 ~N|W1f$D[Hb4^lVve4R@} .t?y)*'s)kOħ.U7^OgQYp aׁy+Ra {d Gy ᄈApƂ|)/Bt4=5e©ne{炝a M @HiW&y |wSo]<t\^ׇ.CT9O|Lر ;ߚ@er^Y+kvenຉq Z4JQʉ"/R}@֏+ˁw#c)~\h[gEι;ON wrz噝B[NR4jz6 gst1e],}4tj0}G2]at!yQ?Ik L c@`[&X AH_z?j`g0=y FM{pMp%n*ր>-Sۘ: X]Vhq,43kKԗcNgUFW<e `wdX[ !jm?@y xGG/ f bI vnX #3 g/8Rʔ3GPTuDgu!8®] bzxx 7vhWS]1=YATdPŴ .v?FK_}-eɴh )ak /P/1\l[<:nZ߮wm1ڭ5_( f) 'j@ʴԟ+AKY<e%L~j?ZD96/'AR3aZ U[s2ޗ :4#@7zS|9RۂY^f2WSµ~@C{nwAlw`FRQS$X4_?Lc@QFlW9q h.tAcXлz!l&íNnhGn{8oH-GQE{oȷk-XK}?okBZΗxz) B{+g;kP+N D^S}L- ^9*m%N@F)0ޒo^Krvx쮺;&o_JAx=>DyQ34 c0Ĭ@E.pբOB*igbhKu J: 3w,q7z+@H[%r؋`$kBQU <_dE9#0Iy`f<-oTr50qPr+<^`qbE3BUv?l}<*lxu R8f8"Mj\nm$**2nW Vz n5]I!Bn^`G$WggA C=Iuyx P+]hKBΖr /fRu29z' 2ͧ+)`ވ>UO+O+ ooel2D $wҥ@sXH|4DNND-:?]0x=eW!Ast;\ D 7 ׻gws f/jZȕ) wM9׌ʓ;x{)]L$w6^&7AN{D0}u̔splC;cŸ|f+ xZ>r+ݣutށ GwUҋ/1mOve(Q^Kw+%z &7Rv]AE\{dqD>yּ ֏k'쏐;Qo0*9B71!V((u=V#aEp/ZQ1ЫDyzY@4hxPW,཮; A]5 nbE2pFbR;qӣ`O {%Qg!:mr)` w"ufd'Lk?G rjC9@.dw~\P]KLJ@Ĵோύpwf6D+35ȹu=A;T~1R܏@SA賈XNyo?j> x=bGS,ȎxOv.Dy]rvbj(p~ɛ6~s+ً"nixQq{\hbJ~:6Agu7dE`޽)⼫A .ͬɡ ?+$ъf:-ϋ@h,NA8;aysk5s@L3&u&n+>@R/`W~]f[ \KmHDްğM>)S;dcm{>zl;FGA.0F +@N*`g||?=w_\rlMڱ7&)t;;@}˥jF >#GEstp1v͠oNJ;,DzbK&{?~,EzlUڸ_A4uvo3nUVVi0j"©MBbrk Q~r̀K,5{?ģ!'{uG0 &VG]T,5?!HpU:(Lip%| uYK1l^H.N> % @EwF]17ylCES0NZ`/IxT)t2s@WEwpCCM^)nJg]Ov*[@/Q]25+Cͅ vv3I,am5 uCm.h9s A^E~ ; w],x.9va냨nZ9 s&unH jM+[r+28M/ NMoWIN^uu( 1ч6ah4@>@b r]'WA4G!i,%e Et (Z=Q=\F#N=ߴ_t僭 lo~"8?Lh> b/Һ/=v7u`=63g8[Wzݑ>C xow%6  \i; =US'3j[+1=]cӀ } .߉z Fy1:ٶ[$,W& dMU[j?xYPu`V}ÄVE7:\ flG\)'_ b웠PFW^`&jb*\+S@Eр(Y*?D$OaTٞ@(a'xb5w!Y& G6em] gU3ʂM5OKAwFya{B27]]Jrؓ\ `nF@>貈Agzuػ⸚ zl} )E}MNu*aa(e7|a0_ӊv)ao"N4@3ŧG "oU93xw'YlꆟCƴpU@} W=<-Hȑ~ ^҇0$PMnQ] eB|QZ&۴!F3f'_p:vF9s@Oj:Z`$ 3l9[.f%d!Ucw^xMpԟ1:\1LW-QD}lSpSv+_#C4]\|ȭw'-|E:3x7D.z  +f hoVԗi("Z|;nv*_ :Ep߳;;Ti NQs@>2*] x f8 t/+X5@pbTUwY9ͦ (m-*%N#[`e|fbR9D KUIsU!ær}&KA9ͺ,$?n A2[@]6St)xlOH[^VhAwS]PT6-[ڳeOG7H_Xoǂm-̀)m _NI?ty5#A'k?{[D dd%S1(k'r ^/輠PQ}YF])@\g׈ lQ'` bHN7\r[:jO P.2x0:i6|1=v;b-=b 4Zic~]wOO^/ŇT %l糣?2ݾo{&?_CK'Ȥ u;U6 H;YX_y bn >$KW9E \#=q)-76q+@VD>&^Zʂf\>Y銁MoEp itn_&^E?9@==:O;Kqnn<6r zjbG?ުO byI@&ՀP5/z+xΫo07įnN)ؗ`w%3H3e3^ښFU ECx?|LLn 6q c ZE}lQ 'YLЋ~ER`މzlT#:JpC\E0*"#H2 ˉnrc}=cn2Cnoq=bܯ_7;S<|xZQ!vm4 s 7RLdGuJ|VenvT'* qJ^tO3/xݜID.t}쳐ؐXQhm0 uop[lz NnMr{e.>xI<♔U})5w@#U 6aڮ;nBr]~ "lzO#ڂ7I;#u<|PbsI?g7ogwBT=4[iMLx'#?):퐖ӔCFzRgkMCD0kU 0i" ywΝ0`[Ff8CQW\Sw8.W^xf_O* eE)n1SlٍNN^G2]WW/ԔȂlLjcJeuoCN g'+}K88eޗs^=OX{_te_8Ԑ Ze!Rg^[/ <cOb.l%g,-ցE5W,W@}+wR*]~$OBl*^MQߌgiĶZ.uqo_K};>nӵ /l_LNuDf]L9 M$b▞. }Kl}lq*!n(iwj_hv{6|h9} Ӟ#ri" 3єf>1IƳ@2z? >a5nw%,._$}&v SK/@K@ .[ m__'gG[DV9Oؼ?S>VRg@$`;/0BwS:Fܟnf*)n!8ue ToJ|\=Ù+^}4X<)j-V]ENO Y0;\vo3l 67gwU.v{I]@ޖO2j]oy˔q Aov..U=tQŽ3A][Dƨ .Ah%KBya}['`[ѯ&id#PWMƅCϽ |~yxkKQL؀L*K2S{EFSS nMoIml$P}@2"/ՠWsb4Ls; rmL7 9i_"po2'j0/7PQ]@uY~"B+^zMˮ>R.)SM䟮޿+k76y=[-*QOW3]tw}-jF`K7tuu H 5+*TЁ ;f'!6лL~݃lI{[:p5z䓹[;z9{ՍR?D9.*TsA|%dPS0 z [BT,JDմG]nPT]]E9&@B{"Ph*C!$GEer9!gk&%q+5r5lYZ uIpUUY?+v"=/ħ*-&v+D{!I~,M>,:J&r 2Clb KeUރe- g +dݺS'g]z҅gj!KsZ}Y*ݴ,(9e2(XF}ܯ}+ pYUnb%WD}zF:6 ^QeiUݓb1O1s~p`c{E]o.EMێn 9H[r@~UuSC]L3:o7)u zr2r6 A uhm+@̝w6wmLBt(*sk [T~O #h~TC}{ %*ʷ_.+-o۸,J)%-yXH12P̍U!Ȣi`;u ִ>[N6+ȇ]e pDWq] j3u0EK!nN *|!qx5:Yʥ^ΉI,m@P *Gquh Td- gCx*@_^I {XI/sܹ_kh/`q\y3b?J`/{Y⤫gjY5quXon,h&您|)߽&xE2e\J5?[."jw[6pqD ,zSB<Jz ًB^?<`O!cxrrP̱]4RΙ]y. ah^Qv[\Au!^qA; 2\<)_kxێFhFpUhud apٿM8Wz?vguPj؍,qivɞz0 Tu* 2K2PP Ct?cN -,z<^9CDl%H_8xdwRǦBs⬹ Z6:djn!J-u Qr̞.'#~ʺN?%@#3ޏU{YƓZUYpfGwm=M$kا\h)T'^U7W Ҧeɺ RɲsEN?vOj*?]s^|g Cj(6ZkɊJNr}V%[NT*X&2Cأ<:NW\.@|* p*ccT DzTWjy^Ćp^!FQȸ+o[F0 l'2hRW]t UGe~9L/3]w07xxDqO_[ ȋ|w gBXL.%xgt,\]}^+6FYp3p˷S"S,_uk_M^BhНʍV8s.Hi2҆ϖ2--iј`ov$Ky140Cb 5AzW6T<]w z-![J8 7ìv;^CqdU~ *)~fw+!*>]Ka1E?tW lq1R-y& S7Ds"pppNKDDC|Y3's+5E(?)qMc2)OGmn/@rSPƌH^th}2N 2MI`[C&bzlnA#h6 ̛ u;ZͻƂ_}n# Z$a1P7n"Rn_0o(=-t0w3$h=ok܈C;~ ٻ rbpՏf ':1"j9b߳IDAT{AJə{bVOvnVsd6tsV~w[ lSr؎ώ<^+ -:o0 KO"g+ĚleA\t8TE55 ԧ@6sQ+ڇ@6-U=%Pmbe@\msV64puZ .4߃[2U%`&PO Ȭvwף |W\ BTK]@̑Yc/ 46fVS̮w~"-E&s9}fkz+̺:$sw)oe'qYL>Q957^ jQr%Ym.9mށhhp%Y_ .k% ߈ <(*m)n _g@ܬʴcd<'W=`-q'=::\ 7=EJ-A Pd%p2CƖS,bJ%n2 ~@qqW-hCTX H+퇔FSOih@Wԉb A\P{]9K#Gb0$RFgd8[<ѠMp bYƀZ G|_FmsbSJYlCG¢`7{c!)΅9*4HYlgí]z*^fp>U`^p{\e@.t /d/9lY7 AL2y%[i|FxMl%!zC^7R=({Baۅ!o'ށtt.ܾxkcuUeuQ%gok@Zo[ "r1+myE;@#~SfyT 26c?Z:HADA"*$Y'ʀkW'J {ڪMUe;U 9~9Q r8 }< .Ͷ' vſ@Jj)k@L[rÉ'Z c^8lW^=P X){DI.l!v|EhaJ7^vwt~ND@3s]p "kA`;M,ifPDfmR\'n[)@=72z>^B vNѓ lc Rb*bExУfAb) ڡTtu/Si0U!+]`yC,_7r^n,cpOAS^r ԉwvE _1܈䆰'a[)[!^L=!Z>r$kޕ^Q_^FV ȸaE ^$`1 h~0ys@|)6 Ň~C ]U_ i`};!R^R{$O~_QW!*h rc7C̱!~4:z'DwE~s`fo4OWܿ[X7g;u|wv]'!.]/9ğU@C "Cu%[lQ7@fi5 |;bKO=SN)nG[@ܶ3D)`{nUM9@,ԏ5.n|#6<rju]1WSs{u%} юcUا8cޑA/e7I>LsceSX[\ X: _W_ˠr5u!{@.D W=~> br Jl ?4< bmZ= ğԧSOUlvU0)N&IP jWs H13},Doj$C&= ٩`ߒ `*?$8[z )V.8e;Ƥ ृ.rSǴ= *5ݿ!7\<, ]d3r{L<ʹ ;@ci-xU]Qꃫ%k8z! ]QG~ j.]Q=h9dq9)5'pOI~:&ysfM0+s|z Ƹa6kz:a?$OȦw[q/~Fu_{]/i2{zb{hؖ춫!z6,% o 80](uݯ\!ʢ5ER1$EmD ؼN|ir<8VSƻɔEts=D9-A?{C}nݞpi(QT98GpD_*TlQt0YzWg E=,C\(n-Δ)#b~^y/w`'+E0ָl"6G)%\)Lg&\w@=HPg];T=u6_L}9[<Ǫ7n9JRwF^v{ Mk{l 7؍xJVȌ#x7F-EK1]0LL37 ' [h~d6%^%1"xUA=Y>9nA.wm C9Y 0vׅO;=,0Ug5r'*+%a 3H1 9S)PWe \!ځɺx܄dCs~~pu\u r䌊̏A?%jӭOw?]cgRk= F'MN0bKEڈ[G2*-jb) nVv+𦋷3L{@|J?6-UpAVekP_3!1 xkTZ,l[-y0%! ƤN]7GUڀ(b0GbY JaLYXjF`B]X>g»:c ONUENpb4l.k) WT0m9YOa9}d_5-r~'H䎖4 e+=n(Gp% woldW`/˄b{O;}~Vc (^@qr_礖?/geM d)UeQ[YUMZE>п|PPE^$Jz '<QY=ܘqdE-JHdCXԕ)J. <~Cx|B$j(OC+3bBB}HKnA`>_.ئ@.yW=bB?'݂7Vƫ#5ۍ`oLEA\V8u5HV5%Jp ۘ{Qm ?sj"{ր߱Ad r, ;ƀ(>r;,)Ӡޔ b8DD)/5~"~Zb9JLii)qa ~ R{]?{t-U3?ȓꊴo--ۯa_]OWܿSRg@O{$%|"Xil#DV@n [L7dOTOx=:> ;L^N;fUwSmlw07S6/~1w ŋ< >WŞN&bi.ګ08MV䋮|ƼҀ!vFM ;بo>3oA=ZB` 7-t/fD4՜76]^W[LvfpKPQv^aNvsޓ`*S!sŁF?tؼX F ;cqK!52\V,/λ nA{]b(N WUʿ7_+kWG 6\55pm#z{֝]\w;Dbco|\= b|F^ncwJO{}-?]}naONra=3{rxxJlA&7@%S#A24fww[>Y@ydyGQ+h=gXDݓ+D\;~(B#9 lm=akC4[f-z͌1A11Й+\&;5:֤V^S2Z<؂"]y-M~ϼr l.1M΄X `;CgAcʧ^ gUmDIy`b+v^;?'W!Ɨ?4dΔyȜcd&L i@e,!CBDB挑!$3xDZ>??}qݟ뺞_k)p!㮚.2N,x_t+vȹ`+%]L0VYOW@P۽rBPq&g/>z-Dĉ<]e4/t[sd_ (.BV#5W'*AP[mrնCpZ`j,jnx D+u<T]k ;z=JSfۛG.Q5`}'J&?'MH܊K?|\sdѕkd6|fu*Ju-?7m"DNϱ9AOo9ey\L4P/fU-(>#@ /@d >yPdrqpށ&W[QP#=VS+"='E 6ݳ6BW[ş$ACc1)3ȟAE {-ŷ 'lF5d&qnxܓ~8Y׮ (N.j,B# D&o=AߔWA>/@ /G|G?r'r 6=fl}/(5A~[9lW;LSrK.v^u<B2m ^ &F5եt\^X/j؁Lq>T]ꙉO S {3[ tOAq'[.bi FA:f"z@smiKe7;S#tk Pb#2|JHjePĠ !r@J9;4 ^nw1*/:;&U !q*u)c25MʔR7K3!D\W%ǁJ8 e'd{tp%jMs{ z'Y, zy\`lY\vU\AW܍w &^2jQ+ ^ڤAլ@OGjLU1Nww|"7D}_XS K A|ʥ]'֋T8sk4B@\'zrmablh?|P*}X|)?{]KͶuU-~pl#Th/è=4#Qz[giCER`';B]L->V=F0TOxJ<svIWSOV@lsઋ3dSpȬv~b%A)?Z c%'Dpw F-߂"'7(󺼭?Dp{]ׁ̌x PWxRVEg0_/:2 ؁]Tvs0v9 AT9 ?W\#.yQ..~DΗkALmd{P !W=p?r*#gf12vq\|'G) t] z0yQΎu/C03MMzi!q(9Q/ u;е{![dhs~rUx/a9 b=J;Ǯ1A+E`F'Cut2 6zJ)~np]nFCtPbQԁh? İMg݋A{^R0/EʹUmYdb Z,J@t3y ~8ݢY{^|Q.{*xǶG=>YlGpXy *GHBd_N-UE/v- z 慨X~v07ܯ&$ۥJn3]BPA,ggǃi,53sb p$){-f]XNe Srм9z鱁 %E3zx4n>3rqo:t{|5=Yg.eN!ZUjC j/*B†)P:HmoE/ zJإ@+ 5EvO\{G.ҽp˳b4){`AȂ Ql[{%]V.<AQDSX`;tzl,j:&q꼷IT;5f|8f,{V('ʔO:P,c7VC/;&>wܳpqT?T W\?*flف2IrF7Pf0,udp&ȰSڂA4" S@UBSVCa4Hh d薥z붡 ɗMؽ5Ŀ-sugQ &6hỹ𱴉{֫*맖ePT\Ra#<}ᮥ bRxZy  LdP@V4-Q%i K]!=ܜ(0s@5ϋ̠nş\>,xRfՃڠ ; :=\wD`Pl ^e203:B&C0$sEپ )%Sgj GM[f>R[Q0Ei!^28oo %*iAkT|ѫ:DzCU=$GHQH1%fA̱l{ m;t[g6dm=D7;Dc{u.o}&8keq<)$;?'&t] z'VLw9hRyL7҅ͨOl_dt+bۃftPDD UK=FV'TGyalxK3@4cb }"tu.cxõQA;B;h2ʪ:r]X^2{Su`cOƿ{[ ς6YM B fV4g u]d˶t5זq_,7J * +JS`-D->`K .v9|O`*\@^4eb5-wm;º]vKJd &>e%`O[r!fʼn~_L |6v!,gz݁r??T1Ӳaw.ʁ>ktXD  nO]{P6 P\&3MzU); ; ]+O?@U#mrJB~ӴO #=8;s ̎0Sjk*=t?SDzBr; 9اw &) R/gY0w_z"ȿs2 3۶Kv>  ޷;DMEu)lFq[w<8 ?UXt>9TZ7o@W 7U 3^+/ ?73"K 5vv-/{䣩Em /^v0 ^SI/G/ +qi`E=uj.(rh鮁RSh?v u($ x??B RIY /|햀#z1VB:,Y;ͥl=u&Q"MR/+*=d?**֩o@\@Ly͉j=Jo?7 ̅dFY<[ l2Y9 N1#:d'~'S7DM2Og%j8h;9҂ȣe Ѡa`_^N])u2K/ຸ(En{{+ͫs2vF{no[=ȔV㋍ o^,(:tikpZWjvσ*Q/rɽ :-ڠ$&_%Xeh@t!xK}; N1W]`-4ql1snW̼:b ?s#W8?"&C{ЯٱtnDe9*DHX Է2r!E*sB;+nn]7޺&/Qr5.zےk ;{\axsEkg\)dR7h_u+_Vͺ4*V|U rnECpOH ğni05ͻFC0={D 3PVw 'J-oZ@o]A7AˣY# / 7Շ Ԙأ֊";cbxYTp_| 4إ*w&ٝ<AYv]5UL<qgIV8\5 ܴ&u)t[J4d$IATX_ wCIDYԡLPԇb`!VJ2⬛Ij- W,U/xD{+ƃ/7%zut?m 6ڙpUa={,CSA5ݓ>6Uū` fx5 ٮ:bGX_$zڑL=$T߁c.ˊ;jocvqk 6[(7oYD( .R,}~iyF7J= +BNgz) w^/ VD~'}rbZ0q?]T55mg"a-hoozK6zytG.vk@yFܠh$uAD^.^6!&.aߓ5\|9!X*0@x`q%t ʶ$~\+^qǸ ƺ., *PLYC rW,cX9]uYG{)?~L3dD x/#&p|-a`D m#PSڪon WH슆zNnonz$|&%ﯰV'!ۧYgauڔr p呵2 s6[`gb.^-sm Fz%:E>qA,p7cPV0\};G\ rl<_WƼ6TWgg 4@>px=DUy+O~P{hl ~.Y?n3 ݢ4ȿU) t zt -LweH~uS|vMuٌ=g*n.' (fBT]4Ap@NqȤEt@ܗ"bJ?( 5+4PDIpo`Ϋ\KǃL]qWy4*"ϊOݟvȵv@";@'m1 X" m%u D }xQ̲{omyۄA2 mkt]L>q5*~ U1ULcy5\JD΃@sqႨ`f75z)jk7R`NQCveނ`S r6yG vxGd \^29[5)Z+j(߫q*T`< .57AUvU.?ewU_AEp3n!Ҩ^'vfA|nAXFg5Q.Rg/++ۊz~`:=:~<^Ss`OAMG-`߰qî0C%@ފSvSMֳh>-r|6O'ռiw;O6i?fOo}8 /ܟeڒ[&sU^,q L4@i~vй(CT=GwlcVn}h*mf"{-,"M^K4Z Vz(s26XuK! Գ}/Eq[[򸾐|_g怾BX(qR^(_d@*RibȤ;&k'[c ϩzPˀf&B1(ۃAwџPv&vD4֬}lxUN0j ةțNyLn|6u Ʃ%;!eY(X7^M:,b~tJ"5OS@21, ػq~ Kv݋W v=xNVDc\" g5 x/`@w3M[cAP v3354Iy (3<tpD}}\M@&1\]/Jj%&@~T`bx􏮗dkA}&{ eQlyH>Z^Y]U®l {FL˄bl{5;Nu(l' I@> d=0j635]30s]Y'@WՎ׽.ˁ➖Ae =٩?D'J5sg,'KE {`y4`_<7Lime Y<Qfz.ת`*!#mMUЗ*eWS;;$*mV^M1{s2x]P߶>'G4~ B7~zU.&Z%fܬ yR+*''>*NtHyBG~#UXQTr+&O. ,Q9x Ĩ 6#V+?hWuH/dY#kʄ  =-*#~+P͝5A_9KMJt4q᧐gD ( sT<_l#. f-udvv (@YT=S 1 [,TI1?,R 2.~ g`6 Xۣth,)ٮ/|AE.t͏ q7} ޗvxxUlY8OP5d1L+8Dť+`W^KЕe!1\rdCAֽpYŨ` &_! 5fY-/'+k`yEA[ydy`tU }m094V 1}̴q9AŠ de3']_;T+φeAN pMQpcAjȻޟ~)EI/V}B[zLS1%ek˦4e'DjRjY!=}H7 f}6Hv7 Aell6D]o>p4Ż WQM=>`Ӌۢ[ :dT.HY/EgCGY;LrLc!erUPPM ӚUtӻ!mw߾\p1 O>sǽHK6N>MtflF0/l)yOL7|Hɝ(=|xh/X`; z+@tmntx?!qXf>N3gv0اR`Ήln4-$[KzpOfGO\e\~QZA*'P]~) {1R_m b-Jpx{#Ei"c0o?ȡn9 0-dY,اQLm'|H\huwlEp{&UT?Bͭ4@Gs\.0HAZ؛| ~:w1=dV dYJT@_WLh n$7{ tdU8Z ɔwAmyVb>{o_1TKvޠ\G1c=-ȇ492@3%bb&?6^EG?;jU %Jy#]:r-!xßH}]/,C/V!0!h^h9 dliGg{1ZWBTʈQAdU|ksꉷ|`z19 "7! Avwqp);tσ($0/^5f wm]H eq~m@ky0Ku *nĚ)Vy?7XAfY  EsU1!XAo!>\v!mmZ `N{0?Ow'#$yi͒٢`]|oL+r%=<ۚ=&} ![)ۓY 73Abib,._-(/Z|B6@züH-[}ip<~3 a e(ǾmE'N) чY+`L% x `l侴2*[A!5/ lu_O+-}2=Be`>O[ &rdiWfWrG q̠b({Kn ln`ķuk.Y [₷2C=3ݷYu~`f#w5>6 /. jY ؿM%D%v]HkɐoϚ/!⚉O[ Z|z$ ˋc#@i&쮳iNW3;HX,n#>y 2~ we;A<fǾf{V7Dv+,wS3]{ .#A.21-1G 2KSc/B./nTPN{Lf mlhfwX|*2죖B{Pqs.%PӃL0D fJuI7 ^md-hr"<&q]wH[PaQvbDsj;LwA9 le OB,wn<()-L9/$n36ze_k( \@ y1˾ }d`~-jKv$1h泰Gx\UBuZ}۱QϾ7o?kLmᯱd>Ox׏To7ˢKz)Qz'K}DGϘ 1 |?vɛ䥍Md3#h}Oq@'ۯT *# :+Lo$ط]6ȱI΀9$Df7Jn`[ctʻ/`ES ^`a(^/Gsl4 `ls dsÏMobDţTʝ7xHHN'Nꍠ2] 䟠71]]zak/}FLS@K0F׌zUU|g:~um579=3GmrR 9#sʾ Q&*.s-d#`W$}/?]@4: BgtOHWF!|< IỆ`ln`IӛAm`' edC.g@씥pol5P!/?A@zdz/{^ Q`Gٟ\gh*8ȗM)pE_9c_O^7Wo3Al>a0&S;- 6! (TV;T[ӿWUph PS{oYHfI :~സ.6d҉zFmnWvPژMԶ'^uA?Lb<Q#ߚzk5[kc)}k  s9K|pp~"ȦLw3iR 4\6>L>#x+ TV '2/u&w_C2ܻ˻|`ֺj. n7Ap]+[FEׁ0.<˂b1U3Ӫ67p BA{EE @Td!SlmJ$NQ|=:7I{T^Y@l #@w rr;P{{;hiY Wv֝A Ջ? t1R΂yZ= ^?@隧(pMd'~9ʀ=& 9 X:T 0^fHS3[$uȨnC:a,貦2.Rt&Gꮃ;fd2 7lsAi@(" !c?,Nn cidAS \.EGslkO9] T9' SDv/ȹ|T1՜%t1p ȥ4Ou,1ρ(R5*pQM)$ (!|]Ynn]zH z7`ST6 \'* @SBA*Yp{,1XtU?_Y"ۃ,/+{gOI-I˳ Z^OY UTw@q'zLu_ !9m?@5t o;QS$?޽_]%TMG ΋FQ+=M<( ^RMlD&& 黓 H>Sm3sAJj 7MVpϫ?mCU^09NW@h-\/i{ަ X=Ag6eFnFྷ=j6'b_)lYBo =H溪4o"xxx߂_Ne(EFhMCطMw \Y/A^z ˅'0.>W}jk> M5cDu7iJx@7?UN].kCpgc-Pu?{Z4Wpsp+^iTO+@cmrnS`fcT&?@7@ 1&,vEm@'}+rD-o:Td9$)NS9@},>d1b v[V5!z]V8OE^WL=LZ$جsӪ$7>l {P?T}=]UT6V/qր,Xɿrl<?=&V0S KӀC&Nb a^V%$L<#΂WL ~g=`6 %ޓ4@uN5C&ݯ HSWJ[ks?:Wφn3m{]o3=׎>?sd ܞz3'>h/\&&`KWM5vO!낷Hu7Uў^zTq6jkYo1k)!~0$X*-f??zDN.6RzA^% =l pg J= iK8v fmFdHAyr{.XTǖ$N@tEi ~hd7BԘ g夲UJBmJy euJA@P<"so8w/*Wb n$ ?Bë rJX% j0@zRZiwAW MfkH>f_+<Oz%`-^r&/]2@|!@b&-N{] |w0:湹j$원-q<\ ͡uA7g:psmۉB߂Z.`kDVDo;;e 6mcu=HML]V;ڣ Dm\_qHv[\aHIn8 ^W[2[ :GخL{FψƂlIdK{b.D5Jf1Xd+`Vp.o}Y6CORr)z]uSnސC Qg8HCmQSH[}5MXOG$v@ɒf]r3&;cV,wAE\HӓٓkA,IJR`F 鋒ρ&Nm `}";,4j%WU qz^Sh)$Ac9Xmj_^߶ CwE gn E$UZ'r,;:+yFy ?X^u uY$*FvcQ0U(B8KVbt3}xkjXSDJfEJ}$xh%x;@(xtM]eIz lu/ | {O)?dGAw\k[YryKazh( \c|9 '@VPUb6~hcAw; (+nIVwYBAh?qUl}+!ZnȨטFٸϐ+ ]!Z3o-;6A<ȯ6#H. ."E@ *V{ :~/iE>7hnj5 I(hux lZ0 Ѕ-@#!~ò`>2Ws@F3^-qZpԳ0cPSUY0>䎀[P.v&we/~NpSMd$ {=<Ѡ/?ݳ _$&;wn(-d 9'POMb^|K[}RY0U_9#pUtfNBҏ~EpԧM095) ߼ ~8ujl+4YV1‹CozaQl+a+`ʨ*n5}fxvL-3<{OEz-:i_p/nj6B71;ߌuI!PJoW!ʙ`Qv펎8nST_ԵCrcXi0s~7/_: 0Jrxe_t Ɇ.J)[A.v7@MwQhB[?7Y[.H迨4bTx~8s dh5A~l"9KU>\:&mN;hy=܌rQF% ڋ9{TwՌ ArÃ%dֹʱ 7!jǘ`0BYA2?v֨z^} .{7`'{Kb{@?`w,j8 A[Y\V\{ASˮ&~c<VEwA:j [4 QGGU` Tc lns4: 3dCп,t Od4A?g?$J;zg cU"<غ܊<|%}e@|OO yot:h,urHSt灿EA{;LEUxIPd`ʲIyU >w.;Xe&Kj饙M:yE3 <.m/7pR>>Ds/C%"PŅAZ>aD>$3 5N#A 5z;n/_i~$ە/* ~'7#o1ŎKwk:cI] )=^SoJY1T|iI^0_p﹬|,C m07^psEi;t)sJ jG zTNh:Z1";x اMEg w@ qTӯhmTꚨ n^X5]By#BQV-}/;..howjFt;I,E L zfQ`'@S; %%˂&LT j v+p\WtwePObt_!`GʓAӼA-PKvՀU{ʨ%ݶqz}66DMB8XR!m|k4Dn+b%@:aNb)oNr X36/״{<"vy {ٜ?TDvx]'M"v`qu.O]o,.[^wQ}hLIP1J3 HF`%v;X=r#=t5 A n9L=\L^r0|,8 JYf|Z?Ţk@ÕZ*M!SL[o fvpVu_xC?'VX/[?+o[?|zwN=>B΅''du`pV {ZA@RNO%^H E թd)zP0=5 U@;H6+:@͔4@n jLX=C} Csƃ6ge ۍ&p FIo=8;2~ ~OOG𮘮 <f0ʡr6$˄D gZ-?^u ԣAJ`\$B=#G},W}nPIHjƯ l36$\E WlozKɧ@l׷¿AoC7/-@We]b*]{کrUIw8S$bAxmp_z#Q )Gʼ) 2F]!-o{!}Cos\Qtw>, ( a=.꺣ӠcSɾ CAe`MQ$[GSDQ[ ƭ!7ז `˸n Mkjثr*BlE .[ޑz@"q68to X=R?8+ Bb%!BV*'ASIp; g=E ޳'.üEPDi#܇ 9;-V2YL& M!냗1x2%SAޔk _YrS; @b @n#ԭ0xUSdͿ깖}?#Oat3@AfF2 dHwQ_ҷZT I9n\K |;`!U-[gCId6qjfJgG[>_ை߈n %%@^$~9*j*Vsdp/p@fاm/NxA|@*_ g@U@\?lr I@7I q,_̷5e8{P1 TWN#|ȝ6d7Nt#_4*h_#qz]1ΛQtٻvճo@HiX 'UK-+ Ts7xu0"l⺞@&pu_ߛzXYӲ$tMePT 3JU7@dpM? mxN6v4we@U@sUe 0=UgcHDM@nu+E-55+(߅( ʧ@t_R<'P|gz@/ȃc`?R< .;F]D0mM pْO[h=E >|2Zg+~,G u<# J\MQ۫V)9b5{NcyI1?tUI-C.`ɣu)1]%Q fKBͯ_`_?~ WT>ի^r.e5Nde)gAW3];^LjTS|`NC41@t@ψo_7\|~-| _n.C?jGp3N-_|aR/ i,jOܖl8 ˌbjS>I=lX:[;HG `G{O̵@OB%E`i?1B->$߱.~w1͎.a^fpCA,_gA^ul;netbmrW?{]&+!!N@_'wLl>U,4 1Kx j))KA!h-yr?];{.A-fف`E>:׽j}Km߶99|ߒ}W`#*Xwؖf Tݼ  v8^khd%Xӓ"GD8c慫!b"hx> >U@y_S2v?٥CXl u@7p?Dӻų.5t6MW\!ķAPUBe rZ%Zf\5VU3T{Dn6 e>P{%*6T:vEsPg`w>$װ8c B5l90B,%uH6 "X7eI &{#q>ja>pLF{ZyZ rǩ U/ED% j rOo`7Ïuaf6SzMwۭ +`flp0Αߝ>{hعvRda@`ƀ8[} ^kmWk`ap/e;d[Q5i/ج^Qd[6:?z1g&'7BuU˻ ZR^Jކ KnEJ>5/.$TɠV3?4dwjGS^7ɼ@Q rjWk8 joJVpD']{Ƹ R,z%t{VT9j'Q|4O @S?@gQ d}6}gxD eeO@M3 hS?7[@~# JP7ٺ"oLIၜs_6!Mo{%_E) fUM&'WoW oE xG , vAa1$V&WcJ2o,LU %K?jwr߂_Ql W\b`_xb7߉ς+]Aઊ\9{gdBnqek Z5GA?oΛ/Fql[ŕgQNb4K@8 ln+~1 G#!\hG:޺U# Q~}uz#a Ǟp vA?+O]{U2f QQ13>8r}!\ty`k0KAWNL~yt@Id8N?;L>V@rq0-zj A݁gIDATxŸ lMAa|qwP8g6/}Q2n}7]\& j6?@2bׂmKA_;==Y4// y @[8( vfNϙw"C,W5v,!!9r' KL*Fj LhsoYR+ܢ+8K;VpxBsw|h xʦ唶)`s`c@ /!Jr\'Ď 8w.@h--,?+VWl;#n-h/yG^/9(Auv\R3̶D\ ԚOmY R*ǫ@*럞.hӄp؃NVtkBi^ wR୒Tp r* D~ K4!JuO~N8`W8rPbLq@:@{B49%w) A 0| &"5>f V_B׽>f HuW7S\E]`F=g!1=XK3QH(pE^Խp 9 V5Pw1T&D}=^J['8+^ tS= uQ'@tuҽL0F wob-er4 t ZXnvNP#O]SQÔ̫E t_7w'|΢8ª~]SqeW=:xAg!T^W@n^#ݞr88}AEoΙ)/og Vr0sDk1BHy7tGp :/>:C0l&GL/Zn*b5`98`b2i1r|bu9S>p:?~H~ qF ʋ*xD!ӊL *Z~ḙaļ; >/ S;Du՗=[u]zlL":j7}γ#CL%H]!(D[[/(QT5Bí)*؃Vʞ /TP җ{@|\Z5@Tw&\M.uTW XmhW6^oܭx/zeS$h-jTA?H<A^ ^MlsO:׼ӆV'ޮtYfu~Ꮚ~TXy'OO~-ǫ9)'t(T?o+P9oGN*S83^9ȵrP`Y⏀睾 | *;U Yd ^ Xi*WL PaHbEWu ˯3+M QZMudO6_Box1, 2 rji+oKVBЗĭ39͹-!Rx7@-t{ď׹SN|u0n/0)}.jm"ɔPa9,.FA0Z>]J峉&7_ւ [ ɺA5яAO9RĬ-oyq mU`Q0F]^?'aZ τK/5RS<0;4Uz6ML+d[}?fS8g.߰a0Tu75C>jL=8FUALẂIHgvT$0行[l1pFA|cI=6jxCMiMj P|M-6yt֜9Q΄wy o#.Ci~Ѓ<\:.rs͌ɠ_П:W`r^?Ď[v:u{Y"JfL 09k2@ioFU@`^*|Ct[9l}9R{YK1D>')$Ny>6u+&/ɿ]]Q~d;m".ɫg$`'>ߕsUa Z>r֥]/\nꚽui%[oWnIm {B?4]u^8y>r :R Maڊ$`g|LG=otLncs9#+l;-AݮG}fz%<j3\XU(T{(k0!=C<zyzqYԌK7Ybg`].si@Zt.f7*pڂ|_'@A=2fgﴵ@W覐>>q:vad!wcda d h,!XJYP6g1;-oۦq[d2ƶ;f~r|flm韁mh(:Y}(6A] c`rޱө Wl5PmYl[!(S 9Y{FD<4YPV:˄[GvI͉AؔA+LEҙX YP~ 9 pGעi 8ڪ'S]5*/63 O7N&ρSEW͔u}mB] #-dPo9]!=y䯦&u>19*H],=gvʯ,Y0 y)jw&4AyX| oV/ji!ȣNv]*ZSp-h25X9ϺA]l27m#~/6 4?sTI ?)b]^Z+`3^6 ^ qM6#{>hbjv˜}lzm2B[Ά@uL^kY.XR.T&/庵"9P?͸zW[?=5׽$)Ɍ/#~#2܄_mFuN:pngM=] Ҧn1:PUxQ-)mVSZ@FUdq9\tiQNфQ7C7V ԣ|wA ou} p"g[ A/ʳWs=<hj _!Y re 4 r[R_%`;Vv xjC; :0 =iA `|5TlW vٚmpk=AO0P7B!(bA5,3Ea0# W2p:} h |̑m9/:H @P'd2UVu CKb,_ɶ#1/-e@o`X-VB4_ڎ4 ZآkVg¼xxm b 8 fw5;H4tVC}ln`.a쳣APPe%kA=O3dU_AECԶ*Zr+Z ?+XҪ͋g[pxE&H@>p 6sH7icl5i٪EtA^೐QCAN@^ j" b$wݢs*8v)μEC0uY6ǝdc8IKYγfL=ۜ5>D9!]9[N3@w@Nmkv-$ MOZs /l3'bK~Lط=>N%KG,6ˡe˲ȹ9o0ZN8PMgk\jh5]\}{]܅. 2 fۚ5mN{_B^~t9id_MݳnC?u_s:?hEz=تZ>ꗔ2\'2)AwMYl 6Fc C0/~N E ; iP}y+ AE4`8),C zI&X漙t3Ke`?P3r6 ,'a~=8(D(?o.`2 י,C$|ў;@X5&tVPPԶ-}?Nā3]+Pa} zv5LGA:O+yܧ#qT_o"O%R#`1tscFy܌(s8VX*zsif-2ɗBMrd۽sTZKKި]D;7zwEƳ*t*`z:Hqecںzw|QxWC_-LU+`2tR7e4D?۝;@1 QJJd}&x3@dsƟفL_CT6 xH/h=./>v(8 "&׃(gAW==ܟ5`zXP_fE2={נbu9 AoB l{;D ̋g q-Q-F:kgt|2]g<`.EgZTl7_q GLlRpxۥ * 欿jsM\ػr|(NQ >Pݙiw_Þ7+9 h~O2#-0XWCU!r _lWGA8 (j(OUAHo6OS/7@ *Gzlʠ$KZ9*}@\{Ns6s!׽*E(1D}-:m@8^YA fAw P#` m<<4S6?;y&xxEs]Sz~ %@2{l5#P(q['[0dS; K ~vdKzlm_-nsb +ǞYub;slK=pZz)o9Ӂ\[~_)>~Pȯhy"]Tep8tP'\Yt>8n:@p0? NI~W^}P2'n/Ki|aTWORLp= Ůǣe!o, ,X5d0o"<8D1"z=`W9ݿ@Nwf1`_0 hۃ[i>vBI^:=3j=]JD SҊ\5p: &ʗ`_7B@呅DY> ~x`"Oub?2 #@0]p"*j r%^w(9Ii+=*\}TN{ _U l0Z뉏1hn{ L{wU(1t se%0۹ ,v` TJɵ[*զk"jMqZ~z; uױ[U?Q_׿.@~6=wox .WOw:C-Lsӽ zgʷST%@}<dGr%2D@2 waLB+}/4=4z9{{b'7 Wop:⦓CmGPD/ݜJnWrSAB:~x@ԝA>Q MD%F*Ū D-r`b?xeuv`o/ص~#M&lȷѹ oaN3 NIok@3{ NKh6r{0̹uQBmw][VwyH&8 AH~,BTZ'<5Q4 (Qn &a r(T9N^] XgQɜ GP]mx_Aw?B+,. rA:~@O< 4.{/Kfޯׁȓɪnj6V魫N``۽إ g7?gymĻQ-M. NrCB0VBsf$1W7S% -7 >:)Pz ?7)Q!<^,>?q9˽ݦ`j_C!U2/`{Yz= xݍv3P4{}!\Y73<j {03E|Fhrd|rVeB~a[жSz{붰$=ܤ0;lӔ\R=zh/yp󌵩A/2Ur;nCKɧ~l%:q_^gU; 2>rA&M8 |C }[,K:9~LU!^9~+9 SnYAA6ϋW@ -s,":56 SwE9s _V2}ZLps*`5b:iM: b?n#S.63o31ZHZ Kv&rso{V6mA(AY?;8^Q%/TEOUVzGB#O > 2l+I zY@]tkƩ}Ϋ;X炠Ip5!)8*I|Xtk]Te{XS`ȽGuTn;)L$;e? bl&|޺34>3E$għb:?:!L%P- ԳsdVk!بjAmƁ)r7gí!)9"} rx Ok@BI IxНE~ty̶7t6nЮڿϿvf˂*z܎Ẅ9U_)zq[V<~tfN)0,=*c`ҝ4YE28!nvHj=; %57;0>]_"[nv &@$~`/W%Pb4&tgXP]'z0g>ěۃ`Ǚ UA ŀ2A_9b๑ pWN =]nV dpƙ i8h.v:Cbe;ϻL2f`D51"wh"x՜ <ʛ8bc,;$%܇ `30ySBu h4!ZCP;\;gaOz@`P@$'~oE.' Ty?ij{RO1 )Bȶ鞃ī)(97u8DuzS3LR]T)cUη&>I񞦝{eάW>^}o=9jHOw]9M[h\콎::*z(WF:" . w jJN; ?ėY+4(!f*8dy^r Lc ։v٦E9/sk/75TNq^9Agjw 63m@V9}ĝ <֙6-$Tf,X!Et:Pt2is|*_}3fD'/į!_ bOC `{+.6KAZyN_LoL€ y*7n*!p{eýfɒ=D^.{b"ODݟ 2r-p9SYX(; ~OdUd.nEb] j ns/e >pFy6.1 A/KI$W ց %1& -lv$Nctb1&CK0.1 g5=s'vb79EC8qB/*^\bǃf)p6dQyNv9t,迃K/%~7%uL(Yd5wT_؉~v}ʦ7 iINd=5sy_7;NL]UZϕy?=OoYuICXeF:q]o"ڏ%j?np7yR&?̗~A`7; L߉.<NV)zN3:/ &{9Amn]ft0 DKp+!|ѻb;58ts 43mA1>N&=F:4;BTm( |;xm>p~wE>ِR27:%zlsYfϯ ⚽d $fpꩫ`6/Gl%}"1'YQ9LSː?ȹwBD~plR~M`3 B-ґA PÛxY#362,W|P<ӽ>hԵgbϥ˔5:W~?=O g\ 1l7(T#ǧ6^j}g\7*ehw5{"$" p'8U~bI0178= {9 H;D=mu 3uUqrA' vˎg ! ~Jn΋4Sۀ2Ax#e<45\=Sk,XP9ʻbpAΛ@_/xeo9DďDu> I*4l0 bc?>~X;j;Akw!ϝ $N P'\هXrǺ@^.1E?w+Jjuv: jio/H߀PSTթBfo 3Oo0lij`Fydi?~/MD%)‹1o8D<3 Jv;7Tǃ`=f9 y;Kz~L/{V<:Kq 8(B5 m# ~DP@5Vl6oAp?x7 \&>egʒKM vrS"s<2l߄NFp;zBVRo+] $- E5;M~r<:' , 5A *%O<~Arn>5$g4ϯE~qL>hE]BxuvWeSV^~<]{tHzujRGzG؅ UW;΍}F"~(O As-8-z`7.`3Ҷ~M}wu){3%&Fx/RwAu:cY`;X ꪓ$_YECWD;(&v&?0iBq L'Fj f>f gFȷ!4P@,dP"@|of:AȶnmOn|BlfBLF;wX%}.5nǻBz0p]]~(~QO<&lcOE`[?+G@4ReA<@ eU!8L^ %1:R⭠M 5't' -'Hܐ<- pU/miʇX%_ 7X~wҥ l 887*-<Teddx""߁ d$ny1QgC*/% 8nPdM(~rIU@t;ր=ej6 =~61-147rWi{18Sm Oym/27W-sN&6R^.k[Ie-{{١NeO?v_UfZSq$ȸꧯMUzy?|op~Y> }/ k*h,ٞ nr@6_gu& ̀c߂-/^\n{{xM>N[X5~rAC }/ox%\;b8rq|2&^IfUo5`x7u_!}9@ܵ>` |ZO;4^i@ N`fۢGAOC7թ~QcT u:Za?4FD$>v;x /Ug7N!T1ô؉f8ţE6*g7@a2,(_4:A {%E[0i b.n Z%XF6]BWL, Z 9*Q: 5kpPFcAtgQ 9S | fsjHmyϢ7cK@~ Cp_ 5,&NǢ_jWAҶw{E;YA3gl 8i˘٠eLYu!8gcw%W,1Iv~7!txOH՝y e =x7AUP0鴲APW~HVv+X ql~ nvؘ=!Sbڟ 5`}VƛB,{0v]3]ڄȮ* m(a"rZx 8ԯficlA0gAU7 =pgI]9!o A}HGgl<LN rN&HONxHvxـkD؁V(=H[ŤCl}0k2kN;upqVB}Ow^N< "_z FeQշvqni&_$Ȝ#cT|ogOz7{|mCOh'[C370<]J3~BsI}iK348MF5< um]ymfSdGoo>j*tn[֤vc(W&@{9b|({5%[@0(N`}y w2^$`mρ?|g?b񬈃}AuldkN6)ih%θCj7p`2LAEg eiyL "UyfrWd(1ϐx;"E9woZ8!ރzD+Y|!d)p8܏@rvu>u'z!dj״Jol<*zZu ω?~\ݎ4S8,z[Bm; =xBEm ܉]*ߊA}Hg Ƃj9!){iH1ylR}ǘ3dr/S1N,ҙ tV8^й`5%> 1G2 eB}0k\r>:KRx* RA -d=Y`/vJO5#Ay ܯEVѷD{NS?4_A/nJ3 f~6 NqQ~ ՠN,P}͜䂡bi6@t;7 Hv'A mNޝ2˒l9N"yϼ?twӊ*Dwk9͸`|'z4{ؗ>}{6("~-Z޽!n/-R~.vنqkK~ Mo|Sv/7n4? Ŷt0L`"8U&BV&FyAp@E)`蜴Qal/o< _O }`z<{骟ճVc:+RUAA0{Eob+zq "~ l%t  MY S() I8nHJo AUV@T6<,g;& Y+鵃i ?Vf zrֺ ȈE+Ə}ƮH2d"r;ȿĵ6f.~Nw΂R 6"BpQ G<:A}y45#gh}+W b@KQ}6(\i }l8V c4}Vg*+;P0L Ju3Ѝr5R Dx$6uu OVNW6<υ#{d\?N-~̖ ?{aoF~#2O~'z /~ܸ r`.-:ecBbB])`kZ+z(دfr{"xo/7 v7:,]tTG-Yn Ǣ'Iv,׻DHIKꪻWp;NPE zi ht7 M8ѺkC'n^z;O=zqkȅ9(o)O*Y53n_skT#{ct=?˓L/2ͨP͊ooVlnjIIIYYs_>/^!m(_C7 |bا q(} ܿ $:_`qko@5#+ϊ gu1~ j;@o<Lu`#LQM+=~[P',iYt K|05F2<Zhj )8Ý BC׀2g@rB wCo'j{T. v/#8+@;f-TEsE?t_3(Sخ[i  vzRӀjֻ N''@=IPмYp{fpA'f Fg f j,_ ͼ^@E[R?n0rEa\I6 N 9rjDV9 ^ U6w1xCF. sL8z:| =-"-zxA ~_(Ԝt<_ѯOK+:˞I3-b`РWSڗYs>Yŗv87vtm^)?/L"_wT2"'qbR N;D`W܋"`#Tl9sA З! noh׽ns MD/VQFJ6Ne0Vwi>qJ`^X=r`o,z $QR]P;0d_ j@:;Mq P ]A]pG$;HwT qA/`p3x_*!l@;ImfwqDnȟ@)Jcl7 ` M,@|t R{MПsX JY%t#bMa^*ǀѻ͂ѻB] PH7A5Mf#pmsIv;‰"둠;ЩW%@_!AY9Ƙ{M6|-!t8wRɘ{~!IYX{VWUGZ[>;'nz?ړ_ȢZg&%&֙O#~x͍nwZIKVE L?dЙm]`ڝ K;`?5SE_{yC-uMA+ V _;W~#Zڻྪ ?S~ח#PW/{zwtzap*=yY2Y^7yZg_LLhσ u>}?%OI/j'} "›#^LJp^!?ṈBz|˞MiY35bY{ȁjH1W.!p굪't;,A=p 3&DrFja |ߢu\(vljQt.sOLϛ:^:#ӝĀU+@ ylrh.k)hfn]4+Q9?~#j?cm|Ⱦꋈ F85j_+KPe:k+&(cx7 C0/u:7cb@Q!h`\3Lj*7 ]N-P\&p@Ր; LCAcbHꪩb)jUt "]~ f_l[yblH8v@T=&Qq5Yg] D@ϑ V׋2 e ww7Is0ز'A"6lyA7}J5łR-?q\}Tc` $[g#WҙOgen=K19[[7n_S*7pnxUOV R2o}oT5a}8BurrœG~~=?FӍvm0 Efg=7UQSy+ R\ RB sOYA 4!Q h$c9Y:Pbr "]eKP7h5=AU ;>xg`˓ p13| :KZjZ^ s "@"!nUu [L%[s WYV@*]-ؒAQMpuimY v9mjJ)EkAdU2D5;};UG7(KAWw& |Iw ^]ʁ)L 넺AJgp^WZU#RCA5AG$WkN̹I'r^:YWmOz&[`ca#LZ:稳O=ۡw;~Kߧ,{4/J-PX)*B $gp8I280<bh5"T-PsU`,f1vt>H/PgBkdBp^wVC;r8ZHY]O&@}* l{)|s !aT!0um B3vBn#PIv "S4]\&EcH/Hao#.LVNyWb_Y'!)FQܭY@۶?Pl!jvt}s knxH; #pMHo;ɗ"bZz4ho!S9Ÿ_[vxt, sA]n/b?eAe?Qؾ @\U?jb5-\L<)AeepÐ~wi! ?QÁC eQx١Ň$4&9ʿxo'NMP?+z,~x?Ɠ- 9KۉX̩w/ޝ`랝o>(L(b UmEfmeP|{kkJ@9LlrƟ.p$PkAf.y`JS>uR rI@3Ob@T·_ f/`\U.߁x8b|lG)5/ٛ kT_*%: zwTA'EAP. dWm@Ŝ`]idvpx>8ߓ:WiA;^YA8 >#e !>0 |Y gTmyt,1xCmB!LZ>. y_ tuf`k;qC of8)k +jܵj3I >oZPH_u&,ʖ YsNRjEno vno¨}fMhy]JQ7+*u={7wʨ@895*غA/>x˸"SC <lu4 2ۛ/jv@q [~*w{s 9Bܷ]n7f~HW ' >P%{^Wj#]!S΄##EG0]W=縿K kbmL>MoJvd0R8 Ju[w0fY>BB5A& m$_s7oΡ΢+m3(/(9XbOXz_Iɩ,~ KAz#Vt$$}04.ti/_p/%HXsNzE|<le9)'MR|/߿(X^0%S,}ٚy|_WuWy苷[/v:%_R< 3e{-simple" start_response("200 OK", [('Content-Type', 'text/html'), ('Content-Length', str(len(body)))]) return [body] class SlowConsumer: """ Consumes an upload slowly... NOTE: This should use the iterator form of ``wsgi.input``, but it isn't implemented in paste.httpserver. """ def __init__(self, chunk_size = 4096, delay = 1, progress = True): self.chunk_size = chunk_size self.delay = delay self.progress = True def __call__(self, environ, start_response): size = 0 total = environ.get('CONTENT_LENGTH') if total: remaining = int(total) while remaining > 0: if self.progress: print("%s of %s remaining" % (remaining, total)) if remaining > 4096: chunk = environ['wsgi.input'].read(4096) else: chunk = environ['wsgi.input'].read(remaining) if not chunk: break size += len(chunk) remaining -= len(chunk) if self.delay: time.sleep(self.delay) body = "%d bytes" % size else: body = (b'\n' b'
\n' b'\n' b'\n' b'
\n') print("bingles") start_response("200 OK", [('Content-Type', 'text/html'), ('Content-Length', str(len(body)))]) return [body] def make_test_app(global_conf): return SimpleApplication() make_test_app.__doc__ = SimpleApplication.__doc__ def make_slow_app(global_conf, chunk_size=4096, delay=1, progress=True): from paste.deploy.converters import asbool return SlowConsumer( chunk_size=int(chunk_size), delay=int(delay), progress=asbool(progress)) make_slow_app.__doc__ = SlowConsumer.__doc__ paste-3.10.1/paste/debug/doctest_webapp.py000077500000000000000000000351031461442501600204770ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php #!/usr/bin/env python2.4 # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ These are functions for use when doctest-testing a document. """ import subprocess import doctest import os import sys import shutil import re import html import rfc822 from io import StringIO from paste.util import PySourceColor here = os.path.abspath(__file__) paste_parent = os.path.dirname( os.path.dirname(os.path.dirname(here))) def run(command): data = run_raw(command) if data: print(data) def run_raw(command): """ Runs the string command, returns any output. """ proc = subprocess.Popen(command, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=_make_env()) data = proc.stdout.read() proc.wait() while data.endswith('\n') or data.endswith('\r'): data = data[:-1] if data: data = '\n'.join( [l for l in data.splitlines() if l]) return data else: return '' def run_command(command, name, and_print=False): output = run_raw(command) data = '$ %s\n%s' % (command, output) show_file('shell-command', name, description='shell transcript', data=data) if and_print and output: print(output) def _make_env(): env = os.environ.copy() env['PATH'] = (env.get('PATH', '') + ':' + os.path.join(paste_parent, 'scripts') + ':' + os.path.join(paste_parent, 'paste', '3rd-party', 'sqlobject-files', 'scripts')) env['PYTHONPATH'] = (env.get('PYTHONPATH', '') + ':' + paste_parent) return env def clear_dir(dir): """ Clears (deletes) the given directory """ shutil.rmtree(dir, True) def ls(dir=None, recurse=False, indent=0): """ Show a directory listing """ dir = dir or os.getcwd() fns = os.listdir(dir) fns.sort() for fn in fns: full = os.path.join(dir, fn) if os.path.isdir(full): fn = fn + '/' print(' '*indent + fn) if os.path.isdir(full) and recurse: ls(dir=full, recurse=True, indent=indent+2) default_app = None default_url = None def set_default_app(app, url): global default_app global default_url default_app = app default_url = url def resource_filename(fn): """ Returns the filename of the resource -- generally in the directory resources/DocumentName/fn """ return os.path.join( os.path.dirname(sys.testing_document_filename), 'resources', os.path.splitext(os.path.basename(sys.testing_document_filename))[0], fn) def show(path_info, example_name): fn = resource_filename(example_name + '.html') out = StringIO() assert default_app is not None, ( "No default_app set") url = default_url + path_info out.write('
%s
\n' % (url, url)) out.write('
\n') proc = subprocess.Popen( ['paster', 'serve' '--server=console', '--no-verbose', '--url=' + path_info], stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=_make_env()) stdout, errors = proc.communicate() stdout = StringIO(stdout) headers = rfc822.Message(stdout) content = stdout.read() for header, value in headers.items(): if header.lower() == 'status' and int(value.split()[0]) == 200: continue if header.lower() in ('content-type', 'content-length'): continue if (header.lower() == 'set-cookie' and value.startswith('_SID_')): continue out.write('%s: %s
\n' % (header, value)) lines = [l for l in content.splitlines() if l.strip()] for line in lines: out.write(line + '\n') if errors: out.write('
%s
' % errors) out.write('
\n') result = out.getvalue() if not os.path.exists(fn): f = open(fn, 'wb') f.write(result) f.close() else: f = open(fn, 'rb') expected = f.read() f.close() if not html_matches(expected, result): print('Pages did not match. Expected from %s:' % fn) print('-'*60) print(expected) print('='*60) print('Actual output:') print('-'*60) print(result) def html_matches(pattern, text): regex = re.escape(pattern) regex = regex.replace(r'\.\.\.', '.*') regex = re.sub(r'0x[0-9a-f]+', '.*', regex) regex = '^%s$' % regex return re.search(regex, text) def convert_docstring_string(data): if data.startswith('\n'): data = data[1:] lines = data.splitlines() new_lines = [] for line in lines: if line.rstrip() == '.': new_lines.append('') else: new_lines.append(line) data = '\n'.join(new_lines) + '\n' return data def create_file(path, version, data): data = convert_docstring_string(data) write_data(path, data) show_file(path, version) def append_to_file(path, version, data): data = convert_docstring_string(data) f = open(path, 'a') f.write(data) f.close() # I think these appends can happen so quickly (in less than a second) # that the .pyc file doesn't appear to be expired, even though it # is after we've made this change; so we have to get rid of the .pyc # file: if path.endswith('.py'): pyc_file = path + 'c' if os.path.exists(pyc_file): os.unlink(pyc_file) show_file(path, version, description='added to %s' % path, data=data) def show_file(path, version, description=None, data=None): ext = os.path.splitext(path)[1] if data is None: f = open(path, 'rb') data = f.read() f.close() if ext == '.py': html = ('
%s
' % PySourceColor.str2html(data, PySourceColor.dark)) else: html = '
%s
' % html.escape(data) html = '%s
%s' % ( description or path, html) write_data(resource_filename('%s.%s.gen.html' % (path, version)), html) def call_source_highlight(input, format): proc = subprocess.Popen(['source-highlight', '--out-format=html', '--no-doc', '--css=none', '--src-lang=%s' % format], shell=False, stdout=subprocess.PIPE) stdout, stderr = proc.communicate(input) result = stdout proc.wait() return result def write_data(path, data): dir = os.path.dirname(os.path.abspath(path)) if not os.path.exists(dir): os.makedirs(dir) f = open(path, 'wb') f.write(data) f.close() def change_file(path, changes): f = open(os.path.abspath(path), 'rb') lines = f.readlines() f.close() for change_type, line, text in changes: if change_type == 'insert': lines[line:line] = [text] elif change_type == 'delete': lines[line:text] = [] else: assert 0, ( "Unknown change_type: %r" % change_type) f = open(path, 'wb') f.write(''.join(lines)) f.close() class LongFormDocTestParser(doctest.DocTestParser): """ This parser recognizes some reST comments as commands, without prompts or expected output, like: .. run: do_this(... ...) """ _EXAMPLE_RE = re.compile(r""" # Source consists of a PS1 line followed by zero or more PS2 lines. (?: (?P (?:^(?P [ ]*) >>> .*) # PS1 line (?:\n [ ]* \.\.\. .*)*) # PS2 lines \n? # Want consists of any non-blank lines that do not start with PS1. (?P (?:(?![ ]*$) # Not a blank line (?![ ]*>>>) # Not a line starting with PS1 .*$\n? # But any other line )*)) | (?: # This is for longer commands that are prefixed with a reST # comment like '.. run:' (two colons makes that a directive). # These commands cannot have any output. (?:^\.\.[ ]*(?Prun):[ ]*\n) # Leading command/command (?:[ ]*\n)? # Blank line following (?P (?:(?P [ ]+)[^ ].*$) (?:\n [ ]+ .*)*) ) | (?: # This is for shell commands (?P (?:^(P [ ]*) [$] .*) # Shell line (?:\n [ ]* [>] .*)*) # Continuation \n? # Want consists of any non-blank lines that do not start with $ (?P (?:(?![ ]*$) (?![ ]*[$]$) .*$\n? )*)) """, re.MULTILINE | re.VERBOSE) def _parse_example(self, m, name, lineno): r""" Given a regular expression match from `_EXAMPLE_RE` (`m`), return a pair `(source, want)`, where `source` is the matched example's source code (with prompts and indentation stripped); and `want` is the example's expected output (with indentation stripped). `name` is the string's name, and `lineno` is the line number where the example starts; both are used for error messages. >>> def parseit(s): ... p = LongFormDocTestParser() ... return p._parse_example(p._EXAMPLE_RE.search(s), '', 1) >>> parseit('>>> 1\n1') ('1', {}, '1', None) >>> parseit('>>> (1\n... +1)\n2') ('(1\n+1)', {}, '2', None) >>> parseit('.. run:\n\n test1\n test2\n') ('test1\ntest2', {}, '', None) """ # Get the example's indentation level. runner = m.group('run') or '' indent = len(m.group('%sindent' % runner)) # Divide source into lines; check that they're properly # indented; and then strip their indentation & prompts. source_lines = m.group('%ssource' % runner).split('\n') if runner: self._check_prefix(source_lines[1:], ' '*indent, name, lineno) else: self._check_prompt_blank(source_lines, indent, name, lineno) self._check_prefix(source_lines[2:], ' '*indent + '.', name, lineno) if runner: source = '\n'.join([sl[indent:] for sl in source_lines]) else: source = '\n'.join([sl[indent+4:] for sl in source_lines]) if runner: want = '' exc_msg = None else: # Divide want into lines; check that it's properly indented; and # then strip the indentation. Spaces before the last newline should # be preserved, so plain rstrip() isn't good enough. want = m.group('want') want_lines = want.split('\n') if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): del want_lines[-1] # forget final newline & spaces after it self._check_prefix(want_lines, ' '*indent, name, lineno + len(source_lines)) want = '\n'.join([wl[indent:] for wl in want_lines]) # If `want` contains a traceback message, then extract it. m = self._EXCEPTION_RE.match(want) if m: exc_msg = m.group('msg') else: exc_msg = None # Extract options from the source. options = self._find_options(source, name, lineno) return source, options, want, exc_msg def parse(self, string, name=''): """ Divide the given string into examples and intervening text, and return them as a list of alternating Examples and strings. Line numbers for the Examples are 0-based. The optional argument `name` is a name identifying this string, and is only used for error messages. """ string = string.expandtabs() # If all lines begin with the same indentation, then strip it. min_indent = self._min_indent(string) if min_indent > 0: string = '\n'.join([l[min_indent:] for l in string.split('\n')]) output = [] charno, lineno = 0, 0 # Find all doctest examples in the string: for m in self._EXAMPLE_RE.finditer(string): # Add the pre-example text to `output`. output.append(string[charno:m.start()]) # Update lineno (lines before this example) lineno += string.count('\n', charno, m.start()) # Extract info from the regexp match. (source, options, want, exc_msg) = \ self._parse_example(m, name, lineno) # Create an Example, and add it to the list. if not self._IS_BLANK_OR_COMMENT(source): # @@: Erg, this is the only line I need to change... output.append(doctest.Example( source, want, exc_msg, lineno=lineno, indent=min_indent+len(m.group('indent') or m.group('runindent')), options=options)) # Update lineno (lines inside this example) lineno += string.count('\n', m.start(), m.end()) # Update charno. charno = m.end() # Add any remaining post-example text to `output`. output.append(string[charno:]) return output if __name__ == '__main__': if sys.argv[1:] and sys.argv[1] == 'doctest': doctest.testmod() sys.exit() if not paste_parent in sys.path: sys.path.append(paste_parent) for fn in sys.argv[1:]: fn = os.path.abspath(fn) # @@: OK, ick; but this module gets loaded twice sys.testing_document_filename = fn doctest.testfile( fn, module_relative=False, optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE, parser=LongFormDocTestParser()) new = os.path.splitext(fn)[0] + '.html' assert new != fn os.system('rst2html.py %s > %s' % (fn, new)) paste-3.10.1/paste/debug/fsdiff.py000066400000000000000000000311261461442501600167330ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Module to find differences over time in a filesystem Basically this takes a snapshot of a directory, then sees what changes were made. The contents of the files are not checked, so you can detect that the content was changed, but not what the old version of the file was. """ import os from fnmatch import fnmatch from datetime import datetime try: # Python 3 import collections.UserDict as IterableUserDict except ImportError: # Python 2.5-2.7 from UserDict import IterableUserDict import operator import re __all__ = ['Diff', 'Snapshot', 'File', 'Dir', 'report_expected_diffs', 'show_diff'] class Diff: """ Represents the difference between two snapshots """ def __init__(self, before, after): self.before = before self.after = after self._calculate() def _calculate(self): before = self.before.data after = self.after.data self.deleted = {} self.updated = {} self.created = after.copy() for path, f in before.items(): if path not in after: self.deleted[path] = f continue del self.created[path] if f.mtime < after[path].mtime: self.updated[path] = after[path] def __str__(self): return self.report() def report(self, header=True, dates=False): s = [] if header: s.append('Difference in %s from %s to %s:' % (self.before.base_path, self.before.calculated, self.after.calculated)) for name, files, show_size in [ ('created', self.created, True), ('deleted', self.deleted, True), ('updated', self.updated, True)]: if files: s.append('-- %s: -------------------' % name) files = files.items() files.sort() last = '' for path, f in files: t = ' %s' % _space_prefix(last, path, indent=4, include_sep=False) last = path if show_size and f.size != 'N/A': t += ' (%s bytes)' % f.size if dates: parts = [] if self.before.get(path): parts.append(self.before[path].mtime) if self.after.get(path): parts.append(self.after[path].mtime) t += ' (mtime: %s)' % ('->'.join(map(repr, parts))) s.append(t) if len(s) == 1: s.append(' (no changes)') return '\n'.join(s) class Snapshot(IterableUserDict): """ Represents a snapshot of a set of files. Has a dictionary-like interface, keyed relative to ``base_path`` """ def __init__(self, base_path, files=None, ignore_wildcards=(), ignore_paths=(), ignore_hidden=True): self.base_path = base_path self.ignore_wildcards = ignore_wildcards self.ignore_hidden = ignore_hidden self.ignore_paths = ignore_paths self.calculated = None self.data = files or {} if files is None: self.find_files() ############################################################ ## File finding ############################################################ def find_files(self): """ Find all the files under the base path, and put them in ``self.data`` """ self._find_traverse('', self.data) self.calculated = datetime.now() def _ignore_file(self, fn): if fn in self.ignore_paths: return True if self.ignore_hidden and os.path.basename(fn).startswith('.'): return True for pat in self.ignore_wildcards: if fnmatch(fn, pat): return True return False def _find_traverse(self, path, result): full = os.path.join(self.base_path, path) if os.path.isdir(full): if path: # Don't actually include the base path result[path] = Dir(self.base_path, path) for fn in os.listdir(full): fn = os.path.join(path, fn) if self._ignore_file(fn): continue self._find_traverse(fn, result) else: result[path] = File(self.base_path, path) def __repr__(self): return '<%s in %r from %r>' % ( self.__class__.__name__, self.base_path, self.calculated or '(no calculation done)') def compare_expected(self, expected, comparison=operator.eq, differ=None, not_found=None, include_success=False): """ Compares a dictionary of ``path: content`` to the found files. Comparison is done by equality, or the ``comparison(actual_content, expected_content)`` function given. Returns dictionary of differences, keyed by path. Each difference is either noted, or the output of ``differ(actual_content, expected_content)`` is given. If a file does not exist and ``not_found`` is given, then ``not_found(path)`` is put in. """ result = {} for path in expected: orig_path = path path = path.strip('/') if path not in self.data: if not_found: msg = not_found(path) else: msg = 'not found' result[path] = msg continue expected_content = expected[orig_path] file = self.data[path] actual_content = file.bytes if not comparison(actual_content, expected_content): if differ: msg = differ(actual_content, expected_content) else: if len(actual_content) < len(expected_content): msg = 'differ (%i bytes smaller)' % ( len(expected_content) - len(actual_content)) elif len(actual_content) > len(expected_content): msg = 'differ (%i bytes larger)' % ( len(actual_content) - len(expected_content)) else: msg = 'diff (same size)' result[path] = msg elif include_success: result[path] = 'same!' return result def diff_to_now(self): return Diff(self, self.clone()) def clone(self): return self.__class__(base_path=self.base_path, ignore_wildcards=self.ignore_wildcards, ignore_paths=self.ignore_paths, ignore_hidden=self.ignore_hidden) class File: """ Represents a single file found as the result of a command. Has attributes: ``path``: The path of the file, relative to the ``base_path`` ``full``: The full path ``stat``: The results of ``os.stat``. Also ``mtime`` and ``size`` contain the ``.st_mtime`` and ``st_size`` of the stat. ``bytes``: The contents of the file. You may use the ``in`` operator with these objects (tested against the contents of the file), and the ``.mustcontain()`` method. """ file = True dir = False def __init__(self, base_path, path): self.base_path = base_path self.path = path self.full = os.path.join(base_path, path) self.stat = os.stat(self.full) self.mtime = self.stat.st_mtime self.size = self.stat.st_size self._bytes = None def bytes__get(self): if self._bytes is None: f = open(self.full, 'rb') self._bytes = f.read() f.close() return self._bytes bytes = property(bytes__get) def __contains__(self, s): return s in self.bytes def mustcontain(self, s): __tracebackhide__ = True bytes = self.bytes if s not in bytes: print('Could not find %r in:' % s) print(bytes) assert s in bytes def __repr__(self): return '<%s %s:%s>' % ( self.__class__.__name__, self.base_path, self.path) class Dir(File): """ Represents a directory created by a command. """ file = False dir = True def __init__(self, base_path, path): self.base_path = base_path self.path = path self.full = os.path.join(base_path, path) self.size = 'N/A' self.mtime = 'N/A' def __repr__(self): return '<%s %s:%s>' % ( self.__class__.__name__, self.base_path, self.path) def bytes__get(self): raise NotImplementedError( "Directory %r doesn't have content" % self) bytes = property(bytes__get) def _space_prefix(pref, full, sep=None, indent=None, include_sep=True): """ Anything shared by pref and full will be replaced with spaces in full, and full returned. Example:: >>> _space_prefix('/foo/bar', '/foo') ' /bar' """ if sep is None: sep = os.path.sep pref = pref.split(sep) full = full.split(sep) padding = [] while pref and full and pref[0] == full[0]: if indent is None: padding.append(' ' * (len(full[0]) + len(sep))) else: padding.append(' ' * indent) full.pop(0) pref.pop(0) if padding: if include_sep: return ''.join(padding) + sep + sep.join(full) else: return ''.join(padding) + sep.join(full) else: return sep.join(full) def report_expected_diffs(diffs, colorize=False): """ Takes the output of compare_expected, and returns a string description of the differences. """ if not diffs: return 'No differences' diffs = diffs.items() diffs.sort() s = [] last = '' for path, desc in diffs: t = _space_prefix(last, path, indent=4, include_sep=False) if colorize: t = color_line(t, 11) last = path if len(desc.splitlines()) > 1: cur_indent = len(re.search(r'^[ ]*', t).group(0)) desc = indent(cur_indent+2, desc) if colorize: t += '\n' for line in desc.splitlines(): if line.strip().startswith('+'): line = color_line(line, 10) elif line.strip().startswith('-'): line = color_line(line, 9) else: line = color_line(line, 14) t += line+'\n' else: t += '\n' + desc else: t += ' '+desc s.append(t) s.append('Files with differences: %s' % len(diffs)) return '\n'.join(s) def color_code(foreground=None, background=None): """ 0 black 1 red 2 green 3 yellow 4 blue 5 magenta (purple) 6 cyan 7 white (gray) Add 8 to get high-intensity """ if foreground is None and background is None: # Reset return '\x1b[0m' codes = [] if foreground is None: codes.append('[39m') elif foreground > 7: codes.append('[1m') codes.append('[%im' % (22+foreground)) else: codes.append('[%im' % (30+foreground)) if background is None: codes.append('[49m') else: codes.append('[%im' % (40+background)) return '\x1b' + '\x1b'.join(codes) def color_line(line, foreground=None, background=None): match = re.search(r'^(\s*)', line) return (match.group(1) + color_code(foreground, background) + line[match.end():] + color_code()) def indent(indent, text): return '\n'.join( [' '*indent + l for l in text.splitlines()]) def show_diff(actual_content, expected_content): actual_lines = [l.strip() for l in actual_content.splitlines() if l.strip()] expected_lines = [l.strip() for l in expected_content.splitlines() if l.strip()] if len(actual_lines) == len(expected_lines) == 1: return '%r not %r' % (actual_lines[0], expected_lines[0]) if not actual_lines: return 'Empty; should have:\n'+expected_content import difflib return '\n'.join(difflib.ndiff(actual_lines, expected_lines)) paste-3.10.1/paste/debug/prints.py000066400000000000000000000124661461442501600170170ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Middleware that displays everything that is printed inline in application pages. Anything printed during the request will get captured and included on the page. It will usually be included as a floating element in the top right hand corner of the page. If you want to override this you can include a tag in your template where it will be placed::


You might want to include ``style="white-space: normal"``, as all the
whitespace will be quoted, and this allows the text to wrap if
necessary.

"""

from io import StringIO
import re
import html
from paste.util import threadedprint
from paste import wsgilib
from paste import response
import sys

_threadedprint_installed = False

__all__ = ['PrintDebugMiddleware']

class TeeFile:

    def __init__(self, files):
        self.files = files

    def write(self, v):
        for file in self.files:
            file.write(v)

class PrintDebugMiddleware:

    """
    This middleware captures all the printed statements, and inlines
    them in HTML pages, so that you can see all the (debug-intended)
    print statements in the page itself.

    There are two keys added to the environment to control this:
    ``environ['paste.printdebug_listeners']`` is a list of functions
    that will be called everytime something is printed.

    ``environ['paste.remove_printdebug']`` is a function that, if
    called, will disable printing of output for that request.

    If you have ``replace_stdout=True`` then stdout is replaced, not
    captured.
    """

    log_template = (
        '
'
        'Log messages
' '%s
') def __init__(self, app, global_conf=None, force_content_type=False, print_wsgi_errors=True, replace_stdout=False): # @@: global_conf should be handled separately and only for # the entry point self.app = app self.force_content_type = force_content_type if isinstance(print_wsgi_errors, str): from paste.deploy.converters import asbool print_wsgi_errors = asbool(print_wsgi_errors) self.print_wsgi_errors = print_wsgi_errors self.replace_stdout = replace_stdout self._threaded_print_stdout = None def __call__(self, environ, start_response): global _threadedprint_installed if environ.get('paste.testing'): # In a testing environment this interception isn't # useful: return self.app(environ, start_response) if (not _threadedprint_installed or self._threaded_print_stdout is not sys.stdout): # @@: Not strictly threadsafe _threadedprint_installed = True threadedprint.install(leave_stdout=not self.replace_stdout) self._threaded_print_stdout = sys.stdout removed = [] def remove_printdebug(): removed.append(None) environ['paste.remove_printdebug'] = remove_printdebug logged = StringIO() listeners = [logged] environ['paste.printdebug_listeners'] = listeners if self.print_wsgi_errors: listeners.append(environ['wsgi.errors']) replacement_stdout = TeeFile(listeners) threadedprint.register(replacement_stdout) try: status, headers, body = wsgilib.intercept_output( environ, self.app) if status is None: # Some error occurred status = '500 Server Error' headers = [('Content-type', 'text/html')] start_response(status, headers) if not body: body = 'An error occurred' content_type = response.header_value(headers, 'content-type') if (removed or (not self.force_content_type and (not content_type or not content_type.startswith('text/html')))): if replacement_stdout == logged: # Then the prints will be lost, unless... environ['wsgi.errors'].write(logged.getvalue()) start_response(status, headers) return [body] response.remove_header(headers, 'content-length') body = self.add_log(body, logged.getvalue()) start_response(status, headers) return [body] finally: threadedprint.deregister() _body_re = re.compile(r']*>', re.I) _explicit_re = re.compile(r']*id="paste-debug-prints".*?>', re.I+re.S) def add_log(self, html, log): if not log: return html text = html.escape(log) text = text.replace('\n', '
') text = text.replace(' ', '  ') match = self._explicit_re.search(html) if not match: text = self.log_template % text match = self._body_re.search(html) if not match: return text + html else: return html[:match.end()] + text + html[match.end():] paste-3.10.1/paste/debug/profile.py000066400000000000000000000165761461442501600171460ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Middleware that profiles the request and displays profiling information at the bottom of each page. """ import sys import os import hotshot import hotshot.stats import threading import html import time from io import StringIO from paste import response __all__ = ['ProfileMiddleware', 'profile_decorator'] class ProfileMiddleware: """ Middleware that profiles all requests. All HTML pages will have profiling information appended to them. The data is isolated to that single request, and does not include data from previous requests. This uses the ``hotshot`` module, which affects performance of the application. It also runs in a single-threaded mode, so it is only usable in development environments. """ style = ('clear: both; background-color: #ff9; color: #000; ' 'border: 2px solid #000; padding: 5px;') def __init__(self, app, global_conf=None, log_filename='profile.log.tmp', limit=40): self.app = app self.lock = threading.Lock() self.log_filename = log_filename self.limit = limit def __call__(self, environ, start_response): catch_response = [] body = [] def replace_start_response(status, headers, exc_info=None): catch_response.extend([status, headers]) start_response(status, headers, exc_info) return body.append def run_app(): app_iter = self.app(environ, replace_start_response) try: body.extend(app_iter) finally: if hasattr(app_iter, 'close'): app_iter.close() self.lock.acquire() try: prof = hotshot.Profile(self.log_filename) prof.addinfo('URL', environ.get('PATH_INFO', '')) try: prof.runcall(run_app) finally: prof.close() body = ''.join(body) headers = catch_response[1] content_type = response.header_value(headers, 'content-type') if content_type is None or not content_type.startswith('text/html'): # We can't add info to non-HTML output return [body] stats = hotshot.stats.load(self.log_filename) stats.strip_dirs() stats.sort_stats('time', 'calls') output = capture_output(stats.print_stats, self.limit) output_callers = capture_output( stats.print_callers, self.limit) body += '
%s\n%s
' % ( self.style, html.escape(output), html.escape(output_callers)) return [body] finally: self.lock.release() def capture_output(func, *args, **kw): # Not threadsafe! (that's okay when ProfileMiddleware uses it, # though, since it synchronizes itself.) out = StringIO() old_stdout = sys.stdout sys.stdout = out try: func(*args, **kw) finally: sys.stdout = old_stdout return out.getvalue() def profile_decorator(**options): """ Profile a single function call. Used around a function, like:: @profile_decorator(options...) def ... All calls to the function will be profiled. The options are all keywords, and are: log_file: The filename to log to (or ``'stdout'`` or ``'stderr'``). Default: stderr. display_limit: Only show the top N items, default: 20. sort_stats: A list of string-attributes to sort on. Default ``('time', 'calls')``. strip_dirs: Strip directories/module names from files? Default True. add_info: If given, this info will be added to the report (for your own tracking). Default: none. log_filename: The temporary filename to log profiling data to. Default; ``./profile_data.log.tmp`` no_profile: If true, then don't actually profile anything. Useful for conditional profiling. """ if options.get('no_profile'): def decorator(func): return func return decorator def decorator(func): def replacement(*args, **kw): return DecoratedProfile(func, **options)(*args, **kw) return replacement return decorator class DecoratedProfile: lock = threading.Lock() def __init__(self, func, **options): self.func = func self.options = options def __call__(self, *args, **kw): self.lock.acquire() try: return self.profile(self.func, *args, **kw) finally: self.lock.release() def profile(self, func, *args, **kw): ops = self.options prof_filename = ops.get('log_filename', 'profile_data.log.tmp') prof = hotshot.Profile(prof_filename) prof.addinfo('Function Call', self.format_function(func, *args, **kw)) if ops.get('add_info'): prof.addinfo('Extra info', ops['add_info']) exc_info = None try: start_time = time.time() try: result = prof.runcall(func, *args, **kw) except Exception: exc_info = sys.exc_info() end_time = time.time() finally: prof.close() stats = hotshot.stats.load(prof_filename) os.unlink(prof_filename) if ops.get('strip_dirs', True): stats.strip_dirs() stats.sort_stats(*ops.get('sort_stats', ('time', 'calls'))) display_limit = ops.get('display_limit', 20) output = capture_output(stats.print_stats, display_limit) output_callers = capture_output( stats.print_callers, display_limit) output_file = ops.get('log_file') if output_file in (None, 'stderr'): f = sys.stderr elif output_file in ('-', 'stdout'): f = sys.stdout else: f = open(output_file, 'a') f.write('\n%s\n' % ('-'*60)) f.write('Date: %s\n' % time.strftime('%c')) f.write('Function call: %s\n' % self.format_function(func, *args, **kw)) f.write('Wall time: %0.2f seconds\n' % (end_time - start_time)) f.write(output) f.write(output_callers) if output_file not in (None, '-', 'stdout', 'stderr'): f.close() if exc_info: # We captured an exception earlier, now we re-raise it raise exc_info return result def format_function(self, func, *args, **kw): args = map(repr, args) args.extend( ['%s=%r' % (k, v) for k, v in kw.items()]) return '%s(%s)' % (func.__name__, ', '.join(args)) def make_profile_middleware( app, global_conf, log_filename='profile.log.tmp', limit=40): """ Wrap the application in a component that will profile each request. The profiling data is then appended to the output of each page. Note that this serializes all requests (i.e., removing concurrency). Therefore never use this in production. """ limit = int(limit) return ProfileMiddleware( app, log_filename=log_filename, limit=limit) paste-3.10.1/paste/debug/testserver.py000077500000000000000000000065031461442501600177040ustar00rootroot00000000000000# (c) 2005 Clark C. Evans # This module is part of the Python Paste Project and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php # This code was written with funding by http://prometheusresearch.com """ WSGI Test Server This builds upon paste.util.baseserver to customize it for regressions where using raw_interactive won't do. """ import time from paste.httpserver import WSGIServer class WSGIRegressionServer(WSGIServer): """ A threaded WSGIServer for use in regression testing. To use this module, call serve(application, regression=True), and then call server.accept() to let it handle one request. When finished, use server.stop() to shutdown the server. Note that all pending requests are processed before the server shuts down. """ defaulttimeout = 10 def __init__ (self, *args, **kwargs): WSGIServer.__init__(self, *args, **kwargs) self.stopping = [] self.pending = [] self.timeout = self.defaulttimeout # this is a local connection, be quick self.socket.settimeout(2) def serve_forever(self): from threading import Thread thread = Thread(target=self.serve_pending) thread.start() def reset_expires(self): if self.timeout: self.expires = time.time() + self.timeout def close_request(self, *args, **kwargs): WSGIServer.close_request(self, *args, **kwargs) self.pending.pop() self.reset_expires() def serve_pending(self): self.reset_expires() while not self.stopping or self.pending: now = time.time() if now > self.expires and self.timeout: # note regression test doesn't handle exceptions in # threads very well; so we just print and exit print("\nWARNING: WSGIRegressionServer timeout exceeded\n") break if self.pending: self.handle_request() time.sleep(.1) def stop(self): """ stop the server (called from tester's thread) """ self.stopping.append(True) def accept(self, count = 1): """ accept another request (called from tester's thread) """ assert not self.stopping [self.pending.append(True) for x in range(count)] def serve(application, host=None, port=None, handler=None): server = WSGIRegressionServer(application, host, port, handler) print("serving on %s:%s" % server.server_address) server.serve_forever() return server if __name__ == '__main__': from urllib.request import urlopen from paste.wsgilib import dump_environ server = serve(dump_environ) baseuri = ("http://%s:%s" % server.server_address) def fetch(path): # tell the server to humor exactly one more request server.accept(1) # not needed; but this is what you do if the server # may not respond in a resonable time period import socket socket.setdefaulttimeout(5) # build a uri, fetch and return return urlopen(baseuri + path).read() assert "PATH_INFO: /foo" in fetch("/foo") assert "PATH_INFO: /womble" in fetch("/womble") # ok, let's make one more final request... server.accept(1) # and then schedule a stop() server.stop() # and then... fetch it... urlopen(baseuri) paste-3.10.1/paste/debug/watchthreads.py000066400000000000000000000234301461442501600201520ustar00rootroot00000000000000""" Watches the key ``paste.httpserver.thread_pool`` to see how many threads there are and report on any wedged threads. """ import sys import time import traceback from io import StringIO from thread import get_ident from paste import httpexceptions from paste.request import construct_url, parse_formvars from paste.util.template import HTMLTemplate, bunch page_template = HTMLTemplate(''' {{title}}

{{title}}

{{if kill_thread_id}}
Thread {{kill_thread_id}} killed
{{endif}}
Pool size: {{nworkers}} {{if actual_workers > nworkers}} + {{actual_workers-nworkers}} extra {{endif}} ({{nworkers_used}} used including current request)
idle: {{len(track_threads["idle"])}}, busy: {{len(track_threads["busy"])}}, hung: {{len(track_threads["hung"])}}, dying: {{len(track_threads["dying"])}}, zombie: {{len(track_threads["zombie"])}}
{{for thread in threads}}
Thread {{if thread.thread_id == this_thread_id}} (this request) {{endif}} {{thread.thread_id}} {{if allow_kill}}
{{endif}}
Time processing request {{thread.time_html|html}}
URI {{if thread.uri == 'unknown'}} unknown {{else}}{{thread.uri_short}} {{endif}}
▸ Show environ {{if thread.traceback}} ▸ Show traceback {{endif}}
{{endfor}} ''', name='watchthreads.page_template') class WatchThreads: """ Application that watches the threads in ``paste.httpserver``, showing the length each thread has been working on a request. If allow_kill is true, then you can kill errant threads through this application. This application can expose private information (specifically in the environment, like cookies), so it should be protected. """ def __init__(self, allow_kill=False): self.allow_kill = allow_kill def __call__(self, environ, start_response): if 'paste.httpserver.thread_pool' not in environ: start_response('403 Forbidden', [('Content-type', 'text/plain')]) return ['You must use the threaded Paste HTTP server to use this application'] if environ.get('PATH_INFO') == '/kill': return self.kill(environ, start_response) else: return self.show(environ, start_response) def show(self, environ, start_response): start_response('200 OK', [('Content-type', 'text/html')]) form = parse_formvars(environ) if form.get('kill'): kill_thread_id = form['kill'] else: kill_thread_id = None thread_pool = environ['paste.httpserver.thread_pool'] nworkers = thread_pool.nworkers now = time.time() workers = thread_pool.worker_tracker.items() workers.sort(key=lambda v: v[1][0]) threads = [] for thread_id, (time_started, worker_environ) in workers: thread = bunch() threads.append(thread) if worker_environ: thread.uri = construct_url(worker_environ) else: thread.uri = 'unknown' thread.thread_id = thread_id thread.time_html = format_time(now-time_started) thread.uri_short = shorten(thread.uri) thread.environ = worker_environ thread.traceback = traceback_thread(thread_id) page = page_template.substitute( title="Thread Pool Worker Tracker", nworkers=nworkers, actual_workers=len(thread_pool.workers), nworkers_used=len(workers), script_name=environ['SCRIPT_NAME'], kill_thread_id=kill_thread_id, allow_kill=self.allow_kill, threads=threads, this_thread_id=get_ident(), track_threads=thread_pool.track_threads()) return [page] def kill(self, environ, start_response): if not self.allow_kill: exc = httpexceptions.HTTPForbidden( 'Killing threads has not been enabled. Shame on you ' 'for trying!') return exc(environ, start_response) vars = parse_formvars(environ) thread_id = int(vars['thread_id']) thread_pool = environ['paste.httpserver.thread_pool'] if thread_id not in thread_pool.worker_tracker: exc = httpexceptions.PreconditionFailed( 'You tried to kill thread %s, but it is not working on ' 'any requests' % thread_id) return exc(environ, start_response) thread_pool.kill_worker(thread_id) script_name = environ['SCRIPT_NAME'] or '/' exc = httpexceptions.HTTPFound( headers=[('Location', script_name+'?kill=%s' % thread_id)]) return exc(environ, start_response) def traceback_thread(thread_id): """ Returns a plain-text traceback of the given thread, or None if it can't get a traceback. """ if not hasattr(sys, '_current_frames'): # Only 2.5 has support for this, with this special function return None frames = sys._current_frames() if not thread_id in frames: return None frame = frames[thread_id] out = StringIO() traceback.print_stack(frame, file=out) return out.getvalue() hide_keys = ['paste.httpserver.thread_pool'] def format_time(time_length): if time_length >= 60*60: # More than an hour time_string = '%i:%02i:%02i' % (int(time_length/60/60), int(time_length/60) % 60, time_length % 60) elif time_length >= 120: time_string = '%i:%02i' % (int(time_length/60), time_length % 60) elif time_length > 60: time_string = '%i sec' % time_length elif time_length > 1: time_string = '%0.1f sec' % time_length else: time_string = '%0.2f sec' % time_length if time_length < 5: return time_string elif time_length < 120: return '%s' % time_string else: return '%s' % time_string def shorten(s): if len(s) > 60: return s[:40]+'...'+s[-10:] else: return s def make_watch_threads(global_conf, allow_kill=False): from paste.deploy.converters import asbool return WatchThreads(allow_kill=asbool(allow_kill)) make_watch_threads.__doc__ = WatchThreads.__doc__ def make_bad_app(global_conf, pause=0): pause = int(pause) def bad_app(environ, start_response): import thread if pause: time.sleep(pause) else: count = 0 while 1: print("I'm alive %s (%s)" % (count, thread.get_ident())) time.sleep(10) count += 1 start_response('200 OK', [('content-type', 'text/plain')]) return ['OK, paused %s seconds' % pause] return bad_app paste-3.10.1/paste/debug/wdg_validate.py000066400000000000000000000102371461442501600201240ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Middleware that tests the validity of all generated HTML using the `WDG HTML Validator `_ """ from io import StringIO import subprocess from paste.response import header_value import re import html __all__ = ['WDGValidateMiddleware'] class WDGValidateMiddleware: """ Middleware that checks HTML and appends messages about the validity of the HTML. Uses: http://www.htmlhelp.com/tools/validator/ -- interacts with the command line client. Use the configuration ``wdg_path`` to override the path (default: looks for ``validate`` in $PATH). To install, in your web context's __init__.py:: def urlparser_wrap(environ, start_response, app): return wdg_validate.WDGValidateMiddleware(app)( environ, start_response) Or in your configuration:: middleware.append('paste.wdg_validate.WDGValidateMiddleware') """ _end_body_regex = re.compile(r'', re.I) def __init__(self, app, global_conf=None, wdg_path='validate'): self.app = app self.wdg_path = wdg_path def __call__(self, environ, start_response): output = StringIO() response = [] def writer_start_response(status, headers, exc_info=None): response.extend((status, headers)) start_response(status, headers, exc_info) return output.write app_iter = self.app(environ, writer_start_response) try: for s in app_iter: output.write(s) finally: if hasattr(app_iter, 'close'): app_iter.close() page = output.getvalue() status, headers = response v = header_value(headers, 'content-type') or '' if (not v.startswith('text/html') and not v.startswith('text/xhtml') and not v.startswith('application/xhtml')): # Can't validate # @@: Should validate CSS too... but using what? return [page] ops = [] if v.startswith('text/xhtml+xml'): ops.append('--xml') # @@: Should capture encoding too html_errors = self.call_wdg_validate( self.wdg_path, ops, page) if html_errors: page = self.add_error(page, html_errors)[0] headers.remove( ('Content-Length', str(header_value(headers, 'content-length')))) headers.append(('Content-Length', str(len(page)))) return [page] def call_wdg_validate(self, wdg_path, ops, page): if subprocess is None: raise ValueError( "This middleware requires the subprocess module from " "Python 2.4") proc = subprocess.Popen([wdg_path] + ops, shell=False, close_fds=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = proc.communicate(page)[0] proc.wait() return stdout def add_error(self, html_page, html_errors): add_text = ('
%s
' % html.escape(html_errors)) match = self._end_body_regex.search(html_page) if match: return [html_page[:match.start()] + add_text + html_page[match.start():]] else: return [html_page + add_text] def make_wdg_validate_middleware( app, global_conf, wdg_path='validate'): """ Wraps the application in the WDG validator from http://www.htmlhelp.com/tools/validator/ Validation errors are appended to the text of each page. You can configure this by giving the path to the validate executable (by default picked up from $PATH) """ return WDGValidateMiddleware( app, global_conf, wdg_path=wdg_path) paste-3.10.1/paste/errordocument.py000066400000000000000000000330451461442501600172760ustar00rootroot00000000000000# (c) 2005-2006 James Gardner # This module is part of the Python Paste Project and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php """ Middleware to display error documents for certain status codes The middleware in this module can be used to intercept responses with specified status codes and internally forward the request to an appropriate URL where the content can be displayed to the user as an error document. """ import warnings import sys from urllib import parse as urlparse from paste.recursive import ForwardRequestException, RecursiveMiddleware, RecursionLoop from paste.util import converters from paste.response import replace_header def forward(app, codes): """ Intercepts a response with a particular status code and returns the content from a specified URL instead. The arguments are: ``app`` The WSGI application or middleware chain. ``codes`` A dictionary of integer status codes and the URL to be displayed if the response uses that code. For example, you might want to create a static file to display a "File Not Found" message at the URL ``/error404.html`` and then use ``forward`` middleware to catch all 404 status codes and display the page you created. In this example ``app`` is your exisiting WSGI applicaiton:: from paste.errordocument import forward app = forward(app, codes={404:'/error404.html'}) """ for code in codes: if not isinstance(code, int): raise TypeError('All status codes should be type int. ' '%s is not valid'%repr(code)) def error_codes_mapper(code, message, environ, global_conf, codes): if code in codes: return codes[code] else: return None #return _StatusBasedRedirect(app, error_codes_mapper, codes=codes) return RecursiveMiddleware( StatusBasedForward( app, error_codes_mapper, codes=codes, ) ) class StatusKeeper: def __init__(self, app, status, url, headers): self.app = app self.status = status self.url = url self.headers = headers def __call__(self, environ, start_response): def keep_status_start_response(status, headers, exc_info=None): for header, value in headers: if header.lower() == 'set-cookie': self.headers.append((header, value)) else: replace_header(self.headers, header, value) return start_response(self.status, self.headers, exc_info) parts = self.url.split('?') environ['PATH_INFO'] = parts[0] if len(parts) > 1: environ['QUERY_STRING'] = parts[1] else: environ['QUERY_STRING'] = '' #raise Exception(self.url, self.status) try: return self.app(environ, keep_status_start_response) except RecursionLoop as e: line = 'Recursion error getting error page: %s\n' % e environ['wsgi.errors'].write(line) keep_status_start_response('500 Server Error', [('Content-type', 'text/plain')], sys.exc_info()) body = ('Error: %s. (Error page could not be fetched)' % self.status) body = body.encode('utf8') return [body] class StatusBasedForward: """ Middleware that lets you test a response against a custom mapper object to programatically determine whether to internally forward to another URL and if so, which URL to forward to. If you don't need the full power of this middleware you might choose to use the simpler ``forward`` middleware instead. The arguments are: ``app`` The WSGI application or middleware chain. ``mapper`` A callable that takes a status code as the first parameter, a message as the second, and accepts optional environ, global_conf and named argments afterwards. It should return a URL to forward to or ``None`` if the code is not to be intercepted. ``global_conf`` Optional default configuration from your config file. If ``debug`` is set to ``true`` a message will be written to ``wsgi.errors`` on each internal forward stating the URL forwarded to. ``**params`` Optional, any other configuration and extra arguments you wish to pass which will in turn be passed back to the custom mapper object. Here is an example where a ``404 File Not Found`` status response would be redirected to the URL ``/error?code=404&message=File%20Not%20Found``. This could be useful for passing the status code and message into another application to display an error document: .. code-block:: python from paste.errordocument import StatusBasedForward from paste.recursive import RecursiveMiddleware from urllib import urlencode def error_mapper(code, message, environ, global_conf, kw) if code in [404, 500]: params = urlencode({'message':message, 'code':code}) url = '/error?'%(params) return url else: return None app = RecursiveMiddleware( StatusBasedForward(app, mapper=error_mapper), ) """ def __init__(self, app, mapper, global_conf=None, **params): if global_conf is None: global_conf = {} # @@: global_conf shouldn't really come in here, only in a # separate make_status_based_forward function if global_conf: self.debug = converters.asbool(global_conf.get('debug', False)) else: self.debug = False self.application = app self.mapper = mapper self.global_conf = global_conf self.params = params def __call__(self, environ, start_response): url = [] def change_response(status, headers, exc_info=None): status_code = status.split(' ') try: code = int(status_code[0]) except (ValueError, TypeError): raise Exception( 'StatusBasedForward middleware ' 'received an invalid status code %s'%repr(status_code[0]) ) message = ' '.join(status_code[1:]) new_url = self.mapper( code, message, environ, self.global_conf, **self.params ) if not (new_url is None or isinstance(new_url, str)): raise TypeError( 'Expected the url to internally ' 'redirect to in the StatusBasedForward mapper' 'to be a string or None, not %r' % new_url) if new_url: url.append([new_url, status, headers]) # We have to allow the app to write stuff, even though # we'll ignore it: return [].append else: return start_response(status, headers, exc_info) app_iter = self.application(environ, change_response) if url: if hasattr(app_iter, 'close'): app_iter.close() def factory(app): return StatusKeeper(app, status=url[0][1], url=url[0][0], headers=url[0][2]) raise ForwardRequestException(factory=factory) else: return app_iter def make_errordocument(app, global_conf, **kw): """ Paste Deploy entry point to create a error document wrapper. Use like:: [filter-app:main] use = egg:Paste#errordocument next = real-app 500 = /lib/msg/500.html 404 = /lib/msg/404.html """ map = {} for status, redir_loc in kw.items(): try: status = int(status) except ValueError: raise ValueError('Bad status code: %r' % status) map[status] = redir_loc forwarder = forward(app, map) return forwarder __pudge_all__ = [ 'forward', 'make_errordocument', 'empty_error', 'make_empty_error', 'StatusBasedForward', ] ############################################################################### ## Deprecated ############################################################################### def custom_forward(app, mapper, global_conf=None, **kw): """ Deprectated; use StatusBasedForward instead. """ warnings.warn( "errordocuments.custom_forward has been deprecated; please " "use errordocuments.StatusBasedForward", DeprecationWarning, 2) if global_conf is None: global_conf = {} return _StatusBasedRedirect(app, mapper, global_conf, **kw) class _StatusBasedRedirect: """ Deprectated; use StatusBasedForward instead. """ def __init__(self, app, mapper, global_conf=None, **kw): warnings.warn( "errordocuments._StatusBasedRedirect has been deprecated; please " "use errordocuments.StatusBasedForward", DeprecationWarning, 2) if global_conf is None: global_conf = {} self.application = app self.mapper = mapper self.global_conf = global_conf self.kw = kw self.fallback_template = """ Error %(code)s

Error %(code)s

%(message)s


Additionally an error occurred trying to produce an error document. A description of the error was logged to wsgi.errors.

""" def __call__(self, environ, start_response): url = [] code_message = [] try: def change_response(status, headers, exc_info=None): new_url = None parts = status.split(' ') try: code = int(parts[0]) except (ValueError, TypeError): raise Exception( '_StatusBasedRedirect middleware ' 'received an invalid status code %s'%repr(parts[0]) ) message = ' '.join(parts[1:]) new_url = self.mapper( code, message, environ, self.global_conf, self.kw ) if not (new_url is None or isinstance(new_url, str)): raise TypeError( 'Expected the url to internally ' 'redirect to in the _StatusBasedRedirect error_mapper' 'to be a string or None, not %s'%repr(new_url) ) if new_url: url.append(new_url) code_message.append([code, message]) return start_response(status, headers, exc_info) app_iter = self.application(environ, change_response) except Exception: try: import sys error = str(sys.exc_info()[1]) except Exception: error = '' try: code, message = code_message[0] except Exception: code, message = ['', ''] environ['wsgi.errors'].write( 'Error occurred in _StatusBasedRedirect ' 'intercepting the response: '+str(error) ) return [self.fallback_template % {'message': message, 'code': code}] else: if url: url_ = url[0] new_environ = {} for k, v in environ.items(): if k != 'QUERY_STRING': new_environ['QUERY_STRING'] = urlparse.urlparse(url_)[4] else: new_environ[k] = v class InvalidForward(Exception): pass def eat_start_response(status, headers, exc_info=None): """ We don't want start_response to do anything since it has already been called """ if status[:3] != '200': raise InvalidForward( "The URL %s to internally forward " "to in order to create an error document did not " "return a '200' status code." % url_ ) forward = environ['paste.recursive.forward'] old_start_response = forward.start_response forward.start_response = eat_start_response try: app_iter = forward(url_, new_environ) except InvalidForward: code, message = code_message[0] environ['wsgi.errors'].write( 'Error occurred in ' '_StatusBasedRedirect redirecting ' 'to new URL: '+str(url[0]) ) return [ self.fallback_template%{ 'message':message, 'code':code, } ] else: forward.start_response = old_start_response return app_iter else: return app_iter paste-3.10.1/paste/evalexception/000077500000000000000000000000001461442501600166755ustar00rootroot00000000000000paste-3.10.1/paste/evalexception/__init__.py000066400000000000000000000004321461442501600210050ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ An exception handler for interactive debugging """ from paste.evalexception.middleware import EvalException paste-3.10.1/paste/evalexception/evalcontext.py000066400000000000000000000041111461442501600216000ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php from io import StringIO import traceback import threading import pdb import sys exec_lock = threading.Lock() class EvalContext: """ Class that represents a interactive interface. It has its own namespace. Use eval_context.exec_expr(expr) to run commands; the output of those commands is returned, as are print statements. This is essentially what doctest does, and is taken directly from doctest. """ def __init__(self, namespace, globs): self.namespace = namespace self.globs = globs def exec_expr(self, s): out = StringIO() exec_lock.acquire() save_stdout = sys.stdout try: debugger = _OutputRedirectingPdb(save_stdout) debugger.reset() pdb.set_trace = debugger.set_trace sys.stdout = out try: code = compile(s, '', "single", 0, 1) exec(code, self.globs, self.namespace) debugger.set_continue() except KeyboardInterrupt: raise except Exception: traceback.print_exc(file=out) debugger.set_continue() finally: sys.stdout = save_stdout exec_lock.release() return out.getvalue() # From doctest class _OutputRedirectingPdb(pdb.Pdb): """ A specialized version of the python debugger that redirects stdout to a given stream when interacting with the user. Stdout is *not* redirected when traced code is executed. """ def __init__(self, out): self.__out = out pdb.Pdb.__init__(self) def trace_dispatch(self, *args): # Redirect stdout to the given stream. save_stdout = sys.stdout sys.stdout = self.__out # Call Pdb's trace dispatch method. try: return pdb.Pdb.trace_dispatch(self, *args) finally: sys.stdout = save_stdout paste-3.10.1/paste/evalexception/media/000077500000000000000000000000001461442501600177545ustar00rootroot00000000000000paste-3.10.1/paste/evalexception/media/MochiKit.packed.js000066400000000000000000006130261461442501600232570ustar00rootroot00000000000000/*** MochiKit.MochiKit 1.4.2 : PACKED VERSION THIS FILE IS AUTOMATICALLY GENERATED. If creating patches, please diff against the source tree, not this file. See for documentation, downloads, license, etc. (c) 2005 Bob Ippolito. All rights Reserved. ***/ if(typeof (dojo)!="undefined"){ dojo.provide("MochiKit.Base"); } if(typeof (MochiKit)=="undefined"){ MochiKit={}; } if(typeof (MochiKit.Base)=="undefined"){ MochiKit.Base={}; } if(typeof (MochiKit.__export__)=="undefined"){ MochiKit.__export__=(MochiKit.__compat__||(typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")); } MochiKit.Base.VERSION="1.4.2"; MochiKit.Base.NAME="MochiKit.Base"; MochiKit.Base.update=function(_1,_2){ if(_1===null||_1===undefined){ _1={}; } for(var i=1;i=0;i--){ _18.unshift(o[i]); } }else{ res.push(o); } } return res; },extend:function(_1b,obj,_1d){ if(!_1d){ _1d=0; } if(obj){ var l=obj.length; if(typeof (l)!="number"){ if(typeof (MochiKit.Iter)!="undefined"){ obj=MochiKit.Iter.list(obj); l=obj.length; }else{ throw new TypeError("Argument not an array-like and MochiKit.Iter not present"); } } if(!_1b){ _1b=[]; } for(var i=_1d;i>b; },zrshift:function(a,b){ return a>>>b; },eq:function(a,b){ return a==b; },ne:function(a,b){ return a!=b; },gt:function(a,b){ return a>b; },ge:function(a,b){ return a>=b; },lt:function(a,b){ return al){ _93=l; } } _91=[]; for(i=0;i<_93;i++){ var _95=[]; for(var j=1;j=0;i--){ _b2=[_ae[i].apply(this,_b2)]; } return _b2[0]; }; },bind:function(_b4,_b5){ if(typeof (_b4)=="string"){ _b4=_b5[_b4]; } var _b6=_b4.im_func; var _b7=_b4.im_preargs; var _b8=_b4.im_self; var m=MochiKit.Base; if(typeof (_b4)=="function"&&typeof (_b4.apply)=="undefined"){ _b4=m._wrapDumbFunction(_b4); } if(typeof (_b6)!="function"){ _b6=_b4; } if(typeof (_b5)!="undefined"){ _b8=_b5; } if(typeof (_b7)=="undefined"){ _b7=[]; }else{ _b7=_b7.slice(); } m.extend(_b7,arguments,2); var _ba=function(){ var _bb=arguments; var me=arguments.callee; if(me.im_preargs.length>0){ _bb=m.concat(me.im_preargs,_bb); } var _bd=me.im_self; if(!_bd){ _bd=this; } return me.im_func.apply(_bd,_bb); }; _ba.im_self=_b8; _ba.im_func=_b6; _ba.im_preargs=_b7; return _ba; },bindLate:function(_be,_bf){ var m=MochiKit.Base; if(typeof (_be)!="string"){ return m.bind.apply(this,arguments); } var _c1=m.extend([],arguments,2); var _c2=function(){ var _c3=arguments; var me=arguments.callee; if(me.im_preargs.length>0){ _c3=m.concat(me.im_preargs,_c3); } var _c5=me.im_self; if(!_c5){ _c5=this; } return _c5[me.im_func].apply(_c5,_c3); }; _c2.im_self=_bf; _c2.im_func=_be; _c2.im_preargs=_c1; return _c2; },bindMethods:function(_c6){ var _c7=MochiKit.Base.bind; for(var k in _c6){ var _c9=_c6[k]; if(typeof (_c9)=="function"){ _c6[k]=_c7(_c9,_c6); } } },registerComparator:function(_ca,_cb,_cc,_cd){ MochiKit.Base.comparatorRegistry.register(_ca,_cb,_cc,_cd); },_primitives:{"boolean":true,"string":true,"number":true},compare:function(a,b){ if(a==b){ return 0; } var _d0=(typeof (a)=="undefined"||a===null); var _d1=(typeof (b)=="undefined"||b===null); if(_d0&&_d1){ return 0; }else{ if(_d0){ return -1; }else{ if(_d1){ return 1; } } } var m=MochiKit.Base; var _d3=m._primitives; if(!(typeof (a) in _d3&&typeof (b) in _d3)){ try{ return m.comparatorRegistry.match(a,b); } catch(e){ if(e!=m.NotFound){ throw e; } } } if(ab){ return 1; } } var _d4=m.repr; throw new TypeError(_d4(a)+" and "+_d4(b)+" can not be compared"); },compareDateLike:function(a,b){ return MochiKit.Base.compare(a.getTime(),b.getTime()); },compareArrayLike:function(a,b){ var _d9=MochiKit.Base.compare; var _da=a.length; var _db=0; if(_da>b.length){ _db=1; _da=b.length; }else{ if(_da=0;i--){ sum+=o[i]; } }else{ sum+=o; } } if(_121<=0){ throw new TypeError("mean() requires at least one argument"); } return sum/_121; },median:function(){ var data=MochiKit.Base.flattenArguments(arguments); if(data.length===0){ throw new TypeError("median() requires at least one argument"); } data.sort(compare); if(data.length%2==0){ var _125=data.length/2; return (data[_125]+data[_125-1])/2; }else{ return data[(data.length-1)/2]; } },findValue:function(lst,_127,_128,end){ if(typeof (end)=="undefined"||end===null){ end=lst.length; } if(typeof (_128)=="undefined"||_128===null){ _128=0; } var cmp=MochiKit.Base.compare; for(var i=_128;i0))){ var kv=MochiKit.DOM.formContents(_135); _135=kv[0]; _136=kv[1]; }else{ if(arguments.length==1){ if(typeof (_135.length)=="number"&&_135.length==2){ return arguments.callee(_135[0],_135[1]); } var o=_135; _135=[]; _136=[]; for(var k in o){ var v=o[k]; if(typeof (v)=="function"){ continue; }else{ if(MochiKit.Base.isArrayLike(v)){ for(var i=0;i=stop){ throw self.StopIteration; } _183+=step; return rval; }}; },imap:function(fun,p,q){ var m=MochiKit.Base; var self=MochiKit.Iter; var _18d=m.map(self.iter,m.extend(null,arguments,1)); var map=m.map; var next=self.next; return {repr:function(){ return "imap(...)"; },toString:m.forwardCall("repr"),next:function(){ return fun.apply(this,map(next,_18d)); }}; },applymap:function(fun,seq,self){ seq=MochiKit.Iter.iter(seq); var m=MochiKit.Base; return {repr:function(){ return "applymap(...)"; },toString:m.forwardCall("repr"),next:function(){ return fun.apply(self,seq.next()); }}; },chain:function(p,q){ var self=MochiKit.Iter; var m=MochiKit.Base; if(arguments.length==1){ return self.iter(arguments[0]); } var _198=m.map(self.iter,arguments); return {repr:function(){ return "chain(...)"; },toString:m.forwardCall("repr"),next:function(){ while(_198.length>1){ try{ var _199=_198[0].next(); return _199; } catch(e){ if(e!=self.StopIteration){ throw e; } _198.shift(); var _199=_198[0].next(); return _199; } } if(_198.length==1){ var arg=_198.shift(); this.next=m.bind("next",arg); return this.next(); } throw self.StopIteration; }}; },takewhile:function(pred,seq){ var self=MochiKit.Iter; seq=self.iter(seq); return {repr:function(){ return "takewhile(...)"; },toString:MochiKit.Base.forwardCall("repr"),next:function(){ var rval=seq.next(); if(!pred(rval)){ this.next=function(){ throw self.StopIteration; }; this.next(); } return rval; }}; },dropwhile:function(pred,seq){ seq=MochiKit.Iter.iter(seq); var m=MochiKit.Base; var bind=m.bind; return {"repr":function(){ return "dropwhile(...)"; },"toString":m.forwardCall("repr"),"next":function(){ while(true){ var rval=seq.next(); if(!pred(rval)){ break; } } this.next=bind("next",seq); return rval; }}; },_tee:function(_1a4,sync,_1a6){ sync.pos[_1a4]=-1; var m=MochiKit.Base; var _1a8=m.listMin; return {repr:function(){ return "tee("+_1a4+", ...)"; },toString:m.forwardCall("repr"),next:function(){ var rval; var i=sync.pos[_1a4]; if(i==sync.max){ rval=_1a6.next(); sync.deque.push(rval); sync.max+=1; sync.pos[_1a4]+=1; }else{ rval=sync.deque[i-sync.min]; sync.pos[_1a4]+=1; if(i==sync.min&&_1a8(sync.pos)!=sync.min){ sync.min+=1; sync.deque.shift(); } } return rval; }}; },tee:function(_1ab,n){ var rval=[]; var sync={"pos":[],"deque":[],"max":-1,"min":-1}; if(arguments.length==1||typeof (n)=="undefined"||n===null){ n=2; } var self=MochiKit.Iter; _1ab=self.iter(_1ab); var _tee=self._tee; for(var i=0;i0&&_1bd>=stop)||(step<0&&_1bd<=stop)){ throw MochiKit.Iter.StopIteration; } var rval=_1bd; _1bd+=step; return rval; },repr:function(){ return "range("+[_1bd,stop,step].join(", ")+")"; },toString:MochiKit.Base.forwardCall("repr")}; },sum:function(_1c1,_1c2){ if(typeof (_1c2)=="undefined"||_1c2===null){ _1c2=0; } var x=_1c2; var self=MochiKit.Iter; _1c1=self.iter(_1c1); try{ while(true){ x+=_1c1.next(); } } catch(e){ if(e!=self.StopIteration){ throw e; } } return x; },exhaust:function(_1c5){ var self=MochiKit.Iter; _1c5=self.iter(_1c5); try{ while(true){ _1c5.next(); } } catch(e){ if(e!=self.StopIteration){ throw e; } } },forEach:function(_1c7,func,obj){ var m=MochiKit.Base; var self=MochiKit.Iter; if(arguments.length>2){ func=m.bind(func,obj); } if(m.isArrayLike(_1c7)&&!self.isIterable(_1c7)){ try{ for(var i=0;i<_1c7.length;i++){ func(_1c7[i]); } } catch(e){ if(e!=self.StopIteration){ throw e; } } }else{ self.exhaust(self.imap(func,_1c7)); } },every:function(_1cd,func){ var self=MochiKit.Iter; try{ self.ifilterfalse(func,_1cd).next(); return false; } catch(e){ if(e!=self.StopIteration){ throw e; } return true; } },sorted:function(_1d0,cmp){ var rval=MochiKit.Iter.list(_1d0); if(arguments.length==1){ cmp=MochiKit.Base.compare; } rval.sort(cmp); return rval; },reversed:function(_1d3){ var rval=MochiKit.Iter.list(_1d3); rval.reverse(); return rval; },some:function(_1d5,func){ var self=MochiKit.Iter; try{ self.ifilter(func,_1d5).next(); return true; } catch(e){ if(e!=self.StopIteration){ throw e; } return false; } },iextend:function(lst,_1d9){ var m=MochiKit.Base; var self=MochiKit.Iter; if(m.isArrayLike(_1d9)&&!self.isIterable(_1d9)){ for(var i=0;i<_1d9.length;i++){ lst.push(_1d9[i]); } }else{ _1d9=self.iter(_1d9); try{ while(true){ lst.push(_1d9.next()); } } catch(e){ if(e!=self.StopIteration){ throw e; } } } return lst; },groupby:function(_1dd,_1de){ var m=MochiKit.Base; var self=MochiKit.Iter; if(arguments.length<2){ _1de=m.operator.identity; } _1dd=self.iter(_1dd); var pk=undefined; var k=undefined; var v; function fetch(){ v=_1dd.next(); k=_1de(v); } function eat(){ var ret=v; v=undefined; return ret; } var _1e5=true; var _1e6=m.compare; return {repr:function(){ return "groupby(...)"; },next:function(){ while(_1e6(k,pk)===0){ fetch(); if(_1e5){ _1e5=false; break; } } pk=k; return [k,{next:function(){ if(v==undefined){ fetch(); } if(_1e6(k,pk)!==0){ throw self.StopIteration; } return eat(); }}]; }}; },groupby_as_array:function(_1e7,_1e8){ var m=MochiKit.Base; var self=MochiKit.Iter; if(arguments.length<2){ _1e8=m.operator.identity; } _1e7=self.iter(_1e7); var _1eb=[]; var _1ec=true; var _1ed; var _1ee=m.compare; while(true){ try{ var _1ef=_1e7.next(); var key=_1e8(_1ef); } catch(e){ if(e==self.StopIteration){ break; } throw e; } if(_1ec||_1ee(key,_1ed)!==0){ var _1f1=[]; _1eb.push([key,_1f1]); } _1f1.push(_1ef); _1ec=false; _1ed=key; } return _1eb; },arrayLikeIter:function(_1f2){ var i=0; return {repr:function(){ return "arrayLikeIter(...)"; },toString:MochiKit.Base.forwardCall("repr"),next:function(){ if(i>=_1f2.length){ throw MochiKit.Iter.StopIteration; } return _1f2[i++]; }}; },hasIterateNext:function(_1f4){ return (_1f4&&typeof (_1f4.iterateNext)=="function"); },iterateNextIter:function(_1f5){ return {repr:function(){ return "iterateNextIter(...)"; },toString:MochiKit.Base.forwardCall("repr"),next:function(){ var rval=_1f5.iterateNext(); if(rval===null||rval===undefined){ throw MochiKit.Iter.StopIteration; } return rval; }}; }}); MochiKit.Iter.EXPORT_OK=["iteratorRegistry","arrayLikeIter","hasIterateNext","iterateNextIter"]; MochiKit.Iter.EXPORT=["StopIteration","registerIteratorFactory","iter","count","cycle","repeat","next","izip","ifilter","ifilterfalse","islice","imap","applymap","chain","takewhile","dropwhile","tee","list","reduce","range","sum","exhaust","forEach","every","sorted","reversed","some","iextend","groupby","groupby_as_array"]; MochiKit.Iter.__new__=function(){ var m=MochiKit.Base; if(typeof (StopIteration)!="undefined"){ this.StopIteration=StopIteration; }else{ this.StopIteration=new m.NamedError("StopIteration"); } this.iteratorRegistry=new m.AdapterRegistry(); this.registerIteratorFactory("arrayLike",m.isArrayLike,this.arrayLikeIter); this.registerIteratorFactory("iterateNext",this.hasIterateNext,this.iterateNextIter); this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; m.nameFunctions(this); }; MochiKit.Iter.__new__(); if(MochiKit.__export__){ reduce=MochiKit.Iter.reduce; } MochiKit.Base._exportSymbols(this,MochiKit.Iter); MochiKit.Base._deps("Logging",["Base"]); MochiKit.Logging.NAME="MochiKit.Logging"; MochiKit.Logging.VERSION="1.4.2"; MochiKit.Logging.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.Logging.toString=function(){ return this.__repr__(); }; MochiKit.Logging.EXPORT=["LogLevel","LogMessage","Logger","alertListener","logger","log","logError","logDebug","logFatal","logWarning"]; MochiKit.Logging.EXPORT_OK=["logLevelAtLeast","isLogMessage","compareLogMessage"]; MochiKit.Logging.LogMessage=function(num,_1f9,info){ this.num=num; this.level=_1f9; this.info=info; this.timestamp=new Date(); }; MochiKit.Logging.LogMessage.prototype={repr:function(){ var m=MochiKit.Base; return "LogMessage("+m.map(m.repr,[this.num,this.level,this.info]).join(", ")+")"; },toString:MochiKit.Base.forwardCall("repr")}; MochiKit.Base.update(MochiKit.Logging,{logLevelAtLeast:function(_1fc){ var self=MochiKit.Logging; if(typeof (_1fc)=="string"){ _1fc=self.LogLevel[_1fc]; } return function(msg){ var _1ff=msg.level; if(typeof (_1ff)=="string"){ _1ff=self.LogLevel[_1ff]; } return _1ff>=_1fc; }; },isLogMessage:function(){ var _200=MochiKit.Logging.LogMessage; for(var i=0;i=MochiKit.Logging.LogLevel.FATAL){ _20f="FATAL"; }else{ if(_20f>=MochiKit.Logging.LogLevel.ERROR){ _20f="ERROR"; }else{ if(_20f>=MochiKit.Logging.LogLevel.WARNING){ _20f="WARNING"; }else{ if(_20f>=MochiKit.Logging.LogLevel.INFO){ _20f="INFO"; }else{ _20f="DEBUG"; } } } } } var msg=new MochiKit.Logging.LogMessage(this.counter,_20f,MochiKit.Base.extend(null,arguments,1)); this._messages.push(msg); this.dispatchListeners(msg); if(this.useNativeConsole){ this.logToConsole(msg.level+": "+msg.info.join(" ")); } this.counter+=1; while(this.maxSize>=0&&this._messages.length>this.maxSize){ this._messages.shift(); } },getMessages:function(_212){ var _213=0; if(!(typeof (_212)=="undefined"||_212===null)){ _213=Math.max(0,this._messages.length-_212); } return this._messages.slice(_213); },getMessageText:function(_214){ if(typeof (_214)=="undefined"||_214===null){ _214=30; } var _215=this.getMessages(_214); if(_215.length){ var lst=map(function(m){ return "\n ["+m.num+"] "+m.level+": "+m.info.join(" "); },_215); lst.unshift("LAST "+_215.length+" MESSAGES:"); return lst.join(""); } return ""; },debuggingBookmarklet:function(_218){ if(typeof (MochiKit.LoggingPane)=="undefined"){ alert(this.getMessageText()); }else{ MochiKit.LoggingPane.createLoggingPane(_218||false); } }}; MochiKit.Logging.__new__=function(){ this.LogLevel={ERROR:40,FATAL:50,WARNING:30,INFO:20,DEBUG:10}; var m=MochiKit.Base; m.registerComparator("LogMessage",this.isLogMessage,this.compareLogMessage); var _21a=m.partial; var _21b=this.Logger; var _21c=_21b.prototype.baseLog; m.update(this.Logger.prototype,{debug:_21a(_21c,"DEBUG"),log:_21a(_21c,"INFO"),error:_21a(_21c,"ERROR"),fatal:_21a(_21c,"FATAL"),warning:_21a(_21c,"WARNING")}); var self=this; var _21e=function(name){ return function(){ self.logger[name].apply(self.logger,arguments); }; }; this.log=_21e("log"); this.logError=_21e("error"); this.logDebug=_21e("debug"); this.logFatal=_21e("fatal"); this.logWarning=_21e("warning"); this.logger=new _21b(); this.logger.useNativeConsole=true; this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; m.nameFunctions(this); }; if(typeof (printfire)=="undefined"&&typeof (document)!="undefined"&&document.createEvent&&typeof (dispatchEvent)!="undefined"){ printfire=function(){ printfire.args=arguments; var ev=document.createEvent("Events"); ev.initEvent("printfire",false,true); dispatchEvent(ev); }; } MochiKit.Logging.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.Logging); MochiKit.Base._deps("DateTime",["Base"]); MochiKit.DateTime.NAME="MochiKit.DateTime"; MochiKit.DateTime.VERSION="1.4.2"; MochiKit.DateTime.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.DateTime.toString=function(){ return this.__repr__(); }; MochiKit.DateTime.isoDate=function(str){ str=str+""; if(typeof (str)!="string"||str.length===0){ return null; } var iso=str.split("-"); if(iso.length===0){ return null; } var date=new Date(iso[0],iso[1]-1,iso[2]); date.setFullYear(iso[0]); date.setMonth(iso[1]-1); date.setDate(iso[2]); return date; }; MochiKit.DateTime._isoRegexp=/(\d{4,})(?:-(\d{1,2})(?:-(\d{1,2})(?:[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d+))?)?(?:(Z)|([+-])(\d{1,2})(?::(\d{1,2}))?)?)?)?)?/; MochiKit.DateTime.isoTimestamp=function(str){ str=str+""; if(typeof (str)!="string"||str.length===0){ return null; } var res=str.match(MochiKit.DateTime._isoRegexp); if(typeof (res)=="undefined"||res===null){ return null; } var year,_227,day,hour,min,sec,msec; year=parseInt(res[1],10); if(typeof (res[2])=="undefined"||res[2]===""){ return new Date(year); } _227=parseInt(res[2],10)-1; day=parseInt(res[3],10); if(typeof (res[4])=="undefined"||res[4]===""){ return new Date(year,_227,day); } hour=parseInt(res[4],10); min=parseInt(res[5],10); sec=(typeof (res[6])!="undefined"&&res[6]!=="")?parseInt(res[6],10):0; if(typeof (res[7])!="undefined"&&res[7]!==""){ msec=Math.round(1000*parseFloat("0."+res[7])); }else{ msec=0; } if((typeof (res[8])=="undefined"||res[8]==="")&&(typeof (res[9])=="undefined"||res[9]==="")){ return new Date(year,_227,day,hour,min,sec,msec); } var ofs; if(typeof (res[9])!="undefined"&&res[9]!==""){ ofs=parseInt(res[10],10)*3600000; if(typeof (res[11])!="undefined"&&res[11]!==""){ ofs+=parseInt(res[11],10)*60000; } if(res[9]=="-"){ ofs=-ofs; } }else{ ofs=0; } return new Date(Date.UTC(year,_227,day,hour,min,sec,msec)-ofs); }; MochiKit.DateTime.toISOTime=function(date,_22f){ if(typeof (date)=="undefined"||date===null){ return null; } var hh=date.getHours(); var mm=date.getMinutes(); var ss=date.getSeconds(); var lst=[((_22f&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)]; return lst.join(":"); }; MochiKit.DateTime.toISOTimestamp=function(date,_235){ if(typeof (date)=="undefined"||date===null){ return null; } var sep=_235?"T":" "; var foot=_235?"Z":""; if(_235){ date=new Date(date.getTime()+(date.getTimezoneOffset()*60000)); } return MochiKit.DateTime.toISODate(date)+sep+MochiKit.DateTime.toISOTime(date,_235)+foot; }; MochiKit.DateTime.toISODate=function(date){ if(typeof (date)=="undefined"||date===null){ return null; } var _239=MochiKit.DateTime._padTwo; var _23a=MochiKit.DateTime._padFour; return [_23a(date.getFullYear()),_239(date.getMonth()+1),_239(date.getDate())].join("-"); }; MochiKit.DateTime.americanDate=function(d){ d=d+""; if(typeof (d)!="string"||d.length===0){ return null; } var a=d.split("/"); return new Date(a[2],a[0]-1,a[1]); }; MochiKit.DateTime._padTwo=function(n){ return (n>9)?n:"0"+n; }; MochiKit.DateTime._padFour=function(n){ switch(n.toString().length){ case 1: return "000"+n; break; case 2: return "00"+n; break; case 3: return "0"+n; break; case 4: default: return n; } }; MochiKit.DateTime.toPaddedAmericanDate=function(d){ if(typeof (d)=="undefined"||d===null){ return null; } var _240=MochiKit.DateTime._padTwo; return [_240(d.getMonth()+1),_240(d.getDate()),d.getFullYear()].join("/"); }; MochiKit.DateTime.toAmericanDate=function(d){ if(typeof (d)=="undefined"||d===null){ return null; } return [d.getMonth()+1,d.getDate(),d.getFullYear()].join("/"); }; MochiKit.DateTime.EXPORT=["isoDate","isoTimestamp","toISOTime","toISOTimestamp","toISODate","americanDate","toPaddedAmericanDate","toAmericanDate"]; MochiKit.DateTime.EXPORT_OK=[]; MochiKit.DateTime.EXPORT_TAGS={":common":MochiKit.DateTime.EXPORT,":all":MochiKit.DateTime.EXPORT}; MochiKit.DateTime.__new__=function(){ var base=this.NAME+"."; for(var k in this){ var o=this[k]; if(typeof (o)=="function"&&typeof (o.NAME)=="undefined"){ try{ o.NAME=base+k; } catch(e){ } } } }; MochiKit.DateTime.__new__(); if(typeof (MochiKit.Base)!="undefined"){ MochiKit.Base._exportSymbols(this,MochiKit.DateTime); }else{ (function(_245,_246){ if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(MochiKit.__export__===false)){ var all=_246.EXPORT_TAGS[":all"]; for(var i=0;i_250){ var i=_258.length-_250; res=fmt.separator+_258.substring(i,_258.length)+res; _258=_258.substring(0,i); } } res=_258+res; if(_24e>0){ while(frac.length<_251){ frac=frac+"0"; } res=res+fmt.decimal+frac; } return _253+res+_254; }; }; MochiKit.Format.numberFormatter=function(_25c,_25d,_25e){ if(typeof (_25d)=="undefined"){ _25d=""; } var _25f=_25c.match(/((?:[0#]+,)?[0#]+)(?:\.([0#]+))?(%)?/); if(!_25f){ throw TypeError("Invalid pattern"); } var _260=_25c.substr(0,_25f.index); var _261=_25c.substr(_25f.index+_25f[0].length); if(_260.search(/-/)==-1){ _260=_260+"-"; } var _262=_25f[1]; var frac=(typeof (_25f[2])=="string"&&_25f[2]!="")?_25f[2]:""; var _264=(typeof (_25f[3])=="string"&&_25f[3]!=""); var tmp=_262.split(/,/); var _266; if(typeof (_25e)=="undefined"){ _25e="default"; } if(tmp.length==1){ _266=null; }else{ _266=tmp[1].length; } var _267=_262.length-_262.replace(/0/g,"").length; var _268=frac.length-frac.replace(/0/g,"").length; var _269=frac.length; var rval=MochiKit.Format._numberFormatter(_25d,_260,_261,_25e,_264,_269,_267,_266,_268); var m=MochiKit.Base; if(m){ var fn=arguments.callee; var args=m.concat(arguments); rval.repr=function(){ return [self.NAME,"(",map(m.repr,args).join(", "),")"].join(""); }; } return rval; }; MochiKit.Format.formatLocale=function(_26e){ if(typeof (_26e)=="undefined"||_26e===null){ _26e="default"; } if(typeof (_26e)=="string"){ var rval=MochiKit.Format.LOCALE[_26e]; if(typeof (rval)=="string"){ rval=arguments.callee(rval); MochiKit.Format.LOCALE[_26e]=rval; } return rval; }else{ return _26e; } }; MochiKit.Format.twoDigitAverage=function(_270,_271){ if(_271){ var res=_270/_271; if(!isNaN(res)){ return MochiKit.Format.twoDigitFloat(res); } } return "0"; }; MochiKit.Format.twoDigitFloat=function(_273){ var res=roundToFixed(_273,2); if(res.indexOf(".00")>0){ return res.substring(0,res.length-3); }else{ if(res.charAt(res.length-1)=="0"){ return res.substring(0,res.length-1); }else{ return res; } } }; MochiKit.Format.lstrip=function(str,_276){ str=str+""; if(typeof (str)!="string"){ return null; } if(!_276){ return str.replace(/^\s+/,""); }else{ return str.replace(new RegExp("^["+_276+"]+"),""); } }; MochiKit.Format.rstrip=function(str,_278){ str=str+""; if(typeof (str)!="string"){ return null; } if(!_278){ return str.replace(/\s+$/,""); }else{ return str.replace(new RegExp("["+_278+"]+$"),""); } }; MochiKit.Format.strip=function(str,_27a){ var self=MochiKit.Format; return self.rstrip(self.lstrip(str,_27a),_27a); }; MochiKit.Format.truncToFixed=function(_27c,_27d){ var res=Math.floor(_27c).toFixed(0); if(_27c<0){ res=Math.ceil(_27c).toFixed(0); if(res.charAt(0)!="-"&&_27d>0){ res="-"+res; } } if(res.indexOf("e")<0&&_27d>0){ var tail=_27c.toString(); if(tail.indexOf("e")>0){ tail="."; }else{ if(tail.indexOf(".")<0){ tail="."; }else{ tail=tail.substring(tail.indexOf(".")); } } if(tail.length-1>_27d){ tail=tail.substring(0,_27d+1); } while(tail.length-1<_27d){ tail+="0"; } res+=tail; } return res; }; MochiKit.Format.roundToFixed=function(_280,_281){ var _282=Math.abs(_280)+0.5*Math.pow(10,-_281); var res=MochiKit.Format.truncToFixed(_282,_281); if(_280<0){ res="-"+res; } return res; }; MochiKit.Format.percentFormat=function(_284){ return MochiKit.Format.twoDigitFloat(100*_284)+"%"; }; MochiKit.Format.EXPORT=["truncToFixed","roundToFixed","numberFormatter","formatLocale","twoDigitAverage","twoDigitFloat","percentFormat","lstrip","rstrip","strip"]; MochiKit.Format.LOCALE={en_US:{separator:",",decimal:".",percent:"%"},de_DE:{separator:".",decimal:",",percent:"%"},pt_BR:{separator:".",decimal:",",percent:"%"},fr_FR:{separator:" ",decimal:",",percent:"%"},"default":"en_US"}; MochiKit.Format.EXPORT_OK=[]; MochiKit.Format.EXPORT_TAGS={":all":MochiKit.Format.EXPORT,":common":MochiKit.Format.EXPORT}; MochiKit.Format.__new__=function(){ var base=this.NAME+"."; var k,v,o; for(k in this.LOCALE){ o=this.LOCALE[k]; if(typeof (o)=="object"){ o.repr=function(){ return this.NAME; }; o.NAME=base+"LOCALE."+k; } } for(k in this){ o=this[k]; if(typeof (o)=="function"&&typeof (o.NAME)=="undefined"){ try{ o.NAME=base+k; } catch(e){ } } } }; MochiKit.Format.__new__(); if(typeof (MochiKit.Base)!="undefined"){ MochiKit.Base._exportSymbols(this,MochiKit.Format); }else{ (function(_289,_28a){ if((typeof (JSAN)=="undefined"&&typeof (dojo)=="undefined")||(MochiKit.__export__===false)){ var all=_28a.EXPORT_TAGS[":all"]; for(var i=0;i1){ fn=MochiKit.Base.partial.apply(null,arguments); } return this.addCallbacks(fn,fn); },addCallback:function(fn){ if(arguments.length>1){ fn=MochiKit.Base.partial.apply(null,arguments); } return this.addCallbacks(fn,null); },addErrback:function(fn){ if(arguments.length>1){ fn=MochiKit.Base.partial.apply(null,arguments); } return this.addCallbacks(null,fn); },addCallbacks:function(cb,eb){ if(this.chained){ throw new Error("Chained Deferreds can not be re-used"); } this.chain.push([cb,eb]); if(this.fired>=0){ this._fire(); } return this; },_fire:function(){ var _299=this.chain; var _29a=this.fired; var res=this.results[_29a]; var self=this; var cb=null; while(_299.length>0&&this.paused===0){ var pair=_299.shift(); var f=pair[_29a]; if(f===null){ continue; } try{ res=f(res); _29a=((res instanceof Error)?1:0); if(res instanceof MochiKit.Async.Deferred){ cb=function(res){ self._resback(res); self.paused--; if((self.paused===0)&&(self.fired>=0)){ self._fire(); } }; this.paused++; } } catch(err){ _29a=1; if(!(err instanceof Error)){ err=new MochiKit.Async.GenericError(err); } res=err; } } this.fired=_29a; this.results[_29a]=res; if(cb&&this.paused){ res.addBoth(cb); res.chained=true; } }}; MochiKit.Base.update(MochiKit.Async,{evalJSONRequest:function(req){ return MochiKit.Base.evalJSON(req.responseText); },succeed:function(_2a2){ var d=new MochiKit.Async.Deferred(); d.callback.apply(d,arguments); return d; },fail:function(_2a4){ var d=new MochiKit.Async.Deferred(); d.errback.apply(d,arguments); return d; },getXMLHttpRequest:function(){ var self=arguments.callee; if(!self.XMLHttpRequest){ var _2a7=[function(){ return new XMLHttpRequest(); },function(){ return new ActiveXObject("Msxml2.XMLHTTP"); },function(){ return new ActiveXObject("Microsoft.XMLHTTP"); },function(){ return new ActiveXObject("Msxml2.XMLHTTP.4.0"); },function(){ throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest"); }]; for(var i=0;i<_2a7.length;i++){ var func=_2a7[i]; try{ self.XMLHttpRequest=func; return func(); } catch(e){ } } } return self.XMLHttpRequest(); },_xhr_onreadystatechange:function(d){ var m=MochiKit.Base; if(this.readyState==4){ try{ this.onreadystatechange=null; } catch(e){ try{ this.onreadystatechange=m.noop; } catch(e){ } } var _2ac=null; try{ _2ac=this.status; if(!_2ac&&m.isNotEmpty(this.responseText)){ _2ac=304; } } catch(e){ } if(_2ac==200||_2ac==201||_2ac==204||_2ac==304||_2ac==1223){ d.callback(this); }else{ var err=new MochiKit.Async.XMLHttpRequestError(this,"Request failed"); if(err.number){ d.errback(err); }else{ d.errback(err); } } } },_xhr_canceller:function(req){ try{ req.onreadystatechange=null; } catch(e){ try{ req.onreadystatechange=MochiKit.Base.noop; } catch(e){ } } req.abort(); },sendXMLHttpRequest:function(req,_2b0){ if(typeof (_2b0)=="undefined"||_2b0===null){ _2b0=""; } var m=MochiKit.Base; var self=MochiKit.Async; var d=new self.Deferred(m.partial(self._xhr_canceller,req)); try{ req.onreadystatechange=m.bind(self._xhr_onreadystatechange,req,d); req.send(_2b0); } catch(e){ try{ req.onreadystatechange=null; } catch(ignore){ } d.errback(e); } return d; },doXHR:function(url,opts){ var self=MochiKit.Async; return self.callLater(0,self._doXHR,url,opts); },_doXHR:function(url,opts){ var m=MochiKit.Base; opts=m.update({method:"GET",sendContent:""},opts); var self=MochiKit.Async; var req=self.getXMLHttpRequest(); if(opts.queryString){ var qs=m.queryString(opts.queryString); if(qs){ url+="?"+qs; } } if("username" in opts){ req.open(opts.method,url,true,opts.username,opts.password); }else{ req.open(opts.method,url,true); } if(req.overrideMimeType&&opts.mimeType){ req.overrideMimeType(opts.mimeType); } req.setRequestHeader("X-Requested-With","XMLHttpRequest"); if(opts.headers){ var _2bd=opts.headers; if(!m.isArrayLike(_2bd)){ _2bd=m.items(_2bd); } for(var i=0;i<_2bd.length;i++){ var _2bf=_2bd[i]; var name=_2bf[0]; var _2c1=_2bf[1]; req.setRequestHeader(name,_2c1); } } return self.sendXMLHttpRequest(req,opts.sendContent); },_buildURL:function(url){ if(arguments.length>1){ var m=MochiKit.Base; var qs=m.queryString.apply(null,m.extend(null,arguments,1)); if(qs){ return url+"?"+qs; } } return url; },doSimpleXMLHttpRequest:function(url){ var self=MochiKit.Async; url=self._buildURL.apply(self,arguments); return self.doXHR(url); },loadJSONDoc:function(url){ var self=MochiKit.Async; url=self._buildURL.apply(self,arguments); var d=self.doXHR(url,{"mimeType":"text/plain","headers":[["Accept","application/json"]]}); d=d.addCallback(self.evalJSONRequest); return d; },wait:function(_2ca,_2cb){ var d=new MochiKit.Async.Deferred(); var m=MochiKit.Base; if(typeof (_2cb)!="undefined"){ d.addCallback(function(){ return _2cb; }); } var _2ce=setTimeout(m.bind("callback",d),Math.floor(_2ca*1000)); d.canceller=function(){ try{ clearTimeout(_2ce); } catch(e){ } }; return d; },callLater:function(_2cf,func){ var m=MochiKit.Base; var _2d2=m.partial.apply(m,m.extend(null,arguments,1)); return MochiKit.Async.wait(_2cf).addCallback(function(res){ return _2d2(); }); }}); MochiKit.Async.DeferredLock=function(){ this.waiting=[]; this.locked=false; this.id=this._nextId(); }; MochiKit.Async.DeferredLock.prototype={__class__:MochiKit.Async.DeferredLock,acquire:function(){ var d=new MochiKit.Async.Deferred(); if(this.locked){ this.waiting.push(d); }else{ this.locked=true; d.callback(this); } return d; },release:function(){ if(!this.locked){ throw TypeError("Tried to release an unlocked DeferredLock"); } this.locked=false; if(this.waiting.length>0){ this.locked=true; this.waiting.shift().callback(this); } },_nextId:MochiKit.Base.counter(),repr:function(){ var _2d5; if(this.locked){ _2d5="locked, "+this.waiting.length+" waiting"; }else{ _2d5="unlocked"; } return "DeferredLock("+this.id+", "+_2d5+")"; },toString:MochiKit.Base.forwardCall("repr")}; MochiKit.Async.DeferredList=function(list,_2d7,_2d8,_2d9,_2da){ MochiKit.Async.Deferred.apply(this,[_2da]); this.list=list; var _2db=[]; this.resultList=_2db; this.finishedCount=0; this.fireOnOneCallback=_2d7; this.fireOnOneErrback=_2d8; this.consumeErrors=_2d9; var cb=MochiKit.Base.bind(this._cbDeferred,this); for(var i=0;i=0){ var opt=elem.options[elem.selectedIndex]; var v=opt.value; if(!v){ var h=opt.outerHTML; if(h&&!h.match(/^[^>]+\svalue\s*=/i)){ v=opt.text; } } _2fa.push(name); _2fb.push(v); return null; } _2fa.push(name); _2fb.push(""); return null; }else{ var opts=elem.options; if(!opts.length){ _2fa.push(name); _2fb.push(""); return null; } for(var i=0;i]+\svalue\s*=/i)){ v=opt.text; } } _2fa.push(name); _2fb.push(v); } return null; } } if(_300==="FORM"||_300==="P"||_300==="SPAN"||_300==="DIV"){ return elem.childNodes; } _2fa.push(name); _2fb.push(elem.value||""); return null; } return elem.childNodes; }); return [_2fa,_2fb]; },withDocument:function(doc,func){ var self=MochiKit.DOM; var _309=self._document; var rval; try{ self._document=doc; rval=func(); } catch(e){ self._document=_309; throw e; } self._document=_309; return rval; },registerDOMConverter:function(name,_30c,wrap,_30e){ MochiKit.DOM.domConverters.register(name,_30c,wrap,_30e); },coerceToDOM:function(node,ctx){ var m=MochiKit.Base; var im=MochiKit.Iter; var self=MochiKit.DOM; if(im){ var iter=im.iter; var _315=im.repeat; } var map=m.map; var _317=self.domConverters; var _318=arguments.callee; var _319=m.NotFound; while(true){ if(typeof (node)=="undefined"||node===null){ return null; } if(typeof (node)=="function"&&typeof (node.length)=="number"&&!(node instanceof Function)){ node=im?im.list(node):m.extend(null,node); } if(typeof (node.nodeType)!="undefined"&&node.nodeType>0){ return node; } if(typeof (node)=="number"||typeof (node)=="boolean"){ node=node.toString(); } if(typeof (node)=="string"){ return self._document.createTextNode(node); } if(typeof (node.__dom__)=="function"){ node=node.__dom__(ctx); continue; } if(typeof (node.dom)=="function"){ node=node.dom(ctx); continue; } if(typeof (node)=="function"){ node=node.apply(ctx,[ctx]); continue; } if(im){ var _31a=null; try{ _31a=iter(node); } catch(e){ } if(_31a){ return map(_318,_31a,_315(ctx)); } }else{ if(m.isArrayLike(node)){ var func=function(n){ return _318(n,ctx); }; return map(func,node); } } try{ node=_317.match(node,ctx); continue; } catch(e){ if(e!=_319){ throw e; } } return self._document.createTextNode(node.toString()); } return undefined; },isChildNode:function(node,_31e){ var self=MochiKit.DOM; if(typeof (node)=="string"){ node=self.getElement(node); } if(typeof (_31e)=="string"){ _31e=self.getElement(_31e); } if(typeof (node)=="undefined"||node===null){ return false; } while(node!=null&&node!==self._document){ if(node===_31e){ return true; } node=node.parentNode; } return false; },setNodeAttribute:function(node,attr,_322){ var o={}; o[attr]=_322; try{ return MochiKit.DOM.updateNodeAttributes(node,o); } catch(e){ } return null; },getNodeAttribute:function(node,attr){ var self=MochiKit.DOM; var _327=self.attributeArray.renames[attr]; var _328=self.attributeArray.ignoreAttr[attr]; node=self.getElement(node); try{ if(_327){ return node[_327]; } var _329=node.getAttribute(attr); if(_329!=_328){ return _329; } } catch(e){ } return null; },removeNodeAttribute:function(node,attr){ var self=MochiKit.DOM; var _32d=self.attributeArray.renames[attr]; node=self.getElement(node); try{ if(_32d){ return node[_32d]; } return node.removeAttribute(attr); } catch(e){ } return null; },updateNodeAttributes:function(node,_32f){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); } if(_32f){ var _332=MochiKit.Base.updatetree; if(self.attributeArray.compliant){ for(var k in _32f){ var v=_32f[k]; if(typeof (v)=="object"&&typeof (elem[k])=="object"){ if(k=="style"&&MochiKit.Style){ MochiKit.Style.setStyle(elem,v); }else{ _332(elem[k],v); } }else{ if(k.substring(0,2)=="on"){ if(typeof (v)=="string"){ v=new Function(v); } elem[k]=v; }else{ elem.setAttribute(k,v); } } if(typeof (elem[k])=="string"&&elem[k]!=v){ elem[k]=v; } } }else{ var _335=self.attributeArray.renames; for(var k in _32f){ v=_32f[k]; var _336=_335[k]; if(k=="style"&&typeof (v)=="string"){ elem.style.cssText=v; }else{ if(typeof (_336)=="string"){ elem[_336]=v; }else{ if(typeof (elem[k])=="object"&&typeof (v)=="object"){ if(k=="style"&&MochiKit.Style){ MochiKit.Style.setStyle(elem,v); }else{ _332(elem[k],v); } }else{ if(k.substring(0,2)=="on"){ if(typeof (v)=="string"){ v=new Function(v); } elem[k]=v; }else{ elem.setAttribute(k,v); } } } } if(typeof (elem[k])=="string"&&elem[k]!=v){ elem[k]=v; } } } } return elem; },appendChildNodes:function(node){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); } var _33a=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; var _33b=MochiKit.Base.concat; while(_33a.length){ var n=_33a.shift(); if(typeof (n)=="undefined"||n===null){ }else{ if(typeof (n.nodeType)=="number"){ elem.appendChild(n); }else{ _33a=_33b(n,_33a); } } } return elem; },insertSiblingNodesBefore:function(node){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); } var _340=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; var _341=elem.parentNode; var _342=MochiKit.Base.concat; while(_340.length){ var n=_340.shift(); if(typeof (n)=="undefined"||n===null){ }else{ if(typeof (n.nodeType)=="number"){ _341.insertBefore(n,elem); }else{ _340=_342(n,_340); } } } return _341; },insertSiblingNodesAfter:function(node){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); } var _347=[self.coerceToDOM(MochiKit.Base.extend(null,arguments,1),elem)]; if(elem.nextSibling){ return self.insertSiblingNodesBefore(elem.nextSibling,_347); }else{ return self.appendChildNodes(elem.parentNode,_347); } },replaceChildNodes:function(node){ var elem=node; var self=MochiKit.DOM; if(typeof (node)=="string"){ elem=self.getElement(node); arguments[0]=elem; } var _34b; while((_34b=elem.firstChild)){ elem.removeChild(_34b); } if(arguments.length<2){ return elem; }else{ return self.appendChildNodes.apply(this,arguments); } },createDOM:function(name,_34d){ var elem; var self=MochiKit.DOM; var m=MochiKit.Base; if(typeof (_34d)=="string"||typeof (_34d)=="number"){ var args=m.extend([name,null],arguments,1); return arguments.callee.apply(this,args); } if(typeof (name)=="string"){ var _352=self._xhtml; if(_34d&&!self.attributeArray.compliant){ var _353=""; if("name" in _34d){ _353+=" name=\""+self.escapeHTML(_34d.name)+"\""; } if(name=="input"&&"type" in _34d){ _353+=" type=\""+self.escapeHTML(_34d.type)+"\""; } if(_353){ name="<"+name+_353+">"; _352=false; } } var d=self._document; if(_352&&d===document){ elem=d.createElementNS("http://www.w3.org/1999/xhtml",name); }else{ elem=d.createElement(name); } }else{ elem=name; } if(_34d){ self.updateNodeAttributes(elem,_34d); } if(arguments.length<=2){ return elem; }else{ var args=m.extend([elem],arguments,2); return self.appendChildNodes.apply(this,args); } },createDOMFunc:function(){ var m=MochiKit.Base; return m.partial.apply(this,m.extend([MochiKit.DOM.createDOM],arguments)); },removeElement:function(elem){ var self=MochiKit.DOM; var e=self.coerceToDOM(self.getElement(elem)); e.parentNode.removeChild(e); return e; },swapDOM:function(dest,src){ var self=MochiKit.DOM; dest=self.getElement(dest); var _35c=dest.parentNode; if(src){ src=self.coerceToDOM(self.getElement(src),_35c); _35c.replaceChild(src,dest); }else{ _35c.removeChild(dest); } return src; },getElement:function(id){ var self=MochiKit.DOM; if(arguments.length==1){ return ((typeof (id)=="string")?self._document.getElementById(id):id); }else{ return MochiKit.Base.map(self.getElement,arguments); } },getElementsByTagAndClassName:function(_35f,_360,_361){ var self=MochiKit.DOM; if(typeof (_35f)=="undefined"||_35f===null){ _35f="*"; } if(typeof (_361)=="undefined"||_361===null){ _361=self._document; } _361=self.getElement(_361); if(_361==null){ return []; } var _363=(_361.getElementsByTagName(_35f)||self._document.all); if(typeof (_360)=="undefined"||_360===null){ return MochiKit.Base.extend(null,_363); } var _364=[]; for(var i=0;i<_363.length;i++){ var _366=_363[i]; var cls=_366.className; if(typeof (cls)!="string"){ cls=_366.getAttribute("class"); } if(typeof (cls)=="string"){ var _368=cls.split(" "); for(var j=0;j<_368.length;j++){ if(_368[j]==_360){ _364.push(_366); break; } } } } return _364; },_newCallStack:function(path,once){ var rval=function(){ var _36d=arguments.callee.callStack; for(var i=0;i<_36d.length;i++){ if(_36d[i].apply(this,arguments)===false){ break; } } if(once){ try{ this[path]=null; } catch(e){ } } }; rval.callStack=[]; return rval; },addToCallStack:function(_36f,path,func,once){ var self=MochiKit.DOM; var _374=_36f[path]; var _375=_374; if(!(typeof (_374)=="function"&&typeof (_374.callStack)=="object"&&_374.callStack!==null)){ _375=self._newCallStack(path,once); if(typeof (_374)=="function"){ _375.callStack.push(_374); } _36f[path]=_375; } _375.callStack.push(func); },addLoadEvent:function(func){ var self=MochiKit.DOM; self.addToCallStack(self._window,"onload",func,true); },focusOnLoad:function(_378){ var self=MochiKit.DOM; self.addLoadEvent(function(){ _378=self.getElement(_378); if(_378){ _378.focus(); } }); },setElementClass:function(_37a,_37b){ var self=MochiKit.DOM; var obj=self.getElement(_37a); if(self.attributeArray.compliant){ obj.setAttribute("class",_37b); }else{ obj.setAttribute("className",_37b); } },toggleElementClass:function(_37e){ var self=MochiKit.DOM; for(var i=1;i/g,">"); },toHTML:function(dom){ return MochiKit.DOM.emitHTML(dom).join(""); },emitHTML:function(dom,lst){ if(typeof (lst)=="undefined"||lst===null){ lst=[]; } var _3a1=[dom]; var self=MochiKit.DOM; var _3a3=self.escapeHTML; var _3a4=self.attributeArray; while(_3a1.length){ dom=_3a1.pop(); if(typeof (dom)=="string"){ lst.push(dom); }else{ if(dom.nodeType==1){ lst.push("<"+dom.tagName.toLowerCase()); var _3a5=[]; var _3a6=_3a4(dom); for(var i=0;i<_3a6.length;i++){ var a=_3a6[i]; _3a5.push([" ",a.name,"=\"",_3a3(a.value),"\""]); } _3a5.sort(); for(i=0;i<_3a5.length;i++){ var _3a9=_3a5[i]; for(var j=0;j<_3a9.length;j++){ lst.push(_3a9[j]); } } if(dom.hasChildNodes()){ lst.push(">"); _3a1.push(""); var _3ab=dom.childNodes; for(i=_3ab.length-1;i>=0;i--){ _3a1.push(_3ab[i]); } }else{ lst.push("/>"); } }else{ if(dom.nodeType==3){ lst.push(_3a3(dom.nodeValue)); } } } } return lst; },scrapeText:function(node,_3ad){ var rval=[]; (function(node){ var cn=node.childNodes; if(cn){ for(var i=0;i0){ var _3ca=m.filter; _3c9=function(node){ return _3ca(_3c9.ignoreAttrFilter,node.attributes); }; _3c9.ignoreAttr={}; var _3cc=_3c8.attributes; var _3cd=_3c9.ignoreAttr; for(var i=0;i<_3cc.length;i++){ var a=_3cc[i]; _3cd[a.name]=a.value; } _3c9.ignoreAttrFilter=function(a){ return (_3c9.ignoreAttr[a.name]!=a.value); }; _3c9.compliant=false; _3c9.renames={"class":"className","checked":"defaultChecked","usemap":"useMap","for":"htmlFor","readonly":"readOnly","colspan":"colSpan","bgcolor":"bgColor","cellspacing":"cellSpacing","cellpadding":"cellPadding"}; }else{ _3c9=function(node){ return node.attributes; }; _3c9.compliant=true; _3c9.ignoreAttr={}; _3c9.renames={}; } this.attributeArray=_3c9; var _3d2=function(_3d3,arr){ var _3d5=arr[0]; var _3d6=arr[1]; var _3d7=_3d6.split(".")[1]; var str=""; str+="if (!MochiKit."+_3d7+") { throw new Error(\""; str+="This function has been deprecated and depends on MochiKit."; str+=_3d7+".\");}"; str+="return "+_3d6+".apply(this, arguments);"; MochiKit[_3d3][_3d5]=new Function(str); }; for(var i=0;i0){ abort(repr(expr)); } },buildMatchExpression:function(){ var repr=MochiKit.Base.repr; var _3e4=this.params; var _3e5=[]; var _3e6,i; function childElements(_3e8){ return "MochiKit.Base.filter(function (node) { return node.nodeType == 1; }, "+_3e8+".childNodes)"; } if(_3e4.wildcard){ _3e5.push("true"); } if(_3e6=_3e4.id){ _3e5.push("element.id == "+repr(_3e6)); } if(_3e6=_3e4.tagName){ _3e5.push("element.tagName.toUpperCase() == "+repr(_3e6)); } if((_3e6=_3e4.classNames).length>0){ for(i=0;i<_3e6.length;i++){ _3e5.push("MochiKit.DOM.hasElementClass(element, "+repr(_3e6[i])+")"); } } if((_3e6=_3e4.pseudoClassNames).length>0){ for(i=0;i<_3e6.length;i++){ var _3e9=_3e6[i].match(/^([^(]+)(?:\((.*)\))?$/); var _3ea=_3e9[1]; var _3eb=_3e9[2]; switch(_3ea){ case "root": _3e5.push("element.nodeType == 9 || element === element.ownerDocument.documentElement"); break; case "nth-child": case "nth-last-child": case "nth-of-type": case "nth-last-of-type": _3e9=_3eb.match(/^((?:(\d+)n\+)?(\d+)|odd|even)$/); if(!_3e9){ throw "Invalid argument to pseudo element nth-child: "+_3eb; } var a,b; if(_3e9[0]=="odd"){ a=2; b=1; }else{ if(_3e9[0]=="even"){ a=2; b=0; }else{ a=_3e9[2]&&parseInt(_3e9)||null; b=parseInt(_3e9[3]); } } _3e5.push("this.nthChild(element,"+a+","+b+","+!!_3ea.match("^nth-last")+","+!!_3ea.match("of-type$")+")"); break; case "first-child": _3e5.push("this.nthChild(element, null, 1)"); break; case "last-child": _3e5.push("this.nthChild(element, null, 1, true)"); break; case "first-of-type": _3e5.push("this.nthChild(element, null, 1, false, true)"); break; case "last-of-type": _3e5.push("this.nthChild(element, null, 1, true, true)"); break; case "only-child": _3e5.push(childElements("element.parentNode")+".length == 1"); break; case "only-of-type": _3e5.push("MochiKit.Base.filter(function (node) { return node.tagName == element.tagName; }, "+childElements("element.parentNode")+").length == 1"); break; case "empty": _3e5.push("element.childNodes.length == 0"); break; case "enabled": _3e5.push("(this.isUIElement(element) && element.disabled === false)"); break; case "disabled": _3e5.push("(this.isUIElement(element) && element.disabled === true)"); break; case "checked": _3e5.push("(this.isUIElement(element) && element.checked === true)"); break; case "not": var _3ee=new MochiKit.Selector.Selector(_3eb); _3e5.push("!( "+_3ee.buildMatchExpression()+")"); break; } } } if(_3e6=_3e4.attributes){ MochiKit.Base.map(function(_3ef){ var _3f0="MochiKit.DOM.getNodeAttribute(element, "+repr(_3ef.name)+")"; var _3f1=function(_3f2){ return _3f0+".split("+repr(_3f2)+")"; }; _3e5.push(_3f0+" != null"); switch(_3ef.operator){ case "=": _3e5.push(_3f0+" == "+repr(_3ef.value)); break; case "~=": _3e5.push("MochiKit.Base.findValue("+_3f1(" ")+", "+repr(_3ef.value)+") > -1"); break; case "^=": _3e5.push(_3f0+".substring(0, "+_3ef.value.length+") == "+repr(_3ef.value)); break; case "$=": _3e5.push(_3f0+".substring("+_3f0+".length - "+_3ef.value.length+") == "+repr(_3ef.value)); break; case "*=": _3e5.push(_3f0+".match("+repr(_3ef.value)+")"); break; case "|=": _3e5.push(_3f1("-")+"[0].toUpperCase() == "+repr(_3ef.value.toUpperCase())); break; case "!=": _3e5.push(_3f0+" != "+repr(_3ef.value)); break; case "": case undefined: break; default: throw "Unknown operator "+_3ef.operator+" in selector"; } },_3e6); } return _3e5.join(" && "); },compileMatcher:function(){ var code="return (!element.tagName) ? false : "+this.buildMatchExpression()+";"; this.match=new Function("element",code); },nthChild:function(_3f4,a,b,_3f7,_3f8){ var _3f9=MochiKit.Base.filter(function(node){ return node.nodeType==1; },_3f4.parentNode.childNodes); if(_3f8){ _3f9=MochiKit.Base.filter(function(node){ return node.tagName==_3f4.tagName; },_3f9); } if(_3f7){ _3f9=MochiKit.Iter.reversed(_3f9); } if(a){ var _3fc=MochiKit.Base.findIdentical(_3f9,_3f4); return ((_3fc+1-b)/a)%1==0; }else{ return b==MochiKit.Base.findIdentical(_3f9,_3f4)+1; } },isUIElement:function(_3fd){ return MochiKit.Base.findValue(["input","button","select","option","textarea","object"],_3fd.tagName.toLowerCase())>-1; },findElements:function(_3fe,axis){ var _400; if(axis==undefined){ axis=""; } function inScope(_401,_402){ if(axis==""){ return MochiKit.DOM.isChildNode(_401,_402); }else{ if(axis==">"){ return _401.parentNode===_402; }else{ if(axis=="+"){ return _401===nextSiblingElement(_402); }else{ if(axis=="~"){ var _403=_402; while(_403=nextSiblingElement(_403)){ if(_401===_403){ return true; } } return false; }else{ throw "Invalid axis: "+axis; } } } } } if(_400=MochiKit.DOM.getElement(this.params.id)){ if(this.match(_400)){ if(!_3fe||inScope(_400,_3fe)){ return [_400]; } } } function nextSiblingElement(node){ node=node.nextSibling; while(node&&node.nodeType!=1){ node=node.nextSibling; } return node; } if(axis==""){ _3fe=(_3fe||MochiKit.DOM.currentDocument()).getElementsByTagName(this.params.tagName||"*"); }else{ if(axis==">"){ if(!_3fe){ throw "> combinator not allowed without preceeding expression"; } _3fe=MochiKit.Base.filter(function(node){ return node.nodeType==1; },_3fe.childNodes); }else{ if(axis=="+"){ if(!_3fe){ throw "+ combinator not allowed without preceeding expression"; } _3fe=nextSiblingElement(_3fe)&&[nextSiblingElement(_3fe)]; }else{ if(axis=="~"){ if(!_3fe){ throw "~ combinator not allowed without preceeding expression"; } var _406=[]; while(nextSiblingElement(_3fe)){ _3fe=nextSiblingElement(_3fe); _406.push(_3fe); } _3fe=_406; } } } } if(!_3fe){ return []; } var _407=MochiKit.Base.filter(MochiKit.Base.bind(function(_408){ return this.match(_408); },this),_3fe); return _407; },repr:function(){ return "Selector("+this.expression+")"; },toString:MochiKit.Base.forwardCall("repr")}; MochiKit.Base.update(MochiKit.Selector,{findChildElements:function(_409,_40a){ var uniq=function(arr){ var res=[]; for(var i=0;i+~]$/)){ _410=match[0]; return _412; }else{ var _414=new MochiKit.Selector.Selector(expr); var _415=MochiKit.Iter.reduce(function(_416,_417){ return MochiKit.Base.extend(_416,_414.findElements(_417||_409,_410)); },_412,[]); _410=""; return _415; } }; var _418=_40f.replace(/(^\s+|\s+$)/g,"").split(/\s+/); return uniq(MochiKit.Iter.reduce(_411,_418,[null])); },_40a)); },findDocElements:function(){ return MochiKit.Selector.findChildElements(MochiKit.DOM.currentDocument(),arguments); },__new__:function(){ var m=MochiKit.Base; this.$$=this.findDocElements; this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; m.nameFunctions(this); }}); MochiKit.Selector.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.Selector); MochiKit.Base._deps("Style",["Base","DOM"]); MochiKit.Style.NAME="MochiKit.Style"; MochiKit.Style.VERSION="1.4.2"; MochiKit.Style.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.Style.toString=function(){ return this.__repr__(); }; MochiKit.Style.EXPORT_OK=[]; MochiKit.Style.EXPORT=["setStyle","setOpacity","getStyle","getElementDimensions","elementDimensions","setElementDimensions","getElementPosition","elementPosition","setElementPosition","makePositioned","undoPositioned","makeClipping","undoClipping","setDisplayForElement","hideElement","showElement","getViewportDimensions","getViewportPosition","Dimensions","Coordinates"]; MochiKit.Style.Dimensions=function(w,h){ this.w=w; this.h=h; }; MochiKit.Style.Dimensions.prototype.__repr__=function(){ var repr=MochiKit.Base.repr; return "{w: "+repr(this.w)+", h: "+repr(this.h)+"}"; }; MochiKit.Style.Dimensions.prototype.toString=function(){ return this.__repr__(); }; MochiKit.Style.Coordinates=function(x,y){ this.x=x; this.y=y; }; MochiKit.Style.Coordinates.prototype.__repr__=function(){ var repr=MochiKit.Base.repr; return "{x: "+repr(this.x)+", y: "+repr(this.y)+"}"; }; MochiKit.Style.Coordinates.prototype.toString=function(){ return this.__repr__(); }; MochiKit.Base.update(MochiKit.Style,{getStyle:function(elem,_421){ var dom=MochiKit.DOM; var d=dom._document; elem=dom.getElement(elem); _421=MochiKit.Base.camelize(_421); if(!elem||elem==d){ return undefined; } if(_421=="opacity"&&typeof (elem.filters)!="undefined"){ var _424=(MochiKit.Style.getStyle(elem,"filter")||"").match(/alpha\(opacity=(.*)\)/); if(_424&&_424[1]){ return parseFloat(_424[1])/100; } return 1; } if(_421=="float"||_421=="cssFloat"||_421=="styleFloat"){ if(elem.style["float"]){ return elem.style["float"]; }else{ if(elem.style.cssFloat){ return elem.style.cssFloat; }else{ if(elem.style.styleFloat){ return elem.style.styleFloat; }else{ return "none"; } } } } var _425=elem.style?elem.style[_421]:null; if(!_425){ if(d.defaultView&&d.defaultView.getComputedStyle){ var css=d.defaultView.getComputedStyle(elem,null); _421=_421.replace(/([A-Z])/g,"-$1").toLowerCase(); _425=css?css.getPropertyValue(_421):null; }else{ if(elem.currentStyle){ _425=elem.currentStyle[_421]; if(/^\d/.test(_425)&&!/px$/.test(_425)&&_421!="fontWeight"){ var left=elem.style.left; var _428=elem.runtimeStyle.left; elem.runtimeStyle.left=elem.currentStyle.left; elem.style.left=_425||0; _425=elem.style.pixelLeft+"px"; elem.style.left=left; elem.runtimeStyle.left=_428; } } } } if(_421=="opacity"){ _425=parseFloat(_425); } if(/Opera/.test(navigator.userAgent)&&(MochiKit.Base.findValue(["left","top","right","bottom"],_421)!=-1)){ if(MochiKit.Style.getStyle(elem,"position")=="static"){ _425="auto"; } } return _425=="auto"?null:_425; },setStyle:function(elem,_42a){ elem=MochiKit.DOM.getElement(elem); for(var name in _42a){ switch(name){ case "opacity": MochiKit.Style.setOpacity(elem,_42a[name]); break; case "float": case "cssFloat": case "styleFloat": if(typeof (elem.style["float"])!="undefined"){ elem.style["float"]=_42a[name]; }else{ if(typeof (elem.style.cssFloat)!="undefined"){ elem.style.cssFloat=_42a[name]; }else{ elem.style.styleFloat=_42a[name]; } } break; default: elem.style[MochiKit.Base.camelize(name)]=_42a[name]; } } },setOpacity:function(elem,o){ elem=MochiKit.DOM.getElement(elem); var self=MochiKit.Style; if(o==1){ var _42f=/Gecko/.test(navigator.userAgent)&&!(/Konqueror|AppleWebKit|KHTML/.test(navigator.userAgent)); elem.style["opacity"]=_42f?0.999999:1; if(/MSIE/.test(navigator.userAgent)){ elem.style["filter"]=self.getStyle(elem,"filter").replace(/alpha\([^\)]*\)/gi,""); } }else{ if(o<0.00001){ o=0; } elem.style["opacity"]=o; if(/MSIE/.test(navigator.userAgent)){ elem.style["filter"]=self.getStyle(elem,"filter").replace(/alpha\([^\)]*\)/gi,"")+"alpha(opacity="+o*100+")"; } } },getElementPosition:function(elem,_431){ var self=MochiKit.Style; var dom=MochiKit.DOM; elem=dom.getElement(elem); if(!elem||(!(elem.x&&elem.y)&&(!elem.parentNode===null||self.getStyle(elem,"display")=="none"))){ return undefined; } var c=new self.Coordinates(0,0); var box=null; var _436=null; var d=MochiKit.DOM._document; var de=d.documentElement; var b=d.body; if(!elem.parentNode&&elem.x&&elem.y){ c.x+=elem.x||0; c.y+=elem.y||0; }else{ if(elem.getBoundingClientRect){ box=elem.getBoundingClientRect(); c.x+=box.left+(de.scrollLeft||b.scrollLeft)-(de.clientLeft||0); c.y+=box.top+(de.scrollTop||b.scrollTop)-(de.clientTop||0); }else{ if(elem.offsetParent){ c.x+=elem.offsetLeft; c.y+=elem.offsetTop; _436=elem.offsetParent; if(_436!=elem){ while(_436){ c.x+=parseInt(_436.style.borderLeftWidth)||0; c.y+=parseInt(_436.style.borderTopWidth)||0; c.x+=_436.offsetLeft; c.y+=_436.offsetTop; _436=_436.offsetParent; } } var ua=navigator.userAgent.toLowerCase(); if((typeof (opera)!="undefined"&&parseFloat(opera.version())<9)||(ua.indexOf("AppleWebKit")!=-1&&self.getStyle(elem,"position")=="absolute")){ c.x-=b.offsetLeft; c.y-=b.offsetTop; } if(elem.parentNode){ _436=elem.parentNode; }else{ _436=null; } while(_436){ var _43b=_436.tagName.toUpperCase(); if(_43b==="BODY"||_43b==="HTML"){ break; } var disp=self.getStyle(_436,"display"); if(disp.search(/^inline|table-row.*$/i)){ c.x-=_436.scrollLeft; c.y-=_436.scrollTop; } if(_436.parentNode){ _436=_436.parentNode; }else{ _436=null; } } } } } if(typeof (_431)!="undefined"){ _431=arguments.callee(_431); if(_431){ c.x-=(_431.x||0); c.y-=(_431.y||0); } } return c; },setElementPosition:function(elem,_43e,_43f){ elem=MochiKit.DOM.getElement(elem); if(typeof (_43f)=="undefined"){ _43f="px"; } var _440={}; var _441=MochiKit.Base.isUndefinedOrNull; if(!_441(_43e.x)){ _440["left"]=_43e.x+_43f; } if(!_441(_43e.y)){ _440["top"]=_43e.y+_43f; } MochiKit.DOM.updateNodeAttributes(elem,{"style":_440}); },makePositioned:function(_442){ _442=MochiKit.DOM.getElement(_442); var pos=MochiKit.Style.getStyle(_442,"position"); if(pos=="static"||!pos){ _442.style.position="relative"; if(/Opera/.test(navigator.userAgent)){ _442.style.top=0; _442.style.left=0; } } },undoPositioned:function(_444){ _444=MochiKit.DOM.getElement(_444); if(_444.style.position=="relative"){ _444.style.position=_444.style.top=_444.style.left=_444.style.bottom=_444.style.right=""; } },makeClipping:function(_445){ _445=MochiKit.DOM.getElement(_445); var s=_445.style; var _447={"overflow":s.overflow,"overflow-x":s.overflowX,"overflow-y":s.overflowY}; if((MochiKit.Style.getStyle(_445,"overflow")||"visible")!="hidden"){ _445.style.overflow="hidden"; _445.style.overflowX="hidden"; _445.style.overflowY="hidden"; } return _447; },undoClipping:function(_448,_449){ _448=MochiKit.DOM.getElement(_448); if(typeof (_449)=="string"){ _448.style.overflow=_449; }else{ if(_449!=null){ _448.style.overflow=_449["overflow"]; _448.style.overflowX=_449["overflow-x"]; _448.style.overflowY=_449["overflow-y"]; } } },getElementDimensions:function(elem,_44b){ var self=MochiKit.Style; var dom=MochiKit.DOM; if(typeof (elem.w)=="number"||typeof (elem.h)=="number"){ return new self.Dimensions(elem.w||0,elem.h||0); } elem=dom.getElement(elem); if(!elem){ return undefined; } var disp=self.getStyle(elem,"display"); if(disp=="none"||disp==""||typeof (disp)=="undefined"){ var s=elem.style; var _450=s.visibility; var _451=s.position; var _452=s.display; s.visibility="hidden"; s.position="absolute"; s.display=self._getDefaultDisplay(elem); var _453=elem.offsetWidth; var _454=elem.offsetHeight; s.display=_452; s.position=_451; s.visibility=_450; }else{ _453=elem.offsetWidth||0; _454=elem.offsetHeight||0; } if(_44b){ var _455="colSpan" in elem&&"rowSpan" in elem; var _456=(_455&&elem.parentNode&&self.getStyle(elem.parentNode,"borderCollapse")=="collapse"); if(_456){ if(/MSIE/.test(navigator.userAgent)){ var _457=elem.previousSibling?0.5:1; var _458=elem.nextSibling?0.5:1; }else{ var _457=0.5; var _458=0.5; } }else{ var _457=1; var _458=1; } _453-=Math.round((parseFloat(self.getStyle(elem,"paddingLeft"))||0)+(parseFloat(self.getStyle(elem,"paddingRight"))||0)+_457*(parseFloat(self.getStyle(elem,"borderLeftWidth"))||0)+_458*(parseFloat(self.getStyle(elem,"borderRightWidth"))||0)); if(_455){ if(/Gecko|Opera/.test(navigator.userAgent)&&!/Konqueror|AppleWebKit|KHTML/.test(navigator.userAgent)){ var _459=0; }else{ if(/MSIE/.test(navigator.userAgent)){ var _459=1; }else{ var _459=_456?0.5:1; } } }else{ var _459=1; } _454-=Math.round((parseFloat(self.getStyle(elem,"paddingTop"))||0)+(parseFloat(self.getStyle(elem,"paddingBottom"))||0)+_459*((parseFloat(self.getStyle(elem,"borderTopWidth"))||0)+(parseFloat(self.getStyle(elem,"borderBottomWidth"))||0))); } return new self.Dimensions(_453,_454); },setElementDimensions:function(elem,_45b,_45c){ elem=MochiKit.DOM.getElement(elem); if(typeof (_45c)=="undefined"){ _45c="px"; } var _45d={}; var _45e=MochiKit.Base.isUndefinedOrNull; if(!_45e(_45b.w)){ _45d["width"]=_45b.w+_45c; } if(!_45e(_45b.h)){ _45d["height"]=_45b.h+_45c; } MochiKit.DOM.updateNodeAttributes(elem,{"style":_45d}); },_getDefaultDisplay:function(elem){ var self=MochiKit.Style; var dom=MochiKit.DOM; elem=dom.getElement(elem); if(!elem){ return undefined; } var _462=elem.tagName.toUpperCase(); return self._defaultDisplay[_462]||"block"; },setDisplayForElement:function(_463,_464){ var _465=MochiKit.Base.extend(null,arguments,1); var _466=MochiKit.DOM.getElement; for(var i=0;i<_465.length;i++){ _464=_466(_465[i]); if(_464){ _464.style.display=_463; } } },getViewportDimensions:function(){ var d=new MochiKit.Style.Dimensions(); var w=MochiKit.DOM._window; var b=MochiKit.DOM._document.body; if(w.innerWidth){ d.w=w.innerWidth; d.h=w.innerHeight; }else{ if(b&&b.parentElement&&b.parentElement.clientWidth){ d.w=b.parentElement.clientWidth; d.h=b.parentElement.clientHeight; }else{ if(b&&b.clientWidth){ d.w=b.clientWidth; d.h=b.clientHeight; } } } return d; },getViewportPosition:function(){ var c=new MochiKit.Style.Coordinates(0,0); var d=MochiKit.DOM._document; var de=d.documentElement; var db=d.body; if(de&&(de.scrollTop||de.scrollLeft)){ c.x=de.scrollLeft; c.y=de.scrollTop; }else{ if(db){ c.x=db.scrollLeft; c.y=db.scrollTop; } } return c; },__new__:function(){ var m=MochiKit.Base; var _470=["A","ABBR","ACRONYM","B","BASEFONT","BDO","BIG","BR","CITE","CODE","DFN","EM","FONT","I","IMG","KBD","LABEL","Q","S","SAMP","SMALL","SPAN","STRIKE","STRONG","SUB","SUP","TEXTAREA","TT","U","VAR"]; this._defaultDisplay={"TABLE":"table","THEAD":"table-header-group","TBODY":"table-row-group","TFOOT":"table-footer-group","COLGROUP":"table-column-group","COL":"table-column","TR":"table-row","TD":"table-cell","TH":"table-cell","CAPTION":"table-caption","LI":"list-item","INPUT":"inline-block","SELECT":"inline-block"}; if(/MSIE/.test(navigator.userAgent)){ for(var k in this._defaultDisplay){ var v=this._defaultDisplay[k]; if(v.indexOf("table")==0){ this._defaultDisplay[k]="block"; } } } for(var i=0;i<_470.length;i++){ this._defaultDisplay[_470[i]]="inline"; } this.elementPosition=this.getElementPosition; this.elementDimensions=this.getElementDimensions; this.hideElement=m.partial(this.setDisplayForElement,"none"); this.showElement=m.partial(this.setDisplayForElement,"block"); this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; m.nameFunctions(this); }}); MochiKit.Style.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.Style); MochiKit.Base._deps("LoggingPane",["Base","Logging"]); MochiKit.LoggingPane.NAME="MochiKit.LoggingPane"; MochiKit.LoggingPane.VERSION="1.4.2"; MochiKit.LoggingPane.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.LoggingPane.toString=function(){ return this.__repr__(); }; MochiKit.LoggingPane.createLoggingPane=function(_474){ var m=MochiKit.LoggingPane; _474=!(!_474); if(m._loggingPane&&m._loggingPane.inline!=_474){ m._loggingPane.closePane(); m._loggingPane=null; } if(!m._loggingPane||m._loggingPane.closed){ m._loggingPane=new m.LoggingPane(_474,MochiKit.Logging.logger); } return m._loggingPane; }; MochiKit.LoggingPane.LoggingPane=function(_476,_477){ if(typeof (_477)=="undefined"||_477===null){ _477=MochiKit.Logging.logger; } this.logger=_477; var _478=MochiKit.Base.update; var _479=MochiKit.Base.updatetree; var bind=MochiKit.Base.bind; var _47b=MochiKit.Base.clone; var win=window; var uid="_MochiKit_LoggingPane"; if(typeof (MochiKit.DOM)!="undefined"){ win=MochiKit.DOM.currentWindow(); } if(!_476){ var url=win.location.href.split("?")[0].replace(/[#:\/.><&%-]/g,"_"); var name=uid+"_"+url; var nwin=win.open("",name,"dependent,resizable,height=200"); if(!nwin){ alert("Not able to open debugging window due to pop-up blocking."); return undefined; } nwin.document.write(""+"[MochiKit.LoggingPane]"+""); nwin.document.close(); nwin.document.title+=" "+win.document.title; win=nwin; } var doc=win.document; this.doc=doc; var _482=doc.getElementById(uid); var _483=!!_482; if(_482&&typeof (_482.loggingPane)!="undefined"){ _482.loggingPane.logger=this.logger; _482.loggingPane.buildAndApplyFilter(); return _482.loggingPane; } if(_483){ var _484; while((_484=_482.firstChild)){ _482.removeChild(_484); } }else{ _482=doc.createElement("div"); _482.id=uid; } _482.loggingPane=this; var _485=doc.createElement("input"); var _486=doc.createElement("input"); var _487=doc.createElement("button"); var _488=doc.createElement("button"); var _489=doc.createElement("button"); var _48a=doc.createElement("button"); var _48b=doc.createElement("div"); var _48c=doc.createElement("div"); var _48d=uid+"_Listener"; this.colorTable=_47b(this.colorTable); var _48e=[]; var _48f=null; var _490=function(msg){ var _492=msg.level; if(typeof (_492)=="number"){ _492=MochiKit.Logging.LogLevel[_492]; } return _492; }; var _493=function(msg){ return msg.info.join(" "); }; var _495=bind(function(msg){ var _497=_490(msg); var text=_493(msg); var c=this.colorTable[_497]; var p=doc.createElement("span"); p.className="MochiKit-LogMessage MochiKit-LogLevel-"+_497; p.style.cssText="margin: 0px; white-space: -moz-pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; wrap-option: emergency; color: "+c; p.appendChild(doc.createTextNode(_497+": "+text)); _48c.appendChild(p); _48c.appendChild(doc.createElement("br")); if(_48b.offsetHeight>_48b.scrollHeight){ _48b.scrollTop=0; }else{ _48b.scrollTop=_48b.scrollHeight; } },this); var _49b=function(msg){ _48e[_48e.length]=msg; _495(msg); }; var _49d=function(){ var _49e,_49f; try{ _49e=new RegExp(_485.value); _49f=new RegExp(_486.value); } catch(e){ logDebug("Error in filter regex: "+e.message); return null; } return function(msg){ return (_49e.test(_490(msg))&&_49f.test(_493(msg))); }; }; var _4a1=function(){ while(_48c.firstChild){ _48c.removeChild(_48c.firstChild); } }; var _4a2=function(){ _48e=[]; _4a1(); }; var _4a3=bind(function(){ if(this.closed){ return; } this.closed=true; if(MochiKit.LoggingPane._loggingPane==this){ MochiKit.LoggingPane._loggingPane=null; } this.logger.removeListener(_48d); try{ try{ _482.loggingPane=null; } catch(e){ logFatal("Bookmarklet was closed incorrectly."); } if(_476){ _482.parentNode.removeChild(_482); }else{ this.win.close(); } } catch(e){ } },this); var _4a4=function(){ _4a1(); for(var i=0;i<_48e.length;i++){ var msg=_48e[i]; if(_48f===null||_48f(msg)){ _495(msg); } } }; this.buildAndApplyFilter=function(){ _48f=_49d(); _4a4(); this.logger.removeListener(_48d); this.logger.addListener(_48d,_48f,_49b); }; var _4a7=bind(function(){ _48e=this.logger.getMessages(); _4a4(); },this); var _4a8=bind(function(_4a9){ _4a9=_4a9||window.event; key=_4a9.which||_4a9.keyCode; if(key==13){ this.buildAndApplyFilter(); } },this); var _4aa="display: block; z-index: 1000; left: 0px; bottom: 0px; position: fixed; width: 100%; background-color: white; font: "+this.logFont; if(_476){ _4aa+="; height: 10em; border-top: 2px solid black"; }else{ _4aa+="; height: 100%;"; } _482.style.cssText=_4aa; if(!_483){ doc.body.appendChild(_482); } _4aa={"cssText":"width: 33%; display: inline; font: "+this.logFont}; _479(_485,{"value":"FATAL|ERROR|WARNING|INFO|DEBUG","onkeypress":_4a8,"style":_4aa}); _482.appendChild(_485); _479(_486,{"value":".*","onkeypress":_4a8,"style":_4aa}); _482.appendChild(_486); _4aa="width: 8%; display:inline; font: "+this.logFont; _487.appendChild(doc.createTextNode("Filter")); _487.onclick=bind("buildAndApplyFilter",this); _487.style.cssText=_4aa; _482.appendChild(_487); _488.appendChild(doc.createTextNode("Load")); _488.onclick=_4a7; _488.style.cssText=_4aa; _482.appendChild(_488); _489.appendChild(doc.createTextNode("Clear")); _489.onclick=_4a2; _489.style.cssText=_4aa; _482.appendChild(_489); _48a.appendChild(doc.createTextNode("Close")); _48a.onclick=_4a3; _48a.style.cssText=_4aa; _482.appendChild(_48a); _48b.style.cssText="overflow: auto; width: 100%"; _48c.style.cssText="width: 100%; height: "+(_476?"8em":"100%"); _48b.appendChild(_48c); _482.appendChild(_48b); this.buildAndApplyFilter(); _4a7(); if(_476){ this.win=undefined; }else{ this.win=win; } this.inline=_476; this.closePane=_4a3; this.closed=false; return this; }; MochiKit.LoggingPane.LoggingPane.prototype={"logFont":"8pt Verdana,sans-serif","colorTable":{"ERROR":"red","FATAL":"darkred","WARNING":"blue","INFO":"black","DEBUG":"green"}}; MochiKit.LoggingPane.EXPORT_OK=["LoggingPane"]; MochiKit.LoggingPane.EXPORT=["createLoggingPane"]; MochiKit.LoggingPane.__new__=function(){ this.EXPORT_TAGS={":common":this.EXPORT,":all":MochiKit.Base.concat(this.EXPORT,this.EXPORT_OK)}; MochiKit.Base.nameFunctions(this); MochiKit.LoggingPane._loggingPane=null; }; MochiKit.LoggingPane.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.LoggingPane); MochiKit.Base._deps("Color",["Base","DOM","Style"]); MochiKit.Color.NAME="MochiKit.Color"; MochiKit.Color.VERSION="1.4.2"; MochiKit.Color.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.Color.toString=function(){ return this.__repr__(); }; MochiKit.Color.Color=function(red,_4ac,blue,_4ae){ if(typeof (_4ae)=="undefined"||_4ae===null){ _4ae=1; } this.rgb={r:red,g:_4ac,b:blue,a:_4ae}; }; MochiKit.Color.Color.prototype={__class__:MochiKit.Color.Color,colorWithAlpha:function(_4af){ var rgb=this.rgb; var m=MochiKit.Color; return m.Color.fromRGB(rgb.r,rgb.g,rgb.b,_4af); },colorWithHue:function(hue){ var hsl=this.asHSL(); hsl.h=hue; var m=MochiKit.Color; return m.Color.fromHSL(hsl); },colorWithSaturation:function(_4b5){ var hsl=this.asHSL(); hsl.s=_4b5; var m=MochiKit.Color; return m.Color.fromHSL(hsl); },colorWithLightness:function(_4b8){ var hsl=this.asHSL(); hsl.l=_4b8; var m=MochiKit.Color; return m.Color.fromHSL(hsl); },darkerColorWithLevel:function(_4bb){ var hsl=this.asHSL(); hsl.l=Math.max(hsl.l-_4bb,0); var m=MochiKit.Color; return m.Color.fromHSL(hsl); },lighterColorWithLevel:function(_4be){ var hsl=this.asHSL(); hsl.l=Math.min(hsl.l+_4be,1); var m=MochiKit.Color; return m.Color.fromHSL(hsl); },blendedColor:function(_4c1,_4c2){ if(typeof (_4c2)=="undefined"||_4c2===null){ _4c2=0.5; } var sf=1-_4c2; var s=this.rgb; var d=_4c1.rgb; var df=_4c2; return MochiKit.Color.Color.fromRGB((s.r*sf)+(d.r*df),(s.g*sf)+(d.g*df),(s.b*sf)+(d.b*df),(s.a*sf)+(d.a*df)); },compareRGB:function(_4c7){ var a=this.asRGB(); var b=_4c7.asRGB(); return MochiKit.Base.compare([a.r,a.g,a.b,a.a],[b.r,b.g,b.b,b.a]); },isLight:function(){ return this.asHSL().b>0.5; },isDark:function(){ return (!this.isLight()); },toHSLString:function(){ var c=this.asHSL(); var ccc=MochiKit.Color.clampColorComponent; var rval=this._hslString; if(!rval){ var mid=(ccc(c.h,360).toFixed(0)+","+ccc(c.s,100).toPrecision(4)+"%"+","+ccc(c.l,100).toPrecision(4)+"%"); var a=c.a; if(a>=1){ a=1; rval="hsl("+mid+")"; }else{ if(a<=0){ a=0; } rval="hsla("+mid+","+a+")"; } this._hslString=rval; } return rval; },toRGBString:function(){ var c=this.rgb; var ccc=MochiKit.Color.clampColorComponent; var rval=this._rgbString; if(!rval){ var mid=(ccc(c.r,255).toFixed(0)+","+ccc(c.g,255).toFixed(0)+","+ccc(c.b,255).toFixed(0)); if(c.a!=1){ rval="rgba("+mid+","+c.a+")"; }else{ rval="rgb("+mid+")"; } this._rgbString=rval; } return rval; },asRGB:function(){ return MochiKit.Base.clone(this.rgb); },toHexString:function(){ var m=MochiKit.Color; var c=this.rgb; var ccc=MochiKit.Color.clampColorComponent; var rval=this._hexString; if(!rval){ rval=("#"+m.toColorPart(ccc(c.r,255))+m.toColorPart(ccc(c.g,255))+m.toColorPart(ccc(c.b,255))); this._hexString=rval; } return rval; },asHSV:function(){ var hsv=this.hsv; var c=this.rgb; if(typeof (hsv)=="undefined"||hsv===null){ hsv=MochiKit.Color.rgbToHSV(this.rgb); this.hsv=hsv; } return MochiKit.Base.clone(hsv); },asHSL:function(){ var hsl=this.hsl; var c=this.rgb; if(typeof (hsl)=="undefined"||hsl===null){ hsl=MochiKit.Color.rgbToHSL(this.rgb); this.hsl=hsl; } return MochiKit.Base.clone(hsl); },toString:function(){ return this.toRGBString(); },repr:function(){ var c=this.rgb; var col=[c.r,c.g,c.b,c.a]; return this.__class__.NAME+"("+col.join(", ")+")"; }}; MochiKit.Base.update(MochiKit.Color.Color,{fromRGB:function(red,_4de,blue,_4e0){ var _4e1=MochiKit.Color.Color; if(arguments.length==1){ var rgb=red; red=rgb.r; _4de=rgb.g; blue=rgb.b; if(typeof (rgb.a)=="undefined"){ _4e0=undefined; }else{ _4e0=rgb.a; } } return new _4e1(red,_4de,blue,_4e0); },fromHSL:function(hue,_4e4,_4e5,_4e6){ var m=MochiKit.Color; return m.Color.fromRGB(m.hslToRGB.apply(m,arguments)); },fromHSV:function(hue,_4e9,_4ea,_4eb){ var m=MochiKit.Color; return m.Color.fromRGB(m.hsvToRGB.apply(m,arguments)); },fromName:function(name){ var _4ee=MochiKit.Color.Color; if(name.charAt(0)=="\""){ name=name.substr(1,name.length-2); } var _4ef=_4ee._namedColors[name.toLowerCase()]; if(typeof (_4ef)=="string"){ return _4ee.fromHexString(_4ef); }else{ if(name=="transparent"){ return _4ee.transparentColor(); } } return null; },fromString:function(_4f0){ var self=MochiKit.Color.Color; var _4f2=_4f0.substr(0,3); if(_4f2=="rgb"){ return self.fromRGBString(_4f0); }else{ if(_4f2=="hsl"){ return self.fromHSLString(_4f0); }else{ if(_4f0.charAt(0)=="#"){ return self.fromHexString(_4f0); } } } return self.fromName(_4f0); },fromHexString:function(_4f3){ if(_4f3.charAt(0)=="#"){ _4f3=_4f3.substring(1); } var _4f4=[]; var i,hex; if(_4f3.length==3){ for(i=0;i<3;i++){ hex=_4f3.substr(i,1); _4f4.push(parseInt(hex+hex,16)/255); } }else{ for(i=0;i<6;i+=2){ hex=_4f3.substr(i,2); _4f4.push(parseInt(hex,16)/255); } } var _4f7=MochiKit.Color.Color; return _4f7.fromRGB.apply(_4f7,_4f4); },_fromColorString:function(pre,_4f9,_4fa,_4fb){ if(_4fb.indexOf(pre)===0){ _4fb=_4fb.substring(_4fb.indexOf("(",3)+1,_4fb.length-1); } var _4fc=_4fb.split(/\s*,\s*/); var _4fd=[]; for(var i=0;i<_4fc.length;i++){ var c=_4fc[i]; var val; var _501=c.substring(c.length-3); if(c.charAt(c.length-1)=="%"){ val=0.01*parseFloat(c.substring(0,c.length-1)); }else{ if(_501=="deg"){ val=parseFloat(c)/360; }else{ if(_501=="rad"){ val=parseFloat(c)/(Math.PI*2); }else{ val=_4fa[i]*parseFloat(c); } } } _4fd.push(val); } return this[_4f9].apply(this,_4fd); },fromComputedStyle:function(elem,_503){ var d=MochiKit.DOM; var cls=MochiKit.Color.Color; for(elem=d.getElement(elem);elem;elem=elem.parentNode){ var _506=MochiKit.Style.getStyle.apply(d,arguments); if(!_506){ continue; } var _507=cls.fromString(_506); if(!_507){ break; } if(_507.asRGB().a>0){ return _507; } } return null; },fromBackground:function(elem){ var cls=MochiKit.Color.Color; return cls.fromComputedStyle(elem,"backgroundColor","background-color")||cls.whiteColor(); },fromText:function(elem){ var cls=MochiKit.Color.Color; return cls.fromComputedStyle(elem,"color","color")||cls.blackColor(); },namedColors:function(){ return MochiKit.Base.clone(MochiKit.Color.Color._namedColors); }}); MochiKit.Base.update(MochiKit.Color,{clampColorComponent:function(v,_50d){ v*=_50d; if(v<0){ return 0; }else{ if(v>_50d){ return _50d; }else{ return v; } } },_hslValue:function(n1,n2,hue){ if(hue>6){ hue-=6; }else{ if(hue<0){ hue+=6; } } var val; if(hue<1){ val=n1+(n2-n1)*hue; }else{ if(hue<3){ val=n2; }else{ if(hue<4){ val=n1+(n2-n1)*(4-hue); }else{ val=n1; } } } return val; },hsvToRGB:function(hue,_513,_514,_515){ if(arguments.length==1){ var hsv=hue; hue=hsv.h; _513=hsv.s; _514=hsv.v; _515=hsv.a; } var red; var _518; var blue; if(_513===0){ red=_514; _518=_514; blue=_514; }else{ var i=Math.floor(hue*6); var f=(hue*6)-i; var p=_514*(1-_513); var q=_514*(1-(_513*f)); var t=_514*(1-(_513*(1-f))); switch(i){ case 1: red=q; _518=_514; blue=p; break; case 2: red=p; _518=_514; blue=t; break; case 3: red=p; _518=q; blue=_514; break; case 4: red=t; _518=p; blue=_514; break; case 5: red=_514; _518=p; blue=q; break; case 6: case 0: red=_514; _518=t; blue=p; break; } } return {r:red,g:_518,b:blue,a:_515}; },hslToRGB:function(hue,_520,_521,_522){ if(arguments.length==1){ var hsl=hue; hue=hsl.h; _520=hsl.s; _521=hsl.l; _522=hsl.a; } var red; var _525; var blue; if(_520===0){ red=_521; _525=_521; blue=_521; }else{ var m2; if(_521<=0.5){ m2=_521*(1+_520); }else{ m2=_521+_520-(_521*_520); } var m1=(2*_521)-m2; var f=MochiKit.Color._hslValue; var h6=hue*6; red=f(m1,m2,h6+2); _525=f(m1,m2,h6); blue=f(m1,m2,h6-2); } return {r:red,g:_525,b:blue,a:_522}; },rgbToHSV:function(red,_52c,blue,_52e){ if(arguments.length==1){ var rgb=red; red=rgb.r; _52c=rgb.g; blue=rgb.b; _52e=rgb.a; } var max=Math.max(Math.max(red,_52c),blue); var min=Math.min(Math.min(red,_52c),blue); var hue; var _533; var _534=max; if(min==max){ hue=0; _533=0; }else{ var _535=(max-min); _533=_535/max; if(red==max){ hue=(_52c-blue)/_535; }else{ if(_52c==max){ hue=2+((blue-red)/_535); }else{ hue=4+((red-_52c)/_535); } } hue/=6; if(hue<0){ hue+=1; } if(hue>1){ hue-=1; } } return {h:hue,s:_533,v:_534,a:_52e}; },rgbToHSL:function(red,_537,blue,_539){ if(arguments.length==1){ var rgb=red; red=rgb.r; _537=rgb.g; blue=rgb.b; _539=rgb.a; } var max=Math.max(red,Math.max(_537,blue)); var min=Math.min(red,Math.min(_537,blue)); var hue; var _53e; var _53f=(max+min)/2; var _540=max-min; if(_540===0){ hue=0; _53e=0; }else{ if(_53f<=0.5){ _53e=_540/(max+min); }else{ _53e=_540/(2-max-min); } if(red==max){ hue=(_537-blue)/_540; }else{ if(_537==max){ hue=2+((blue-red)/_540); }else{ hue=4+((red-_537)/_540); } } hue/=6; if(hue<0){ hue+=1; } if(hue>1){ hue-=1; } } return {h:hue,s:_53e,l:_53f,a:_539}; },toColorPart:function(num){ num=Math.round(num); var _542=num.toString(16); if(num<16){ return "0"+_542; } return _542; },__new__:function(){ var m=MochiKit.Base; this.Color.fromRGBString=m.bind(this.Color._fromColorString,this.Color,"rgb","fromRGB",[1/255,1/255,1/255,1]); this.Color.fromHSLString=m.bind(this.Color._fromColorString,this.Color,"hsl","fromHSL",[1/360,0.01,0.01,1]); var _544=1/3; var _545={black:[0,0,0],blue:[0,0,1],brown:[0.6,0.4,0.2],cyan:[0,1,1],darkGray:[_544,_544,_544],gray:[0.5,0.5,0.5],green:[0,1,0],lightGray:[2*_544,2*_544,2*_544],magenta:[1,0,1],orange:[1,0.5,0],purple:[0.5,0,0.5],red:[1,0,0],transparent:[0,0,0,0],white:[1,1,1],yellow:[1,1,0]}; var _546=function(name,r,g,b,a){ var rval=this.fromRGB(r,g,b,a); this[name]=function(){ return rval; }; return rval; }; for(var k in _545){ var name=k+"Color"; var _54f=m.concat([_546,this.Color,name],_545[k]); this.Color[name]=m.bind.apply(null,_54f); } var _550=function(){ for(var i=0;i1){ var src=MochiKit.DOM.getElement(arguments[0]); var sig=arguments[1]; var obj=arguments[2]; var func=arguments[3]; for(var i=_592.length-1;i>=0;i--){ var o=_592[i]; if(o.source===src&&o.signal===sig&&o.objOrFunc===obj&&o.funcOrStr===func){ self._disconnect(o); if(!self._lock){ _592.splice(i,1); }else{ self._dirty=true; } return true; } } }else{ var idx=m.findIdentical(_592,_590); if(idx>=0){ self._disconnect(_590); if(!self._lock){ _592.splice(idx,1); }else{ self._dirty=true; } return true; } } return false; },disconnectAllTo:function(_59b,_59c){ var self=MochiKit.Signal; var _59e=self._observers; var _59f=self._disconnect; var _5a0=self._lock; var _5a1=self._dirty; if(typeof (_59c)==="undefined"){ _59c=null; } for(var i=_59e.length-1;i>=0;i--){ var _5a3=_59e[i]; if(_5a3.objOrFunc===_59b&&(_59c===null||_5a3.funcOrStr===_59c)){ _59f(_5a3); if(_5a0){ _5a1=true; }else{ _59e.splice(i,1); } } } self._dirty=_5a1; },disconnectAll:function(src,sig){ src=MochiKit.DOM.getElement(src); var m=MochiKit.Base; var _5a7=m.flattenArguments(m.extend(null,arguments,1)); var self=MochiKit.Signal; var _5a9=self._disconnect; var _5aa=self._observers; var i,_5ac; var _5ad=self._lock; var _5ae=self._dirty; if(_5a7.length===0){ for(i=_5aa.length-1;i>=0;i--){ _5ac=_5aa[i]; if(_5ac.source===src){ _5a9(_5ac); if(!_5ad){ _5aa.splice(i,1); }else{ _5ae=true; } } } }else{ var sigs={}; for(i=0;i<_5a7.length;i++){ sigs[_5a7[i]]=true; } for(i=_5aa.length-1;i>=0;i--){ _5ac=_5aa[i]; if(_5ac.source===src&&_5ac.signal in sigs){ _5a9(_5ac); if(!_5ad){ _5aa.splice(i,1); }else{ _5ae=true; } } } } self._dirty=_5ae; },signal:function(src,sig){ var self=MochiKit.Signal; var _5b3=self._observers; src=MochiKit.DOM.getElement(src); var args=MochiKit.Base.extend(null,arguments,2); var _5b5=[]; self._lock=true; for(var i=0;i<_5b3.length;i++){ var _5b7=_5b3[i]; if(_5b7.source===src&&_5b7.signal===sig&&_5b7.connected){ try{ _5b7.listener.apply(src,args); } catch(e){ _5b5.push(e); } } } self._lock=false; if(self._dirty){ self._dirty=false; for(var i=_5b3.length-1;i>=0;i--){ if(!_5b3[i].connected){ _5b3.splice(i,1); } } } if(_5b5.length==1){ throw _5b5[0]; }else{ if(_5b5.length>1){ var e=new Error("Multiple errors thrown in handling 'sig', see errors property"); e.errors=_5b5; throw e; } } }}); MochiKit.Signal.EXPORT_OK=[]; MochiKit.Signal.EXPORT=["connect","disconnect","signal","disconnectAll","disconnectAllTo"]; MochiKit.Signal.__new__=function(win){ var m=MochiKit.Base; this._document=document; this._window=win; this._lock=false; this._dirty=false; try{ this.connect(window,"onunload",this._unloadCache); } catch(e){ } this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; m.nameFunctions(this); }; MochiKit.Signal.__new__(this); if(MochiKit.__export__){ connect=MochiKit.Signal.connect; disconnect=MochiKit.Signal.disconnect; disconnectAll=MochiKit.Signal.disconnectAll; signal=MochiKit.Signal.signal; } MochiKit.Base._exportSymbols(this,MochiKit.Signal); MochiKit.Base._deps("Position",["Base","DOM","Style"]); MochiKit.Position.NAME="MochiKit.Position"; MochiKit.Position.VERSION="1.4.2"; MochiKit.Position.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.Position.toString=function(){ return this.__repr__(); }; MochiKit.Position.EXPORT_OK=[]; MochiKit.Position.EXPORT=[]; MochiKit.Base.update(MochiKit.Position,{includeScrollOffsets:false,prepare:function(){ var _5bb=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0; var _5bc=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0; this.windowOffset=new MochiKit.Style.Coordinates(_5bb,_5bc); },cumulativeOffset:function(_5bd){ var _5be=0; var _5bf=0; do{ _5be+=_5bd.offsetTop||0; _5bf+=_5bd.offsetLeft||0; _5bd=_5bd.offsetParent; }while(_5bd); return new MochiKit.Style.Coordinates(_5bf,_5be); },realOffset:function(_5c0){ var _5c1=0; var _5c2=0; do{ _5c1+=_5c0.scrollTop||0; _5c2+=_5c0.scrollLeft||0; _5c0=_5c0.parentNode; }while(_5c0); return new MochiKit.Style.Coordinates(_5c2,_5c1); },within:function(_5c3,x,y){ if(this.includeScrollOffsets){ return this.withinIncludingScrolloffsets(_5c3,x,y); } this.xcomp=x; this.ycomp=y; this.offset=this.cumulativeOffset(_5c3); if(_5c3.style.position=="fixed"){ this.offset.x+=this.windowOffset.x; this.offset.y+=this.windowOffset.y; } return (y>=this.offset.y&&y=this.offset.x&&x=this.offset.y&&this.ycomp=this.offset.x&&this.xcomp"+el.innerHTML+""; },_roundTopCorners:function(el,_5f5,_5f6){ var _5f7=this._createCorner(_5f6); for(var i=0;i=0;i--){ _5fc.appendChild(this._createCornerSlice(_5fa,_5fb,i,"bottom")); } el.style.paddingBottom=0; el.appendChild(_5fc); },_createCorner:function(_5fe){ var dom=MochiKit.DOM; return dom.DIV({style:{backgroundColor:_5fe.toString()}}); },_createCornerSlice:function(_600,_601,n,_603){ var _604=MochiKit.DOM.SPAN(); var _605=_604.style; _605.backgroundColor=_600.toString(); _605.display="block"; _605.height="1px"; _605.overflow="hidden"; _605.fontSize="1px"; var _606=this._borderColor(_600,_601); if(this.options.border&&n===0){ _605.borderTopStyle="solid"; _605.borderTopWidth="1px"; _605.borderLeftWidth="0px"; _605.borderRightWidth="0px"; _605.borderBottomWidth="0px"; _605.height="0px"; _605.borderColor=_606.toString(); }else{ if(_606){ _605.borderColor=_606.toString(); _605.borderStyle="solid"; _605.borderWidth="0px 1px"; } } if(!this.options.compact&&(n==(this.options.numSlices-1))){ _605.height="2px"; } this._setMargin(_604,n,_603); this._setBorder(_604,n,_603); return _604; },_setOptions:function(_607){ this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:true,border:false,compact:false,__unstable__wrapElement:false}; MochiKit.Base.update(this.options,_607); this.options.numSlices=(this.options.compact?2:4); },_whichSideTop:function(){ var _608=this.options.corners; if(this._hasString(_608,"all","top")){ return ""; } var _609=(_608.indexOf("tl")!=-1); var _60a=(_608.indexOf("tr")!=-1); if(_609&&_60a){ return ""; } if(_609){ return "left"; } if(_60a){ return "right"; } return ""; },_whichSideBottom:function(){ var _60b=this.options.corners; if(this._hasString(_60b,"all","bottom")){ return ""; } var _60c=(_60b.indexOf("bl")!=-1); var _60d=(_60b.indexOf("br")!=-1); if(_60c&&_60d){ return ""; } if(_60c){ return "left"; } if(_60d){ return "right"; } return ""; },_borderColor:function(_60e,_60f){ if(_60e=="transparent"){ return _60f; }else{ if(this.options.border){ return this.options.border; }else{ if(this.options.blend){ return _60f.blendedColor(_60e); } } } return ""; },_setMargin:function(el,n,_612){ var _613=this._marginSize(n)+"px"; var _614=(_612=="top"?this._whichSideTop():this._whichSideBottom()); var _615=el.style; if(_614=="left"){ _615.marginLeft=_613; _615.marginRight="0px"; }else{ if(_614=="right"){ _615.marginRight=_613; _615.marginLeft="0px"; }else{ _615.marginLeft=_613; _615.marginRight=_613; } } },_setBorder:function(el,n,_618){ var _619=this._borderSize(n)+"px"; var _61a=(_618=="top"?this._whichSideTop():this._whichSideBottom()); var _61b=el.style; if(_61a=="left"){ _61b.borderLeftWidth=_619; _61b.borderRightWidth="0px"; }else{ if(_61a=="right"){ _61b.borderRightWidth=_619; _61b.borderLeftWidth="0px"; }else{ _61b.borderLeftWidth=_619; _61b.borderRightWidth=_619; } } },_marginSize:function(n){ if(this.isTransparent){ return 0; } var o=this.options; if(o.compact&&o.blend){ var _61e=[1,0]; return _61e[n]; }else{ if(o.compact){ var _61f=[2,1]; return _61f[n]; }else{ if(o.blend){ var _620=[3,2,1,0]; return _620[n]; }else{ var _621=[5,3,2,1]; return _621[n]; } } } },_borderSize:function(n){ var o=this.options; var _624; if(o.compact&&(o.blend||this.isTransparent)){ return 1; }else{ if(o.compact){ _624=[1,0]; }else{ if(o.blend){ _624=[2,1,1,1]; }else{ if(o.border){ _624=[0,2,0,0]; }else{ if(this.isTransparent){ _624=[5,3,2,1]; }else{ return 0; } } } } } return _624[n]; },_hasString:function(str){ for(var i=1;i=(_651||i)){ _651=i; } },this.effects); _64d=_651||_64d; break; case "break": ma(function(e){ e.finalize(); },this.effects); break; } _64c.startOn+=_64d; _64c.finishOn+=_64d; if(!_64c.options.queue.limit||this.effects.length<_64c.options.queue.limit){ this.effects.push(_64c); } if(!this.interval){ this.interval=this.startLoop(MochiKit.Base.bind(this.loop,this),40); } },startLoop:function(func,_656){ return setInterval(func,_656); },remove:function(_657){ this.effects=MochiKit.Base.filter(function(e){ return e!=_657; },this.effects); if(!this.effects.length){ this.stopLoop(this.interval); this.interval=null; } },stopLoop:function(_659){ clearInterval(_659); },loop:function(){ var _65a=new Date().getTime(); MochiKit.Base.map(function(_65b){ _65b.loop(_65a); },this.effects); }}); MochiKit.Visual.Queues={instances:{},get:function(_65c){ if(typeof (_65c)!="string"){ return _65c; } if(!this.instances[_65c]){ this.instances[_65c]=new MochiKit.Visual.ScopedQueue(); } return this.instances[_65c]; }}; MochiKit.Visual.Queue=MochiKit.Visual.Queues.get("global"); MochiKit.Visual.DefaultOptions={transition:MochiKit.Visual.Transitions.sinoidal,duration:1,fps:25,sync:false,from:0,to:1,delay:0,queue:"parallel"}; MochiKit.Visual.Base=function(){ }; MochiKit.Visual.Base.prototype={__class__:MochiKit.Visual.Base,start:function(_65d){ var v=MochiKit.Visual; this.options=MochiKit.Base.setdefault(_65d,v.DefaultOptions); this.currentFrame=0; this.state="idle"; this.startOn=this.options.delay*1000; this.finishOn=this.startOn+(this.options.duration*1000); this.event("beforeStart"); if(!this.options.sync){ v.Queues.get(typeof (this.options.queue)=="string"?"global":this.options.queue.scope).add(this); } },loop:function(_65f){ if(_65f>=this.startOn){ if(_65f>=this.finishOn){ return this.finalize(); } var pos=(_65f-this.startOn)/(this.finishOn-this.startOn); var _661=Math.round(pos*this.options.fps*this.options.duration); if(_661>this.currentFrame){ this.render(pos); this.currentFrame=_661; } } },render:function(pos){ if(this.state=="idle"){ this.state="running"; this.event("beforeSetup"); this.setup(); this.event("afterSetup"); } if(this.state=="running"){ if(this.options.transition){ pos=this.options.transition(pos); } pos*=(this.options.to-this.options.from); pos+=this.options.from; this.event("beforeUpdate"); this.update(pos); this.event("afterUpdate"); } },cancel:function(){ if(!this.options.sync){ MochiKit.Visual.Queues.get(typeof (this.options.queue)=="string"?"global":this.options.queue.scope).remove(this); } this.state="finished"; },finalize:function(){ this.render(1); this.cancel(); this.event("beforeFinish"); this.finish(); this.event("afterFinish"); },setup:function(){ },finish:function(){ },update:function(_663){ },event:function(_664){ if(this.options[_664+"Internal"]){ this.options[_664+"Internal"](this); } if(this.options[_664]){ this.options[_664](this); } },repr:function(){ return "["+this.__class__.NAME+", options:"+MochiKit.Base.repr(this.options)+"]"; }}; MochiKit.Visual.Parallel=function(_665,_666){ var cls=arguments.callee; if(!(this instanceof cls)){ return new cls(_665,_666); } this.__init__(_665,_666); }; MochiKit.Visual.Parallel.prototype=new MochiKit.Visual.Base(); MochiKit.Base.update(MochiKit.Visual.Parallel.prototype,{__class__:MochiKit.Visual.Parallel,__init__:function(_668,_669){ this.effects=_668||[]; this.start(_669); },update:function(_66a){ MochiKit.Base.map(function(_66b){ _66b.render(_66a); },this.effects); },finish:function(){ MochiKit.Base.map(function(_66c){ _66c.finalize(); },this.effects); }}); MochiKit.Visual.Sequence=function(_66d,_66e){ var cls=arguments.callee; if(!(this instanceof cls)){ return new cls(_66d,_66e); } this.__init__(_66d,_66e); }; MochiKit.Visual.Sequence.prototype=new MochiKit.Visual.Base(); MochiKit.Base.update(MochiKit.Visual.Sequence.prototype,{__class__:MochiKit.Visual.Sequence,__init__:function(_670,_671){ var defs={transition:MochiKit.Visual.Transitions.linear,duration:0}; this.effects=_670||[]; MochiKit.Base.map(function(_673){ defs.duration+=_673.options.duration; },this.effects); MochiKit.Base.setdefault(_671,defs); this.start(_671); },update:function(_674){ var time=_674*this.options.duration; for(var i=0;i0){ this.fontSize=parseFloat(_694); this.fontSizeType=_695; } },this),["em","px","%"]); this.factor=(this.options.scaleTo-this.options.scaleFrom)/100; if(/^content/.test(this.options.scaleMode)){ this.dims=[this.element.scrollHeight,this.element.scrollWidth]; }else{ if(this.options.scaleMode=="box"){ this.dims=[this.element.offsetHeight,this.element.offsetWidth]; }else{ this.dims=[this.options.scaleMode.originalHeight,this.options.scaleMode.originalWidth]; } } },update:function(_696){ var _697=(this.options.scaleFrom/100)+(this.factor*_696); if(this.options.scaleContent&&this.fontSize){ MochiKit.Style.setStyle(this.element,{fontSize:this.fontSize*_697+this.fontSizeType}); } this.setDimensions(this.dims[0]*_697,this.dims[1]*_697); },finish:function(){ if(this.restoreAfterFinish){ MochiKit.Style.setStyle(this.element,this.originalStyle); } },setDimensions:function(_698,_699){ var d={}; var r=Math.round; if(/MSIE/.test(navigator.userAgent)){ r=Math.ceil; } if(this.options.scaleX){ d.width=r(_699)+"px"; } if(this.options.scaleY){ d.height=r(_698)+"px"; } if(this.options.scaleFromCenter){ var topd=(_698-this.dims[0])/2; var _69d=(_699-this.dims[1])/2; if(this.elementPositioning=="absolute"){ if(this.options.scaleY){ d.top=this.originalTop-topd+"px"; } if(this.options.scaleX){ d.left=this.originalLeft-_69d+"px"; } }else{ if(this.options.scaleY){ d.top=-topd+"px"; } if(this.options.scaleX){ d.left=-_69d+"px"; } } } MochiKit.Style.setStyle(this.element,d); }}); MochiKit.Visual.Highlight=function(_69e,_69f){ var cls=arguments.callee; if(!(this instanceof cls)){ return new cls(_69e,_69f); } this.__init__(_69e,_69f); }; MochiKit.Visual.Highlight.prototype=new MochiKit.Visual.Base(); MochiKit.Base.update(MochiKit.Visual.Highlight.prototype,{__class__:MochiKit.Visual.Highlight,__init__:function(_6a1,_6a2){ this.element=MochiKit.DOM.getElement(_6a1); _6a2=MochiKit.Base.update({startcolor:"#ffff99"},_6a2); this.start(_6a2); },setup:function(){ var b=MochiKit.Base; var s=MochiKit.Style; if(s.getStyle(this.element,"display")=="none"){ this.cancel(); return; } this.oldStyle={backgroundImage:s.getStyle(this.element,"background-image")}; s.setStyle(this.element,{backgroundImage:"none"}); if(!this.options.endcolor){ this.options.endcolor=MochiKit.Color.Color.fromBackground(this.element).toHexString(); } if(b.isUndefinedOrNull(this.options.restorecolor)){ this.options.restorecolor=s.getStyle(this.element,"background-color"); } this._base=b.map(b.bind(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16); },this),[0,1,2]); this._delta=b.map(b.bind(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i]; },this),[0,1,2]); },update:function(_6a7){ var m="#"; MochiKit.Base.map(MochiKit.Base.bind(function(i){ m+=MochiKit.Color.toColorPart(Math.round(this._base[i]+this._delta[i]*_6a7)); },this),[0,1,2]); MochiKit.Style.setStyle(this.element,{backgroundColor:m}); },finish:function(){ MochiKit.Style.setStyle(this.element,MochiKit.Base.update(this.oldStyle,{backgroundColor:this.options.restorecolor})); }}); MochiKit.Visual.ScrollTo=function(_6aa,_6ab){ var cls=arguments.callee; if(!(this instanceof cls)){ return new cls(_6aa,_6ab); } this.__init__(_6aa,_6ab); }; MochiKit.Visual.ScrollTo.prototype=new MochiKit.Visual.Base(); MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype,{__class__:MochiKit.Visual.ScrollTo,__init__:function(_6ad,_6ae){ this.element=MochiKit.DOM.getElement(_6ad); this.start(_6ae); },setup:function(){ var p=MochiKit.Position; p.prepare(); var _6b0=p.cumulativeOffset(this.element); if(this.options.offset){ _6b0.y+=this.options.offset; } var max; if(window.innerHeight){ max=window.innerHeight-window.height; }else{ if(document.documentElement&&document.documentElement.clientHeight){ max=document.documentElement.clientHeight-document.body.scrollHeight; }else{ if(document.body){ max=document.body.clientHeight-document.body.scrollHeight; } } } this.scrollStart=p.windowOffset.y; this.delta=(_6b0.y>max?max:_6b0.y)-this.scrollStart; },update:function(_6b2){ var p=MochiKit.Position; p.prepare(); window.scrollTo(p.windowOffset.x,this.scrollStart+(_6b2*this.delta)); }}); MochiKit.Visual.CSS_LENGTH=/^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; MochiKit.Visual.Morph=function(_6b4,_6b5){ var cls=arguments.callee; if(!(this instanceof cls)){ return new cls(_6b4,_6b5); } this.__init__(_6b4,_6b5); }; MochiKit.Visual.Morph.prototype=new MochiKit.Visual.Base(); MochiKit.Base.update(MochiKit.Visual.Morph.prototype,{__class__:MochiKit.Visual.Morph,__init__:function(_6b7,_6b8){ this.element=MochiKit.DOM.getElement(_6b7); this.start(_6b8); },setup:function(){ var b=MochiKit.Base; var _6ba=this.options.style; this.styleStart={}; this.styleEnd={}; this.units={}; var _6bb,unit; for(var s in _6ba){ _6bb=_6ba[s]; s=b.camelize(s); if(MochiKit.Visual.CSS_LENGTH.test(_6bb)){ var _6be=_6bb.match(/^([\+\-]?[0-9\.]+)(.*)$/); _6bb=parseFloat(_6be[1]); unit=(_6be.length==3)?_6be[2]:null; this.styleEnd[s]=_6bb; this.units[s]=unit; _6bb=MochiKit.Style.getStyle(this.element,s); _6be=_6bb.match(/^([\+\-]?[0-9\.]+)(.*)$/); _6bb=parseFloat(_6be[1]); this.styleStart[s]=_6bb; }else{ if(/[Cc]olor$/.test(s)){ var c=MochiKit.Color.Color; _6bb=c.fromString(_6bb); if(_6bb){ this.units[s]="color"; this.styleEnd[s]=_6bb.toHexString(); _6bb=MochiKit.Style.getStyle(this.element,s); this.styleStart[s]=c.fromString(_6bb).toHexString(); this.styleStart[s]=b.map(b.bind(function(i){ return parseInt(this.styleStart[s].slice(i*2+1,i*2+3),16); },this),[0,1,2]); this.styleEnd[s]=b.map(b.bind(function(i){ return parseInt(this.styleEnd[s].slice(i*2+1,i*2+3),16); },this),[0,1,2]); } }else{ this.element.style[s]=_6bb; } } } },update:function(_6c2){ var _6c3; for(var s in this.styleStart){ if(this.units[s]=="color"){ var m="#"; var _6c6=this.styleStart[s]; var end=this.styleEnd[s]; MochiKit.Base.map(MochiKit.Base.bind(function(i){ m+=MochiKit.Color.toColorPart(Math.round(_6c6[i]+(end[i]-_6c6[i])*_6c2)); },this),[0,1,2]); this.element.style[s]=m; }else{ _6c3=this.styleStart[s]+Math.round((this.styleEnd[s]-this.styleStart[s])*_6c2*1000)/1000+this.units[s]; this.element.style[s]=_6c3; } } }}); MochiKit.Visual.fade=function(_6c9,_6ca){ var s=MochiKit.Style; var _6cc=s.getStyle(_6c9,"opacity"); _6ca=MochiKit.Base.update({from:s.getStyle(_6c9,"opacity")||1,to:0,afterFinishInternal:function(_6cd){ if(_6cd.options.to!==0){ return; } s.hideElement(_6cd.element); s.setStyle(_6cd.element,{"opacity":_6cc}); }},_6ca); return new MochiKit.Visual.Opacity(_6c9,_6ca); }; MochiKit.Visual.appear=function(_6ce,_6cf){ var s=MochiKit.Style; var v=MochiKit.Visual; _6cf=MochiKit.Base.update({from:(s.getStyle(_6ce,"display")=="none"?0:s.getStyle(_6ce,"opacity")||0),to:1,afterFinishInternal:function(_6d2){ v.forceRerendering(_6d2.element); },beforeSetupInternal:function(_6d3){ s.setStyle(_6d3.element,{"opacity":_6d3.options.from}); s.showElement(_6d3.element); }},_6cf); return new v.Opacity(_6ce,_6cf); }; MochiKit.Visual.puff=function(_6d4,_6d5){ var s=MochiKit.Style; var v=MochiKit.Visual; _6d4=MochiKit.DOM.getElement(_6d4); var _6d8=MochiKit.Style.getElementDimensions(_6d4,true); var _6d9={position:s.getStyle(_6d4,"position"),top:_6d4.style.top,left:_6d4.style.left,width:_6d4.style.width,height:_6d4.style.height,opacity:s.getStyle(_6d4,"opacity")}; _6d5=MochiKit.Base.update({beforeSetupInternal:function(_6da){ MochiKit.Position.absolutize(_6da.effects[0].element); },afterFinishInternal:function(_6db){ s.hideElement(_6db.effects[0].element); s.setStyle(_6db.effects[0].element,_6d9); },scaleContent:true,scaleFromCenter:true},_6d5); return new v.Parallel([new v.Scale(_6d4,200,{sync:true,scaleFromCenter:_6d5.scaleFromCenter,scaleMode:{originalHeight:_6d8.h,originalWidth:_6d8.w},scaleContent:_6d5.scaleContent,restoreAfterFinish:true}),new v.Opacity(_6d4,{sync:true,to:0})],_6d5); }; MochiKit.Visual.blindUp=function(_6dc,_6dd){ var d=MochiKit.DOM; var s=MochiKit.Style; _6dc=d.getElement(_6dc); var _6e0=s.getElementDimensions(_6dc,true); var _6e1=s.makeClipping(_6dc); _6dd=MochiKit.Base.update({scaleContent:false,scaleX:false,scaleMode:{originalHeight:_6e0.h,originalWidth:_6e0.w},restoreAfterFinish:true,afterFinishInternal:function(_6e2){ s.hideElement(_6e2.element); s.undoClipping(_6e2.element,_6e1); }},_6dd); return new MochiKit.Visual.Scale(_6dc,0,_6dd); }; MochiKit.Visual.blindDown=function(_6e3,_6e4){ var d=MochiKit.DOM; var s=MochiKit.Style; _6e3=d.getElement(_6e3); var _6e7=s.getElementDimensions(_6e3,true); var _6e8; _6e4=MochiKit.Base.update({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:_6e7.h,originalWidth:_6e7.w},restoreAfterFinish:true,afterSetupInternal:function(_6e9){ _6e8=s.makeClipping(_6e9.element); s.setStyle(_6e9.element,{height:"0px"}); s.showElement(_6e9.element); },afterFinishInternal:function(_6ea){ s.undoClipping(_6ea.element,_6e8); }},_6e4); return new MochiKit.Visual.Scale(_6e3,100,_6e4); }; MochiKit.Visual.switchOff=function(_6eb,_6ec){ var d=MochiKit.DOM; var s=MochiKit.Style; _6eb=d.getElement(_6eb); var _6ef=s.getElementDimensions(_6eb,true); var _6f0=s.getStyle(_6eb,"opacity"); var _6f1; _6ec=MochiKit.Base.update({duration:0.7,restoreAfterFinish:true,beforeSetupInternal:function(_6f2){ s.makePositioned(_6eb); _6f1=s.makeClipping(_6eb); },afterFinishInternal:function(_6f3){ s.hideElement(_6eb); s.undoClipping(_6eb,_6f1); s.undoPositioned(_6eb); s.setStyle(_6eb,{"opacity":_6f0}); }},_6ec); var v=MochiKit.Visual; return new v.Sequence([new v.appear(_6eb,{sync:true,duration:0.57*_6ec.duration,from:0,transition:v.Transitions.flicker}),new v.Scale(_6eb,1,{sync:true,duration:0.43*_6ec.duration,scaleFromCenter:true,scaleX:false,scaleMode:{originalHeight:_6ef.h,originalWidth:_6ef.w},scaleContent:false,restoreAfterFinish:true})],_6ec); }; MochiKit.Visual.dropOut=function(_6f5,_6f6){ var d=MochiKit.DOM; var s=MochiKit.Style; _6f5=d.getElement(_6f5); var _6f9={top:s.getStyle(_6f5,"top"),left:s.getStyle(_6f5,"left"),opacity:s.getStyle(_6f5,"opacity")}; _6f6=MochiKit.Base.update({duration:0.5,distance:100,beforeSetupInternal:function(_6fa){ s.makePositioned(_6fa.effects[0].element); },afterFinishInternal:function(_6fb){ s.hideElement(_6fb.effects[0].element); s.undoPositioned(_6fb.effects[0].element); s.setStyle(_6fb.effects[0].element,_6f9); }},_6f6); var v=MochiKit.Visual; return new v.Parallel([new v.Move(_6f5,{x:0,y:_6f6.distance,sync:true}),new v.Opacity(_6f5,{sync:true,to:0})],_6f6); }; MochiKit.Visual.shake=function(_6fd,_6fe){ var d=MochiKit.DOM; var v=MochiKit.Visual; var s=MochiKit.Style; _6fd=d.getElement(_6fd); var _702={top:s.getStyle(_6fd,"top"),left:s.getStyle(_6fd,"left")}; _6fe=MochiKit.Base.update({duration:0.5,afterFinishInternal:function(_703){ s.undoPositioned(_6fd); s.setStyle(_6fd,_702); }},_6fe); return new v.Sequence([new v.Move(_6fd,{sync:true,duration:0.1*_6fe.duration,x:20,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:-40,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:40,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:-40,y:0}),new v.Move(_6fd,{sync:true,duration:0.2*_6fe.duration,x:40,y:0}),new v.Move(_6fd,{sync:true,duration:0.1*_6fe.duration,x:-20,y:0})],_6fe); }; MochiKit.Visual.slideDown=function(_704,_705){ var d=MochiKit.DOM; var b=MochiKit.Base; var s=MochiKit.Style; _704=d.getElement(_704); if(!_704.firstChild){ throw new Error("MochiKit.Visual.slideDown must be used on a element with a child"); } d.removeEmptyTextNodes(_704); var _709=s.getStyle(_704.firstChild,"bottom")||0; var _70a=s.getElementDimensions(_704,true); var _70b; _705=b.update({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:_70a.h,originalWidth:_70a.w},restoreAfterFinish:true,afterSetupInternal:function(_70c){ s.makePositioned(_70c.element); s.makePositioned(_70c.element.firstChild); if(/Opera/.test(navigator.userAgent)){ s.setStyle(_70c.element,{top:""}); } _70b=s.makeClipping(_70c.element); s.setStyle(_70c.element,{height:"0px"}); s.showElement(_70c.element); },afterUpdateInternal:function(_70d){ var _70e=s.getElementDimensions(_70d.element,true); s.setStyle(_70d.element.firstChild,{bottom:(_70d.dims[0]-_70e.h)+"px"}); },afterFinishInternal:function(_70f){ s.undoClipping(_70f.element,_70b); if(/MSIE/.test(navigator.userAgent)){ s.undoPositioned(_70f.element); s.undoPositioned(_70f.element.firstChild); }else{ s.undoPositioned(_70f.element.firstChild); s.undoPositioned(_70f.element); } s.setStyle(_70f.element.firstChild,{bottom:_709}); }},_705); return new MochiKit.Visual.Scale(_704,100,_705); }; MochiKit.Visual.slideUp=function(_710,_711){ var d=MochiKit.DOM; var b=MochiKit.Base; var s=MochiKit.Style; _710=d.getElement(_710); if(!_710.firstChild){ throw new Error("MochiKit.Visual.slideUp must be used on a element with a child"); } d.removeEmptyTextNodes(_710); var _715=s.getStyle(_710.firstChild,"bottom"); var _716=s.getElementDimensions(_710,true); var _717; _711=b.update({scaleContent:false,scaleX:false,scaleMode:{originalHeight:_716.h,originalWidth:_716.w},scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(_718){ s.makePositioned(_718.element); s.makePositioned(_718.element.firstChild); if(/Opera/.test(navigator.userAgent)){ s.setStyle(_718.element,{top:""}); } _717=s.makeClipping(_718.element); s.showElement(_718.element); },afterUpdateInternal:function(_719){ var _71a=s.getElementDimensions(_719.element,true); s.setStyle(_719.element.firstChild,{bottom:(_719.dims[0]-_71a.h)+"px"}); },afterFinishInternal:function(_71b){ s.hideElement(_71b.element); s.undoClipping(_71b.element,_717); s.undoPositioned(_71b.element.firstChild); s.undoPositioned(_71b.element); s.setStyle(_71b.element.firstChild,{bottom:_715}); }},_711); return new MochiKit.Visual.Scale(_710,0,_711); }; MochiKit.Visual.squish=function(_71c,_71d){ var d=MochiKit.DOM; var b=MochiKit.Base; var s=MochiKit.Style; var _721=s.getElementDimensions(_71c,true); var _722; _71d=b.update({restoreAfterFinish:true,scaleMode:{originalHeight:_721.w,originalWidth:_721.h},beforeSetupInternal:function(_723){ _722=s.makeClipping(_723.element); },afterFinishInternal:function(_724){ s.hideElement(_724.element); s.undoClipping(_724.element,_722); }},_71d); return new MochiKit.Visual.Scale(_71c,/Opera/.test(navigator.userAgent)?1:0,_71d); }; MochiKit.Visual.grow=function(_725,_726){ var d=MochiKit.DOM; var v=MochiKit.Visual; var s=MochiKit.Style; _725=d.getElement(_725); _726=MochiKit.Base.update({direction:"center",moveTransition:v.Transitions.sinoidal,scaleTransition:v.Transitions.sinoidal,opacityTransition:v.Transitions.full,scaleContent:true,scaleFromCenter:false},_726); var _72a={top:_725.style.top,left:_725.style.left,height:_725.style.height,width:_725.style.width,opacity:s.getStyle(_725,"opacity")}; var dims=s.getElementDimensions(_725,true); var _72c,_72d; var _72e,_72f; switch(_726.direction){ case "top-left": _72c=_72d=_72e=_72f=0; break; case "top-right": _72c=dims.w; _72d=_72f=0; _72e=-dims.w; break; case "bottom-left": _72c=_72e=0; _72d=dims.h; _72f=-dims.h; break; case "bottom-right": _72c=dims.w; _72d=dims.h; _72e=-dims.w; _72f=-dims.h; break; case "center": _72c=dims.w/2; _72d=dims.h/2; _72e=-dims.w/2; _72f=-dims.h/2; break; } var _730=MochiKit.Base.update({beforeSetupInternal:function(_731){ s.setStyle(_731.effects[0].element,{height:"0px"}); s.showElement(_731.effects[0].element); },afterFinishInternal:function(_732){ s.undoClipping(_732.effects[0].element); s.undoPositioned(_732.effects[0].element); s.setStyle(_732.effects[0].element,_72a); }},_726); return new v.Move(_725,{x:_72c,y:_72d,duration:0.01,beforeSetupInternal:function(_733){ s.hideElement(_733.element); s.makeClipping(_733.element); s.makePositioned(_733.element); },afterFinishInternal:function(_734){ new v.Parallel([new v.Opacity(_734.element,{sync:true,to:1,from:0,transition:_726.opacityTransition}),new v.Move(_734.element,{x:_72e,y:_72f,sync:true,transition:_726.moveTransition}),new v.Scale(_734.element,100,{scaleMode:{originalHeight:dims.h,originalWidth:dims.w},sync:true,scaleFrom:/Opera/.test(navigator.userAgent)?1:0,transition:_726.scaleTransition,scaleContent:_726.scaleContent,scaleFromCenter:_726.scaleFromCenter,restoreAfterFinish:true})],_730); }}); }; MochiKit.Visual.shrink=function(_735,_736){ var d=MochiKit.DOM; var v=MochiKit.Visual; var s=MochiKit.Style; _735=d.getElement(_735); _736=MochiKit.Base.update({direction:"center",moveTransition:v.Transitions.sinoidal,scaleTransition:v.Transitions.sinoidal,opacityTransition:v.Transitions.none,scaleContent:true,scaleFromCenter:false},_736); var _73a={top:_735.style.top,left:_735.style.left,height:_735.style.height,width:_735.style.width,opacity:s.getStyle(_735,"opacity")}; var dims=s.getElementDimensions(_735,true); var _73c,_73d; switch(_736.direction){ case "top-left": _73c=_73d=0; break; case "top-right": _73c=dims.w; _73d=0; break; case "bottom-left": _73c=0; _73d=dims.h; break; case "bottom-right": _73c=dims.w; _73d=dims.h; break; case "center": _73c=dims.w/2; _73d=dims.h/2; break; } var _73e; var _73f=MochiKit.Base.update({beforeStartInternal:function(_740){ s.makePositioned(_740.effects[0].element); _73e=s.makeClipping(_740.effects[0].element); },afterFinishInternal:function(_741){ s.hideElement(_741.effects[0].element); s.undoClipping(_741.effects[0].element,_73e); s.undoPositioned(_741.effects[0].element); s.setStyle(_741.effects[0].element,_73a); }},_736); return new v.Parallel([new v.Opacity(_735,{sync:true,to:0,from:1,transition:_736.opacityTransition}),new v.Scale(_735,/Opera/.test(navigator.userAgent)?1:0,{scaleMode:{originalHeight:dims.h,originalWidth:dims.w},sync:true,transition:_736.scaleTransition,scaleContent:_736.scaleContent,scaleFromCenter:_736.scaleFromCenter,restoreAfterFinish:true}),new v.Move(_735,{x:_73c,y:_73d,sync:true,transition:_736.moveTransition})],_73f); }; MochiKit.Visual.pulsate=function(_742,_743){ var d=MochiKit.DOM; var v=MochiKit.Visual; var b=MochiKit.Base; var _747=MochiKit.Style.getStyle(_742,"opacity"); _743=b.update({duration:3,from:0,afterFinishInternal:function(_748){ MochiKit.Style.setStyle(_748.element,{"opacity":_747}); }},_743); var _749=_743.transition||v.Transitions.sinoidal; _743.transition=function(pos){ return _749(1-v.Transitions.pulse(pos,_743.pulses)); }; return new v.Opacity(_742,_743); }; MochiKit.Visual.fold=function(_74b,_74c){ var d=MochiKit.DOM; var v=MochiKit.Visual; var s=MochiKit.Style; _74b=d.getElement(_74b); var _750=s.getElementDimensions(_74b,true); var _751={top:_74b.style.top,left:_74b.style.left,width:_74b.style.width,height:_74b.style.height}; var _752=s.makeClipping(_74b); _74c=MochiKit.Base.update({scaleContent:false,scaleX:false,scaleMode:{originalHeight:_750.h,originalWidth:_750.w},afterFinishInternal:function(_753){ new v.Scale(_74b,1,{scaleContent:false,scaleY:false,scaleMode:{originalHeight:_750.h,originalWidth:_750.w},afterFinishInternal:function(_754){ s.hideElement(_754.element); s.undoClipping(_754.element,_752); s.setStyle(_754.element,_751); }}); }},_74c); return new v.Scale(_74b,5,_74c); }; MochiKit.Visual.Color=MochiKit.Color.Color; MochiKit.Visual.getElementsComputedStyle=MochiKit.DOM.computedStyle; MochiKit.Visual.__new__=function(){ var m=MochiKit.Base; m.nameFunctions(this); this.EXPORT_TAGS={":common":this.EXPORT,":all":m.concat(this.EXPORT,this.EXPORT_OK)}; }; MochiKit.Visual.EXPORT=["roundElement","roundClass","tagifyText","multiple","toggle","Parallel","Sequence","Opacity","Move","Scale","Highlight","ScrollTo","Morph","fade","appear","puff","blindUp","blindDown","switchOff","dropOut","shake","slideDown","slideUp","squish","grow","shrink","pulsate","fold"]; MochiKit.Visual.EXPORT_OK=["Base","PAIRS"]; MochiKit.Visual.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.Visual); MochiKit.Base._deps("DragAndDrop",["Base","Iter","DOM","Signal","Visual","Position"]); MochiKit.DragAndDrop.NAME="MochiKit.DragAndDrop"; MochiKit.DragAndDrop.VERSION="1.4.2"; MochiKit.DragAndDrop.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.DragAndDrop.toString=function(){ return this.__repr__(); }; MochiKit.DragAndDrop.EXPORT=["Droppable","Draggable"]; MochiKit.DragAndDrop.EXPORT_OK=["Droppables","Draggables"]; MochiKit.DragAndDrop.Droppables={drops:[],remove:function(_756){ this.drops=MochiKit.Base.filter(function(d){ return d.element!=MochiKit.DOM.getElement(_756); },this.drops); },register:function(drop){ this.drops.push(drop); },unregister:function(drop){ this.drops=MochiKit.Base.filter(function(d){ return d!=drop; },this.drops); },prepare:function(_75b){ MochiKit.Base.map(function(drop){ if(drop.isAccepted(_75b)){ if(drop.options.activeclass){ MochiKit.DOM.addElementClass(drop.element,drop.options.activeclass); } drop.options.onactive(drop.element,_75b); } },this.drops); },findDeepestChild:function(_75d){ deepest=_75d[0]; for(i=1;i<_75d.length;++i){ if(MochiKit.DOM.isChildNode(_75d[i].element,deepest.element)){ deepest=_75d[i]; } } return deepest; },show:function(_75e,_75f){ if(!this.drops.length){ return; } var _760=[]; if(this.last_active){ this.last_active.deactivate(); } MochiKit.Iter.forEach(this.drops,function(drop){ if(drop.isAffected(_75e,_75f)){ _760.push(drop); } }); if(_760.length>0){ drop=this.findDeepestChild(_760); MochiKit.Position.within(drop.element,_75e.page.x,_75e.page.y); drop.options.onhover(_75f,drop.element,MochiKit.Position.overlap(drop.options.overlap,drop.element)); drop.activate(); } },fire:function(_762,_763){ if(!this.last_active){ return; } MochiKit.Position.prepare(); if(this.last_active.isAffected(_762.mouse(),_763)){ this.last_active.options.ondrop(_763,this.last_active.element,_762); } },reset:function(_764){ MochiKit.Base.map(function(drop){ if(drop.options.activeclass){ MochiKit.DOM.removeElementClass(drop.element,drop.options.activeclass); } drop.options.ondesactive(drop.element,_764); },this.drops); if(this.last_active){ this.last_active.deactivate(); } }}; MochiKit.DragAndDrop.Droppable=function(_766,_767){ var cls=arguments.callee; if(!(this instanceof cls)){ return new cls(_766,_767); } this.__init__(_766,_767); }; MochiKit.DragAndDrop.Droppable.prototype={__class__:MochiKit.DragAndDrop.Droppable,__init__:function(_769,_76a){ var d=MochiKit.DOM; var b=MochiKit.Base; this.element=d.getElement(_769); this.options=b.update({greedy:true,hoverclass:null,activeclass:null,hoverfunc:b.noop,accept:null,onactive:b.noop,ondesactive:b.noop,onhover:b.noop,ondrop:b.noop,containment:[],tree:false},_76a); this.options._containers=[]; b.map(MochiKit.Base.bind(function(c){ this.options._containers.push(d.getElement(c)); },this),this.options.containment); MochiKit.Style.makePositioned(this.element); MochiKit.DragAndDrop.Droppables.register(this); },isContained:function(_76e){ if(this.options._containers.length){ var _76f; if(this.options.tree){ _76f=_76e.treeNode; }else{ _76f=_76e.parentNode; } return MochiKit.Iter.some(this.options._containers,function(c){ return _76f==c; }); }else{ return true; } },isAccepted:function(_771){ return ((!this.options.accept)||MochiKit.Iter.some(this.options.accept,function(c){ return MochiKit.DOM.hasElementClass(_771,c); })); },isAffected:function(_773,_774){ return ((this.element!=_774)&&this.isContained(_774)&&this.isAccepted(_774)&&MochiKit.Position.within(this.element,_773.page.x,_773.page.y)); },deactivate:function(){ if(this.options.hoverclass){ MochiKit.DOM.removeElementClass(this.element,this.options.hoverclass); } this.options.hoverfunc(this.element,false); MochiKit.DragAndDrop.Droppables.last_active=null; },activate:function(){ if(this.options.hoverclass){ MochiKit.DOM.addElementClass(this.element,this.options.hoverclass); } this.options.hoverfunc(this.element,true); MochiKit.DragAndDrop.Droppables.last_active=this; },destroy:function(){ MochiKit.DragAndDrop.Droppables.unregister(this); },repr:function(){ return "["+this.__class__.NAME+", options:"+MochiKit.Base.repr(this.options)+"]"; }}; MochiKit.DragAndDrop.Draggables={drags:[],register:function(_775){ if(this.drags.length===0){ var conn=MochiKit.Signal.connect; this.eventMouseUp=conn(document,"onmouseup",this,this.endDrag); this.eventMouseMove=conn(document,"onmousemove",this,this.updateDrag); this.eventKeypress=conn(document,"onkeypress",this,this.keyPress); } this.drags.push(_775); },unregister:function(_777){ this.drags=MochiKit.Base.filter(function(d){ return d!=_777; },this.drags); if(this.drags.length===0){ var disc=MochiKit.Signal.disconnect; disc(this.eventMouseUp); disc(this.eventMouseMove); disc(this.eventKeypress); } },activate:function(_77a){ window.focus(); this.activeDraggable=_77a; },deactivate:function(){ this.activeDraggable=null; },updateDrag:function(_77b){ if(!this.activeDraggable){ return; } var _77c=_77b.mouse(); if(this._lastPointer&&(MochiKit.Base.repr(this._lastPointer.page)==MochiKit.Base.repr(_77c.page))){ return; } this._lastPointer=_77c; this.activeDraggable.updateDrag(_77b,_77c); },endDrag:function(_77d){ if(!this.activeDraggable){ return; } this._lastPointer=null; this.activeDraggable.endDrag(_77d); this.activeDraggable=null; },keyPress:function(_77e){ if(this.activeDraggable){ this.activeDraggable.keyPress(_77e); } },notify:function(_77f,_780,_781){ MochiKit.Signal.signal(this,_77f,_780,_781); }}; MochiKit.DragAndDrop.Draggable=function(_782,_783){ var cls=arguments.callee; if(!(this instanceof cls)){ return new cls(_782,_783); } this.__init__(_782,_783); }; MochiKit.DragAndDrop.Draggable.prototype={__class__:MochiKit.DragAndDrop.Draggable,__init__:function(_785,_786){ var v=MochiKit.Visual; var b=MochiKit.Base; _786=b.update({handle:false,starteffect:function(_789){ this._savedOpacity=MochiKit.Style.getStyle(_789,"opacity")||1; new v.Opacity(_789,{duration:0.2,from:this._savedOpacity,to:0.7}); },reverteffect:function(_78a,_78b,_78c){ var dur=Math.sqrt(Math.abs(_78b^2)+Math.abs(_78c^2))*0.02; return new v.Move(_78a,{x:-_78c,y:-_78b,duration:dur}); },endeffect:function(_78e){ new v.Opacity(_78e,{duration:0.2,from:0.7,to:this._savedOpacity}); },onchange:b.noop,zindex:1000,revert:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,snap:false},_786); var d=MochiKit.DOM; this.element=d.getElement(_785); if(_786.handle&&(typeof (_786.handle)=="string")){ this.handle=d.getFirstElementByTagAndClassName(null,_786.handle,this.element); } if(!this.handle){ this.handle=d.getElement(_786.handle); } if(!this.handle){ this.handle=this.element; } if(_786.scroll&&!_786.scroll.scrollTo&&!_786.scroll.outerHTML){ _786.scroll=d.getElement(_786.scroll); this._isScrollChild=MochiKit.DOM.isChildNode(this.element,_786.scroll); } MochiKit.Style.makePositioned(this.element); this.delta=this.currentDelta(); this.options=_786; this.dragging=false; this.eventMouseDown=MochiKit.Signal.connect(this.handle,"onmousedown",this,this.initDrag); MochiKit.DragAndDrop.Draggables.register(this); },destroy:function(){ MochiKit.Signal.disconnect(this.eventMouseDown); MochiKit.DragAndDrop.Draggables.unregister(this); },currentDelta:function(){ var s=MochiKit.Style.getStyle; return [parseInt(s(this.element,"left")||"0"),parseInt(s(this.element,"top")||"0")]; },initDrag:function(_791){ if(!_791.mouse().button.left){ return; } var src=_791.target(); var _793=(src.tagName||"").toUpperCase(); if(_793==="INPUT"||_793==="SELECT"||_793==="OPTION"||_793==="BUTTON"||_793==="TEXTAREA"){ return; } if(this._revert){ this._revert.cancel(); this._revert=null; } var _794=_791.mouse(); var pos=MochiKit.Position.cumulativeOffset(this.element); this.offset=[_794.page.x-pos.x,_794.page.y-pos.y]; MochiKit.DragAndDrop.Draggables.activate(this); _791.stop(); },startDrag:function(_796){ this.dragging=true; if(this.options.selectclass){ MochiKit.DOM.addElementClass(this.element,this.options.selectclass); } if(this.options.zindex){ this.originalZ=parseInt(MochiKit.Style.getStyle(this.element,"z-index")||"0"); this.element.style.zIndex=this.options.zindex; } if(this.options.ghosting){ this._clone=this.element.cloneNode(true); this.ghostPosition=MochiKit.Position.absolutize(this.element); this.element.parentNode.insertBefore(this._clone,this.element); } if(this.options.scroll){ if(this.options.scroll==window){ var _797=this._getWindowScroll(this.options.scroll); this.originalScrollLeft=_797.left; this.originalScrollTop=_797.top; }else{ this.originalScrollLeft=this.options.scroll.scrollLeft; this.originalScrollTop=this.options.scroll.scrollTop; } } MochiKit.DragAndDrop.Droppables.prepare(this.element); MochiKit.DragAndDrop.Draggables.notify("start",this,_796); if(this.options.starteffect){ this.options.starteffect(this.element); } },updateDrag:function(_798,_799){ if(!this.dragging){ this.startDrag(_798); } MochiKit.Position.prepare(); MochiKit.DragAndDrop.Droppables.show(_799,this.element); MochiKit.DragAndDrop.Draggables.notify("drag",this,_798); this.draw(_799); this.options.onchange(this); if(this.options.scroll){ this.stopScrolling(); var p,q; if(this.options.scroll==window){ var s=this._getWindowScroll(this.options.scroll); p=new MochiKit.Style.Coordinates(s.left,s.top); q=new MochiKit.Style.Coordinates(s.left+s.width,s.top+s.height); }else{ p=MochiKit.Position.page(this.options.scroll); p.x+=this.options.scroll.scrollLeft; p.y+=this.options.scroll.scrollTop; p.x+=(window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0); p.y+=(window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0); q=new MochiKit.Style.Coordinates(p.x+this.options.scroll.offsetWidth,p.y+this.options.scroll.offsetHeight); } var _79d=[0,0]; if(_799.page.x>(q.x-this.options.scrollSensitivity)){ _79d[0]=_799.page.x-(q.x-this.options.scrollSensitivity); }else{ if(_799.page.x<(p.x+this.options.scrollSensitivity)){ _79d[0]=_799.page.x-(p.x+this.options.scrollSensitivity); } } if(_799.page.y>(q.y-this.options.scrollSensitivity)){ _79d[1]=_799.page.y-(q.y-this.options.scrollSensitivity); }else{ if(_799.page.y<(p.y+this.options.scrollSensitivity)){ _79d[1]=_799.page.y-(p.y+this.options.scrollSensitivity); } } this.startScrolling(_79d); } if(/AppleWebKit/.test(navigator.appVersion)){ window.scrollBy(0,0); } _798.stop(); },finishDrag:function(_79e,_79f){ var dr=MochiKit.DragAndDrop; this.dragging=false; if(this.options.selectclass){ MochiKit.DOM.removeElementClass(this.element,this.options.selectclass); } if(this.options.ghosting){ MochiKit.Position.relativize(this.element,this.ghostPosition); MochiKit.DOM.removeElement(this._clone); this._clone=null; } if(_79f){ dr.Droppables.fire(_79e,this.element); } dr.Draggables.notify("end",this,_79e); var _7a1=this.options.revert; if(_7a1&&typeof (_7a1)=="function"){ _7a1=_7a1(this.element); } var d=this.currentDelta(); if(_7a1&&this.options.reverteffect){ this._revert=this.options.reverteffect(this.element,d[1]-this.delta[1],d[0]-this.delta[0]); }else{ this.delta=d; } if(this.options.zindex){ this.element.style.zIndex=this.originalZ; } if(this.options.endeffect){ this.options.endeffect(this.element); } dr.Draggables.deactivate(); dr.Droppables.reset(this.element); },keyPress:function(_7a3){ if(_7a3.key().string!="KEY_ESCAPE"){ return; } this.finishDrag(_7a3,false); _7a3.stop(); },endDrag:function(_7a4){ if(!this.dragging){ return; } this.stopScrolling(); this.finishDrag(_7a4,true); _7a4.stop(); },draw:function(_7a5){ var pos=MochiKit.Position.cumulativeOffset(this.element); var d=this.currentDelta(); pos.x-=d[0]; pos.y-=d[1]; if(this.options.scroll&&(this.options.scroll!=window&&this._isScrollChild)){ pos.x-=this.options.scroll.scrollLeft-this.originalScrollLeft; pos.y-=this.options.scroll.scrollTop-this.originalScrollTop; } var p=[_7a5.page.x-pos.x-this.offset[0],_7a5.page.y-pos.y-this.offset[1]]; if(this.options.snap){ if(typeof (this.options.snap)=="function"){ p=this.options.snap(p[0],p[1]); }else{ if(this.options.snap instanceof Array){ var i=-1; p=MochiKit.Base.map(MochiKit.Base.bind(function(v){ i+=1; return Math.round(v/this.options.snap[i])*this.options.snap[i]; },this),p); }else{ p=MochiKit.Base.map(MochiKit.Base.bind(function(v){ return Math.round(v/this.options.snap)*this.options.snap; },this),p); } } } var _7ac=this.element.style; if((!this.options.constraint)||(this.options.constraint=="horizontal")){ _7ac.left=p[0]+"px"; } if((!this.options.constraint)||(this.options.constraint=="vertical")){ _7ac.top=p[1]+"px"; } if(_7ac.visibility=="hidden"){ _7ac.visibility=""; } },stopScrolling:function(){ if(this.scrollInterval){ clearInterval(this.scrollInterval); this.scrollInterval=null; MochiKit.DragAndDrop.Draggables._lastScrollPointer=null; } },startScrolling:function(_7ad){ if(!_7ad[0]&&!_7ad[1]){ return; } this.scrollSpeed=[_7ad[0]*this.options.scrollSpeed,_7ad[1]*this.options.scrollSpeed]; this.lastScrolled=new Date(); this.scrollInterval=setInterval(MochiKit.Base.bind(this.scroll,this),10); },scroll:function(){ var _7ae=new Date(); var _7af=_7ae-this.lastScrolled; this.lastScrolled=_7ae; if(this.options.scroll==window){ var s=this._getWindowScroll(this.options.scroll); if(this.scrollSpeed[0]||this.scrollSpeed[1]){ var dm=_7af/1000; this.options.scroll.scrollTo(s.left+dm*this.scrollSpeed[0],s.top+dm*this.scrollSpeed[1]); } }else{ this.options.scroll.scrollLeft+=this.scrollSpeed[0]*_7af/1000; this.options.scroll.scrollTop+=this.scrollSpeed[1]*_7af/1000; } var d=MochiKit.DragAndDrop; MochiKit.Position.prepare(); d.Droppables.show(d.Draggables._lastPointer,this.element); d.Draggables.notify("drag",this); if(this._isScrollChild){ d.Draggables._lastScrollPointer=d.Draggables._lastScrollPointer||d.Draggables._lastPointer; d.Draggables._lastScrollPointer.x+=this.scrollSpeed[0]*_7af/1000; d.Draggables._lastScrollPointer.y+=this.scrollSpeed[1]*_7af/1000; if(d.Draggables._lastScrollPointer.x<0){ d.Draggables._lastScrollPointer.x=0; } if(d.Draggables._lastScrollPointer.y<0){ d.Draggables._lastScrollPointer.y=0; } this.draw(d.Draggables._lastScrollPointer); } this.options.onchange(this); },_getWindowScroll:function(win){ var vp,w,h; MochiKit.DOM.withWindow(win,function(){ vp=MochiKit.Style.getViewportPosition(win.document); }); if(win.innerWidth){ w=win.innerWidth; h=win.innerHeight; }else{ if(win.document.documentElement&&win.document.documentElement.clientWidth){ w=win.document.documentElement.clientWidth; h=win.document.documentElement.clientHeight; }else{ w=win.document.body.offsetWidth; h=win.document.body.offsetHeight; } } return {top:vp.y,left:vp.x,width:w,height:h}; },repr:function(){ return "["+this.__class__.NAME+", options:"+MochiKit.Base.repr(this.options)+"]"; }}; MochiKit.DragAndDrop.__new__=function(){ MochiKit.Base.nameFunctions(this); this.EXPORT_TAGS={":common":this.EXPORT,":all":MochiKit.Base.concat(this.EXPORT,this.EXPORT_OK)}; }; MochiKit.DragAndDrop.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.DragAndDrop); MochiKit.Base._deps("Sortable",["Base","Iter","DOM","Position","DragAndDrop"]); MochiKit.Sortable.NAME="MochiKit.Sortable"; MochiKit.Sortable.VERSION="1.4.2"; MochiKit.Sortable.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.Sortable.toString=function(){ return this.__repr__(); }; MochiKit.Sortable.EXPORT=[]; MochiKit.Sortable.EXPORT_OK=[]; MochiKit.Base.update(MochiKit.Sortable,{sortables:{},_findRootElement:function(_7b7){ while(_7b7.tagName.toUpperCase()!="BODY"){ if(_7b7.id&&MochiKit.Sortable.sortables[_7b7.id]){ return _7b7; } _7b7=_7b7.parentNode; } },_createElementId:function(_7b8){ if(_7b8.id==null||_7b8.id==""){ var d=MochiKit.DOM; var id; var _7bb=1; while(d.getElement(id="sortable"+_7bb)!=null){ _7bb+=1; } d.setNodeAttribute(_7b8,"id",id); } },options:function(_7bc){ _7bc=MochiKit.Sortable._findRootElement(MochiKit.DOM.getElement(_7bc)); if(!_7bc){ return; } return MochiKit.Sortable.sortables[_7bc.id]; },destroy:function(_7bd){ var s=MochiKit.Sortable.options(_7bd); var b=MochiKit.Base; var d=MochiKit.DragAndDrop; if(s){ MochiKit.Signal.disconnect(s.startHandle); MochiKit.Signal.disconnect(s.endHandle); b.map(function(dr){ d.Droppables.remove(dr); },s.droppables); b.map(function(dr){ dr.destroy(); },s.draggables); delete MochiKit.Sortable.sortables[s.element.id]; } },create:function(_7c3,_7c4){ _7c3=MochiKit.DOM.getElement(_7c3); var self=MochiKit.Sortable; self._createElementId(_7c3); _7c4=MochiKit.Base.update({element:_7c3,tag:"li",dropOnEmpty:false,tree:false,treeTag:"ul",overlap:"vertical",constraint:"vertical",containment:[_7c3],handle:false,only:false,hoverclass:null,ghosting:false,scroll:false,scrollSensitivity:20,scrollSpeed:15,format:/^[^_]*_(.*)$/,onChange:MochiKit.Base.noop,onUpdate:MochiKit.Base.noop,accept:null},_7c4); self.destroy(_7c3); var _7c6={revert:true,ghosting:_7c4.ghosting,scroll:_7c4.scroll,scrollSensitivity:_7c4.scrollSensitivity,scrollSpeed:_7c4.scrollSpeed,constraint:_7c4.constraint,handle:_7c4.handle}; if(_7c4.starteffect){ _7c6.starteffect=_7c4.starteffect; } if(_7c4.reverteffect){ _7c6.reverteffect=_7c4.reverteffect; }else{ if(_7c4.ghosting){ _7c6.reverteffect=function(_7c7){ _7c7.style.top=0; _7c7.style.left=0; }; } } if(_7c4.endeffect){ _7c6.endeffect=_7c4.endeffect; } if(_7c4.zindex){ _7c6.zindex=_7c4.zindex; } var _7c8={overlap:_7c4.overlap,containment:_7c4.containment,hoverclass:_7c4.hoverclass,onhover:self.onHover,tree:_7c4.tree,accept:_7c4.accept}; var _7c9={onhover:self.onEmptyHover,overlap:_7c4.overlap,containment:_7c4.containment,hoverclass:_7c4.hoverclass,accept:_7c4.accept}; MochiKit.DOM.removeEmptyTextNodes(_7c3); _7c4.draggables=[]; _7c4.droppables=[]; if(_7c4.dropOnEmpty||_7c4.tree){ new MochiKit.DragAndDrop.Droppable(_7c3,_7c9); _7c4.droppables.push(_7c3); } MochiKit.Base.map(function(e){ var _7cb=_7c4.handle?MochiKit.DOM.getFirstElementByTagAndClassName(null,_7c4.handle,e):e; _7c4.draggables.push(new MochiKit.DragAndDrop.Draggable(e,MochiKit.Base.update(_7c6,{handle:_7cb}))); new MochiKit.DragAndDrop.Droppable(e,_7c8); if(_7c4.tree){ e.treeNode=_7c3; } _7c4.droppables.push(e); },(self.findElements(_7c3,_7c4)||[])); if(_7c4.tree){ MochiKit.Base.map(function(e){ new MochiKit.DragAndDrop.Droppable(e,_7c9); e.treeNode=_7c3; _7c4.droppables.push(e); },(self.findTreeElements(_7c3,_7c4)||[])); } self.sortables[_7c3.id]=_7c4; _7c4.lastValue=self.serialize(_7c3); _7c4.startHandle=MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables,"start",MochiKit.Base.partial(self.onStart,_7c3)); _7c4.endHandle=MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables,"end",MochiKit.Base.partial(self.onEnd,_7c3)); },onStart:function(_7cd,_7ce){ var self=MochiKit.Sortable; var _7d0=self.options(_7cd); _7d0.lastValue=self.serialize(_7d0.element); },onEnd:function(_7d1,_7d2){ var self=MochiKit.Sortable; self.unmark(); var _7d4=self.options(_7d1); if(_7d4.lastValue!=self.serialize(_7d4.element)){ _7d4.onUpdate(_7d4.element); } },findElements:function(_7d5,_7d6){ return MochiKit.Sortable.findChildren(_7d5,_7d6.only,_7d6.tree,_7d6.tag); },findTreeElements:function(_7d7,_7d8){ return MochiKit.Sortable.findChildren(_7d7,_7d8.only,_7d8.tree?true:false,_7d8.treeTag); },findChildren:function(_7d9,only,_7db,_7dc){ if(!_7d9.hasChildNodes()){ return null; } _7dc=_7dc.toUpperCase(); if(only){ only=MochiKit.Base.flattenArray([only]); } var _7dd=[]; MochiKit.Base.map(function(e){ if(e.tagName&&e.tagName.toUpperCase()==_7dc&&(!only||MochiKit.Iter.some(only,function(c){ return MochiKit.DOM.hasElementClass(e,c); }))){ _7dd.push(e); } if(_7db){ var _7e0=MochiKit.Sortable.findChildren(e,only,_7db,_7dc); if(_7e0&&_7e0.length>0){ _7dd=_7dd.concat(_7e0); } } },_7d9.childNodes); return _7dd; },onHover:function(_7e1,_7e2,_7e3){ if(MochiKit.DOM.isChildNode(_7e2,_7e1)){ return; } var self=MochiKit.Sortable; if(_7e3>0.33&&_7e3<0.66&&self.options(_7e2).tree){ return; }else{ if(_7e3>0.5){ self.mark(_7e2,"before"); if(_7e2.previousSibling!=_7e1){ var _7e5=_7e1.parentNode; _7e1.style.visibility="hidden"; _7e2.parentNode.insertBefore(_7e1,_7e2); if(_7e2.parentNode!=_7e5){ self.options(_7e5).onChange(_7e1); } self.options(_7e2.parentNode).onChange(_7e1); } }else{ self.mark(_7e2,"after"); var _7e6=_7e2.nextSibling||null; if(_7e6!=_7e1){ var _7e5=_7e1.parentNode; _7e1.style.visibility="hidden"; _7e2.parentNode.insertBefore(_7e1,_7e6); if(_7e2.parentNode!=_7e5){ self.options(_7e5).onChange(_7e1); } self.options(_7e2.parentNode).onChange(_7e1); } } } },_offsetSize:function(_7e7,type){ if(type=="vertical"||type=="height"){ return _7e7.offsetHeight; }else{ return _7e7.offsetWidth; } },onEmptyHover:function(_7e9,_7ea,_7eb){ var _7ec=_7e9.parentNode; var self=MochiKit.Sortable; var _7ee=self.options(_7ea); if(!MochiKit.DOM.isChildNode(_7ea,_7e9)){ var _7ef; var _7f0=self.findElements(_7ea,{tag:_7ee.tag,only:_7ee.only}); var _7f1=null; if(_7f0){ var _7f2=self._offsetSize(_7ea,_7ee.overlap)*(1-_7eb); for(_7ef=0;_7ef<_7f0.length;_7ef+=1){ if(_7f2-self._offsetSize(_7f0[_7ef],_7ee.overlap)>=0){ _7f2-=self._offsetSize(_7f0[_7ef],_7ee.overlap); }else{ if(_7f2-(self._offsetSize(_7f0[_7ef],_7ee.overlap)/2)>=0){ _7f1=_7ef+1<_7f0.length?_7f0[_7ef+1]:null; break; }else{ _7f1=_7f0[_7ef]; break; } } } } _7ea.insertBefore(_7e9,_7f1); self.options(_7ec).onChange(_7e9); _7ee.onChange(_7e9); } },unmark:function(){ var m=MochiKit.Sortable._marker; if(m){ MochiKit.Style.hideElement(m); } },mark:function(_7f4,_7f5){ var d=MochiKit.DOM; var self=MochiKit.Sortable; var _7f8=self.options(_7f4.parentNode); if(_7f8&&!_7f8.ghosting){ return; } if(!self._marker){ self._marker=d.getElement("dropmarker")||document.createElement("DIV"); MochiKit.Style.hideElement(self._marker); d.addElementClass(self._marker,"dropmarker"); self._marker.style.position="absolute"; document.getElementsByTagName("body").item(0).appendChild(self._marker); } var _7f9=MochiKit.Position.cumulativeOffset(_7f4); self._marker.style.left=_7f9.x+"px"; self._marker.style.top=_7f9.y+"px"; if(_7f5=="after"){ if(_7f8.overlap=="horizontal"){ self._marker.style.left=(_7f9.x+_7f4.clientWidth)+"px"; }else{ self._marker.style.top=(_7f9.y+_7f4.clientHeight)+"px"; } } MochiKit.Style.showElement(self._marker); },_tree:function(_7fa,_7fb,_7fc){ var self=MochiKit.Sortable; var _7fe=self.findElements(_7fa,_7fb)||[]; for(var i=0;i<_7fe.length;++i){ var _800=_7fe[i].id.match(_7fb.format); if(!_800){ continue; } var _801={id:encodeURIComponent(_800?_800[1]:null),element:_7fa,parent:_7fc,children:[],position:_7fc.children.length,container:self._findChildrenElement(_7fe[i],_7fb.treeTag.toUpperCase())}; if(_801.container){ self._tree(_801.container,_7fb,_801); } _7fc.children.push(_801); } return _7fc; },_findChildrenElement:function(_802,_803){ if(_802&&_802.hasChildNodes){ _803=_803.toUpperCase(); for(var i=0;i<_802.childNodes.length;++i){ if(_802.childNodes[i].tagName.toUpperCase()==_803){ return _802.childNodes[i]; } } } return null; },tree:function(_805,_806){ _805=MochiKit.DOM.getElement(_805); var _807=MochiKit.Sortable.options(_805); _806=MochiKit.Base.update({tag:_807.tag,treeTag:_807.treeTag,only:_807.only,name:_805.id,format:_807.format},_806||{}); var root={id:null,parent:null,children:new Array,container:_805,position:0}; return MochiKit.Sortable._tree(_805,_806,root); },setSequence:function(_809,_80a,_80b){ var self=MochiKit.Sortable; var b=MochiKit.Base; _809=MochiKit.DOM.getElement(_809); _80b=b.update(self.options(_809),_80b||{}); var _80e={}; b.map(function(n){ var m=n.id.match(_80b.format); if(m){ _80e[m[1]]=[n,n.parentNode]; } n.parentNode.removeChild(n); },self.findElements(_809,_80b)); b.map(function(_811){ var n=_80e[_811]; if(n){ n[1].appendChild(n[0]); delete _80e[_811]; } },_80a); },_constructIndex:function(node){ var _814=""; do{ if(node.id){ _814="["+node.position+"]"+_814; } }while((node=node.parent)!=null); return _814; },sequence:function(_815,_816){ _815=MochiKit.DOM.getElement(_815); var self=MochiKit.Sortable; var _816=MochiKit.Base.update(self.options(_815),_816||{}); return MochiKit.Base.map(function(item){ return item.id.match(_816.format)?item.id.match(_816.format)[1]:""; },MochiKit.DOM.getElement(self.findElements(_815,_816)||[])); },serialize:function(_819,_81a){ _819=MochiKit.DOM.getElement(_819); var self=MochiKit.Sortable; _81a=MochiKit.Base.update(self.options(_819),_81a||{}); var name=encodeURIComponent(_81a.name||_819.id); if(_81a.tree){ return MochiKit.Base.flattenArray(MochiKit.Base.map(function(item){ return [name+self._constructIndex(item)+"[id]="+encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); },self.tree(_819,_81a).children)).join("&"); }else{ return MochiKit.Base.map(function(item){ return name+"[]="+encodeURIComponent(item); },self.sequence(_819,_81a)).join("&"); } }}); MochiKit.Sortable.Sortable=MochiKit.Sortable; MochiKit.Sortable.__new__=function(){ MochiKit.Base.nameFunctions(this); this.EXPORT_TAGS={":common":this.EXPORT,":all":MochiKit.Base.concat(this.EXPORT,this.EXPORT_OK)}; }; MochiKit.Sortable.__new__(); MochiKit.Base._exportSymbols(this,MochiKit.Sortable); if(typeof (MochiKit)=="undefined"){ MochiKit={}; } if(typeof (MochiKit.MochiKit)=="undefined"){ MochiKit.MochiKit={}; } MochiKit.MochiKit.NAME="MochiKit.MochiKit"; MochiKit.MochiKit.VERSION="1.4.2"; MochiKit.MochiKit.__repr__=function(){ return "["+this.NAME+" "+this.VERSION+"]"; }; MochiKit.MochiKit.toString=function(){ return this.__repr__(); }; MochiKit.MochiKit.SUBMODULES=["Base","Iter","Logging","DateTime","Format","Async","DOM","Selector","Style","LoggingPane","Color","Signal","Position","Visual","DragAndDrop","Sortable"]; if(typeof (JSAN)!="undefined"||typeof (dojo)!="undefined"){ if(typeof (dojo)!="undefined"){ dojo.provide("MochiKit.MochiKit"); (function(lst){ for(var i=0;i"); } } })(); } paste-3.10.1/paste/evalexception/media/debug.js000066400000000000000000000102411461442501600213760ustar00rootroot00000000000000function showFrame(anchor) { var tbid = anchor.getAttribute('tbid'); var expanded = anchor.expanded; if (expanded) { MochiKit.DOM.hideElement(anchor.expandedElement); anchor.expanded = false; _swapImage(anchor); return false; } anchor.expanded = true; if (anchor.expandedElement) { MochiKit.DOM.showElement(anchor.expandedElement); _swapImage(anchor); $('debug_input_'+tbid).focus(); return false; } var url = debug_base + '/show_frame?tbid=' + tbid + '&debugcount=' + debug_count; var d = MochiKit.Async.doSimpleXMLHttpRequest(url); d.addCallbacks(function (data) { var el = MochiKit.DOM.DIV({}); anchor.parentNode.insertBefore(el, anchor.nextSibling); el.innerHTML = data.responseText; anchor.expandedElement = el; _swapImage(anchor); $('debug_input_'+tbid).focus(); }, function (error) { showError(error.req.responseText); }); return false; } function _swapImage(anchor) { var el = anchor.getElementsByTagName('IMG')[0]; if (anchor.expanded) { var img = 'minus.jpg'; } else { var img = 'plus.jpg'; } el.src = debug_base + '/media/' + img; } function submitInput(button, tbid) { var input = $(button.getAttribute('input-from')); var output = $(button.getAttribute('output-to')); var url = debug_base + '/exec_input'; var history = input.form.history; input.historyPosition = 0; if (! history) { history = input.form.history = []; } history.push(input.value); var vars = { tbid: tbid, debugcount: debug_count, input: input.value }; MochiKit.DOM.showElement(output); var d = MochiKit.Async.doSimpleXMLHttpRequest(url, vars); d.addCallbacks(function (data) { var result = data.responseText; output.innerHTML += result; input.value = ''; input.focus(); }, function (error) { showError(error.req.responseText); }); return false; } function showError(msg) { var el = $('error-container'); if (el.innerHTML) { el.innerHTML += '
\n' + msg; } else { el.innerHTML = msg; } MochiKit.DOM.showElement('error-area'); } function clearError() { var el = $('error-container'); el.innerHTML = ''; MochiKit.DOM.hideElement('error-area'); } function expandInput(button) { var input = button.form.elements.input; stdops = { name: 'input', style: 'width: 100%', autocomplete: 'off' }; if (input.tagName == 'INPUT') { var newEl = MochiKit.DOM.TEXTAREA(stdops); var text = 'Contract'; } else { stdops['type'] = 'text'; stdops['onkeypress'] = 'upArrow(this)'; var newEl = MochiKit.DOM.INPUT(stdops); var text = 'Expand'; } newEl.value = input.value; newEl.id = input.id; MochiKit.DOM.swapDOM(input, newEl); newEl.focus(); button.value = text; return false; } function upArrow(input, event) { if (window.event) { event = window.event; } if (event.keyCode != 38 && event.keyCode != 40) { // not an up- or down-arrow return true; } var dir = event.keyCode == 38 ? 1 : -1; var history = input.form.history; if (! history) { history = input.form.history = []; } var pos = input.historyPosition || 0; if (! pos && dir == -1) { return true; } if (! pos && input.value) { history.push(input.value); pos = 1; } pos += dir; if (history.length-pos < 0) { pos = 1; } if (history.length-pos > history.length-1) { input.value = ''; return true; } input.historyPosition = pos; var line = history[history.length-pos]; input.value = line; } function expandLong(anchor) { var span = anchor; while (span) { if (span.style && span.style.display == 'none') { break; } span = span.nextSibling; } if (! span) { return false; } MochiKit.DOM.showElement(span); MochiKit.DOM.hideElement(anchor); return false; } paste-3.10.1/paste/evalexception/media/minus.jpg000066400000000000000000000005471461442501600216170ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222 "$!4BTs! ?ZTz9mCQA7^g;j?WQJM{Oh,paste-3.10.1/paste/evalexception/media/plus.jpg000066400000000000000000000005511461442501600214420ustar00rootroot00000000000000JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222 "#"#4Ts! ?gnIe=%iȑ>󧼎n.sU1}\p7?/Z2Y?paste-3.10.1/paste/evalexception/middleware.py000066400000000000000000000532711461442501600213740ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Exception-catching middleware that allows interactive debugging. This middleware catches all unexpected exceptions. A normal traceback, like produced by ``paste.exceptions.errormiddleware.ErrorMiddleware`` is given, plus controls to see local variables and evaluate expressions in a local context. This can only be used in single-process environments, because subsequent requests must go back to the same process that the exception originally occurred in. Threaded or non-concurrent environments both work. This shouldn't be used in production in any way. That would just be silly. If calling from an XMLHttpRequest call, if the GET variable ``_`` is given then it will make the response more compact (and less Javascripty), since if you use innerHTML it'll kill your browser. You can look for the header X-Debug-URL in your 500 responses if you want to see the full debuggable traceback. Also, this URL is printed to ``wsgi.errors``, so you can open it up in another browser window. """ import html import sys import os import traceback from io import StringIO import pprint import itertools import time import re from paste.exceptions import errormiddleware, formatter, collector from paste import wsgilib from paste import urlparser from paste import httpexceptions from paste import registry from paste import request from paste import response from paste.evalexception import evalcontext limit = 200 def cmp(a, b): return (a > b) - (a < b) def html_quote(v): """ Escape HTML characters, plus translate None to '' """ if v is None: return '' return html.escape(str(v), 1) def preserve_whitespace(v, quote=True): """ Quote a value for HTML, preserving whitespace (translating newlines to ``
`` and multiple spaces to use `` ``). If ``quote`` is true, then the value will be HTML quoted first. """ if quote: v = html_quote(v) v = v.replace('\n', '
\n') v = re.sub(r'()( +)', _repl_nbsp, v) v = re.sub(r'(\n)( +)', _repl_nbsp, v) v = re.sub(r'^()( +)', _repl_nbsp, v) return '%s' % v def _repl_nbsp(match): if len(match.group(2)) == 1: return ' ' return match.group(1) + ' ' * (len(match.group(2))-1) + ' ' def simplecatcher(application): """ A simple middleware that catches errors and turns them into simple tracebacks. """ def simplecatcher_app(environ, start_response): try: return application(environ, start_response) except Exception: out = StringIO() traceback.print_exc(file=out) start_response('500 Server Error', [('content-type', 'text/html')], sys.exc_info()) res = out.getvalue() return ['

Error

%s
' % html_quote(res)] return simplecatcher_app def wsgiapp(): """ Turns a function or method into a WSGI application. """ def decorator(func): def wsgiapp_wrapper(*args): # we get 3 args when this is a method, two when it is # a function :( if len(args) == 3: environ = args[1] start_response = args[2] args = [args[0]] else: environ, start_response = args args = [] def application(environ, start_response): form = wsgilib.parse_formvars(environ, include_get_vars=True) headers = response.HeaderDict( {'content-type': 'text/html', 'status': '200 OK'}) form['environ'] = environ form['headers'] = headers res = func(*args, **form.mixed()) status = headers.pop('status') start_response(status, headers.headeritems()) return [res] app = httpexceptions.make_middleware(application) app = simplecatcher(app) return app(environ, start_response) wsgiapp_wrapper.exposed = True return wsgiapp_wrapper return decorator def get_debug_info(func): """ A decorator (meant to be used under ``wsgiapp()``) that resolves the ``debugcount`` variable to a ``DebugInfo`` object (or gives an error if it can't be found). """ def debug_info_replacement(self, **form): try: if 'debugcount' not in form: raise ValueError('You must provide a debugcount parameter') debugcount = form.pop('debugcount') try: debugcount = int(debugcount) except ValueError: raise ValueError('Bad value for debugcount') if debugcount not in self.debug_infos: raise ValueError( 'Debug %s no longer found (maybe it has expired?)' % debugcount) debug_info = self.debug_infos[debugcount] return func(self, debug_info=debug_info, **form) except ValueError as e: form['headers']['status'] = '500 Server Error' return 'There was an error: %s' % html_quote(e) return debug_info_replacement debug_counter = itertools.count(int(time.time())) def get_debug_count(environ): """ Return the unique debug count for the current request """ if 'paste.evalexception.debug_count' in environ: return environ['paste.evalexception.debug_count'] else: environ['paste.evalexception.debug_count'] = _next = next(debug_counter) return _next class EvalException: def __init__(self, application, global_conf=None, xmlhttp_key=None): self.application = application self.debug_infos = {} if xmlhttp_key is None: if global_conf is None: xmlhttp_key = '_' else: xmlhttp_key = global_conf.get('xmlhttp_key', '_') self.xmlhttp_key = xmlhttp_key def __call__(self, environ, start_response): assert not environ['wsgi.multiprocess'], ( "The EvalException middleware is not usable in a " "multi-process environment") environ['paste.evalexception'] = self if environ.get('PATH_INFO', '').startswith('/_debug/'): return self.debug(environ, start_response) else: return self.respond(environ, start_response) def debug(self, environ, start_response): assert request.path_info_pop(environ) == '_debug' next_part = request.path_info_pop(environ) method = getattr(self, next_part, None) if not method: exc = httpexceptions.HTTPNotFound( '%r not found when parsing %r' % (next_part, wsgilib.construct_url(environ))) return exc.wsgi_application(environ, start_response) if not getattr(method, 'exposed', False): exc = httpexceptions.HTTPForbidden( '%r not allowed' % next_part) return exc.wsgi_application(environ, start_response) return method(environ, start_response) def media(self, environ, start_response): """ Static path where images and other files live """ app = urlparser.StaticURLParser( os.path.join(os.path.dirname(__file__), 'media')) return app(environ, start_response) media.exposed = True def mochikit(self, environ, start_response): """ Static path where MochiKit lives """ app = urlparser.StaticURLParser( os.path.join(os.path.dirname(__file__), 'mochikit')) return app(environ, start_response) mochikit.exposed = True def summary(self, environ, start_response): """ Returns a JSON-format summary of all the cached exception reports """ start_response('200 OK', [('Content-type', 'text/x-json')]) data = []; items = self.debug_infos.values() items.sort(lambda a, b: cmp(a.created, b.created)) data = [item.json() for item in items] return [repr(data)] summary.exposed = True def view(self, environ, start_response): """ View old exception reports """ id = int(request.path_info_pop(environ)) if id not in self.debug_infos: start_response( '500 Server Error', [('Content-type', 'text/html')]) return [ "Traceback by id %s does not exist (maybe " "the server has been restarted?)" % id] debug_info = self.debug_infos[id] return debug_info.wsgi_application(environ, start_response) view.exposed = True def make_view_url(self, environ, base_path, count): return base_path + '/_debug/view/%s' % count #@wsgiapp() #@get_debug_info def show_frame(self, tbid, debug_info, **kw): frame = debug_info.frame(int(tbid)) vars = frame.tb_frame.f_locals if vars: registry.restorer.restoration_begin(debug_info.counter) local_vars = make_table(vars) registry.restorer.restoration_end() else: local_vars = 'No local vars' return input_form(tbid, debug_info) + local_vars show_frame = wsgiapp()(get_debug_info(show_frame)) #@wsgiapp() #@get_debug_info def exec_input(self, tbid, debug_info, input, **kw): if not input.strip(): return '' input = input.rstrip() + '\n' frame = debug_info.frame(int(tbid)) vars = frame.tb_frame.f_locals glob_vars = frame.tb_frame.f_globals context = evalcontext.EvalContext(vars, glob_vars) registry.restorer.restoration_begin(debug_info.counter) output = context.exec_expr(input) registry.restorer.restoration_end() input_html = formatter.str2html(input) return ('>>> ' '%s
\n%s' % (preserve_whitespace(input_html, quote=False), preserve_whitespace(output))) exec_input = wsgiapp()(get_debug_info(exec_input)) def respond(self, environ, start_response): if environ.get('paste.throw_errors'): return self.application(environ, start_response) base_path = request.construct_url(environ, with_path_info=False, with_query_string=False) environ['paste.throw_errors'] = True started = [] def detect_start_response(status, headers, exc_info=None): try: return start_response(status, headers, exc_info) except Exception: raise else: started.append(True) try: __traceback_supplement__ = errormiddleware.Supplement, self, environ app_iter = self.application(environ, detect_start_response) try: return_iter = list(app_iter) return return_iter finally: if hasattr(app_iter, 'close'): app_iter.close() except Exception: exc_info = sys.exc_info() for expected in environ.get('paste.expected_exceptions', []): if isinstance(exc_info[1], expected): raise # Tell the Registry to save its StackedObjectProxies current state # for later restoration registry.restorer.save_registry_state(environ) count = get_debug_count(environ) view_uri = self.make_view_url(environ, base_path, count) if not started: headers = [('content-type', 'text/html')] headers.append(('X-Debug-URL', view_uri)) start_response('500 Internal Server Error', headers, exc_info) msg = 'Debug at: %s\n' % view_uri environ['wsgi.errors'].write(msg) exc_data = collector.collect_exception(*exc_info) debug_info = DebugInfo(count, exc_info, exc_data, base_path, environ, view_uri) assert count not in self.debug_infos self.debug_infos[count] = debug_info if self.xmlhttp_key: get_vars = request.parse_querystring(environ) if dict(get_vars).get(self.xmlhttp_key): exc_data = collector.collect_exception(*exc_info) html = formatter.format_html( exc_data, include_hidden_frames=False, include_reusable=False, show_extra_data=False) return [html] # @@: it would be nice to deal with bad content types here return debug_info.content() def exception_handler(self, exc_info, environ): simple_html_error = False if self.xmlhttp_key: get_vars = request.parse_querystring(environ) if dict(get_vars).get(self.xmlhttp_key): simple_html_error = True return errormiddleware.handle_exception( exc_info, environ['wsgi.errors'], html=True, debug_mode=True, simple_html_error=simple_html_error) class DebugInfo: def __init__(self, counter, exc_info, exc_data, base_path, environ, view_uri): self.counter = counter self.exc_data = exc_data self.base_path = base_path self.environ = environ self.view_uri = view_uri self.created = time.time() self.exc_type, self.exc_value, self.tb = exc_info __exception_formatter__ = 1 self.frames = [] n = 0 tb = self.tb while tb is not None and (limit is None or n < limit): if tb.tb_frame.f_locals.get('__exception_formatter__'): # Stop recursion. @@: should make a fake ExceptionFrame break self.frames.append(tb) tb = tb.tb_next n += 1 def json(self): """Return the JSON-able representation of this object""" return { 'uri': self.view_uri, 'created': time.strftime('%c', time.gmtime(self.created)), 'created_timestamp': self.created, 'exception_type': str(self.exc_type), 'exception': str(self.exc_value), } def frame(self, tbid): for frame in self.frames: if id(frame) == tbid: return frame else: raise ValueError("No frame by id %s found from %r" % (tbid, self.frames)) def wsgi_application(self, environ, start_response): start_response('200 OK', [('content-type', 'text/html')]) return self.content() def content(self): html = format_eval_html(self.exc_data, self.base_path, self.counter) head_html = (formatter.error_css + formatter.hide_display_js) head_html += self.eval_javascript() repost_button = make_repost_button(self.environ) page = error_template % { 'repost_button': repost_button or '', 'head_html': head_html, 'body': html} page = page.encode('utf8') return [page] def eval_javascript(self): base_path = self.base_path + '/_debug' return ( '\n' '\n' '\n' % (base_path, base_path, base_path, self.counter)) class EvalHTMLFormatter(formatter.HTMLFormatter): def __init__(self, base_path, counter, **kw): super(EvalHTMLFormatter, self).__init__(**kw) self.base_path = base_path self.counter = counter def format_source_line(self, filename, frame): line = formatter.HTMLFormatter.format_source_line( self, filename, frame) return (line + '     ' '    ' % (frame.tbid, self.base_path)) def make_table(items): if isinstance(items, dict): items = sorted(items.items()) rows = [] i = 0 for name, value in items: i += 1 out = StringIO() try: pprint.pprint(value, out) except Exception as e: print('Error: %s' % e, file=out) value = html_quote(out.getvalue()) if len(value) > 100: # @@: This can actually break the HTML :( # should I truncate before quoting? orig_value = value value = value[:100] value += '...' value += '%s' % orig_value[100:] value = formatter.make_wrappable(value) if i % 2: attr = ' class="even"' else: attr = ' class="odd"' rows.append('' '%s%s' % (attr, html_quote(name), preserve_whitespace(value, quote=False))) return '%s
' % ( '\n'.join(rows)) def format_eval_html(exc_data, base_path, counter): short_formatter = EvalHTMLFormatter( base_path=base_path, counter=counter, include_reusable=False) short_er = short_formatter.format_collected_data(exc_data) long_formatter = EvalHTMLFormatter( base_path=base_path, counter=counter, show_hidden_frames=True, show_extra_data=False, include_reusable=False) long_er = long_formatter.format_collected_data(exc_data) text_er = formatter.format_text(exc_data, show_hidden_frames=True) if short_formatter.filter_frames(exc_data.frames) != \ long_formatter.filter_frames(exc_data.frames): # Only display the full traceback when it differs from the # short version full_traceback_html = """
%s
""" % long_er else: full_traceback_html = '' return """ %s %s
""" % (short_er, full_traceback_html, html.escape(text_er)) def make_repost_button(environ): url = request.construct_url(environ) if environ['REQUEST_METHOD'] == 'GET': return ('
' % url) else: # @@: I'd like to reconstruct this, but I can't because # the POST body is probably lost at this point, and # I can't get it back :( return None # @@: Use or lose the following code block """ fields = [] for name, value in wsgilib.parse_formvars( environ, include_get_vars=False).items(): if hasattr(value, 'filename'): # @@: Arg, we'll just submit the body, and leave out # the filename :( value = value.value fields.append( '' % (html_quote(name), html_quote(value))) return '''
%s
''' % (url, '\n'.join(fields)) """ def input_form(tbid, debug_info): return '''

''' % {'tbid': tbid} error_template = ''' Server Error %(head_html)s %(repost_button)s %(body)s ''' def make_eval_exception(app, global_conf, xmlhttp_key=None): """ Wraps the application in an interactive debugger. This debugger is a major security hole, and should only be used during development. xmlhttp_key is a string that, if present in QUERY_STRING, indicates that the request is an XMLHttp request, and the Javascript/interactive debugger should not be returned. (If you try to put the debugger somewhere with innerHTML, you will often crash the browser) """ if xmlhttp_key is None: xmlhttp_key = global_conf.get('xmlhttp_key', '_') return EvalException(app, xmlhttp_key=xmlhttp_key) paste-3.10.1/paste/exceptions/000077500000000000000000000000001461442501600162105ustar00rootroot00000000000000paste-3.10.1/paste/exceptions/__init__.py000066400000000000000000000003741461442501600203250ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Package for catching exceptions and displaying annotated exception reports """ paste-3.10.1/paste/exceptions/collector.py000066400000000000000000000463651461442501600205660ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php ############################################################################## # # Copyright (c) 2001, 2002 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## ## Originally zExceptions.ExceptionFormatter from Zope; ## Modified by Ian Bicking, Imaginary Landscape, 2005 """ An exception collector that finds traceback information plus supplements """ import sys import traceback import time from io import StringIO import linecache from paste.exceptions import serial_number_generator import warnings DEBUG_EXCEPTION_FORMATTER = True DEBUG_IDENT_PREFIX = 'E-' FALLBACK_ENCODING = 'UTF-8' __all__ = ['collect_exception', 'ExceptionCollector'] class ExceptionCollector: """ Produces a data structure that can be used by formatters to display exception reports. Magic variables: If you define one of these variables in your local scope, you can add information to tracebacks that happen in that context. This allows applications to add all sorts of extra information about the context of the error, including URLs, environmental variables, users, hostnames, etc. These are the variables we look for: ``__traceback_supplement__``: You can define this locally or globally (unlike all the other variables, which must be defined locally). ``__traceback_supplement__`` is a tuple of ``(factory, arg1, arg2...)``. When there is an exception, ``factory(arg1, arg2, ...)`` is called, and the resulting object is inspected for supplemental information. ``__traceback_info__``: This information is added to the traceback, usually fairly literally. ``__traceback_hide__``: If set and true, this indicates that the frame should be hidden from abbreviated tracebacks. This way you can hide some of the complexity of the larger framework and let the user focus on their own errors. By setting it to ``'before'``, all frames before this one will be thrown away. By setting it to ``'after'`` then all frames after this will be thrown away until ``'reset'`` is found. In each case the frame where it is set is included, unless you append ``'_and_this'`` to the value (e.g., ``'before_and_this'``). Note that formatters will ignore this entirely if the frame that contains the error wouldn't normally be shown according to these rules. ``__traceback_reporter__``: This should be a reporter object (see the reporter module), or a list/tuple of reporter objects. All reporters found this way will be given the exception, innermost first. ``__traceback_decorator__``: This object (defined in a local or global scope) will get the result of this function (the CollectedException defined below). It may modify this object in place, or return an entirely new object. This gives the object the ability to manipulate the traceback arbitrarily. The actually interpretation of these values is largely up to the reporters and formatters. ``collect_exception(*sys.exc_info())`` will return an object with several attributes: ``frames``: A list of frames ``exception_formatted``: The formatted exception, generally a full traceback ``exception_type``: The type of the exception, like ``ValueError`` ``exception_value``: The string value of the exception, like ``'x not in list'`` ``identification_code``: A hash of the exception data meant to identify the general exception, so that it shares this code with other exceptions that derive from the same problem. The code is a hash of all the module names and function names in the traceback, plus exception_type. This should be shown to users so they can refer to the exception later. (@@: should it include a portion that allows identification of the specific instance of the exception as well?) The list of frames goes innermost first. Each frame has these attributes; some values may be None if they could not be determined. ``modname``: the name of the module ``filename``: the filename of the module ``lineno``: the line of the error ``revision``: the contents of __version__ or __revision__ ``name``: the function name ``supplement``: an object created from ``__traceback_supplement__`` ``supplement_exception``: a simple traceback of any exception ``__traceback_supplement__`` created ``traceback_info``: the str() of any ``__traceback_info__`` variable found in the local scope (@@: should it str()-ify it or not?) ``traceback_hide``: the value of any ``__traceback_hide__`` variable ``traceback_log``: the value of any ``__traceback_log__`` variable ``__traceback_supplement__`` is thrown away, but a fixed set of attributes are captured; each of these attributes is optional. ``object``: the name of the object being visited ``source_url``: the original URL requested ``line``: the line of source being executed (for interpreters, like ZPT) ``column``: the column of source being executed ``expression``: the expression being evaluated (also for interpreters) ``warnings``: a list of (string) warnings to be displayed ``getInfo``: a function/method that takes no arguments, and returns a string describing any extra information ``extraData``: a function/method that takes no arguments, and returns a dictionary. The contents of this dictionary will not be displayed in the context of the traceback, but globally for the exception. Results will be grouped by the keys in the dictionaries (which also serve as titles). The keys can also be tuples of (importance, title); in this case the importance should be ``important`` (shows up at top), ``normal`` (shows up somewhere; unspecified), ``supplemental`` (shows up at bottom), or ``extra`` (shows up hidden or not at all). These are used to create an object with attributes of the same names (``getInfo`` becomes a string attribute, not a method). ``__traceback_supplement__`` implementations should be careful to produce values that are relatively static and unlikely to cause further errors in the reporting system -- any complex introspection should go in ``getInfo()`` and should ultimately return a string. Note that all attributes are optional, and under certain circumstances may be None or may not exist at all -- the collector can only do a best effort, but must avoid creating any exceptions itself. Formatters may want to use ``__traceback_hide__`` as a hint to hide frames that are part of the 'framework' or underlying system. There are a variety of rules about special values for this variables that formatters should be aware of. TODO: More attributes in __traceback_supplement__? Maybe an attribute that gives a list of local variables that should also be collected? Also, attributes that would be explicitly meant for the entire request, not just a single frame. Right now some of the fixed set of attributes (e.g., source_url) are meant for this use, but there's no explicit way for the supplement to indicate new values, e.g., logged-in user, HTTP referrer, environment, etc. Also, the attributes that do exist are Zope/Web oriented. More information on frames? cgitb, for instance, produces extensive information on local variables. There exists the possibility that getting this information may cause side effects, which can make debugging more difficult; but it also provides fodder for post-mortem debugging. However, the collector is not meant to be configurable, but to capture everything it can and let the formatters be configurable. Maybe this would have to be a configuration value, or maybe it could be indicated by another magical variable (which would probably mean 'show all local variables below this frame') """ show_revisions = 0 def __init__(self, limit=None): self.limit = limit def getLimit(self): limit = self.limit if limit is None: limit = getattr(sys, 'tracebacklimit', None) return limit def getRevision(self, globals): if not self.show_revisions: return None revision = globals.get('__revision__', None) if revision is None: # Incorrect but commonly used spelling revision = globals.get('__version__', None) if revision is not None: try: revision = str(revision).strip() except Exception: revision = '???' return revision def collectSupplement(self, supplement, tb): result = {} for name in ('object', 'source_url', 'line', 'column', 'expression', 'warnings'): result[name] = getattr(supplement, name, None) func = getattr(supplement, 'getInfo', None) if func: result['info'] = func() else: result['info'] = None func = getattr(supplement, 'extraData', None) if func: result['extra'] = func() else: result['extra'] = None return SupplementaryData(**result) def collectLine(self, tb, extra_data): f = tb.tb_frame lineno = tb.tb_lineno co = f.f_code filename = co.co_filename name = co.co_name globals = f.f_globals locals = f.f_locals if not hasattr(locals, 'keys'): # Something weird about this frame; it's not a real dict warnings.warn( "Frame %s has an invalid locals(): %r" % ( globals.get('__name__', 'unknown'), locals)) locals = {} data = {} data['modname'] = globals.get('__name__', None) data['filename'] = filename data['lineno'] = lineno data['revision'] = self.getRevision(globals) data['name'] = name data['tbid'] = id(tb) # Output a traceback supplement, if any. if '__traceback_supplement__' in locals: # Use the supplement defined in the function. tbs = locals['__traceback_supplement__'] elif '__traceback_supplement__' in globals: # Use the supplement defined in the module. # This is used by Scripts (Python). tbs = globals['__traceback_supplement__'] else: tbs = None if tbs is not None: factory = tbs[0] args = tbs[1:] try: supp = factory(*args) data['supplement'] = self.collectSupplement(supp, tb) if data['supplement'].extra: for key, value in data['supplement'].extra.items(): extra_data.setdefault(key, []).append(value) except Exception: if DEBUG_EXCEPTION_FORMATTER: out = StringIO() traceback.print_exc(file=out) text = out.getvalue() data['supplement_exception'] = text # else just swallow the exception. try: tbi = locals.get('__traceback_info__', None) if tbi is not None: data['traceback_info'] = str(tbi) except Exception: pass marker = [] for name in ('__traceback_hide__', '__traceback_log__', '__traceback_decorator__'): try: tbh = locals.get(name, globals.get(name, marker)) if tbh is not marker: data[name[2:-2]] = tbh except Exception: pass return data def collectExceptionOnly(self, etype, value): return traceback.format_exception_only(etype, value) def collectException(self, etype, value, tb, limit=None): # The next line provides a way to detect recursion. __exception_formatter__ = 1 frames = [] ident_data = [] traceback_decorators = [] if limit is None: limit = self.getLimit() n = 0 extra_data = {} while tb is not None and (limit is None or n < limit): if tb.tb_frame.f_locals.get('__exception_formatter__'): # Stop recursion. @@: should make a fake ExceptionFrame frames.append('(Recursive formatException() stopped)\n') break data = self.collectLine(tb, extra_data) frame = ExceptionFrame(**data) frames.append(frame) if frame.traceback_decorator is not None: traceback_decorators.append(frame.traceback_decorator) ident_data.append(frame.modname or '?') ident_data.append(frame.name or '?') tb = tb.tb_next n = n + 1 ident_data.append(str(etype)) ident = serial_number_generator.hash_identifier( ' '.join(ident_data), length=5, upper=True, prefix=DEBUG_IDENT_PREFIX) result = CollectedException( frames=frames, exception_formatted=self.collectExceptionOnly(etype, value), exception_type=etype, exception_value=self.safeStr(value), identification_code=ident, date=time.localtime(), extra_data=extra_data) if etype is ImportError: extra_data[('important', 'sys.path')] = [sys.path] for decorator in traceback_decorators: try: new_result = decorator(result) if new_result is not None: result = new_result except Exception: pass return result def safeStr(self, obj): try: return str(obj) except UnicodeEncodeError: try: return str(obj).encode(FALLBACK_ENCODING, 'replace') except UnicodeEncodeError: # This is when something is really messed up, but this can # happen when the __str__ of an object has to handle unicode return repr(obj) limit = 200 class Bunch: """ A generic container """ def __init__(self, **attrs): for name, value in attrs.items(): setattr(self, name, value) def __repr__(self): name = '<%s ' % self.__class__.__name__ name += ' '.join(['%s=%r' % (name, str(value)[:30]) for name, value in self.__dict__.items() if not name.startswith('_')]) return name + '>' class CollectedException(Bunch): """ This is the result of collection the exception; it contains copies of data of interest. """ # A list of frames (ExceptionFrame instances), innermost last: frames = [] # The result of traceback.format_exception_only; this looks # like a normal traceback you'd see in the interactive interpreter exception_formatted = None # The *string* representation of the type of the exception # (@@: should we give the # actual class? -- we can't keep the # actual exception around, but the class should be safe) # Something like 'ValueError' exception_type = None # The string representation of the exception, from ``str(e)``. exception_value = None # An identifier which should more-or-less classify this particular # exception, including where in the code it happened. identification_code = None # The date, as time.localtime() returns: date = None # A dictionary of supplemental data: extra_data = {} class SupplementaryData(Bunch): """ The result of __traceback_supplement__. We don't keep the supplement object around, for fear of GC problems and whatnot. (@@: Maybe I'm being too superstitious about copying only specific information over) """ # These attributes are copied from the object, or left as None # if the object doesn't have these attributes: object = None source_url = None line = None column = None expression = None warnings = None # This is the *return value* of supplement.getInfo(): info = None class ExceptionFrame(Bunch): """ This represents one frame of the exception. Each frame is a context in the call stack, typically represented by a line number and module name in the traceback. """ # The name of the module; can be None, especially when the code # isn't associated with a module. modname = None # The filename (@@: when no filename, is it None or '?'?) filename = None # Line number lineno = None # The value of __revision__ or __version__ -- but only if # show_revision = True (by defaut it is false). (@@: Why not # collect this?) revision = None # The name of the function with the error (@@: None or '?' when # unknown?) name = None # A SupplementaryData object, if __traceback_supplement__ was found # (and produced no errors) supplement = None # If accessing __traceback_supplement__ causes any error, the # plain-text traceback is stored here supplement_exception = None # The str() of any __traceback_info__ value found traceback_info = None # The value of __traceback_hide__ traceback_hide = False # The value of __traceback_decorator__ traceback_decorator = None # The id() of the traceback scope, can be used to reference the # scope for use elsewhere tbid = None def get_source_line(self, context=0): """ Return the source of the current line of this frame. You probably want to .strip() it as well, as it is likely to have leading whitespace. If context is given, then that many lines on either side will also be returned. E.g., context=1 will give 3 lines. """ if not self.filename or not self.lineno: return None lines = [] for lineno in range(self.lineno-context, self.lineno+context+1): lines.append(linecache.getline(self.filename, lineno)) return ''.join(lines) if hasattr(sys, 'tracebacklimit'): limit = min(limit, sys.tracebacklimit) col = ExceptionCollector() def collect_exception(t, v, tb, limit=None): """ Collection an exception from ``sys.exc_info()``. Use like:: try: blah blah except Exception: exc_data = collect_exception(*sys.exc_info()) """ return col.collectException(t, v, tb, limit=limit) paste-3.10.1/paste/exceptions/errormiddleware.py000066400000000000000000000411061461442501600217530ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Error handler middleware """ import sys import traceback import html from io import StringIO from paste.exceptions import formatter, collector, reporter from paste import wsgilib from paste import request __all__ = ['ErrorMiddleware', 'handle_exception'] class _NoDefault: def __repr__(self): return '' NoDefault = _NoDefault() class ErrorMiddleware: """ Error handling middleware Usage:: error_catching_wsgi_app = ErrorMiddleware(wsgi_app) Settings: ``debug``: If true, then tracebacks will be shown in the browser. ``error_email``: an email address (or list of addresses) to send exception reports to ``error_log``: a filename to append tracebacks to ``show_exceptions_in_wsgi_errors``: If true, then errors will be printed to ``wsgi.errors`` (frequently a server error log, or stderr). ``from_address``, ``smtp_server``, ``error_subject_prefix``, ``smtp_username``, ``smtp_password``, ``smtp_use_tls``: variables to control the emailed exception reports ``error_message``: When debug mode is off, the error message to show to users. ``xmlhttp_key``: When this key (default ``_``) is in the request GET variables (not POST!), expect that this is an XMLHttpRequest, and the response should be more minimal; it should not be a complete HTML page. Environment Configuration: ``paste.throw_errors``: If this setting in the request environment is true, then this middleware is disabled. This can be useful in a testing situation where you don't want errors to be caught and transformed. ``paste.expected_exceptions``: When this middleware encounters an exception listed in this environment variable and when the ``start_response`` has not yet occurred, the exception will be re-raised instead of being caught. This should generally be set by middleware that may (but probably shouldn't be) installed above this middleware, and wants to get certain exceptions. Exceptions raised after ``start_response`` have been called are always caught since by definition they are no longer expected. """ def __init__(self, application, global_conf=None, debug=NoDefault, error_email=None, error_log=None, show_exceptions_in_wsgi_errors=NoDefault, from_address=None, smtp_server=None, smtp_username=None, smtp_password=None, smtp_use_tls=False, error_subject_prefix=None, error_message=None, xmlhttp_key=None): from paste.util import converters self.application = application # @@: global_conf should be handled elsewhere in a separate # function for the entry point if global_conf is None: global_conf = {} if debug is NoDefault: debug = converters.asbool(global_conf.get('debug')) if show_exceptions_in_wsgi_errors is NoDefault: show_exceptions_in_wsgi_errors = converters.asbool(global_conf.get('show_exceptions_in_wsgi_errors')) self.debug_mode = converters.asbool(debug) if error_email is None: error_email = (global_conf.get('error_email') or global_conf.get('admin_email') or global_conf.get('webmaster_email') or global_conf.get('sysadmin_email')) self.error_email = converters.aslist(error_email) self.error_log = error_log self.show_exceptions_in_wsgi_errors = show_exceptions_in_wsgi_errors if from_address is None: from_address = global_conf.get('error_from_address', 'errors@localhost') self.from_address = from_address if smtp_server is None: smtp_server = global_conf.get('smtp_server', 'localhost') self.smtp_server = smtp_server self.smtp_username = smtp_username or global_conf.get('smtp_username') self.smtp_password = smtp_password or global_conf.get('smtp_password') self.smtp_use_tls = smtp_use_tls or converters.asbool(global_conf.get('smtp_use_tls')) self.error_subject_prefix = error_subject_prefix or '' if error_message is None: error_message = global_conf.get('error_message') self.error_message = error_message if xmlhttp_key is None: xmlhttp_key = global_conf.get('xmlhttp_key', '_') self.xmlhttp_key = xmlhttp_key def __call__(self, environ, start_response): """ The WSGI application interface. """ # We want to be careful about not sending headers twice, # and the content type that the app has committed to (if there # is an exception in the iterator body of the response) if environ.get('paste.throw_errors'): return self.application(environ, start_response) environ['paste.throw_errors'] = True try: __traceback_supplement__ = Supplement, self, environ sr_checker = ResponseStartChecker(start_response) app_iter = self.application(environ, sr_checker) return self.make_catching_iter(app_iter, environ, sr_checker) except Exception: exc_info = sys.exc_info() try: for expect in environ.get('paste.expected_exceptions', []): if isinstance(exc_info[1], expect): raise start_response('500 Internal Server Error', [('content-type', 'text/html')], exc_info) # @@: it would be nice to deal with bad content types here response = self.exception_handler(exc_info, environ) response = response.encode('utf8') return [response] finally: # clean up locals... exc_info = None def make_catching_iter(self, app_iter, environ, sr_checker): if isinstance(app_iter, (list, tuple)): # These don't raise return app_iter return CatchingIter(app_iter, environ, sr_checker, self) def exception_handler(self, exc_info, environ): simple_html_error = False if self.xmlhttp_key: get_vars = request.parse_querystring(environ) if dict(get_vars).get(self.xmlhttp_key): simple_html_error = True return handle_exception( exc_info, environ['wsgi.errors'], html=True, debug_mode=self.debug_mode, error_email=self.error_email, error_log=self.error_log, show_exceptions_in_wsgi_errors=self.show_exceptions_in_wsgi_errors, error_email_from=self.from_address, smtp_server=self.smtp_server, smtp_username=self.smtp_username, smtp_password=self.smtp_password, smtp_use_tls=self.smtp_use_tls, error_subject_prefix=self.error_subject_prefix, error_message=self.error_message, simple_html_error=simple_html_error) class ResponseStartChecker: def __init__(self, start_response): self.start_response = start_response self.response_started = False def __call__(self, *args): self.response_started = True self.start_response(*args) class CatchingIter: """ A wrapper around the application iterator that will catch exceptions raised by the a generator, or by the close method, and display or report as necessary. """ def __init__(self, app_iter, environ, start_checker, error_middleware): self.app_iterable = app_iter self.app_iterator = iter(app_iter) self.environ = environ self.start_checker = start_checker self.error_middleware = error_middleware self.closed = False def __iter__(self): return self def next(self): __traceback_supplement__ = ( Supplement, self.error_middleware, self.environ) if self.closed: raise StopIteration try: return next(self.app_iterator) except StopIteration: self.closed = True close_response = self._close() if close_response is not None: return close_response else: raise StopIteration except Exception: self.closed = True close_response = self._close() exc_info = sys.exc_info() response = self.error_middleware.exception_handler( exc_info, self.environ) if close_response is not None: response += ( '
Error in .close():
%s' % close_response) if not self.start_checker.response_started: self.start_checker('500 Internal Server Error', [('content-type', 'text/html')], exc_info) response = response.encode('utf8') return response __next__ = next def close(self): # This should at least print something to stderr if the # close method fails at this point if not self.closed: self._close() def _close(self): """Close and return any error message""" if not hasattr(self.app_iterable, 'close'): return None try: self.app_iterable.close() return None except Exception: close_response = self.error_middleware.exception_handler( sys.exc_info(), self.environ) return close_response class Supplement: """ This is a supplement used to display standard WSGI information in the traceback. """ def __init__(self, middleware, environ): self.middleware = middleware self.environ = environ self.source_url = request.construct_url(environ) def extraData(self): data = {} cgi_vars = data[('extra', 'CGI Variables')] = {} wsgi_vars = data[('extra', 'WSGI Variables')] = {} hide_vars = ['paste.config', 'wsgi.errors', 'wsgi.input', 'wsgi.multithread', 'wsgi.multiprocess', 'wsgi.run_once', 'wsgi.version', 'wsgi.url_scheme'] for name, value in self.environ.items(): if name.upper() == name: if value: cgi_vars[name] = value elif name not in hide_vars: wsgi_vars[name] = value if self.environ['wsgi.version'] != (1, 0): wsgi_vars['wsgi.version'] = self.environ['wsgi.version'] proc_desc = tuple([int(bool(self.environ[key])) for key in ('wsgi.multiprocess', 'wsgi.multithread', 'wsgi.run_once')]) wsgi_vars['wsgi process'] = self.process_combos[proc_desc] wsgi_vars['application'] = self.middleware.application if 'paste.config' in self.environ: data[('extra', 'Configuration')] = dict(self.environ['paste.config']) return data process_combos = { # multiprocess, multithread, run_once (0, 0, 0): 'Non-concurrent server', (0, 1, 0): 'Multithreaded', (1, 0, 0): 'Multiprocess', (1, 1, 0): 'Multi process AND threads (?)', (0, 0, 1): 'Non-concurrent CGI', (0, 1, 1): 'Multithread CGI (?)', (1, 0, 1): 'CGI', (1, 1, 1): 'Multi thread/process CGI (?)', } def handle_exception(exc_info, error_stream, html=True, debug_mode=False, error_email=None, error_log=None, show_exceptions_in_wsgi_errors=False, error_email_from='errors@localhost', smtp_server='localhost', smtp_username=None, smtp_password=None, smtp_use_tls=False, error_subject_prefix='', error_message=None, simple_html_error=False, ): """ For exception handling outside of a web context Use like:: import sys from paste.exceptions.errormiddleware import handle_exception try: do stuff except Exception: handle_exception( sys.exc_info(), sys.stderr, html=False, ...other config...) If you want to report, but not fully catch the exception, call ``raise`` after ``handle_exception``, which (when given no argument) will reraise the exception. """ reported = False exc_data = collector.collect_exception(*exc_info) extra_data = '' if error_email: rep = reporter.EmailReporter( to_addresses=error_email, from_address=error_email_from, smtp_server=smtp_server, smtp_username=smtp_username, smtp_password=smtp_password, smtp_use_tls=smtp_use_tls, subject_prefix=error_subject_prefix) rep_err = send_report(rep, exc_data, html=html) if rep_err: extra_data += rep_err else: reported = True if error_log: rep = reporter.LogReporter( filename=error_log) rep_err = send_report(rep, exc_data, html=html) if rep_err: extra_data += rep_err else: reported = True if show_exceptions_in_wsgi_errors: rep = reporter.FileReporter( file=error_stream) rep_err = send_report(rep, exc_data, html=html) if rep_err: extra_data += rep_err else: reported = True else: line = ('Error - %s: %s\n' % (exc_data.exception_type, exc_data.exception_value)) error_stream.write(line) if html: if debug_mode and simple_html_error: return_error = formatter.format_html( exc_data, include_hidden_frames=False, include_reusable=False, show_extra_data=False) reported = True elif debug_mode and not simple_html_error: error_html = formatter.format_html( exc_data, include_hidden_frames=True, include_reusable=False) head_html = formatter.error_css + formatter.hide_display_js return_error = error_template( head_html, error_html, extra_data) extra_data = '' reported = True else: msg = error_message or ''' An error occurred. See the error logs for more information. (Turn debug on to display exception reports here) ''' return_error = error_template('', msg, '') else: return_error = None if not reported and error_stream: err_report = formatter.format_text(exc_data, show_hidden_frames=True) err_report += '\n' + '-'*60 + '\n' error_stream.write(err_report) if extra_data: error_stream.write(extra_data) return return_error def send_report(rep, exc_data, html=True): try: rep.report(exc_data) except Exception: output = StringIO() traceback.print_exc(file=output) if html: return """

Additionally an error occurred while sending the %s report:

%s

""" % ( html.escape(str(rep)), output.getvalue()) else: return ( "Additionally an error occurred while sending the " "%s report:\n%s" % (str(rep), output.getvalue())) else: return '' def error_template(head_html, exception, extra): return ''' Server Error %s

Server Error

%s %s ''' % (head_html, exception, extra) def make_error_middleware(app, global_conf, **kw): return ErrorMiddleware(app, global_conf=global_conf, **kw) doc_lines = ErrorMiddleware.__doc__.splitlines(True) for i in range(len(doc_lines)): if doc_lines[i].strip().startswith('Settings'): make_error_middleware.__doc__ = ''.join(doc_lines[i:]) break del i, doc_lines paste-3.10.1/paste/exceptions/formatter.py000066400000000000000000000460241461442501600205730ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php """ Formatters for the exception data that comes from ExceptionCollector. """ # @@: TODO: # Use this: http://www.zope.org/Members/tino/VisualTraceback/VisualTracebackNews import re import html from paste.util import PySourceColor def html_quote(s): return html.escape(str(s), True) class AbstractFormatter: general_data_order = ['object', 'source_url'] def __init__(self, show_hidden_frames=False, include_reusable=True, show_extra_data=True, trim_source_paths=()): self.show_hidden_frames = show_hidden_frames self.trim_source_paths = trim_source_paths self.include_reusable = include_reusable self.show_extra_data = show_extra_data def format_collected_data(self, exc_data): general_data = {} if self.show_extra_data: for name, value_list in exc_data.extra_data.items(): if isinstance(name, tuple): importance, title = name else: importance, title = 'normal', name for value in value_list: general_data[(importance, name)] = self.format_extra_data( importance, title, value) lines = [] frames = self.filter_frames(exc_data.frames) for frame in frames: sup = frame.supplement if sup: if sup.object: general_data[('important', 'object')] = self.format_sup_object( sup.object) if sup.source_url: general_data[('important', 'source_url')] = self.format_sup_url( sup.source_url) if sup.line: lines.append(self.format_sup_line_pos(sup.line, sup.column)) if sup.expression: lines.append(self.format_sup_expression(sup.expression)) if sup.warnings: for warning in sup.warnings: lines.append(self.format_sup_warning(warning)) if sup.info: lines.extend(self.format_sup_info(sup.info)) if frame.supplement_exception: lines.append('Exception in supplement:') lines.append(self.quote_long(frame.supplement_exception)) if frame.traceback_info: lines.append(self.format_traceback_info(frame.traceback_info)) filename = frame.filename if filename and self.trim_source_paths: for path, repl in self.trim_source_paths: if filename.startswith(path): filename = repl + filename[len(path):] break lines.append(self.format_source_line(filename or '?', frame)) source = frame.get_source_line() long_source = frame.get_source_line(2) if source: lines.append(self.format_long_source( source, long_source)) etype = exc_data.exception_type if not isinstance(etype, str): etype = etype.__name__ exc_info = self.format_exception_info( etype, exc_data.exception_value) data_by_importance = {'important': [], 'normal': [], 'supplemental': [], 'extra': []} for (importance, name), value in general_data.items(): data_by_importance[importance].append( (name, value)) for value in data_by_importance.values(): value.sort() return self.format_combine(data_by_importance, lines, exc_info) def filter_frames(self, frames): """ Removes any frames that should be hidden, according to the values of traceback_hide, self.show_hidden_frames, and the hidden status of the final frame. """ if self.show_hidden_frames: return frames new_frames = [] hidden = False for frame in frames: hide = frame.traceback_hide # @@: It would be nice to signal a warning if an unknown # hide string was used, but I'm not sure where to put # that warning. if hide == 'before': new_frames = [] hidden = False elif hide == 'before_and_this': new_frames = [] hidden = False continue elif hide == 'reset': hidden = False elif hide == 'reset_and_this': hidden = False continue elif hide == 'after': hidden = True elif hide == 'after_and_this': hidden = True continue elif hide: continue elif hidden: continue new_frames.append(frame) if frames[-1] not in new_frames: # We must include the last frame; that we don't indicates # that the error happened where something was "hidden", # so we just have to show everything return frames return new_frames def pretty_string_repr(self, s): """ Formats the string as a triple-quoted string when it contains newlines. """ if '\n' in s: s = repr(s) s = s[0]*3 + s[1:-1] + s[-1]*3 s = s.replace('\\n', '\n') return s else: return repr(s) def long_item_list(self, lst): """ Returns true if the list contains items that are long, and should be more nicely formatted. """ how_many = 0 for item in lst: if len(repr(item)) > 40: how_many += 1 if how_many >= 3: return True return False class TextFormatter(AbstractFormatter): def quote(self, s): return s def quote_long(self, s): return s def emphasize(self, s): return s def format_sup_object(self, obj): return 'In object: %s' % self.emphasize(self.quote(repr(obj))) def format_sup_url(self, url): return 'URL: %s' % self.quote(url) def format_sup_line_pos(self, line, column): if column: return self.emphasize('Line %i, Column %i' % (line, column)) else: return self.emphasize('Line %i' % line) def format_sup_expression(self, expr): return self.emphasize('In expression: %s' % self.quote(expr)) def format_sup_warning(self, warning): return 'Warning: %s' % self.quote(warning) def format_sup_info(self, info): return [self.quote_long(info)] def format_source_line(self, filename, frame): return 'File %r, line %s in %s' % ( filename, frame.lineno or '?', frame.name or '?') def format_long_source(self, source, long_source): return self.format_source(source) def format_source(self, source_line): return ' ' + self.quote(source_line.strip()) def format_exception_info(self, etype, evalue): return self.emphasize( '%s: %s' % (self.quote(etype), self.quote(evalue))) def format_traceback_info(self, info): return info def format_combine(self, data_by_importance, lines, exc_info): lines[:0] = [value for n, value in data_by_importance['important']] lines.append(exc_info) for name in 'normal', 'supplemental', 'extra': lines.extend([value for n, value in data_by_importance[name]]) return self.format_combine_lines(lines) def format_combine_lines(self, lines): return '\n'.join(lines) def format_extra_data(self, importance, title, value): if isinstance(value, str): s = self.pretty_string_repr(value) if '\n' in s: return '%s:\n%s' % (title, s) else: return '%s: %s' % (title, s) elif isinstance(value, dict): lines = ['\n', title, '-'*len(title)] items = value.items() items = sorted(items) for n, v in items: try: v = repr(v) except Exception as e: v = 'Cannot display: %s' % e v = truncate(v) lines.append(' %s: %s' % (n, v)) return '\n'.join(lines) elif (isinstance(value, (list, tuple)) and self.long_item_list(value)): parts = [truncate(repr(v)) for v in value] return '%s: [\n %s]' % ( title, ',\n '.join(parts)) else: return '%s: %s' % (title, truncate(repr(value))) class HTMLFormatter(TextFormatter): def quote(self, s): return html_quote(s) def quote_long(self, s): return '
%s
' % self.quote(s) def emphasize(self, s): return '%s' % s def format_sup_url(self, url): return 'URL: %s' % (url, url) def format_combine_lines(self, lines): return '
\n'.join(lines) def format_source_line(self, filename, frame): name = self.quote(frame.name or '?') return 'Module %s:%s in %s' % ( filename, frame.modname or '?', frame.lineno or '?', name) return 'File %r, line %s in %s' % ( filename, frame.lineno, name) def format_long_source(self, source, long_source): q_long_source = str2html(long_source, False, 4, True) q_source = str2html(source, True, 0, False) return ('' '>>  %s' % (q_long_source, q_source)) def format_source(self, source_line): return '  %s' % self.quote(source_line.strip()) def format_traceback_info(self, info): return '
%s
' % self.quote(info) def format_extra_data(self, importance, title, value): if isinstance(value, str): s = self.pretty_string_repr(value) if '\n' in s: return '%s:
%s
' % (title, self.quote(s)) else: return '%s: %s' % (title, self.quote(s)) elif isinstance(value, dict): return self.zebra_table(title, value) elif (isinstance(value, (list, tuple)) and self.long_item_list(value)): return '%s: [
\n    %s]
' % ( title, ',
    '.join(map(self.quote, map(repr, value)))) else: return '%s: %s' % (title, self.quote(repr(value))) def format_combine(self, data_by_importance, lines, exc_info): lines[:0] = [value for n, value in data_by_importance['important']] lines.append(exc_info) for name in 'normal', 'supplemental': lines.extend([value for n, value in data_by_importance[name]]) if data_by_importance['extra']: lines.append( '\n' + '
\n') lines.extend([value for n, value in data_by_importance['extra']]) lines.append('
') text = self.format_combine_lines(lines) if self.include_reusable: return error_css + hide_display_js + text else: # Usually because another error is already on this page, # and so the js & CSS are unneeded return text def zebra_table(self, title, rows, table_class="variables"): if isinstance(rows, dict): rows = rows.items() rows = sorted(rows) table = ['' % table_class, '' % self.quote(title)] odd = False for name, value in rows: try: value = repr(value) except Exception as e: value = 'Cannot print: %s' % e odd = not odd table.append( '' % (odd and 'odd' or 'even', self.quote(name))) table.append( '' % make_wrappable(self.quote(truncate(value)))) table.append('
%s
%s%s
') return '\n'.join(table) hide_display_js = r''' ''' error_css = """ """ def format_html(exc_data, include_hidden_frames=False, **ops): if not include_hidden_frames: return HTMLFormatter(**ops).format_collected_data(exc_data) short_er = format_html(exc_data, show_hidden_frames=False, **ops) # @@: This should have a way of seeing if the previous traceback # was actually trimmed at all ops['include_reusable'] = False ops['show_extra_data'] = False long_er = format_html(exc_data, show_hidden_frames=True, **ops) text_er = format_text(exc_data, show_hidden_frames=True, **ops) return """ %s
%s

""" % (short_er, long_er, html.escape(text_er)) def format_text(exc_data, **ops): return TextFormatter(**ops).format_collected_data(exc_data) whitespace_re = re.compile(r' +') pre_re = re.compile(r'') error_re = re.compile(r'

ERROR: .*?

') def str2html(src, strip=False, indent_subsequent=0, highlight_inner=False): """ Convert a string to HTML. Try to be really safe about it, returning a quoted version of the string if nothing else works. """ try: return _str2html(src, strip=strip, indent_subsequent=indent_subsequent, highlight_inner=highlight_inner) except Exception: return html_quote(src) def _str2html(src, strip=False, indent_subsequent=0, highlight_inner=False): if strip: src = src.strip() orig_src = src try: src = PySourceColor.str2html(src, form='snip') src = error_re.sub('', src) src = pre_re.sub('', src) src = re.sub(r'^[\n\r]{0,1}', '', src) src = re.sub(r'[\n\r]{0,1}$', '', src) except Exception: src = html_quote(orig_src) lines = src.splitlines() if len(lines) == 1: return lines[0] indent = ' '*indent_subsequent for i in range(1, len(lines)): lines[i] = indent+lines[i] if highlight_inner and i == len(lines)/2: lines[i] = '%s' % lines[i] src = '
\n'.join(lines) src = whitespace_re.sub( lambda m: ' '*(len(m.group(0))-1) + ' ', src) return src def truncate(string, limit=1000): """ Truncate the string to the limit number of characters """ if len(string) > limit: return string[:limit-20]+'...'+string[-17:] else: return string def make_wrappable(html, wrap_limit=60, split_on=';?&@!$#-/\\"\''): # Currently using , maybe should use ​ # http://www.cs.tut.fi/~jkorpela/html/nobr.html if len(html) <= wrap_limit: return html words = html.split() new_words = [] for word in words: wrapped_word = '' while len(word) > wrap_limit: for char in split_on: if char in word: first, rest = word.split(char, 1) wrapped_word += first+char+'' word = rest break else: for i in range(0, len(word), wrap_limit): wrapped_word += word[i:i+wrap_limit]+'' word = '' wrapped_word += word new_words.append(wrapped_word) return ' '.join(new_words) def make_pre_wrappable(html, wrap_limit=60, split_on=';?&@!$#-/\\"\''): """ Like ``make_wrappable()`` but intended for text that will go in a ``
`` block, so wrap on a line-by-line basis.
    """
    lines = html.splitlines()
    new_lines = []
    for line in lines:
        if len(line) > wrap_limit:
            for char in split_on:
                if char in line:
                    parts = line.split(char)
                    line = ''.join(parts)
                    break
        new_lines.append(line)
    return '\n'.join(lines)
paste-3.10.1/paste/exceptions/reporter.py000066400000000000000000000107151461442501600204300ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php

from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
import time
try:
    from socket import sslerror
except ImportError:
    sslerror = None
from paste.exceptions import formatter

class Reporter:

    def __init__(self, **conf):
        for name, value in conf.items():
            if not hasattr(self, name):
                raise TypeError(
                    "The keyword argument %s was not expected"
                    % name)
            setattr(self, name, value)
        self.check_params()

    def check_params(self):
        pass

    def format_date(self, exc_data):
        return time.strftime('%c', exc_data.date)

    def format_html(self, exc_data, **kw):
        return formatter.format_html(exc_data, **kw)

    def format_text(self, exc_data, **kw):
        return formatter.format_text(exc_data, **kw)

class EmailReporter(Reporter):

    to_addresses = None
    from_address = None
    smtp_server = 'localhost'
    smtp_username = None
    smtp_password = None
    smtp_use_tls = False
    subject_prefix = ''

    def report(self, exc_data):
        msg = self.assemble_email(exc_data)
        server = smtplib.SMTP(self.smtp_server)
        if self.smtp_use_tls:
            server.ehlo()
            server.starttls()
            server.ehlo()
        if self.smtp_username and self.smtp_password:
            server.login(self.smtp_username, self.smtp_password)
        server.sendmail(self.from_address,
                        self.to_addresses, msg.as_string())
        try:
            server.quit()
        except sslerror:
            # sslerror is raised in tls connections on closing sometimes
            pass

    def check_params(self):
        if not self.to_addresses:
            raise ValueError("You must set to_addresses")
        if not self.from_address:
            raise ValueError("You must set from_address")
        if isinstance(self.to_addresses, str):
            self.to_addresses = [self.to_addresses]

    def assemble_email(self, exc_data):
        short_html_version = self.format_html(
            exc_data, show_hidden_frames=False)
        long_html_version = self.format_html(
            exc_data, show_hidden_frames=True)
        text_version = self.format_text(
            exc_data, show_hidden_frames=False)
        msg = MIMEMultipart()
        msg.set_type('multipart/alternative')
        msg.preamble = msg.epilogue = ''
        text_msg = MIMEText(text_version)
        text_msg.set_type('text/plain')
        text_msg.set_param('charset', 'ASCII')
        msg.attach(text_msg)
        html_msg = MIMEText(short_html_version)
        html_msg.set_type('text/html')
        # @@: Correct character set?
        html_msg.set_param('charset', 'UTF-8')
        html_long = MIMEText(long_html_version)
        html_long.set_type('text/html')
        html_long.set_param('charset', 'UTF-8')
        msg.attach(html_msg)
        msg.attach(html_long)
        subject = '%s: %s' % (exc_data.exception_type,
                              formatter.truncate(str(exc_data.exception_value)))
        msg['Subject'] = self.subject_prefix + subject
        msg['From'] = self.from_address
        msg['To'] = ', '.join(self.to_addresses)
        return msg

class LogReporter(Reporter):

    filename = None
    show_hidden_frames = True

    def check_params(self):
        assert self.filename is not None, (
            "You must give a filename")

    def report(self, exc_data):
        text = self.format_text(
            exc_data, show_hidden_frames=self.show_hidden_frames)
        f = open(self.filename, 'a')
        try:
            f.write(text + '\n' + '-'*60 + '\n')
        finally:
            f.close()

class FileReporter(Reporter):

    file = None
    show_hidden_frames = True

    def check_params(self):
        assert self.file is not None, (
            "You must give a file object")

    def report(self, exc_data):
        text = self.format_text(
            exc_data, show_hidden_frames=self.show_hidden_frames)
        self.file.write(text + '\n' + '-'*60 + '\n')

class WSGIAppReporter(Reporter):

    def __init__(self, exc_data):
        self.exc_data = exc_data

    def __call__(self, environ, start_response):
        start_response('500 Server Error', [('Content-type', 'text/html')])
        return [formatter.format_html(self.exc_data)]
paste-3.10.1/paste/exceptions/serial_number_generator.py000066400000000000000000000100101461442501600234470ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php

"""
Creates a human-readable identifier, using numbers and digits,
avoiding ambiguous numbers and letters.  hash_identifier can be used
to create compact representations that are unique for a certain string
(or concatenation of strings)
"""

try:
    from hashlib import md5
except ImportError:
    from md5 import md5

import operator
byte2int = operator.itemgetter(0)

good_characters = "23456789abcdefghjkmnpqrtuvwxyz"

base = len(good_characters)

def make_identifier(number):
    """
    Encodes a number as an identifier.
    """
    if not isinstance(number, int):
        raise ValueError(
            "You can only make identifiers out of integers (not %r)"
            % number)
    if number < 0:
        raise ValueError(
            "You cannot make identifiers out of negative numbers: %r"
            % number)
    result = []
    while number:
        next = number % base
        result.append(good_characters[next])
        # Note, this depends on integer rounding of results:
        number = number // base
    return ''.join(result)

def hash_identifier(s, length, pad=True, hasher=md5, prefix='',
                    group=None, upper=False):
    """
    Hashes the string (with the given hashing module), then turns that
    hash into an identifier of the given length (using modulo to
    reduce the length of the identifier).  If ``pad`` is False, then
    the minimum-length identifier will be used; otherwise the
    identifier will be padded with 0's as necessary.

    ``prefix`` will be added last, and does not count towards the
    target length.  ``group`` will group the characters with ``-`` in
    the given lengths, and also does not count towards the target
    length.  E.g., ``group=4`` will cause a identifier like
    ``a5f3-hgk3-asdf``.  Grouping occurs before the prefix.
    """
    if not callable(hasher):
        # Accept sha/md5 modules as well as callables
        hasher = hasher.new
    if length > 26 and hasher is md5:
        raise ValueError(
            "md5 cannot create hashes longer than 26 characters in "
            "length (you gave %s)" % length)
    if isinstance(s, str):
        s = s.encode('utf-8')
    elif not isinstance(s, bytes):
        s = str(s)
        s = s.encode('utf-8')
    h = hasher(s)
    bin_hash = h.digest()
    modulo = base ** length
    number = 0
    for c in list(bin_hash):
        number = (number * 256 + byte2int([c])) % modulo
    ident = make_identifier(number)
    if pad:
        ident = good_characters[0]*(length-len(ident)) + ident
    if group:
        parts = []
        while ident:
            parts.insert(0, ident[-group:])
            ident = ident[:-group]
        ident = '-'.join(parts)
    if upper:
        ident = ident.upper()
    return prefix + ident

# doctest tests:
__test__ = {
    'make_identifier': """
    >>> make_identifier(0)
    ''
    >>> make_identifier(1000)
    'c53'
    >>> make_identifier(-100)
    Traceback (most recent call last):
        ...
    ValueError: You cannot make identifiers out of negative numbers: -100
    >>> make_identifier('test')
    Traceback (most recent call last):
        ...
    ValueError: You can only make identifiers out of integers (not 'test')
    >>> make_identifier(1000000000000)
    'c53x9rqh3'
    """,
    'hash_identifier': """
    >>> hash_identifier(0, 5)
    'cy2dr'
    >>> hash_identifier(0, 10)
    'cy2dr6rg46'
    >>> hash_identifier('this is a test of a long string', 5)
    'awatu'
    >>> hash_identifier(0, 26)
    'cy2dr6rg46cx8t4w2f3nfexzk4'
    >>> hash_identifier(0, 30)
    Traceback (most recent call last):
        ...
    ValueError: md5 cannot create hashes longer than 26 characters in length (you gave 30)
    >>> hash_identifier(0, 10, group=4)
    'cy-2dr6-rg46'
    >>> hash_identifier(0, 10, group=4, upper=True, prefix='M-')
    'M-CY-2DR6-RG46'
    """}

if __name__ == '__main__':
    import doctest
    doctest.testmod()

paste-3.10.1/paste/fileapp.py000066400000000000000000000333261461442501600160300ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
# (c) 2005 Ian Bicking, Clark C. Evans and contributors
# This module is part of the Python Paste Project and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
"""
This module handles sending static content such as in-memory data or
files.  At this time it has cache helpers and understands the
if-modified-since request header.
"""

import os, time, mimetypes, zipfile, tarfile
from paste.httpexceptions import (
    HTTPBadRequest,
    HTTPForbidden,
    HTTPMethodNotAllowed,
    HTTPNotFound,
    HTTPRequestRangeNotSatisfiable,
)
from paste.httpheaders import (
    get_header,
    list_headers,
    ACCEPT_RANGES,
    CACHE_CONTROL,
    CONTENT_DISPOSITION,
    CONTENT_LENGTH,
    CONTENT_RANGE,
    CONTENT_TYPE,
    ETAG,
    EXPIRES,
    IF_MODIFIED_SINCE,
    IF_NONE_MATCH,
    LAST_MODIFIED,
    RANGE,
)

CACHE_SIZE = 4096
BLOCK_SIZE = 4096 * 16

__all__ = ['DataApp', 'FileApp', 'DirectoryApp', 'ArchiveStore']

class DataApp:
    """
    Returns an application that will send content in a single chunk,
    this application has support for setting cache-control and for
    responding to conditional (or HEAD) requests.

    Constructor Arguments:

        ``content``     the content being sent to the client

        ``headers``     the headers to send /w the response

        The remaining ``kwargs`` correspond to headers, where the
        underscore is replaced with a dash.  These values are only
        added to the headers if they are not already provided; thus,
        they can be used for default values.  Examples include, but
        are not limited to:

            ``content_type``
            ``content_encoding``
            ``content_location``

    ``cache_control()``

        This method provides validated construction of the ``Cache-Control``
        header as well as providing for automated filling out of the
        ``EXPIRES`` header for HTTP/1.0 clients.

    ``set_content()``

        This method provides a mechanism to set the content after the
        application has been constructed.  This method does things
        like changing ``Last-Modified`` and ``Content-Length`` headers.

    """

    allowed_methods = ('GET', 'HEAD')

    def __init__(self, content, headers=None, allowed_methods=None,
                 **kwargs):
        assert isinstance(headers, (type(None), list))
        self.expires = None
        self.content = None
        self.content_length = None
        self.last_modified = 0
        if allowed_methods is not None:
            self.allowed_methods = allowed_methods
        self.headers = headers or []
        for (k, v) in kwargs.items():
            header = get_header(k)
            header.update(self.headers, v)
        ACCEPT_RANGES.update(self.headers, bytes=True)
        if not CONTENT_TYPE(self.headers):
            CONTENT_TYPE.update(self.headers)
        if content is not None:
            self.set_content(content)

    def cache_control(self, **kwargs):
        self.expires = CACHE_CONTROL.apply(self.headers, **kwargs) or None
        return self

    def set_content(self, content, last_modified=None):
        assert content is not None
        if last_modified is None:
            self.last_modified = time.time()
        else:
            self.last_modified = last_modified
        self.content = content
        self.content_length = len(content)
        LAST_MODIFIED.update(self.headers, time=self.last_modified)
        return self

    def content_disposition(self, **kwargs):
        CONTENT_DISPOSITION.apply(self.headers, **kwargs)
        return self

    def __call__(self, environ, start_response):
        method = environ['REQUEST_METHOD'].upper()
        if method not in self.allowed_methods:
            exc = HTTPMethodNotAllowed(
                'You cannot %s a file' % method,
                headers=[('Allow', ','.join(self.allowed_methods))])
            return exc(environ, start_response)
        return self.get(environ, start_response)

    def calculate_etag(self):
        return '"%s-%s"' % (self.last_modified, self.content_length)

    def get(self, environ, start_response):
        headers = self.headers[:]
        current_etag = self.calculate_etag()
        ETAG.update(headers, current_etag)
        if self.expires is not None:
            EXPIRES.update(headers, delta=self.expires)

        try:
            client_etags = IF_NONE_MATCH.parse(environ)
            if client_etags:
                for etag in client_etags:
                    if etag == current_etag or etag == '*':
                        # horribly inefficient, n^2 performance, yuck!
                        for head in list_headers(entity=True):
                            head.delete(headers)
                        start_response('304 Not Modified', headers)
                        return [b'']
        except HTTPBadRequest as exce:
            return exce.wsgi_application(environ, start_response)

        # If we get If-None-Match and If-Modified-Since, and
        # If-None-Match doesn't match, then we should not try to
        # figure out If-Modified-Since (which has 1-second granularity
        # and just isn't as accurate)
        if not client_etags:
            try:
                client_clock = IF_MODIFIED_SINCE.parse(environ)
                if (client_clock is not None
                    and client_clock >= int(self.last_modified)):
                    # horribly inefficient, n^2 performance, yuck!
                    for head in list_headers(entity=True):
                        head.delete(headers)
                    start_response('304 Not Modified', headers)
                    return [b''] # empty body
            except HTTPBadRequest as exce:
                return exce.wsgi_application(environ, start_response)

        (lower, upper) = (0, self.content_length - 1)
        range = RANGE.parse(environ)
        if range and 'bytes' == range[0] and 1 == len(range[1]):
            (lower, upper) = range[1][0]
            upper = upper or (self.content_length - 1)
            if upper >= self.content_length or lower > upper:
                return HTTPRequestRangeNotSatisfiable((
                  "Range request was made beyond the end of the content,\r\n"
                  "which is %s long.\r\n  Range: %s\r\n") % (
                     self.content_length, RANGE(environ))
                ).wsgi_application(environ, start_response)

        content_length = upper - lower + 1
        CONTENT_RANGE.update(headers, first_byte=lower, last_byte=upper,
                            total_length = self.content_length)
        CONTENT_LENGTH.update(headers, content_length)
        if range or content_length != self.content_length:
            start_response('206 Partial Content', headers)
        else:
            start_response('200 OK', headers)
        if self.content is not None:
            return [self.content[lower:upper+1]]
        return (lower, content_length)

class FileApp(DataApp):
    """
    Returns an application that will send the file at the given
    filename.  Adds a mime type based on ``mimetypes.guess_type()``.
    See DataApp for the arguments beyond ``filename``.
    """

    def __init__(self, filename, headers=None, **kwargs):
        self.filename = filename
        content_type, content_encoding = self.guess_type()
        if content_type and 'content_type' not in kwargs:
            kwargs['content_type'] = content_type
        if content_encoding and 'content_encoding' not in kwargs:
            kwargs['content_encoding'] = content_encoding
        DataApp.__init__(self, None, headers, **kwargs)

    def guess_type(self):
        return mimetypes.guess_type(self.filename)

    def update(self, force=False):
        stat = os.stat(self.filename)
        if not force and stat.st_mtime == self.last_modified:
            return
        self.last_modified = stat.st_mtime
        if stat.st_size < CACHE_SIZE:
            fh = open(self.filename,"rb")
            self.set_content(fh.read(), stat.st_mtime)
            fh.close()
        else:
            self.content = None
            self.content_length = stat.st_size
            # This is updated automatically if self.set_content() is
            # called
            LAST_MODIFIED.update(self.headers, time=self.last_modified)

    def get(self, environ, start_response):
        is_head = environ['REQUEST_METHOD'].upper() == 'HEAD'
        if 'max-age=0' in CACHE_CONTROL(environ).lower():
            self.update(force=True) # RFC 2616 13.2.6
        else:
            self.update()
        if not self.content:
            if not os.path.exists(self.filename):
                exc = HTTPNotFound(
                    'The resource does not exist',
                    comment="No file at %r" % self.filename)
                return exc(environ, start_response)
            try:
                file = open(self.filename, 'rb')
            except (IOError, OSError) as e:
                exc = HTTPForbidden(
                    'You are not permitted to view this file (%s)' % e)
                return exc.wsgi_application(
                    environ, start_response)
        retval = DataApp.get(self, environ, start_response)
        if isinstance(retval, list):
            # cached content, exception, or not-modified
            if is_head:
                return [b'']
            return retval
        (lower, content_length) = retval
        if is_head:
            return [b'']
        file.seek(lower)
        file_wrapper = environ.get('wsgi.file_wrapper', None)
        if file_wrapper:
            return file_wrapper(file, BLOCK_SIZE)
        else:
            return _FileIter(file, size=content_length)

class _FileIter:

    def __init__(self, file, block_size=None, size=None):
        self.file = file
        self.size = size
        self.block_size = block_size or BLOCK_SIZE

    def __iter__(self):
        return self

    def next(self):
        chunk_size = self.block_size
        if self.size is not None:
            if chunk_size > self.size:
                chunk_size = self.size
            self.size -= chunk_size
        data = self.file.read(chunk_size)
        if not data:
            raise StopIteration
        return data
    __next__ = next

    def close(self):
        self.file.close()


class DirectoryApp:
    """
    Returns an application that dispatches requests to corresponding FileApps based on PATH_INFO.
    FileApp instances are cached. This app makes sure not to serve any files that are not in a subdirectory.
    To customize FileApp creation override ``DirectoryApp.make_fileapp``
    """

    def __init__(self, path):
        self.path = os.path.abspath(path)
        if not self.path.endswith(os.path.sep):
            self.path += os.path.sep
        assert os.path.isdir(self.path)
        self.cached_apps = {}

    make_fileapp = FileApp

    def __call__(self, environ, start_response):
        path_info = environ['PATH_INFO']
        app = self.cached_apps.get(path_info)
        if app is None:
            path = os.path.join(self.path, path_info.lstrip('/'))
            if not os.path.normpath(path).startswith(self.path):
                app = HTTPForbidden()
            elif os.path.isfile(path):
                app = self.make_fileapp(path)
                self.cached_apps[path_info] = app
            else:
                app = HTTPNotFound(comment=path)
        return app(environ, start_response)


class ArchiveStore:
    """
    Returns an application that serves up a DataApp for items requested
    in a given zip or tar archive.

    Constructor Arguments:

        ``filepath``    the path to the archive being served

    ``cache_control()``

        This method provides validated construction of the ``Cache-Control``
        header as well as providing for automated filling out of the
        ``EXPIRES`` header for HTTP/1.0 clients.
    """

    def __init__(self, filepath):
        if zipfile.is_zipfile(filepath):
            self.archive = zipfile.ZipFile(filepath,"r")
        elif tarfile.is_tarfile(filepath):
            self.archive = tarfile.TarFileCompat(filepath,"r")
        else:
            raise AssertionError("filepath '%s' is not a zip or tar " % filepath)
        self.expires = None
        self.last_modified = time.time()
        self.cache = {}

    def cache_control(self, **kwargs):
        self.expires = CACHE_CONTROL.apply(self.headers, **kwargs) or None
        return self

    def __call__(self, environ, start_response):
        path = environ.get("PATH_INFO","")
        if path.startswith("/"):
            path = path[1:]
        application = self.cache.get(path)
        if application:
            return application(environ, start_response)
        try:
            info = self.archive.getinfo(path)
        except KeyError:
            exc = HTTPNotFound("The file requested, '%s', was not found." % path)
            return exc.wsgi_application(environ, start_response)
        if info.filename.endswith("/"):
            exc = HTTPNotFound("Path requested, '%s', is not a file." % path)
            return exc.wsgi_application(environ, start_response)
        content_type, content_encoding = mimetypes.guess_type(info.filename)
        # 'None' is not a valid content-encoding, so don't set the header if
        # mimetypes.guess_type returns None
        if content_encoding is not None:
            app = DataApp(None, content_type = content_type,
                                content_encoding = content_encoding)
        else:
            app = DataApp(None, content_type = content_type)
        app.set_content(self.archive.read(path),
                time.mktime(info.date_time + (0,0,0)))
        self.cache[path] = app
        app.expires = self.expires
        return app(environ, start_response)

paste-3.10.1/paste/fixture.py000066400000000000000000001637411461442501600161030ustar00rootroot00000000000000# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
"""
Routines for testing WSGI applications.

Most interesting is the `TestApp `_
for testing WSGI applications, and the `TestFileEnvironment
`_ class for testing the
effects of command-line scripts.
"""

import sys
import io
import random
import mimetypes
import time
import os
import shutil
import smtplib
import shlex
import re
import subprocess
from urllib import parse as urlparse
from urllib.parse import urlencode
from http.cookies import BaseCookie

from paste import lint, wsgilib
from paste.response import HeaderDict
from paste.util import NO_DEFAULT

def ensure_binary(s):
    if isinstance(s, bytes):
        return s
    else:
        return s.encode('utf-8')

def ensure_str(s, encoding='utf-8', errors='strict'):
    if type(s) is str:
        return s
    else:
        return s.decode(encoding, errors)

def tempnam_no_warning(*args):
    """
    An os.tempnam with the warning turned off, because sometimes
    you just need to use this and don't care about the stupid
    security warning.
    """
    return os.tempnam(*args)

def sorted(l):
    l = list(l)
    l.sort()
    return l

class Dummy_smtplib:

    existing = None

    def __init__(self, server):
        import warnings
        warnings.warn(
            'Dummy_smtplib is not maintained and is deprecated',
            DeprecationWarning, 2)
        assert not self.existing, (
            "smtplib.SMTP() called again before Dummy_smtplib.existing.reset() "
            "called.")
        self.server = server
        self.open = True
        self.__class__.existing = self

    def quit(self):
        assert self.open, (
            "Called %s.quit() twice" % self)
        self.open = False

    def sendmail(self, from_address, to_addresses, msg):
        self.from_address = from_address
        self.to_addresses = to_addresses
        self.message = msg

    def install(cls):
        smtplib.SMTP = cls

    install = classmethod(install)

    def reset(self):
        assert not self.open, (
            "SMTP connection not quit")
        self.__class__.existing = None


class AppError(Exception):
    pass


class TestApp:

    __test__ = False  # Ignore with pytest test collection.

    def __init__(self, app, namespace=None, relative_to=None,
                 extra_environ=None, pre_request_hook=None,
                 post_request_hook=None):
        """
        Wraps a WSGI application in a more convenient interface for
        testing.

        ``app`` may be an application, or a Paste Deploy app
        URI, like ``'config:filename.ini#test'``.

        ``namespace`` is a dictionary that will be written to (if
        provided).  This can be used with doctest or some other
        system, and the variable ``res`` will be assigned everytime
        you make a request (instead of returning the request).

        ``relative_to`` is a directory, and filenames used for file
        uploads are calculated relative to this.  Also ``config:``
        URIs that aren't absolute.

        ``extra_environ`` is a dictionary of values that should go
        into the environment for each request.  These can provide a
        communication channel with the application.

        ``pre_request_hook`` is a function to be called prior to
        making requests (such as ``post`` or ``get``). This function
        must take one argument (the instance of the TestApp).

        ``post_request_hook`` is a function, similar to
        ``pre_request_hook``, to be called after requests are made.
        """
        if isinstance(app, (bytes, str)):
            from paste.deploy import loadapp
            # @@: Should pick up relative_to from calling module's
            # __file__
            app = loadapp(app, relative_to=relative_to)
        self.app = app
        self.namespace = namespace
        self.relative_to = relative_to
        if extra_environ is None:
            extra_environ = {}
        self.extra_environ = extra_environ
        self.pre_request_hook = pre_request_hook
        self.post_request_hook = post_request_hook
        self.reset()

    def reset(self):
        """
        Resets the state of the application; currently just clears
        saved cookies.
        """
        self.cookies = {}

    def _make_environ(self):
        environ = self.extra_environ.copy()
        environ['paste.throw_errors'] = True
        return environ

    def get(self, url, params=None, headers=None, extra_environ=None,
            status=None, expect_errors=False):
        """
        Get the given url (well, actually a path like
        ``'/page.html'``).

        ``params``:
            A query string, or a dictionary that will be encoded
            into a query string.  You may also include a query
            string on the ``url``.

        ``headers``:
            A dictionary of extra headers to send.

        ``extra_environ``:
            A dictionary of environmental variables that should
            be added to the request.

        ``status``:
            The integer status code you expect (if not 200 or 3xx).
            If you expect a 404 response, for instance, you must give
            ``status=404`` or it will be an error.  You can also give
            a wildcard, like ``'3*'`` or ``'*'``.

        ``expect_errors``:
            If this is not true, then if anything is written to
            ``wsgi.errors`` it will be an error.  If it is true, then
            non-200/3xx responses are also okay.

        Returns a `response object
        `_
        """
        if extra_environ is None:
            extra_environ = {}
        __tracebackhide__ = True  # Hide from pytest:
        if params:
            if not isinstance(params, (bytes, str)):
                params = urlencode(params, doseq=True)
            if '?' in url:
                url += '&'
            else:
                url += '?'
            url += params
        environ = self._make_environ()
        url = str(url)
        if '?' in url:
            url, environ['QUERY_STRING'] = url.split('?', 1)
        else:
            environ['QUERY_STRING'] = ''
        self._set_headers(headers, environ)
        environ.update(extra_environ)
        req = TestRequest(url, environ, expect_errors)
        return self.do_request(req, status=status)

    def _gen_request(self, method, url, params=b'', headers=None, extra_environ=None,
             status=None, upload_files=None, expect_errors=False):
        """
        Do a generic request.
        """
        if headers is None:
            headers = {}
        if extra_environ is None:
            extra_environ = {}
        environ = self._make_environ()
        # @@: Should this be all non-strings?
        if isinstance(params, (list, tuple, dict)):
            params = urlencode(params)
        if hasattr(params, 'items'):
            # Some other multi-dict like format
            params = urlencode(params.items())
        if isinstance(params, str):
            params = params.encode('utf8')
        if upload_files:
            params = urlparse.parse_qsl(params, keep_blank_values=True)
            content_type, params = self.encode_multipart(
                params, upload_files)
            environ['CONTENT_TYPE'] = content_type
        elif params:
            environ.setdefault('CONTENT_TYPE', 'application/x-www-form-urlencoded')
        url = str(url)
        if '?' in url:
            url, environ['QUERY_STRING'] = url.split('?', 1)
        else:
            environ['QUERY_STRING'] = ''
        environ['CONTENT_LENGTH'] = str(len(params))
        environ['REQUEST_METHOD'] = method
        environ['wsgi.input'] = io.BytesIO(params)
        self._set_headers(headers, environ)
        environ.update(extra_environ)
        req = TestRequest(url, environ, expect_errors)
        return self.do_request(req, status=status)

    def post(self, url, params=b'', headers=None, extra_environ=None,
             status=None, upload_files=None, expect_errors=False):
        """
        Do a POST request.  Very like the ``.get()`` method.
        ``params`` are put in the body of the request.

        ``upload_files`` is for file uploads.  It should be a list of
        ``[(fieldname, filename, file_content)]``.  You can also use
        just ``[(fieldname, filename)]`` and the file content will be
        read from disk.

        Returns a `response object
        `_
        """
        return self._gen_request('POST', url, params=params, headers=headers,
                                 extra_environ=extra_environ,status=status,
                                 upload_files=upload_files,
                                 expect_errors=expect_errors)

    def put(self, url, params=b'', headers=None, extra_environ=None,
             status=None, upload_files=None, expect_errors=False):
        """
        Do a PUT request.  Very like the ``.get()`` method.
        ``params`` are put in the body of the request.

        ``upload_files`` is for file uploads.  It should be a list of
        ``[(fieldname, filename, file_content)]``.  You can also use
        just ``[(fieldname, filename)]`` and the file content will be
        read from disk.

        Returns a `response object
        `_
        """
        return self._gen_request('PUT', url, params=params, headers=headers,
                                 extra_environ=extra_environ,status=status,
                                 upload_files=upload_files,
                                 expect_errors=expect_errors)

    def delete(self, url, params=b'', headers=None, extra_environ=None,
               status=None, expect_errors=False):
        """
        Do a DELETE request.  Very like the ``.get()`` method.
        ``params`` are put in the body of the request.

        Returns a `response object
        `_
        """
        return self._gen_request('DELETE', url, params=params, headers=headers,
                                 extra_environ=extra_environ,status=status,
                                 upload_files=None, expect_errors=expect_errors)

    def head(self, url, headers=None, extra_environ=None,
             status=None, expect_errors=False):
        """
        Do a HEAD request.  Very like the ``.get()`` method.

        Returns a `response object
        `_
        """
        return self._gen_request('HEAD', url, headers=headers,
                                 extra_environ=extra_environ,status=status,
                                 upload_files=None, expect_errors=expect_errors)




    def _set_headers(self, headers, environ):
        """
        Turn any headers into environ variables
        """
        if not headers:
            return
        for header, value in headers.items():
            if header.lower() == 'content-type':
                var = 'CONTENT_TYPE'
            elif header.lower() == 'content-length':
                var = 'CONTENT_LENGTH'
            else:
                var = 'HTTP_%s' % header.replace('-', '_').upper()
            environ[var] = value

    def encode_multipart(self, params, files):
        """
        Encodes a set of parameters (typically a name/value list) and
        a set of files (a list of (name, filename, file_body)) into a
        typical POST body, returning the (content_type, body).
        """
        boundary = '----------a_BoUnDaRy%s$' % random.random()
        content_type = 'multipart/form-data; boundary=%s' % boundary
        boundary = boundary.encode('ascii')

        lines = []
        for key, value in params:
            lines.append(b'--'+boundary)
            line = b'Content-Disposition: form-data; name="%s"' % ensure_binary(key)
            lines.append(line)
            lines.append(b'')
            line = ensure_binary(value)
            lines.append(line)
        for file_info in files:
            key, filename, value = self._get_file_info(file_info)
            lines.append(b'--'+boundary)
            line = (b'Content-Disposition: form-data; name="%s"; filename="%s"'
                         % (ensure_binary(key), ensure_binary(filename)))
            lines.append(line)
            fcontent = mimetypes.guess_type(ensure_str(filename, 'ascii', 'ignore'))[0]
            line = (b'Content-Type: %s'
                    % (fcontent.encode('ascii') if fcontent else b'application/octet-stream'))
            lines.append(line)
            lines.append(b'')
            lines.append(value)
        lines.append(b'--' + boundary + b'--')
        lines.append(b'')
        body = b'\r\n'.join(lines)
        return content_type, body

    def _get_file_info(self, file_info):
        if len(file_info) == 2:
            # It only has a filename
            filename = file_info[1]
            if self.relative_to:
                filename = os.path.join(self.relative_to, filename)
            f = open(filename, 'rb')
            content = f.read()
            f.close()
            return (file_info[0], filename, content)
        elif len(file_info) == 3:
            return file_info
        else:
            raise ValueError(
                "upload_files need to be a list of tuples of (fieldname, "
                "filename, filecontent) or (fieldname, filename); "
                "you gave: %r"
                % repr(file_info)[:100])

    def do_request(self, req, status):
        """
        Executes the given request (``req``), with the expected
        ``status``.  Generally ``.get()`` and ``.post()`` are used
        instead.
        """
        if self.pre_request_hook:
            self.pre_request_hook(self)
        __tracebackhide__ = True
        if self.cookies:
            c = BaseCookie()
            for name, value in self.cookies.items():
                c[name] = value
            hc = '; '.join(['='.join([m.key, m.value]) for m in c.values()])
            req.environ['HTTP_COOKIE'] = hc
        req.environ['paste.testing'] = True
        req.environ['paste.testing_variables'] = {}
        app = lint.middleware(self.app)
        old_stdout = sys.stdout
        out = CaptureStdout(old_stdout)
        try:
            sys.stdout = out
            start_time = time.time()
            raise_on_wsgi_error = not req.expect_errors
            raw_res = wsgilib.raw_interactive(
                app, req.url,
                raise_on_wsgi_error=raise_on_wsgi_error,
                **req.environ)
            end_time = time.time()
        finally:
            sys.stdout = old_stdout
            sys.stderr.write(out.getvalue())
        res = self._make_response(raw_res, end_time - start_time)
        res.request = req
        for name, value in req.environ['paste.testing_variables'].items():
            if hasattr(res, name):
                raise ValueError(
                    "paste.testing_variables contains the variable %r, but "
                    "the response object already has an attribute by that "
                    "name" % name)
            setattr(res, name, value)
        if self.namespace is not None:
            self.namespace['res'] = res
        if not req.expect_errors:
            self._check_status(status, res)
            self._check_errors(res)
        res.cookies_set = {}
        for header in res.all_headers('set-cookie'):
            c = BaseCookie(header)
            for key, morsel in c.items():
                self.cookies[key] = morsel.value
                res.cookies_set[key] = morsel.value
        if self.post_request_hook:
            self.post_request_hook(self)
        if self.namespace is None:
            # It's annoying to return the response in doctests, as it'll
            # be printed, so we only return it is we couldn't assign
            # it anywhere
            return res

    def _check_status(self, status, res):
        __tracebackhide__ = True
        if status == '*':
            return
        if isinstance(status, (list, tuple)):
            if res.status not in status:
                raise AppError(
                    "Bad response: %s (not one of %s for %s)\n%s"
                    % (res.full_status, ', '.join(map(str, status)),
                       res.request.url, res.body))
            return
        if status is None:
            if res.status >= 200 and res.status < 400:
                return
            body = res.body
            body = body.decode('utf8', 'xmlcharrefreplace')
            raise AppError(
                "Bad response: %s (not 200 OK or 3xx redirect for %s)\n%s"
                % (res.full_status, res.request.url,
                   body))
        if status != res.status:
            raise AppError(
                "Bad response: %s (not %s)" % (res.full_status, status))

    def _check_errors(self, res):
        if res.errors:
            raise AppError(
                "Application had errors logged:\n%s" % res.errors)

    def _make_response(self, resp, total_time):
        status, headers, body, errors = resp
        return TestResponse(self, status, headers, body, errors,
                            total_time)

class CaptureStdout:

    def __init__(self, actual):
        self.captured = io.StringIO()
        self.actual = actual

    def write(self, s):
        self.captured.write(s)
        self.actual.write(s)

    def flush(self):
        self.actual.flush()

    def writelines(self, lines):
        for item in lines:
            self.write(item)

    def getvalue(self):
        return self.captured.getvalue()


class TestResponse:

    __test__ = False  # Ignore with pytest test collection.

    """
    Instances of this class are return by `TestApp
    `_
    """

    def __init__(self, test_app, status, headers, body, errors,
                 total_time):
        self.test_app = test_app
        self.status = int(status.split()[0])
        self.full_status = status
        self.headers = headers
        self.header_dict = HeaderDict.fromlist(self.headers)
        self.body = body
        self.errors = errors
        self._normal_body = None
        self.time = total_time
        self._forms_indexed = None

    def forms__get(self):
        """
        Returns a dictionary of ``Form`` objects.  Indexes are both in
        order (from zero) and by form id (if the form is given an id).
        """
        if self._forms_indexed is None:
            self._parse_forms()
        return self._forms_indexed

    forms = property(forms__get,
                     doc="""
                     A list of 
s found on the page (instances of `Form `_) """) def form__get(self): forms = self.forms if not forms: raise TypeError( "You used response.form, but no forms exist") if 1 in forms: # There is more than one form raise TypeError( "You used response.form, but more than one form exists") return forms[0] form = property(form__get, doc=""" Returns a single `Form `_ instance; it is an error if there are multiple forms on the page. """) _tag_re = re.compile(r'<(/?)([:a-z0-9_\-]*)(.*?)>', re.S|re.I) def _parse_forms(self): forms = self._forms_indexed = {} form_texts = [] started = None body = self.body body = body.decode('utf8', 'xmlcharrefreplace') for match in self._tag_re.finditer(body): end = match.group(1) == '/' tag = match.group(2).lower() if tag != 'form': continue if end: assert started, ( " unexpected at %s" % match.start()) form_texts.append(body[started:match.end()]) started = None else: assert not started, ( "Nested form tags at %s" % match.start()) started = match.start() assert not started, ( "Dangling form: %r" % body[started:]) for i, text in enumerate(form_texts): form = Form(self, text) forms[i] = form if form.id: forms[form.id] = form def header(self, name, default=NO_DEFAULT): """ Returns the named header; an error if there is not exactly one matching header (unless you give a default -- always an error if there is more than one header) """ found = None for cur_name, value in self.headers: if cur_name.lower() == name.lower(): assert not found, ( "Ambiguous header: %s matches %r and %r" % (name, found, value)) found = value if found is None: if default is NO_DEFAULT: raise KeyError( "No header found: %r (from %s)" % (name, ', '.join([n for n, v in self.headers]))) else: return default return found def all_headers(self, name): """ Gets all headers by the ``name``, returns as a list """ found = [] for cur_name, value in self.headers: if cur_name.lower() == name.lower(): found.append(value) return found def follow(self, **kw): """ If this request is a redirect, follow that redirect. It is an error if this is not a redirect response. Returns another response object. """ assert self.status >= 300 and self.status < 400, ( "You can only follow redirect responses (not %s)" % self.full_status) location = self.header('location') # @@: We should test that it's not a remote redirect return self.test_app.get(location, **kw) def click(self, description=None, linkid=None, href=None, anchor=None, index=None, verbose=False): """ Click the link as described. Each of ``description``, ``linkid``, and ``url`` are *patterns*, meaning that they are either strings (regular expressions), compiled regular expressions (objects with a ``search`` method), or callables returning true or false. All the given patterns are ANDed together: * ``description`` is a pattern that matches the contents of the anchor (HTML and all -- everything between ```` and ````) * ``linkid`` is a pattern that matches the ``id`` attribute of the anchor. It will receive the empty string if no id is given. * ``href`` is a pattern that matches the ``href`` of the anchor; the literal content of that attribute, not the fully qualified attribute. * ``anchor`` is a pattern that matches the entire anchor, with its contents. If more than one link matches, then the ``index`` link is followed. If ``index`` is not given and more than one link matches, or if no link matches, then ``IndexError`` will be raised. If you give ``verbose`` then messages will be printed about each link, and why it does or doesn't match. If you use ``app.click(verbose=True)`` you'll see a list of all the links. You can use multiple criteria to essentially assert multiple aspects about the link, e.g., where the link's destination is. """ __tracebackhide__ = True found_html, found_desc, found_attrs = self._find_element( tag='a', href_attr='href', href_extract=None, content=description, id=linkid, href_pattern=href, html_pattern=anchor, index=index, verbose=verbose) return self.goto(found_attrs['uri']) def clickbutton(self, description=None, buttonid=None, href=None, button=None, index=None, verbose=False): """ Like ``.click()``, except looks for link-like buttons. This kind of button should look like ``