summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2012-12-07 00:34:49 -0500
committerRyan Petrello <lists@ryanpetrello.com>2012-12-07 00:34:49 -0500
commitfea00ca8feebdf494a76bdc4aab3524d7ff425a9 (patch)
tree1a9db21e61b5ff4ff92022e28a691da6936c8132
parent9dc346e23ecc1a18a529d8ffe080c18df6e0a30b (diff)
parent338cb6ddcaefee1d390a1d7ec3b01c1bd2438d22 (diff)
downloadpecan-fea00ca8feebdf494a76bdc4aab3524d7ff425a9.tar.gz
Merge branch 'next'
-rw-r--r--AUTHORS3
-rw-r--r--README.rst2
-rw-r--r--docs/source/conf.py4
-rw-r--r--docs/source/configuration.rst9
-rw-r--r--docs/source/hooks.rst3
-rw-r--r--docs/source/quick_start.rst9
-rw-r--r--docs/source/routing.rst2
-rw-r--r--docs/source/sessions.rst2
-rw-r--r--pecan/__init__.py42
-rw-r--r--pecan/commands/shell.py11
-rw-r--r--pecan/core.py90
-rw-r--r--pecan/extensions.py2
-rw-r--r--pecan/hooks.py33
-rw-r--r--pecan/middleware/debug.py590
-rw-r--r--pecan/middleware/recursive.py21
-rw-r--r--pecan/middleware/resources/__init__.py26
-rw-r--r--pecan/middleware/resources/brush-python.js64
-rw-r--r--pecan/middleware/resources/pecan.pngbin0 -> 4454 bytes
-rw-r--r--pecan/middleware/resources/syntax.css226
-rw-r--r--pecan/middleware/resources/syntax.js17
-rw-r--r--pecan/middleware/resources/theme.css117
-rw-r--r--pecan/rest.py6
-rw-r--r--pecan/secure.py31
-rw-r--r--pecan/templating.py6
-rw-r--r--pecan/tests/middleware/test_debug.py26
-rw-r--r--pecan/tests/middleware/test_recursive.py5
-rw-r--r--pecan/tests/middleware/test_static.py4
-rw-r--r--pecan/tests/test_base.py55
-rw-r--r--pecan/tests/test_conf.py9
-rw-r--r--pecan/tests/test_hooks.py9
-rw-r--r--pecan/tests/test_jsonify.py9
-rw-r--r--pecan/tests/test_scaffolds.py59
-rw-r--r--pecan/tests/test_secure.py43
-rw-r--r--setup.py14
-rw-r--r--tox.ini6
35 files changed, 827 insertions, 728 deletions
diff --git a/AUTHORS b/AUTHORS
index 2bd4c5a..27abd07 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -10,3 +10,6 @@ Jeremy Jones
Benjamin W. Smith
Pete Chudykowski
Mike Perez
+Justin Barber
+Wesley Spikes
+Steven Berler
diff --git a/README.rst b/README.rst
index 8884f5a..4492de5 100644
--- a/README.rst
+++ b/README.rst
@@ -19,7 +19,7 @@ Installing
...or, for the latest (unstable) tip::
$ git clone https://github.com/dreamhost/pecan.git -b next
- $ cd pecan && python setup.py develop
+ $ cd pecan && python setup.py install
Running Tests
-------------
diff --git a/docs/source/conf.py b/docs/source/conf.py
index d9f58f7..a80fd3a 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -49,9 +49,9 @@ copyright = u'2010, Jonathan LaCour'
# built documents.
#
# The short X.Y version.
-version = '0.2.0a'
+version = '0.2.0b'
# The full version, including alpha/beta/rc tags.
-release = '0.2.0a'
+release = '0.2.0b'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst
index befefcc..84549a7 100644
--- a/docs/source/configuration.rst
+++ b/docs/source/configuration.rst
@@ -49,6 +49,15 @@ Let's look at each value and what it means:
**app** is a reserved variable name for the configuration, so make sure you
don't override it.
+**modules** is a list of modules where pecan will search for applications.
+Generally this should contain a single item, the name of your project's
+python package.
+At least one of the listed modules must contain an ``app.setup_app`` function
+which is called to create the WSGI app. In other words, this package should
+be where your ``app.py`` file is located, and this file should contain a
+``setup_app`` function.
+See :ref:`app_template` for more about the ``app.py`` file.
+
**root** The root controller of your application. Remember to provide
a string representing a Python path to some callable (e.g.,
``"yourapp.controllers.root.RootController"``).
diff --git a/docs/source/hooks.rst b/docs/source/hooks.rst
index 1c86d76..bc00c75 100644
--- a/docs/source/hooks.rst
+++ b/docs/source/hooks.rst
@@ -61,9 +61,10 @@ Hooks can also be applied selectively to controllers and their sub-controllers
using the ``__hooks__`` attribute on one or more controllers::
from pecan import expose
+ from pecan.hooks import HookController
from my_hooks import SimpleHook
- class SimpleController(object):
+ class SimpleController(HookController):
__hooks__ = [SimpleHook()]
diff --git a/docs/source/quick_start.rst b/docs/source/quick_start.rst
index 941c076..c35ce84 100644
--- a/docs/source/quick_start.rst
+++ b/docs/source/quick_start.rst
@@ -9,6 +9,7 @@ Let's create a small sample project with Pecan.
This guide does not cover the installation of Pecan. If you need
instructions for installing Pecan, go to :ref:`installation`.
+.. _app_template:
Base Application Template
-------------------------
@@ -76,6 +77,13 @@ remaining directories encompass your models, controllers and templates...
* **test_project/tests**: All of the tests for your application.
+The **test_project/app.py** file controls how the Pecan application will be
+created. This file must contain a ``setup_app`` function which returns the
+WSGI application object. Generally you will not need to modify the ``app.py``
+file provided by the base application template unless you need to customize
+your app in a way that cannot be accomplished using config. See
+:ref:`python_based_config` below.
+
To avoid unneeded dependencies and to remain as flexible as possible, Pecan
doesn't impose any database or ORM
(`Object Relational Mapper
@@ -111,6 +119,7 @@ configuration, it will bring up the development server and serve the app::
The location for the configuration file and the argument itself are very
flexible - you can pass an absolute or relative path to the file.
+.. _python_based_config:
Python-Based Configuration
--------------------------
diff --git a/docs/source/routing.rst b/docs/source/routing.rst
index bf8bcaf..ac6a9b2 100644
--- a/docs/source/routing.rst
+++ b/docs/source/routing.rst
@@ -252,7 +252,7 @@ explicitly to method arguments:
::
- $ curl http://localhost:8080/one/two/three
+ $ curl http://localhost:8080/args/one/two/three
one,two,three
The same effect can be achieved with HTTP ``POST`` body variables:
diff --git a/docs/source/sessions.rst b/docs/source/sessions.rst
index b7960b2..0faa83d 100644
--- a/docs/source/sessions.rst
+++ b/docs/source/sessions.rst
@@ -18,7 +18,7 @@ Here's an example of wrapping your WSGI application with Beaker's
``SessionMiddleware`` in your project's `app.py`::
from pecan import conf, make_app
- from beaker import SessionMiddleware
+ from beaker.middleware import SessionMiddleware
from test_project import model
app = make_app(
diff --git a/pecan/__init__.py b/pecan/__init__.py
index 82812ad..705b1f2 100644
--- a/pecan/__init__.py
+++ b/pecan/__init__.py
@@ -27,13 +27,47 @@ __all__ = [
def make_app(root, static_root=None, logging={}, debug=False,
wrap_app=None, **kw):
+ '''
+ Utility for creating the Pecan application object. This function should
+ generally be called from the ``setup_app`` function in your project's
+ ``app.py`` file.
+ :param root: A string representing a root controller object (e.g.,
+ "myapp.controller.root.RootController")
+ :param static_root: The relative path to a directory containing static
+ files. Serving static files is only enabled when
+ debug mode is set.
+ :param logging: A dictionary used to configure logging. This uses
+ ``logging.config.dictConfig``.
+ :param debug: A flag to enable debug mode. This enables the debug
+ middleware and serving static files.
+ :param wrap_app: A function or middleware class to wrap the Pecan app.
+ This must either be a wsgi middleware class or a
+ function that returns a wsgi application. This wrapper
+ is applied first before wrapping the application in
+ other middlewares such as Pecan's debug middleware.
+ This should be used if you want to use middleware to
+ perform authentication or intercept all requests before
+ they are routed to the root controller.
+
+ All other keyword arguments are passed in to the Pecan app constructor.
+
+ :returns: a ``Pecan`` object.
+ '''
# A shortcut for the RequestViewerHook middleware.
if hasattr(conf, 'requestviewer'):
existing_hooks = kw.get('hooks', [])
existing_hooks.append(RequestViewerHook(conf.requestviewer))
kw['hooks'] = existing_hooks
+ # Pass logging configuration (if it exists) on to the Python logging module
+ if logging:
+ if isinstance(logging, Config):
+ logging = logging.to_dict()
+ if 'version' not in logging:
+ logging['version'] = 1
+ load_logging_config(logging)
+
# Instantiate the WSGI app by passing **kw onward
app = Pecan(root, **kw)
@@ -48,14 +82,6 @@ def make_app(root, static_root=None, logging={}, debug=False,
# Included for internal redirect support
app = RecursiveMiddleware(app)
- # Pass logging configuration (if it exists) on to the Python logging module
- if logging:
- if isinstance(logging, Config):
- logging = logging.to_dict()
- if 'version' not in logging:
- logging['version'] = 1
- load_logging_config(logging)
-
# When in debug mode, load our exception dumping middleware
if debug:
app = DebugMiddleware(app)
diff --git a/pecan/commands/shell.py b/pecan/commands/shell.py
index 49a6ef3..8f7a38a 100644
--- a/pecan/commands/shell.py
+++ b/pecan/commands/shell.py
@@ -51,7 +51,12 @@ class IPythonShell(object):
from IPython.frontend.terminal.embed import (
InteractiveShellEmbed
)
- shell = InteractiveShellEmbed(banner2=banner)
+ # try and load their default profile
+ from IPython.frontend.terminal.ipapp import (
+ load_default_config
+ )
+ config = load_default_config()
+ shell = InteractiveShellEmbed(config=config, banner2=banner)
shell(local_ns=ns)
except ImportError:
# Support for the IPython <= 0.10 shell API
@@ -149,8 +154,8 @@ class ShellCommand(BaseCommand):
try:
shell().invoke(locs, banner)
except ImportError, e:
- warn(
- ("%s is not installed, `%s`, "
+ warn((
+ "%s is not installed, `%s`, "
"falling back to native shell") % (self.args.shell, e),
RuntimeWarning
)
diff --git a/pecan/core.py b/pecan/core.py
index 0184764..8155180 100644
--- a/pecan/core.py
+++ b/pecan/core.py
@@ -1,27 +1,29 @@
-from templating import RendererFactory
-from routing import lookup_controller, NonCanonicalPath
-from util import _cfg, encode_if_needed
-from middleware.recursive import ForwardRequestException
-
-from webob import Request, Response, exc
+import urllib
+import sys
+try:
+ from simplejson import loads
+except ImportError: # pragma: no cover
+ from json import loads # noqa
from threading import local
from itertools import chain
from mimetypes import guess_type, add_type
from urlparse import urlsplit, urlunsplit
from os.path import splitext
+import logging
-try:
- from simplejson import loads
-except ImportError: # pragma: no cover
- from json import loads # noqa
+from webob import Request, Response, exc, acceptparse
+
+from templating import RendererFactory
+from routing import lookup_controller, NonCanonicalPath
+from util import _cfg, encode_if_needed
+from middleware.recursive import ForwardRequestException
-import urllib
-import sys
# make sure that json is defined in mimetypes
add_type('application/json', '.json', True)
state = local()
+logger = logging.getLogger(__name__)
def proxy(key):
@@ -178,16 +180,9 @@ class Pecan(object):
require canonical URLs.
'''
- def __init__(self, root,
- default_renderer='mako',
- template_path='templates',
- hooks=[],
- custom_renderers={},
- extra_template_vars={},
- force_canonical=True
- ):
- '''
- '''
+ def __init__(self, root, default_renderer='mako',
+ template_path='templates', hooks=[], custom_renderers={},
+ extra_template_vars={}, force_canonical=True):
if isinstance(root, basestring):
root = self.__translate_root__(root)
@@ -236,13 +231,13 @@ class Pecan(object):
return node, remainder
except NonCanonicalPath, e:
if self.force_canonical and \
- not _cfg(e.controller).get('accept_noncanonical', False):
+ not _cfg(e.controller).get('accept_noncanonical', False):
if request.method == 'POST':
raise RuntimeError(
- "You have POSTed to a URL '%s' which '\
- 'requires a slash. Most browsers will not maintain '\
- 'POST data when redirected. Please update your code '\
- 'to POST to '%s/' or set force_canonical to False" % \
+ "You have POSTed to a URL '%s' which "
+ "requires a slash. Most browsers will not maintain "
+ "POST data when redirected. Please update your code "
+ "to POST to '%s/' or set force_canonical to False" %
(request.pecan['routing_path'],
request.pecan['routing_path'])
)
@@ -374,6 +369,7 @@ class Pecan(object):
# by the file extension on the URI
path = request.pecan['routing_path']
+ # attempt to guess the content type based on the file extension
if not request.pecan['content_type'] and '.' in path.split('/')[-1]:
path, extension = splitext(path)
request.pecan['extension'] = extension
@@ -399,23 +395,45 @@ class Pecan(object):
# if unsure ask the controller for the default content type
if not request.pecan['content_type']:
- request.pecan['content_type'] = cfg.get(
- 'content_type',
- 'text/html'
- )
+ # attempt to find a best match based on accept headers (if they
+ # exist)
+ if 'Accept' in request.headers:
+ best_default = acceptparse.MIMEAccept(
+ request.headers['Accept']
+ ).best_match(
+ cfg.get('content_types', {}).keys()
+ )
+
+ if best_default is None:
+ msg = "Controller '%s' defined does not support " + \
+ "content_type '%s'. Supported type(s): %s"
+ logger.error(
+ msg % (
+ controller.__name__,
+ request.pecan['content_type'],
+ cfg.get('content_types', {}).keys()
+ )
+ )
+ raise exc.HTTPNotAcceptable()
+
+ request.pecan['content_type'] = best_default
+ else:
+ request.pecan['content_type'] = cfg.get(
+ 'content_type',
+ 'text/html'
+ )
elif cfg.get('content_type') is not None and \
- request.pecan['content_type'] not in cfg.get('content_types', {}):
+ request.pecan['content_type'] not in \
+ cfg.get('content_types', {}):
- import warnings
msg = "Controller '%s' defined does not support content_type " + \
"'%s'. Supported type(s): %s"
- warnings.warn(
+ logger.error(
msg % (
controller.__name__,
request.pecan['content_type'],
cfg.get('content_types', {}).keys()
- ),
- RuntimeWarning
+ )
)
raise exc.HTTPNotFound
diff --git a/pecan/extensions.py b/pecan/extensions.py
index 51edf57..0ad2f8f 100644
--- a/pecan/extensions.py
+++ b/pecan/extensions.py
@@ -44,7 +44,7 @@ class PecanExtensionImporter(object):
def __eq__(self, b):
return self.__class__.__module__ == b.__class__.__module__ and \
- self.__class__.__name__ == b.__class__.__name__
+ self.__class__.__name__ == b.__class__.__name__
def __ne__(self, b):
return not self.__eq__(b)
diff --git a/pecan/hooks.py b/pecan/hooks.py
index 3c05296..97dc416 100644
--- a/pecan/hooks.py
+++ b/pecan/hooks.py
@@ -1,9 +1,10 @@
import sys
-from inspect import getmembers
+from inspect import getmembers
+
from webob.exc import HTTPFound
-from util import iscontroller, _cfg
-from routing import lookup_controller
+from util import iscontroller, _cfg
+from routing import lookup_controller
__all__ = [
'PecanHook', 'TransactionHook', 'HookController',
@@ -250,7 +251,7 @@ class RequestViewerHook(PecanHook):
available = ['path', 'status', 'method', 'controller', 'params', 'hooks']
def __init__(self, config=None, writer=sys.stdout, terminal=True,
- headers=True):
+ headers=True):
if not config:
self.config = {'items': self.available}
@@ -269,20 +270,20 @@ class RequestViewerHook(PecanHook):
# Default and/or custom response information
responses = {
- 'controller': lambda self, state: self.get_controller(state),
- 'method': lambda self, state: state.request.method,
- 'path': lambda self, state: state.request.path,
- 'params': lambda self, state: [
- (p[0].encode('utf-8'), p[1].encode('utf-8'))
- for p in state.request.params.items()
- ],
- 'status': lambda self, state: state.response.status,
- 'hooks': lambda self, state: self.format_hooks(state.app.hooks),
- }
+ 'controller': lambda self, state: self.get_controller(state),
+ 'method': lambda self, state: state.request.method,
+ 'path': lambda self, state: state.request.path,
+ 'params': lambda self, state: [
+ (p[0].encode('utf-8'), p[1].encode('utf-8'))
+ for p in state.request.params.items()
+ ],
+ 'status': lambda self, state: state.response.status,
+ 'hooks': lambda self, state: self.format_hooks(state.app.hooks),
+ }
is_available = [
- i for i in self.items
- if i in self.available or hasattr(state.request, i)
+ i for i in self.items
+ if i in self.available or hasattr(state.request, i)
]
terminal = []
diff --git a/pecan/middleware/debug.py b/pecan/middleware/debug.py
index d100e0b..9b753e1 100644
--- a/pecan/middleware/debug.py
+++ b/pecan/middleware/debug.py
@@ -1,21 +1,22 @@
from cStringIO import StringIO
from traceback import print_exc
from pprint import pformat
+import pdb
from mako.template import Template
-
from webob import Response
-import pdb
+from .resources import (pecan_image, syntax_js, syntax_css, theme, brush)
+
debug_template_raw = '''<html>
<head>
<title>Pecan - Application Error</title>
- <link rel="stylesheet" type="text/css" href="${shcorecss}" />
- <link rel="stylesheet" type="text/css" href="${shthemedefault}" />
+ <link rel="stylesheet" type="text/css" href="${syntax_css}" />
+ <link rel="stylesheet" type="text/css" href="${theme}" />
- <script type="text/javascript" src="${shcore}">
+ <script type="text/javascript" src="${syntax_js}">
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
@@ -33,7 +34,7 @@ debug_template_raw = '''<html>
* Dual licensed under the MIT and GPL licenses.
*/
</script>
- <script type="text/javascript" src="${shbrushpython}">
+ <script type="text/javascript" src="${brush}">
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
@@ -269,6 +270,9 @@ class DebugMiddleware(object):
"The DebugMiddleware middleware is not usable in a "
"multi-process environment")
+ if environ.get('paste.testing'):
+ return self.app(environ, start_response)
+
# initiate a PDB session if requested
global __debug_environ__
debugging = environ['PATH_INFO'] == '/__pecan_initiate_pdb__'
@@ -297,10 +301,10 @@ class DebugMiddleware(object):
traceback=out.getvalue(),
environment=formatted_environ,
pecan_image=pecan_image,
- shcore=shcore,
- shbrushpython=shbrushpython,
- shcorecss=shcorecss,
- shthemedefault=shthemedefault,
+ syntax_js=syntax_js,
+ brush=brush,
+ syntax_css=syntax_css,
+ theme=theme,
debugging=debugging
)
@@ -309,569 +313,3 @@ class DebugMiddleware(object):
response.status_int = 400
response.unicode_body = result
return response(environ, start_response)
-
-pecan_image = ("data:image/png;base64,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%3D")
-
-shcore = ("data:application/x-javascript;base64,LyoqCiAqIFN5bnRheEhpZ2hsaWdodG"
-"VyCiAqIGh0dHA6Ly9hbGV4Z29yYmF0Y2hldi5jb20vU3ludGF4SGlnaGxpZ2h0ZXIKICoKICogU3l"
-"udGF4SGlnaGxpZ2h0ZXIgaXMgZG9uYXRpb253YXJlLiBJZiB5b3UgYXJlIHVzaW5nIGl0LCBwbGVh"
-"c2UgZG9uYXRlLgogKiBodHRwOi8vYWxleGdvcmJhdGNoZXYuY29tL1N5bnRheEhpZ2hsaWdodGVyL"
-"2RvbmF0ZS5odG1sCiAqCiAqIEB2ZXJzaW9uCiAqIDMuMC44MyAoSnVseSAwMiAyMDEwKQogKiAKIC"
-"ogQGNvcHlyaWdodAogKiBDb3B5cmlnaHQgKEMpIDIwMDQtMjAxMCBBbGV4IEdvcmJhdGNoZXYuCiA"
-"qCiAqIEBsaWNlbnNlCiAqIER1YWwgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBhbmQgR1BMIGxpY2Vu"
-"c2VzLgogKi8KZXZhbChmdW5jdGlvbihwLGEsYyxrLGUsZCl7ZT1mdW5jdGlvbihjKXtyZXR1cm4oY"
-"zxhPycnOmUocGFyc2VJbnQoYy9hKSkpKygoYz1jJWEpPjM1P1N0cmluZy5mcm9tQ2hhckNvZGUoYy"
-"syOSk6Yy50b1N0cmluZygzNikpfTtpZighJycucmVwbGFjZSgvXi8sU3RyaW5nKSl7d2hpbGUoYy0"
-"tKXtkW2UoYyldPWtbY118fGUoYyl9az1bZnVuY3Rpb24oZSl7cmV0dXJuIGRbZV19XTtlPWZ1bmN0"
-"aW9uKCl7cmV0dXJuJ1xcdysnfTtjPTF9O3doaWxlKGMtLSl7aWYoa1tjXSl7cD1wLnJlcGxhY2Uob"
-"mV3IFJlZ0V4cCgnXFxiJytlKGMpKydcXGInLCdnJyksa1tjXSl9fXJldHVybiBwfSgnSyBNO0koTS"
-"kxUyAyVSgiMmFcJ3QgNGsgTSA0SyAyZyAzbCA0RyA0SCIpOyg2KCl7NiByKGYsZSl7SSghTS4xUih"
-"mKSkxUyAzbSgiM3MgMTUgNFIiKTtLIGE9Zi4xdztmPU0oZi4xbSx0KGYpKyhlfHwiIikpO0koYSlm"
-"LjF3PXsxbTphLjFtLDE5OmEuMTk/YS4xOS4xYSgwKTpOfTtIIGZ9NiB0KGYpe0goZi4xSj8iZyI6I"
-"iIpKyhmLjRzPyJpIjoiIikrKGYuNHA/Im0iOiIiKSsoZi40dj8ieCI6IiIpKyhmLjNuPyJ5IjoiIi"
-"l9NiBCKGYsZSxhLGIpe0sgYz11LkwsZCxoLGc7dj1SOzVLe08oO2MtLTspe2c9dVtjXTtJKGEmZy4"
-"zciYmKCFnLjJwfHxnLjJwLlcoYikpKXtnLjJxLjEyPWU7SSgoaD1nLjJxLlgoZikpJiZoLlA9PT1l"
-"KXtkPXszazpnLjJiLlcoYixoLGEpLDFDOmh9OzFOfX19fTV2KGkpezFTIGl9NXF7dj0xMX1IIGR9N"
-"iBwKGYsZSxhKXtJKDNiLlouMWkpSCBmLjFpKGUsYSk7TyhhPWF8fDA7YTxmLkw7YSsrKUkoZlthXT"
-"09PWUpSCBhO0gtMX1NPTYoZixlKXtLIGE9W10sYj1NLjFCLGM9MCxkLGg7SShNLjFSKGYpKXtJKGU"
-"hPT0xZCkxUyAzbSgiMmFcJ3QgNXIgNUkgNUYgNUIgNUMgMTUgNUUgNXAiKTtIIHIoZil9SSh2KTFT"
-"IDJVKCIyYVwndCBXIDNsIE0gNTkgNW0gNWcgNXggNWkiKTtlPWV8fCIiO08oZD17Mk46MTEsMTk6W"
-"10sMks6NihnKXtIIGUuMWkoZyk+LTF9LDNkOjYoZyl7ZSs9Z319O2M8Zi5MOylJKGg9QihmLGMsYi"
-"xkKSl7YS5VKGguM2spO2MrPWguMUNbMF0uTHx8MX1ZIEkoaD1uLlguVyh6W2JdLGYuMWEoYykpKXt"
-"hLlUoaFswXSk7Yys9aFswXS5MfVl7aD1mLjNhKGMpO0koaD09PSJbIiliPU0uMkk7WSBJKGg9PT0i"
-"XSIpYj1NLjFCO2EuVShoKTtjKyt9YT0xNShhLjFLKCIiKSxuLlEuVyhlLHcsIiIpKTthLjF3PXsxb"
-"TpmLDE5OmQuMk4/ZC4xOTpOfTtIIGF9O00uM3Y9IjEuNS4wIjtNLjJJPTE7TS4xQj0yO0sgQz0vXF"
-"wkKD86KFxcZFxcZD98WyQmYFwnXSl8eyhbJFxcd10rKX0pL2csdz0vW141aF0rfChbXFxzXFxTXSk"
-"oPz1bXFxzXFxTXSpcXDEpL2csQT0vXig/Ols/KitdfHtcXGQrKD86LFxcZCopP30pXFw/Py8sdj0x"
-"MSx1PVtdLG49e1g6MTUuWi5YLDFBOjE1LlouMUEsMUM6MXIuWi4xQyxROjFyLlouUSwxZToxci5aL"
-"jFlfSx4PW4uWC5XKC8oKT8/LywiIilbMV09PT0xZCxEPTYoKXtLIGY9L14vZztuLjFBLlcoZiwiIi"
-"k7SCFmLjEyfSgpLHk9Nigpe0sgZj0veC9nO24uUS5XKCJ4IixmLCIiKTtIIWYuMTJ9KCksRT0xNS5"
-"aLjNuIT09MWQsej17fTt6W00uMkldPS9eKD86XFxcXCg/OlswLTNdWzAtN117MCwyfXxbNC03XVsw"
-"LTddP3x4W1xcMjktMjYtZl17Mn18dVtcXDI5LTI2LWZdezR9fGNbQS0zby16XXxbXFxzXFxTXSkpL"
-"zt6W00uMUJdPS9eKD86XFxcXCg/OjAoPzpbMC0zXVswLTddezAsMn18WzQtN11bMC03XT8pP3xbMS"
-"05XVxcZCp8eFtcXDI5LTI2LWZdezJ9fHVbXFwyOS0yNi1mXXs0fXxjW0EtM28tel18W1xcc1xcU10"
-"pfFxcKFxcP1s6PSFdfFs/KitdXFw/fHtcXGQrKD86LFxcZCopP31cXD8/KS87TS4xaD02KGYsZSxh"
-"LGIpe3UuVSh7MnE6cihmLCJnIisoRT8ieSI6IiIpKSwyYjplLDNyOmF8fE0uMUIsMnA6Ynx8Tn0pf"
-"TtNLjJuPTYoZixlKXtLIGE9ZisiLyIrKGV8fCIiKTtIIE0uMm5bYV18fChNLjJuW2FdPU0oZixlKS"
-"l9O00uM2M9NihmKXtIIHIoZiwiZyIpfTtNLjVsPTYoZil7SCBmLlEoL1stW1xcXXt9KCkqKz8uLFx"
-"cXFxeJHwjXFxzXS9nLCJcXFxcJCYiKX07TS41ZT02KGYsZSxhLGIpe2U9cihlLCJnIisoYiYmRT8i"
-"eSI6IiIpKTtlLjEyPWE9YXx8MDtmPWUuWChmKTtIIGI/ZiYmZi5QPT09YT9mOk46Zn07TS4zcT02K"
-"Cl7TS4xaD02KCl7MVMgMlUoIjJhXCd0IDU1IDFoIDU0IDNxIil9fTtNLjFSPTYoZil7SCA1My5aLj"
-"FxLlcoZik9PT0iWzJtIDE1XSJ9O00uM3A9NihmLGUsYSxiKXtPKEsgYz1yKGUsImciKSxkPS0xLGg"
-"7aD1jLlgoZik7KXthLlcoYixoLCsrZCxmLGMpO2MuMTI9PT1oLlAmJmMuMTIrK31JKGUuMUopZS4x"
-"Mj0wfTtNLjU3PTYoZixlKXtIIDYgYShiLGMpe0sgZD1lW2NdLjFJP2VbY106ezFJOmVbY119LGg9c"
-"ihkLjFJLCJnIiksZz1bXSxpO08oaT0wO2k8Yi5MO2krKylNLjNwKGJbaV0saCw2KGspe2cuVShkLj"
-"NqP2tbZC4zal18fCIiOmtbMF0pfSk7SCBjPT09ZS5MLTF8fCFnLkw/ZzphKGcsYysxKX0oW2ZdLDA"
-"pfTsxNS5aLjFwPTYoZixlKXtIIEouWChlWzBdKX07MTUuWi5XPTYoZixlKXtIIEouWChlKX07MTUu"
-"Wi5YPTYoZil7SyBlPW4uWC4xcChKLDE0KSxhO0koZSl7SSgheCYmZS5MPjEmJnAoZSwiIik+LTEpe"
-"2E9MTUoSi4xbSxuLlEuVyh0KEopLCJnIiwiIikpO24uUS5XKGYuMWEoZS5QKSxhLDYoKXtPKEsgYz"
-"0xO2M8MTQuTC0yO2MrKylJKDE0W2NdPT09MWQpZVtjXT0xZH0pfUkoSi4xdyYmSi4xdy4xOSlPKEs"
-"gYj0xO2I8ZS5MO2IrKylJKGE9Si4xdy4xOVtiLTFdKWVbYV09ZVtiXTshRCYmSi4xSiYmIWVbMF0u"
-"TCYmSi4xMj5lLlAmJkouMTItLX1IIGV9O0koIUQpMTUuWi4xQT02KGYpeyhmPW4uWC5XKEosZikpJ"
-"iZKLjFKJiYhZlswXS5MJiZKLjEyPmYuUCYmSi4xMi0tO0ghIWZ9OzFyLlouMUM9NihmKXtNLjFSKG"
-"YpfHwoZj0xNShmKSk7SShmLjFKKXtLIGU9bi4xQy4xcChKLDE0KTtmLjEyPTA7SCBlfUggZi5YKEo"
-"pfTsxci5aLlE9NihmLGUpe0sgYT1NLjFSKGYpLGIsYztJKGEmJjFqIGUuNTgoKT09PSIzZiImJmUu"
-"MWkoIiR7Iik9PT0tMSYmeSlIIG4uUS4xcChKLDE0KTtJKGEpe0koZi4xdyliPWYuMXcuMTl9WSBmK"
-"z0iIjtJKDFqIGU9PT0iNiIpYz1uLlEuVyhKLGYsNigpe0koYil7MTRbMF09MWYgMXIoMTRbMF0pO0"
-"8oSyBkPTA7ZDxiLkw7ZCsrKUkoYltkXSkxNFswXVtiW2RdXT0xNFtkKzFdfUkoYSYmZi4xSilmLjE"
-"yPTE0WzE0LkwtMl0rMTRbMF0uTDtIIGUuMXAoTiwxNCl9KTtZe2M9SisiIjtjPW4uUS5XKGMsZiw2"
-"KCl7SyBkPTE0O0ggbi5RLlcoZSxDLDYoaCxnLGkpe0koZyk1YihnKXsyNCIkIjpIIiQiOzI0IiYiO"
-"kggZFswXTsyNCJgIjpIIGRbZC5MLTFdLjFhKDAsZFtkLkwtMl0pOzI0IlwnIjpIIGRbZC5MLTFdLj"
-"FhKGRbZC5MLTJdK2RbMF0uTCk7NWE6aT0iIjtnPStnO0koIWcpSCBoO08oO2c+ZC5MLTM7KXtpPTF"
-"yLlouMWEuVyhnLC0xKStpO2c9MVEuM2koZy8xMCl9SChnP2RbZ118fCIiOiIkIikraX1Ze2c9K2k7"
-"SShnPD1kLkwtMylIIGRbZ107Zz1iP3AoYixpKTotMTtIIGc+LTE/ZFtnKzFdOmh9fSl9KX1JKGEmJ"
-"mYuMUopZi4xMj0wO0ggY307MXIuWi4xZT02KGYsZSl7SSghTS4xUihmKSlIIG4uMWUuMXAoSiwxNC"
-"k7SyBhPUorIiIsYj1bXSxjPTAsZCxoO0koZT09PTFkfHwrZTwwKWU9NUQ7WXtlPTFRLjNpKCtlKTt"
-"JKCFlKUhbXX1PKGY9TS4zYyhmKTtkPWYuWChhKTspe0koZi4xMj5jKXtiLlUoYS4xYShjLGQuUCkp"
-"O2QuTD4xJiZkLlA8YS5MJiYzYi5aLlUuMXAoYixkLjFhKDEpKTtoPWRbMF0uTDtjPWYuMTI7SShiL"
-"kw+PWUpMU59Zi4xMj09PWQuUCYmZi4xMisrfUkoYz09PWEuTCl7SSghbi4xQS5XKGYsIiIpfHxoKW"
-"IuVSgiIil9WSBiLlUoYS4xYShjKSk7SCBiLkw+ZT9iLjFhKDAsZSk6Yn07TS4xaCgvXFwoXFw/I1t"
-"eKV0qXFwpLyw2KGYpe0ggbi4xQS5XKEEsZi4yUy4xYShmLlArZlswXS5MKSk/IiI6Iig/OikifSk7"
-"TS4xaCgvXFwoKD8hXFw/KS8sNigpe0ouMTkuVShOKTtIIigifSk7TS4xaCgvXFwoXFw/PChbJFxcd"
-"10rKT4vLDYoZil7Si4xOS5VKGZbMV0pO0ouMk49UjtIIigifSk7TS4xaCgvXFxcXGs8KFtcXHckXS"
-"spPi8sNihmKXtLIGU9cChKLjE5LGZbMV0pO0ggZT4tMT8iXFxcXCIrKGUrMSkrKDNSKGYuMlMuM2E"
-"oZi5QK2ZbMF0uTCkpPyIiOiIoPzopIik6ZlswXX0pO00uMWgoL1xcW1xcXj9dLyw2KGYpe0ggZlsw"
-"XT09PSJbXSI/IlxcXFxiXFxcXEIiOiJbXFxcXHNcXFxcU10ifSk7TS4xaCgvXlxcKFxcPyhbNUFdK"
-"ylcXCkvLDYoZil7Si4zZChmWzFdKTtIIiJ9KTtNLjFoKC8oPzpcXHMrfCMuKikrLyw2KGYpe0ggbi"
-"4xQS5XKEEsZi4yUy4xYShmLlArZlswXS5MKSk/IiI6Iig/OikifSxNLjFCLDYoKXtIIEouMksoIng"
-"iKX0pO00uMWgoL1xcLi8sNigpe0giW1xcXFxzXFxcXFNdIn0sTS4xQiw2KCl7SCBKLjJLKCJzIil9"
-"KX0pKCk7MWogMmUhPSIxZCImJigyZS5NPU0pO0sgMXY9NigpezYgcihhLGIpe2EuMWwuMWkoYikhP"
-"S0xfHwoYS4xbCs9IiAiK2IpfTYgdChhKXtIIGEuMWkoIjNlIik9PTA/YToiM2UiK2F9NiBCKGEpe0"
-"ggZS4xWS4yQVt0KGEpXX02IHAoYSxiLGMpe0koYT09TilIIE47SyBkPWMhPVI/YS4zRzpbYS4yR10"
-"saD17IiMiOiIxYyIsIi4iOiIxbCJ9W2IuMW8oMCwxKV18fCIzaCIsZyxpO2c9aCE9IjNoIj9iLjFv"
-"KDEpOmIuNXUoKTtJKChhW2hdfHwiIikuMWkoZykhPS0xKUggYTtPKGE9MDtkJiZhPGQuTCYmaT09T"
-"jthKyspaT1wKGRbYV0sYixjKTtIIGl9NiBDKGEsYil7SyBjPXt9LGQ7TyhkIDJnIGEpY1tkXT1hW2"
-"RdO08oZCAyZyBiKWNbZF09YltkXTtIIGN9NiB3KGEsYixjLGQpezYgaChnKXtnPWd8fDFQLjV5O0k"
-"oIWcuMUYpe2cuMUY9Zy41MjtnLjNOPTYoKXtKLjV3PTExfX1jLlcoZHx8MVAsZyl9YS4zZz9hLjNn"
-"KCI0VSIrYixoKTphLjR5KGIsaCwxMSl9NiBBKGEsYil7SyBjPWUuMVkuMmosZD1OO0koYz09Til7Y"
-"z17fTtPKEsgaCAyZyBlLjFVKXtLIGc9ZS4xVVtoXTtkPWcuNHg7SShkIT1OKXtnLjFWPWguNHcoKT"
-"tPKGc9MDtnPGQuTDtnKyspY1tkW2ddXT1ofX1lLjFZLjJqPWN9ZD1lLjFVW2NbYV1dO2Q9PU4mJmI"
-"hPTExJiYxUC4xWChlLjEzLjF4LjFYKyhlLjEzLjF4LjNFK2EpKTtIIGR9NiB2KGEsYil7TyhLIGM9"
-"YS4xZSgiXFxuIiksZD0wO2Q8Yy5MO2QrKyljW2RdPWIoY1tkXSxkKTtIIGMuMUsoIlxcbiIpfTYgd"
-"ShhLGIpe0koYT09Tnx8YS5MPT0wfHxhPT0iXFxuIilIIGE7YT1hLlEoLzwvZywiJjF5OyIpO2E9YS"
-"5RKC8gezIsfS9nLDYoYyl7TyhLIGQ9IiIsaD0wO2g8Yy5MLTE7aCsrKWQrPWUuMTMuMVc7SCBkKyI"
-"gIn0pO0koYiE9TilhPXYoYSw2KGMpe0koYy5MPT0wKUgiIjtLIGQ9IiI7Yz1jLlEoL14oJjJzO3wg"
-"KSsvLDYoaCl7ZD1oO0giIn0pO0koYy5MPT0wKUggZDtIIGQrXCc8MTcgMWc9IlwnK2IrXCciPlwnK"
-"2MrIjwvMTc+In0pO0ggYX02IG4oYSxiKXthLjFlKCJcXG4iKTtPKEsgYz0iIixkPTA7ZDw1MDtkKy"
-"spYys9IiAgICAgICAgICAgICAgICAgICAgIjtIIGE9dihhLDYoaCl7SShoLjFpKCJcXHQiKT09LTE"
-"pSCBoO08oSyBnPTA7KGc9aC4xaSgiXFx0IikpIT0tMTspaD1oLjFvKDAsZykrYy4xbygwLGItZyVi"
-"KStoLjFvKGcrMSxoLkwpO0ggaH0pfTYgeChhKXtIIGEuUSgvXlxccyt8XFxzKyQvZywiIil9NiBEK"
-"GEsYil7SShhLlA8Yi5QKUgtMTtZIEkoYS5QPmIuUClIIDE7WSBJKGEuTDxiLkwpSC0xO1kgSShhLk"
-"w+Yi5MKUggMTtIIDB9NiB5KGEsYil7NiBjKGspe0gga1swXX1PKEsgZD1OLGg9W10sZz1iLjJEP2I"
-"uMkQ6YzsoZD1iLjFJLlgoYSkpIT1OOyl7SyBpPWcoZCxiKTtJKDFqIGk9PSIzZiIpaT1bMWYgZS4y"
-"TChpLGQuUCxiLjIzKV07aD1oLjFPKGkpfUggaH02IEUoYSl7SyBiPS8oLiopKCgmMUc7fCYxeTspL"
-"iopLztIIGEuUShlLjNBLjNNLDYoYyl7SyBkPSIiLGg9TjtJKGg9Yi5YKGMpKXtjPWhbMV07ZD1oWz"
-"JdfUhcJzxhIDJoPSJcJytjK1wnIj5cJytjKyI8L2E+IitkfSl9NiB6KCl7TyhLIGE9MUUuMzYoIjF"
-"rIiksYj1bXSxjPTA7YzxhLkw7YysrKWFbY10uM3M9PSIyMCImJmIuVShhW2NdKTtIIGJ9NiBmKGEp"
-"e2E9YS4xRjtLIGI9cChhLCIuMjAiLFIpO2E9cChhLCIuM08iLFIpO0sgYz0xRS40aSgiM3QiKTtJK"
-"CEoIWF8fCFifHxwKGEsIjN0IikpKXtCKGIuMWMpO3IoYiwiMW0iKTtPKEsgZD1hLjNHLGg9W10sZz"
-"0wO2c8ZC5MO2crKyloLlUoZFtnXS40enx8ZFtnXS40QSk7aD1oLjFLKCJcXHIiKTtjLjM5KDFFLjR"
-"EKGgpKTthLjM5KGMpO2MuMkMoKTtjLjRDKCk7dyhjLCI0dSIsNigpe2MuMkcuNEUoYyk7Yi4xbD1i"
-"LjFsLlEoIjFtIiwiIil9KX19SSgxaiAzRiE9IjFkIiYmMWogTT09IjFkIilNPTNGKCJNIikuTTtLI"
-"GU9ezJ2OnsiMWctMjciOiIiLCIyaS0xcyI6MSwiMnotMXMtMnQiOjExLDFNOk4sMXQ6TiwiNDItND"
-"UiOlIsIjQzLTIyIjo0LDF1OlIsMTY6UiwiM1YtMTciOlIsMmw6MTEsIjQxLTQwIjpSLDJrOjExLCI"
-"xei0xayI6MTF9LDEzOnsxVzoiJjJzOyIsMk06Uiw0NjoxMSw0NDoxMSwzNDoiNG4iLDF4OnsyMToi"
-"NG8gMW0iLDJQOiI/IiwxWDoiMXZcXG5cXG4iLDNFOiI0clwndCA0dCAxRCBPOiAiLDRnOiI0bSA0Q"
-"lwndCA1MSBPIDF6LTFrIDRGOiAiLDM3OlwnPCE0VCAxeiA0UyAiLS8vNFYvLzNIIDRXIDEuMCA0Wi"
-"8vNFkiICIxWjovLzJ5LjNMLjNLLzRYLzNJLzNILzNJLTRQLjRKIj48MXogNEk9IjFaOi8vMnkuM0w"
-"uM0svNEwvNUwiPjwzSj48NE4gMVotNE09IjVHLTVNIiA2Sz0iMk8vMXo7IDZKPTZJLTgiIC8+PDF0"
-"PjZMIDF2PC8xdD48LzNKPjwzQiAxTD0iMjUtNk06NlEsNlAsNk8sNk4tNkY7NnktMmY6IzZ4OzJmO"
-"iM2dzsyNS0yMjo2djsyTy0zRDozQzsiPjxUIDFMPSIyTy0zRDozQzszdy0zMjoxLjZ6OyI+PFQgMU"
-"w9IjI1LTIyOjZBLTZFOyI+MXY8L1Q+PFQgMUw9IjI1LTIyOi42Qzszdy02Qjo2UjsiPjxUPjN2IDM"
-"uMC43NiAoNzIgNzMgM3gpPC9UPjxUPjxhIDJoPSIxWjovLzN1LjJ3LzF2IiAxRj0iMzgiIDFMPSIy"
-"ZjojM3kiPjFaOi8vM3UuMncvMXY8L2E+PC9UPjxUPjcwIDE3IDZVIDcxLjwvVD48VD42VCA2WC0ze"
-"CA2WSA2RC48L1Q+PC9UPjxUPjZ0IDYxIDYwIEogMWssIDVaIDxhIDJoPSI2dTovLzJ5LjYyLjJ3Lz"
-"YzLTY2LzY1PzY0PTVYLTVXJjVQPTVPIiAxTD0iMmY6IzN5Ij41UjwvYT4gNVYgPDJSLz41VSA1VCA"
-"1UyE8L1Q+PC9UPjwvM0I+PC8xej5cJ319LDFZOnsyajpOLDJBOnt9fSwxVTp7fSwzQTp7Nm46L1xc"
-"L1xcKltcXHNcXFNdKj9cXCpcXC8vMmMsNm06L1xcL1xcLy4qJC8yYyw2bDovIy4qJC8yYyw2azovI"
-"ihbXlxcXFwiXFxuXXxcXFxcLikqIi9nLDZvOi9cJyhbXlxcXFxcJ1xcbl18XFxcXC4pKlwnL2csNn"
-"A6MWYgTShcJyIoW15cXFxcXFxcXCJdfFxcXFxcXFxcLikqIlwnLCIzeiIpLDZzOjFmIE0oIlwnKFt"
-"eXFxcXFxcXFxcJ118XFxcXFxcXFwuKSpcJyIsIjN6IiksNnE6LygmMXk7fDwpIS0tW1xcc1xcU10q"
-"Py0tKCYxRzt8PikvMmMsM006L1xcdys6XFwvXFwvW1xcdy0uXFwvPyUmPTpAO10qL2csNmE6ezE4O"
-"i8oJjF5O3w8KVxcPz0/L2csMWI6L1xcPygmMUc7fD4pL2d9LDY5OnsxODovKCYxeTt8PCklPT8vZy"
-"wxYjovJSgmMUc7fD4pL2d9LDZkOnsxODovKCYxeTt8PClcXHMqMWsuKj8oJjFHO3w+KS8yVCwxYjo"
-"vKCYxeTt8PClcXC9cXHMqMWtcXHMqKCYxRzt8PikvMlR9fSwxNjp7MUg6NihhKXs2IGIoaSxrKXtI"
-"IGUuMTYuMm8oaSxrLGUuMTMuMXhba10pfU8oSyBjPVwnPFQgMWc9IjE2Ij5cJyxkPWUuMTYuMngsa"
-"D1kLjJYLGc9MDtnPGguTDtnKyspYys9KGRbaFtnXV0uMUh8fGIpKGEsaFtnXSk7Yys9IjwvVD4iO0"
-"ggY30sMm86NihhLGIsYyl7SFwnPDJXPjxhIDJoPSIjIiAxZz0iNmUgNmhcJytiKyIgIitiK1wnIj5"
-"cJytjKyI8L2E+PC8yVz4ifSwyYjo2KGEpe0sgYj1hLjFGLGM9Yi4xbHx8IiI7Yj1CKHAoYiwiLjIw"
-"IixSKS4xYyk7SyBkPTYoaCl7SChoPTE1KGgrIjZmKFxcXFx3KykiKS5YKGMpKT9oWzFdOk59KCI2Z"
-"yIpO2ImJmQmJmUuMTYuMnhbZF0uMkIoYik7YS4zTigpfSwyeDp7Mlg6WyIyMSIsIjJQIl0sMjE6ez"
-"FIOjYoYSl7SShhLlYoIjJsIikhPVIpSCIiO0sgYj1hLlYoIjF0Iik7SCBlLjE2LjJvKGEsIjIxIix"
-"iP2I6ZS4xMy4xeC4yMSl9LDJCOjYoYSl7YT0xRS42aih0KGEuMWMpKTthLjFsPWEuMWwuUSgiNDci"
-"LCIiKX19LDJQOnsyQjo2KCl7SyBhPSI2OD0wIjthKz0iLCAxOD0iKygzMS4zMC0zMykvMisiLCAzM"
-"j0iKygzMS4yWi0yWSkvMisiLCAzMD0zMywgMlo9MlkiO2E9YS5RKC9eLC8sIiIpO2E9MVAuNlooIi"
-"IsIjM4IixhKTthLjJDKCk7SyBiPWEuMUU7Yi42VyhlLjEzLjF4LjM3KTtiLjZWKCk7YS4yQygpfX1"
-"9fSwzNTo2KGEsYil7SyBjO0koYiljPVtiXTtZe2M9MUUuMzYoZS4xMy4zNCk7TyhLIGQ9W10saD0w"
-"O2g8Yy5MO2grKylkLlUoY1toXSk7Yz1kfWM9YztkPVtdO0koZS4xMy4yTSljPWMuMU8oeigpKTtJK"
-"GMuTD09PTApSCBkO08oaD0wO2g8Yy5MO2grKyl7TyhLIGc9Y1toXSxpPWEsaz1jW2hdLjFsLGo9M1"
-"cgMCxsPXt9LG09MWYgTSgiXlxcXFxbKD88MlY+KC4qPykpXFxcXF0kIikscz0xZiBNKCIoPzwyNz5"
-"bXFxcXHctXSspXFxcXHMqOlxcXFxzKig/PDFUPltcXFxcdy0lI10rfFxcXFxbLio/XFxcXF18XFwi"
-"Lio/XFwifFwnLio/XCcpXFxcXHMqOz8iLCJnIik7KGo9cy5YKGspKSE9Tjspe0sgbz1qLjFULlEoL"
-"15bXCciXXxbXCciXSQvZywiIik7SShvIT1OJiZtLjFBKG8pKXtvPW0uWChvKTtvPW8uMlYuTD4wP2"
-"8uMlYuMWUoL1xccyosXFxzKi8pOltdfWxbai4yN109b31nPXsxRjpnLDFuOkMoaSxsKX07Zy4xbi4"
-"xRCE9TiYmZC5VKGcpfUggZH0sMU06NihhLGIpe0sgYz1KLjM1KGEsYiksZD1OLGg9ZS4xMztJKGMu"
-"TCE9PTApTyhLIGc9MDtnPGMuTDtnKyspe2I9Y1tnXTtLIGk9Yi4xRixrPWIuMW4saj1rLjFELGw7S"
-"ShqIT1OKXtJKGtbIjF6LTFrIl09PSJSInx8ZS4ydlsiMXotMWsiXT09Uil7ZD0xZiBlLjRsKGopO2"
-"o9IjRPIn1ZIEkoZD1BKGopKWQ9MWYgZDtZIDZIO2w9aS4zWDtJKGguMk0pe2w9bDtLIG09eChsKSx"
-"zPTExO0kobS4xaSgiPCFbNkdbIik9PTApe209bS40aCg5KTtzPVJ9SyBvPW0uTDtJKG0uMWkoIl1d"
-"XFw+Iik9PW8tMyl7bT1tLjRoKDAsby0zKTtzPVJ9bD1zP206bH1JKChpLjF0fHwiIikhPSIiKWsuM"
-"XQ9aS4xdDtrLjFEPWo7ZC4yUShrKTtiPWQuMkYobCk7SSgoaS4xY3x8IiIpIT0iIiliLjFjPWkuMW"
-"M7aS4yRy43NChiLGkpfX19LDJFOjYoYSl7dygxUCwiNGsiLDYoKXtlLjFNKGEpfSl9fTtlLjJFPWU"
-"uMkU7ZS4xTT1lLjFNO2UuMkw9NihhLGIsYyl7Si4xVD1hO0ouUD1iO0ouTD1hLkw7Si4yMz1jO0ou"
-"MVY9Tn07ZS4yTC5aLjFxPTYoKXtIIEouMVR9O2UuNGw9NihhKXs2IGIoaixsKXtPKEsgbT0wO208a"
-"i5MO20rKylqW21dLlArPWx9SyBjPUEoYSksZCxoPTFmIGUuMVUuNVksZz1KLGk9IjJGIDFIIDJRIi"
-"4xZSgiICIpO0koYyE9Til7ZD0xZiBjO08oSyBrPTA7azxpLkw7aysrKSg2KCl7SyBqPWlba107Z1t"
-"qXT02KCl7SCBoW2pdLjFwKGgsMTQpfX0pKCk7ZC4yOD09Tj8xUC4xWChlLjEzLjF4LjFYKyhlLjEz"
-"LjF4LjRnK2EpKTpoLjJKLlUoezFJOmQuMjguMTcsMkQ6NihqKXtPKEsgbD1qLjE3LG09W10scz1kL"
-"jJKLG89ai5QK2ouMTguTCxGPWQuMjgscSxHPTA7RzxzLkw7RysrKXtxPXkobCxzW0ddKTtiKHEsby"
-"k7bT1tLjFPKHEpfUkoRi4xOCE9TiYmai4xOCE9Til7cT15KGouMTgsRi4xOCk7YihxLGouUCk7bT1"
-"tLjFPKHEpfUkoRi4xYiE9TiYmai4xYiE9Til7cT15KGouMWIsRi4xYik7YihxLGouUCtqWzBdLjVR"
-"KGouMWIpKTttPW0uMU8ocSl9TyhqPTA7ajxtLkw7aisrKW1bal0uMVY9Yy4xVjtIIG19fSl9fTtlL"
-"jRqPTYoKXt9O2UuNGouWj17Vjo2KGEsYil7SyBjPUouMW5bYV07Yz1jPT1OP2I6YztLIGQ9eyJSIj"
-"pSLCIxMSI6MTF9W2NdO0ggZD09Tj9jOmR9LDNZOjYoYSl7SCAxRS40aShhKX0sNGM6NihhLGIpe0s"
-"gYz1bXTtJKGEhPU4pTyhLIGQ9MDtkPGEuTDtkKyspSSgxaiBhW2RdPT0iMm0iKWM9Yy4xTyh5KGIs"
-"YVtkXSkpO0ggSi40ZShjLjZiKEQpKX0sNGU6NihhKXtPKEsgYj0wO2I8YS5MO2IrKylJKGFbYl0hP"
-"T1OKU8oSyBjPWFbYl0sZD1jLlArYy5MLGg9YisxO2g8YS5MJiZhW2JdIT09TjtoKyspe0sgZz1hW2"
-"hdO0koZyE9PU4pSShnLlA+ZCkxTjtZIEkoZy5QPT1jLlAmJmcuTD5jLkwpYVtiXT1OO1kgSShnLlA"
-"+PWMuUCYmZy5QPGQpYVtoXT1OfUggYX0sNGQ6NihhKXtLIGI9W10sYz0ydShKLlYoIjJpLTFzIikp"
-"O3YoYSw2KGQsaCl7Yi5VKGgrYyl9KTtIIGJ9LDNVOjYoYSl7SyBiPUouVigiMU0iLFtdKTtJKDFqI"
-"GIhPSIybSImJmIuVT09TiliPVtiXTthOnthPWEuMXEoKTtLIGM9M1cgMDtPKGM9Yz0xUS42YyhjfH"
-"wwLDApO2M8Yi5MO2MrKylJKGJbY109PWEpe2I9YzsxTiBhfWI9LTF9SCBiIT0tMX0sMnI6NihhLGI"
-"sYyl7YT1bIjFzIiwiNmkiK2IsIlAiK2EsIjZyIisoYiUyPT0wPzE6MikuMXEoKV07Si4zVShiKSYm"
-"YS5VKCI2NyIpO2I9PTAmJmEuVSgiMU4iKTtIXCc8VCAxZz0iXCcrYS4xSygiICIpK1wnIj5cJytjK"
-"yI8L1Q+In0sM1E6NihhLGIpe0sgYz0iIixkPWEuMWUoIlxcbiIpLkwsaD0ydShKLlYoIjJpLTFzIi"
-"kpLGc9Si5WKCIyei0xcy0ydCIpO0koZz09UilnPShoK2QtMSkuMXEoKS5MO1kgSSgzUihnKT09Uil"
-"nPTA7TyhLIGk9MDtpPGQ7aSsrKXtLIGs9Yj9iW2ldOmgraSxqO0koaz09MClqPWUuMTMuMVc7WXtq"
-"PWc7TyhLIGw9ay4xcSgpO2wuTDxqOylsPSIwIitsO2o9bH1hPWo7Yys9Si4ycihpLGssYSl9SCBjf"
-"Sw0OTo2KGEsYil7YT14KGEpO0sgYz1hLjFlKCJcXG4iKTtKLlYoIjJ6LTFzLTJ0Iik7SyBkPTJ1KE"
-"ouVigiMmktMXMiKSk7YT0iIjtPKEsgaD1KLlYoIjFEIiksZz0wO2c8Yy5MO2crKyl7SyBpPWNbZ10"
-"saz0vXigmMnM7fFxccykrLy5YKGkpLGo9TixsPWI/YltnXTpkK2c7SShrIT1OKXtqPWtbMF0uMXEo"
-"KTtpPWkuMW8oai5MKTtqPWouUSgiICIsZS4xMy4xVyl9aT14KGkpO0koaS5MPT0wKWk9ZS4xMy4xV"
-"zthKz1KLjJyKGcsbCwoaiE9Tj9cJzwxNyAxZz0iXCcraCtcJyA1TiI+XCcraisiPC8xNz4iOiIiKS"
-"tpKX1IIGF9LDRmOjYoYSl7SCBhPyI8NGE+IithKyI8LzRhPiI6IiJ9LDRiOjYoYSxiKXs2IGMobCl"
-"7SChsPWw/bC4xVnx8ZzpnKT9sKyIgIjoiIn1PKEsgZD0wLGg9IiIsZz1KLlYoIjFEIiwiIiksaT0w"
-"O2k8Yi5MO2krKyl7SyBrPWJbaV0sajtJKCEoaz09PU58fGsuTD09PTApKXtqPWMoayk7aCs9dShhL"
-"jFvKGQsay5QLWQpLGorIjQ4IikrdShrLjFULGoray4yMyk7ZD1rLlAray5MKyhrLjc1fHwwKX19aC"
-"s9dShhLjFvKGQpLGMoKSsiNDgiKTtIIGh9LDFIOjYoYSl7SyBiPSIiLGM9WyIyMCJdLGQ7SShKLlY"
-"oIjJrIik9PVIpSi4xbi4xNj1KLjFuLjF1PTExOzFsPSIyMCI7Si5WKCIybCIpPT1SJiZjLlUoIjQ3"
-"Iik7SSgoMXU9Si5WKCIxdSIpKT09MTEpYy5VKCI2UyIpO2MuVShKLlYoIjFnLTI3IikpO2MuVShKL"
-"lYoIjFEIikpO2E9YS5RKC9eWyBdKltcXG5dK3xbXFxuXSpbIF0qJC9nLCIiKS5RKC9cXHIvZywiIC"
-"IpO2I9Si5WKCI0My0yMiIpO0koSi5WKCI0Mi00NSIpPT1SKWE9bihhLGIpO1l7TyhLIGg9IiIsZz0"
-"wO2c8YjtnKyspaCs9IiAiO2E9YS5RKC9cXHQvZyxoKX1hPWE7YTp7Yj1hPWE7aD0vPDJSXFxzKlxc"
-"Lz8+fCYxeTsyUlxccypcXC8/JjFHOy8yVDtJKGUuMTMuNDY9PVIpYj1iLlEoaCwiXFxuIik7SShlL"
-"jEzLjQ0PT1SKWI9Yi5RKGgsIiIpO2I9Yi4xZSgiXFxuIik7aD0vXlxccyovO2c9NFE7TyhLIGk9MD"
-"tpPGIuTCYmZz4wO2krKyl7SyBrPWJbaV07SSh4KGspLkwhPTApe2s9aC5YKGspO0koaz09Til7YT1"
-"hOzFOIGF9Zz0xUS40cShrWzBdLkwsZyl9fUkoZz4wKU8oaT0wO2k8Yi5MO2krKyliW2ldPWJbaV0u"
-"MW8oZyk7YT1iLjFLKCJcXG4iKX1JKDF1KWQ9Si40ZChhKTtiPUouNGMoSi4ySixhKTtiPUouNGIoY"
-"SxiKTtiPUouNDkoYixkKTtJKEouVigiNDEtNDAiKSliPUUoYik7MWogMkghPSIxZCImJjJILjNTJi"
-"YySC4zUy4xQygvNXMvKSYmYy5VKCI1dCIpO0ggYj1cJzxUIDFjPSJcJyt0KEouMWMpK1wnIiAxZz0"
-"iXCcrYy4xSygiICIpK1wnIj5cJysoSi5WKCIxNiIpP2UuMTYuMUgoSik6IiIpK1wnPDNaIDV6PSIw"
-"IiA1SD0iMCIgNUo9IjAiPlwnK0ouNGYoSi5WKCIxdCIpKSsiPDNUPjwzUD4iKygxdT9cJzwyZCAxZ"
-"z0iMXUiPlwnK0ouM1EoYSkrIjwvMmQ+IjoiIikrXCc8MmQgMWc9IjE3Ij48VCAxZz0iM08iPlwnK2"
-"IrIjwvVD48LzJkPjwvM1A+PC8zVD48LzNaPjwvVD4ifSwyRjo2KGEpe0koYT09PU4pYT0iIjtKLjE"
-"3PWE7SyBiPUouM1koIlQiKTtiLjNYPUouMUgoYSk7Si5WKCIxNiIpJiZ3KHAoYiwiLjE2IiksIjVj"
-"IixlLjE2LjJiKTtKLlYoIjNWLTE3IikmJncocChiLCIuMTciKSwiNTYiLGYpO0ggYn0sMlE6NihhK"
-"XtKLjFjPSIiKzFRLjVkKDFRLjVuKCkqNWspLjFxKCk7ZS4xWS4yQVt0KEouMWMpXT1KO0ouMW49Qy"
-"hlLjJ2LGF8fHt9KTtJKEouVigiMmsiKT09UilKLjFuLjE2PUouMW4uMXU9MTF9LDVqOjYoYSl7YT1"
-"hLlEoL15cXHMrfFxccyskL2csIiIpLlEoL1xccysvZywifCIpO0giXFxcXGIoPzoiK2ErIilcXFxc"
-"YiJ9LDVmOjYoYSl7Si4yOD17MTg6ezFJOmEuMTgsMjM6IjFrIn0sMWI6ezFJOmEuMWIsMjM6IjFrI"
-"n0sMTc6MWYgTSgiKD88MTg+IithLjE4LjFtKyIpKD88MTc+Lio/KSg/PDFiPiIrYS4xYi4xbSsiKS"
-"IsIjVvIil9fX07SCBlfSgpOzFqIDJlIT0iMWQiJiYoMmUuMXY9MXYpOycsNjIsNDQxLCd8fHx8fHx"
-"mdW5jdGlvbnx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHxyZXR1cm58aWZ8dGhp"
-"c3x2YXJ8bGVuZ3RofFhSZWdFeHB8bnVsbHxmb3J8aW5kZXh8cmVwbGFjZXx0cnVlfHxkaXZ8cHVza"
-"HxnZXRQYXJhbXxjYWxsfGV4ZWN8ZWxzZXxwcm90b3R5cGV8fGZhbHNlfGxhc3RJbmRleHxjb25maW"
-"d8YXJndW1lbnRzfFJlZ0V4cHx0b29sYmFyfGNvZGV8bGVmdHxjYXB0dXJlTmFtZXN8c2xpY2V8cml"
-"naHR8aWR8dW5kZWZpbmVkfHNwbGl0fG5ld3xjbGFzc3xhZGRUb2tlbnxpbmRleE9mfHR5cGVvZnxz"
-"Y3JpcHR8Y2xhc3NOYW1lfHNvdXJjZXxwYXJhbXN8c3Vic3RyfGFwcGx5fHRvU3RyaW5nfFN0cmluZ"
-"3xsaW5lfHRpdGxlfGd1dHRlcnxTeW50YXhIaWdobGlnaHRlcnxfeHJlZ2V4cHxzdHJpbmdzfGx0fG"
-"h0bWx8dGVzdHxPVVRTSURFX0NMQVNTfG1hdGNofGJydXNofGRvY3VtZW50fHRhcmdldHxndHxnZXR"
-"IdG1sfHJlZ2V4fGdsb2JhbHxqb2lufHN0eWxlfGhpZ2hsaWdodHxicmVha3xjb25jYXR8d2luZG93"
-"fE1hdGh8aXNSZWdFeHB8dGhyb3d8dmFsdWV8YnJ1c2hlc3xicnVzaE5hbWV8c3BhY2V8YWxlcnR8d"
-"mFyc3xodHRwfHN5bnRheGhpZ2hsaWdodGVyfGV4cGFuZFNvdXJjZXxzaXplfGNzc3xjYXNlfGZvbn"
-"R8RmF8bmFtZXxodG1sU2NyaXB0fGRBfGNhbnxoYW5kbGVyfGdtfHRkfGV4cG9ydHN8Y29sb3J8aW5"
-"8aHJlZnxmaXJzdHxkaXNjb3ZlcmVkQnJ1c2hlc3xsaWdodHxjb2xsYXBzZXxvYmplY3R8Y2FjaGV8"
-"Z2V0QnV0dG9uSHRtbHx0cmlnZ2VyfHBhdHRlcm58Z2V0TGluZUh0bWx8bmJzcHxudW1iZXJzfHBhc"
-"nNlSW50fGRlZmF1bHRzfGNvbXxpdGVtc3x3d3d8cGFkfGhpZ2hsaWdodGVyc3xleGVjdXRlfGZvY3"
-"VzfGZ1bmN8YWxsfGdldERpdnxwYXJlbnROb2RlfG5hdmlnYXRvcnxJTlNJREVfQ0xBU1N8cmVnZXh"
-"MaXN0fGhhc0ZsYWd8TWF0Y2h8dXNlU2NyaXB0VGFnc3xoYXNOYW1lZENhcHR1cmV8dGV4dHxoZWxw"
-"fGluaXR8YnJ8aW5wdXR8Z2l8RXJyb3J8dmFsdWVzfHNwYW58bGlzdHwyNTB8aGVpZ2h0fHdpZHRof"
-"HNjcmVlbnx0b3B8NTAwfHRhZ05hbWV8ZmluZEVsZW1lbnRzfGdldEVsZW1lbnRzQnlUYWdOYW1lfG"
-"Fib3V0RGlhbG9nfF9ibGFua3xhcHBlbmRDaGlsZHxjaGFyQXR8QXJyYXl8Y29weUFzR2xvYmFsfHN"
-"ldEZsYWd8aGlnaGxpZ2h0ZXJffHN0cmluZ3xhdHRhY2hFdmVudHxub2RlTmFtZXxmbG9vcnxiYWNr"
-"cmVmfG91dHB1dHx0aGV8VHlwZUVycm9yfHN0aWNreXxaYXxpdGVyYXRlfGZyZWV6ZVRva2Vuc3xzY"
-"29wZXx0eXBlfHRleHRhcmVhfGFsZXhnb3JiYXRjaGV2fHZlcnNpb258bWFyZ2lufDIwMTB8MDA1OD"
-"k2fGdzfHJlZ2V4TGlifGJvZHl8Y2VudGVyfGFsaWdufG5vQnJ1c2h8cmVxdWlyZXxjaGlsZE5vZGV"
-"zfERURHx4aHRtbDF8aGVhZHxvcmd8dzN8dXJsfHByZXZlbnREZWZhdWx0fGNvbnRhaW5lcnx0cnxn"
-"ZXRMaW5lTnVtYmVyc0h0bWx8aXNOYU58dXNlckFnZW50fHRib2R5fGlzTGluZUhpZ2hsaWdodGVkf"
-"HF1aWNrfHZvaWR8aW5uZXJIVE1MfGNyZWF0ZXx0YWJsZXxsaW5rc3xhdXRvfHNtYXJ0fHRhYnxzdH"
-"JpcEJyc3x0YWJzfGJsb2dnZXJNb2RlfGNvbGxhcHNlZHxwbGFpbnxnZXRDb2RlTGluZXNIdG1sfGN"
-"hcHRpb258Z2V0TWF0Y2hlc0h0bWx8ZmluZE1hdGNoZXN8ZmlndXJlT3V0TGluZU51bWJlcnN8cmVt"
-"b3ZlTmVzdGVkTWF0Y2hlc3xnZXRUaXRsZUh0bWx8YnJ1c2hOb3RIdG1sU2NyaXB0fHN1YnN0cmluZ"
-"3xjcmVhdGVFbGVtZW50fEhpZ2hsaWdodGVyfGxvYWR8SHRtbFNjcmlwdHxCcnVzaHxwcmV8ZXhwYW"
-"5kfG11bHRpbGluZXxtaW58Q2FufGlnbm9yZUNhc2V8ZmluZHxibHVyfGV4dGVuZGVkfHRvTG93ZXJ"
-"DYXNlfGFsaWFzZXN8YWRkRXZlbnRMaXN0ZW5lcnxpbm5lclRleHR8dGV4dENvbnRlbnR8d2Fzbnxz"
-"ZWxlY3R8Y3JlYXRlVGV4dE5vZGV8cmVtb3ZlQ2hpbGR8b3B0aW9ufHNhbWV8ZnJhbWV8eG1sbnN8Z"
-"HRkfHR3aWNlfDE5OTl8ZXF1aXZ8bWV0YXxodG1sc2NyaXB0fHRyYW5zaXRpb25hbHwxRTN8ZXhwZW"
-"N0ZWR8UFVCTElDfERPQ1RZUEV8b258VzNDfFhIVE1MfFRSfEVOfFRyYW5zaXRpb25hbHx8Y29uZml"
-"ndXJlZHxzcmNFbGVtZW50fE9iamVjdHxhZnRlcnxydW58ZGJsY2xpY2t8bWF0Y2hDaGFpbnx2YWx1"
-"ZU9mfGNvbnN0cnVjdG9yfGRlZmF1bHR8c3dpdGNofGNsaWNrfHJvdW5kfGV4ZWNBdHxmb3JIdG1sU"
-"2NyaXB0fHRva2VufGdpbXl8ZnVuY3Rpb25zfGdldEtleXdvcmRzfDFFNnxlc2NhcGV8d2l0aGlufH"
-"JhbmRvbXxzZ2l8YW5vdGhlcnxmaW5hbGx5fHN1cHBseXxNU0lFfGllfHRvVXBwZXJDYXNlfGNhdGN"
-"ofHJldHVyblZhbHVlfGRlZmluaXRpb258ZXZlbnR8Ym9yZGVyfGltc3h8Y29uc3RydWN0aW5nfG9u"
-"ZXxJbmZpbml0eXxmcm9tfHdoZW58Q29udGVudHxjZWxscGFkZGluZ3xmbGFnc3xjZWxsc3BhY2luZ"
-"3x0cnl8eGh0bWx8VHlwZXxzcGFjZXN8MjkzMDQwMnxob3N0ZWRfYnV0dG9uX2lkfGxhc3RJbmRleE"
-"9mfGRvbmF0ZXxhY3RpdmV8ZGV2ZWxvcG1lbnR8a2VlcHx0b3x4Y2xpY2t8X3N8WG1sfHBsZWFzZXx"
-"saWtlfHlvdXxwYXlwYWx8Y2dpfGNtZHx3ZWJzY3J8YmlufGhpZ2hsaWdodGVkfHNjcm9sbGJhcnN8"
-"YXNwU2NyaXB0VGFnc3xwaHBTY3JpcHRUYWdzfHNvcnR8bWF4fHNjcmlwdFNjcmlwdFRhZ3N8dG9vb"
-"GJhcl9pdGVtfF98Y29tbWFuZHxjb21tYW5kX3xudW1iZXJ8Z2V0RWxlbWVudEJ5SWR8ZG91YmxlUX"
-"VvdGVkU3RyaW5nfHNpbmdsZUxpbmVQZXJsQ29tbWVudHN8c2luZ2xlTGluZUNDb21tZW50c3xtdWx"
-"0aUxpbmVDQ29tbWVudHN8c2luZ2xlUXVvdGVkU3RyaW5nfG11bHRpTGluZURvdWJsZVF1b3RlZFN0"
-"cmluZ3x4bWxDb21tZW50c3xhbHR8bXVsdGlMaW5lU2luZ2xlUXVvdGVkU3RyaW5nfElmfGh0dHBzf"
-"DFlbXwwMDB8ZmZmfGJhY2tncm91bmR8NWVtfHh4fGJvdHRvbXw3NWVtfEdvcmJhdGNoZXZ8bGFyZ2"
-"V8c2VyaWZ8Q0RBVEF8Y29udGludWV8dXRmfGNoYXJzZXR8Y29udGVudHxBYm91dHxmYW1pbHl8c2F"
-"uc3xIZWx2ZXRpY2F8QXJpYWx8R2VuZXZhfDNlbXxub2d1dHRlcnxDb3B5cmlnaHR8c3ludGF4fGNs"
-"b3NlfHdyaXRlfDIwMDR8QWxleHxvcGVufEphdmFTY3JpcHR8aGlnaGxpZ2h0ZXJ8SnVseXwwMnxyZ"
-"XBsYWNlQ2hpbGR8b2Zmc2V0fDgzJy5zcGxpdCgnfCcpLDAse30pKQo%3D")
-
-shbrushpython = ("data:application/x-javascript;base64,LyoqCiAqIFN5bnRheEhpZ2h"
-"saWdodGVyCiAqIGh0dHA6Ly9hbGV4Z29yYmF0Y2hldi5jb20vU3ludGF4SGlnaGxpZ2h0ZXIKICoK"
-"ICogU3ludGF4SGlnaGxpZ2h0ZXIgaXMgZG9uYXRpb253YXJlLiBJZiB5b3UgYXJlIHVzaW5nIGl0L"
-"CBwbGVhc2UgZG9uYXRlLgogKiBodHRwOi8vYWxleGdvcmJhdGNoZXYuY29tL1N5bnRheEhpZ2hsaW"
-"dodGVyL2RvbmF0ZS5odG1sCiAqCiAqIEB2ZXJzaW9uCiAqIDMuMC44MyAoSnVseSAwMiAyMDEwKQo"
-"gKiAKICogQGNvcHlyaWdodAogKiBDb3B5cmlnaHQgKEMpIDIwMDQtMjAxMCBBbGV4IEdvcmJhdGNo"
-"ZXYuCiAqCiAqIEBsaWNlbnNlCiAqIER1YWwgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBhbmQgR1BMI"
-"GxpY2Vuc2VzLgogKi8KOyhmdW5jdGlvbigpCnsKCS8vIENvbW1vbkpTCgl0eXBlb2YocmVxdWlyZS"
-"kgIT0gJ3VuZGVmaW5lZCcgPyBTeW50YXhIaWdobGlnaHRlciA9IHJlcXVpcmUoJ3NoQ29yZScpLlN"
-"5bnRheEhpZ2hsaWdodGVyIDogbnVsbDsKCglmdW5jdGlvbiBCcnVzaCgpCgl7CgkJLy8gQ29udHJp"
-"YnV0ZWQgYnkgR2hlb3JnaGUgTWlsYXMgYW5kIEFobWFkIFNoZXJpZgoJCgkJdmFyIGtleXdvcmRzI"
-"D0gICdhbmQgYXNzZXJ0IGJyZWFrIGNsYXNzIGNvbnRpbnVlIGRlZiBkZWwgZWxpZiBlbHNlICcgKw"
-"oJCQkJCQknZXhjZXB0IGV4ZWMgZmluYWxseSBmb3IgZnJvbSBnbG9iYWwgaWYgaW1wb3J0IGluIGl"
-"zICcgKwoJCQkJCQknbGFtYmRhIG5vdCBvciBwYXNzIHByaW50IHJhaXNlIHJldHVybiB0cnkgeWll"
-"bGQgd2hpbGUnOwoKCQl2YXIgZnVuY3MgPSAnX19pbXBvcnRfXyBhYnMgYWxsIGFueSBhcHBseSBiY"
-"XNlc3RyaW5nIGJpbiBib29sIGJ1ZmZlciBjYWxsYWJsZSAnICsKCQkJCQknY2hyIGNsYXNzbWV0aG"
-"9kIGNtcCBjb2VyY2UgY29tcGlsZSBjb21wbGV4IGRlbGF0dHIgZGljdCBkaXIgJyArCgkJCQkJJ2R"
-"pdm1vZCBlbnVtZXJhdGUgZXZhbCBleGVjZmlsZSBmaWxlIGZpbHRlciBmbG9hdCBmb3JtYXQgZnJv"
-"emVuc2V0ICcgKwoJCQkJCSdnZXRhdHRyIGdsb2JhbHMgaGFzYXR0ciBoYXNoIGhlbHAgaGV4IGlkI"
-"GlucHV0IGludCBpbnRlcm4gJyArCgkJCQkJJ2lzaW5zdGFuY2UgaXNzdWJjbGFzcyBpdGVyIGxlbi"
-"BsaXN0IGxvY2FscyBsb25nIG1hcCBtYXggbWluIG5leHQgJyArCgkJCQkJJ29iamVjdCBvY3Qgb3B"
-"lbiBvcmQgcG93IHByaW50IHByb3BlcnR5IHJhbmdlIHJhd19pbnB1dCByZWR1Y2UgJyArCgkJCQkJ"
-"J3JlbG9hZCByZXByIHJldmVyc2VkIHJvdW5kIHNldCBzZXRhdHRyIHNsaWNlIHNvcnRlZCBzdGF0a"
-"WNtZXRob2QgJyArCgkJCQkJJ3N0ciBzdW0gc3VwZXIgdHVwbGUgdHlwZSB0eXBlIHVuaWNociB1bm"
-"ljb2RlIHZhcnMgeHJhbmdlIHppcCc7CgoJCXZhciBzcGVjaWFsID0gICdOb25lIFRydWUgRmFsc2U"
-"gc2VsZiBjbHMgY2xhc3NfJzsKCgkJdGhpcy5yZWdleExpc3QgPSBbCgkJCQl7IHJlZ2V4OiBTeW50"
-"YXhIaWdobGlnaHRlci5yZWdleExpYi5zaW5nbGVMaW5lUGVybENvbW1lbnRzLCBjc3M6ICdjb21tZ"
-"W50cycgfSwKCQkJCXsgcmVnZXg6IC9eXHMqQFx3Ky9nbSwgCQkJCQkJCQkJCWNzczogJ2RlY29yYX"
-"RvcicgfSwKCQkJCXsgcmVnZXg6IC8oWydcIl17M30pKFteXDFdKSo/XDEvZ20sIAkJCQkJCWNzczo"
-"gJ2NvbW1lbnRzJyB9LAoJCQkJeyByZWdleDogLyIoPyEiKSg/OlwufFxcXCJ8W15cIiJcbl0pKiIv"
-"Z20sIAkJCQkJY3NzOiAnc3RyaW5nJyB9LAoJCQkJeyByZWdleDogLycoPyEnKSg/OlwufChcXFwnK"
-"XxbXlwnJ1xuXSkqJy9nbSwgCQkJCWNzczogJ3N0cmluZycgfSwKCQkJCXsgcmVnZXg6IC9cK3xcLX"
-"xcKnxcL3xcJXw9fD09L2dtLCAJCQkJCQkJY3NzOiAna2V5d29yZCcgfSwKCQkJCXsgcmVnZXg6IC9"
-"cYlxkK1wuP1x3Ki9nLCAJCQkJCQkJCQljc3M6ICd2YWx1ZScgfSwKCQkJCXsgcmVnZXg6IG5ldyBS"
-"ZWdFeHAodGhpcy5nZXRLZXl3b3JkcyhmdW5jcyksICdnbWknKSwJCWNzczogJ2Z1bmN0aW9ucycgf"
-"SwKCQkJCXsgcmVnZXg6IG5ldyBSZWdFeHAodGhpcy5nZXRLZXl3b3JkcyhrZXl3b3JkcyksICdnbS"
-"cpLCAJCWNzczogJ2tleXdvcmQnIH0sCgkJCQl7IHJlZ2V4OiBuZXcgUmVnRXhwKHRoaXMuZ2V0S2V"
-"5d29yZHMoc3BlY2lhbCksICdnbScpLCAJCWNzczogJ2NvbG9yMScgfQoJCQkJXTsKCQkJCgkJdGhp"
-"cy5mb3JIdG1sU2NyaXB0KFN5bnRheEhpZ2hsaWdodGVyLnJlZ2V4TGliLmFzcFNjcmlwdFRhZ3MpO"
-"woJfTsKCglCcnVzaC5wcm90b3R5cGUJPSBuZXcgU3ludGF4SGlnaGxpZ2h0ZXIuSGlnaGxpZ2h0ZX"
-"IoKTsKCUJydXNoLmFsaWFzZXMJPSBbJ3B5JywgJ3B5dGhvbiddOwoKCVN5bnRheEhpZ2hsaWdodGV"
-"yLmJydXNoZXMuUHl0aG9uID0gQnJ1c2g7CgoJLy8gQ29tbW9uSlMKCXR5cGVvZihleHBvcnRzKSAh"
-"PSAndW5kZWZpbmVkJyA/IGV4cG9ydHMuQnJ1c2ggPSBCcnVzaCA6IG51bGw7Cn0pKCk7Cg%3D%3D")
-
-shcorecss = ("data:text/css;base64,LyoqCiAqIFN5bnRheEhpZ2hsaWdodGVyCiAqIGh0dHA"
-"6Ly9hbGV4Z29yYmF0Y2hldi5jb20vU3ludGF4SGlnaGxpZ2h0ZXIKICoKICogU3ludGF4SGlnaGxp"
-"Z2h0ZXIgaXMgZG9uYXRpb253YXJlLiBJZiB5b3UgYXJlIHVzaW5nIGl0LCBwbGVhc2UgZG9uYXRlL"
-"gogKiBodHRwOi8vYWxleGdvcmJhdGNoZXYuY29tL1N5bnRheEhpZ2hsaWdodGVyL2RvbmF0ZS5odG"
-"1sCiAqCiAqIEB2ZXJzaW9uCiAqIDMuMC44MyAoSnVseSAwMiAyMDEwKQogKiAKICogQGNvcHlyaWd"
-"odAogKiBDb3B5cmlnaHQgKEMpIDIwMDQtMjAxMCBBbGV4IEdvcmJhdGNoZXYuCiAqCiAqIEBsaWNl"
-"bnNlCiAqIER1YWwgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBhbmQgR1BMIGxpY2Vuc2VzLgogKi8KL"
-"nN5bnRheGhpZ2hsaWdodGVyIGEsCi5zeW50YXhoaWdobGlnaHRlciBkaXYsCi5zeW50YXhoaWdobG"
-"lnaHRlciBjb2RlLAouc3ludGF4aGlnaGxpZ2h0ZXIgdGFibGUsCi5zeW50YXhoaWdobGlnaHRlciB"
-"0YWJsZSB0ZCwKLnN5bnRheGhpZ2hsaWdodGVyIHRhYmxlIHRyLAouc3ludGF4aGlnaGxpZ2h0ZXIg"
-"dGFibGUgdGJvZHksCi5zeW50YXhoaWdobGlnaHRlciB0YWJsZSB0aGVhZCwKLnN5bnRheGhpZ2hsa"
-"WdodGVyIHRhYmxlIGNhcHRpb24sCi5zeW50YXhoaWdobGlnaHRlciB0ZXh0YXJlYSB7CiAgLW1vei"
-"1ib3JkZXItcmFkaXVzOiAwIDAgMCAwICFpbXBvcnRhbnQ7CiAgLXdlYmtpdC1ib3JkZXItcmFkaXV"
-"zOiAwIDAgMCAwICFpbXBvcnRhbnQ7CiAgYmFja2dyb3VuZDogbm9uZSAhaW1wb3J0YW50OwogIGJv"
-"cmRlcjogMCAhaW1wb3J0YW50OwogIGJvdHRvbTogYXV0byAhaW1wb3J0YW50OwogIGZsb2F0OiBub"
-"25lICFpbXBvcnRhbnQ7CiAgaGVpZ2h0OiBhdXRvICFpbXBvcnRhbnQ7CiAgbGVmdDogYXV0byAhaW"
-"1wb3J0YW50OwogIGxpbmUtaGVpZ2h0OiAxLjFlbSAhaW1wb3J0YW50OwogIG1hcmdpbjogMCAhaW1"
-"wb3J0YW50OwogIG91dGxpbmU6IDAgIWltcG9ydGFudDsKICBvdmVyZmxvdzogdmlzaWJsZSAhaW1w"
-"b3J0YW50OwogIHBhZGRpbmc6IDAgIWltcG9ydGFudDsKICBwb3NpdGlvbjogc3RhdGljICFpbXBvc"
-"nRhbnQ7CiAgcmlnaHQ6IGF1dG8gIWltcG9ydGFudDsKICB0ZXh0LWFsaWduOiBsZWZ0ICFpbXBvcn"
-"RhbnQ7CiAgdG9wOiBhdXRvICFpbXBvcnRhbnQ7CiAgdmVydGljYWwtYWxpZ246IGJhc2VsaW5lICF"
-"pbXBvcnRhbnQ7CiAgd2lkdGg6IGF1dG8gIWltcG9ydGFudDsKICBib3gtc2l6aW5nOiBjb250ZW50"
-"LWJveCAhaW1wb3J0YW50OwogIGZvbnQtZmFtaWx5OiAiQ29uc29sYXMiLCAiQml0c3RyZWFtIFZlc"
-"mEgU2FucyBNb25vIiwgIkNvdXJpZXIgTmV3IiwgQ291cmllciwgbW9ub3NwYWNlICFpbXBvcnRhbn"
-"Q7CiAgZm9udC13ZWlnaHQ6IG5vcm1hbCAhaW1wb3J0YW50OwogIGZvbnQtc3R5bGU6IG5vcm1hbCA"
-"haW1wb3J0YW50OwogIGZvbnQtc2l6ZTogMWVtICFpbXBvcnRhbnQ7CiAgbWluLWhlaWdodDogaW5o"
-"ZXJpdCAhaW1wb3J0YW50OwogIG1pbi1oZWlnaHQ6IGF1dG8gIWltcG9ydGFudDsKfQoKLnN5bnRhe"
-"GhpZ2hsaWdodGVyIHsKICB3aWR0aDogMTAwJSAhaW1wb3J0YW50OwogIG1hcmdpbjogMWVtIDAgMW"
-"VtIDAgIWltcG9ydGFudDsKICBwb3NpdGlvbjogcmVsYXRpdmUgIWltcG9ydGFudDsKICBvdmVyZmx"
-"vdzogYXV0byAhaW1wb3J0YW50OwogIGZvbnQtc2l6ZTogMWVtICFpbXBvcnRhbnQ7Cn0KLnN5bnRh"
-"eGhpZ2hsaWdodGVyLnNvdXJjZSB7CiAgb3ZlcmZsb3c6IGhpZGRlbiAhaW1wb3J0YW50Owp9Ci5ze"
-"W50YXhoaWdobGlnaHRlciAuYm9sZCB7CiAgZm9udC13ZWlnaHQ6IGJvbGQgIWltcG9ydGFudDsKfQ"
-"ouc3ludGF4aGlnaGxpZ2h0ZXIgLml0YWxpYyB7CiAgZm9udC1zdHlsZTogaXRhbGljICFpbXBvcnR"
-"hbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyIC5saW5lIHsKICB3aGl0ZS1zcGFjZTogcHJlICFpbXBv"
-"cnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyIHRhYmxlIHsKICB3aWR0aDogMTAwJSAhaW1wb3J0Y"
-"W50Owp9Ci5zeW50YXhoaWdobGlnaHRlciB0YWJsZSBjYXB0aW9uIHsKICB0ZXh0LWFsaWduOiBsZW"
-"Z0ICFpbXBvcnRhbnQ7CiAgcGFkZGluZzogLjVlbSAwIDAuNWVtIDFlbSAhaW1wb3J0YW50Owp9Ci5"
-"zeW50YXhoaWdobGlnaHRlciB0YWJsZSB0ZC5jb2RlIHsKICB3aWR0aDogMTAwJSAhaW1wb3J0YW50"
-"Owp9Ci5zeW50YXhoaWdobGlnaHRlciB0YWJsZSB0ZC5jb2RlIC5jb250YWluZXIgewogIHBvc2l0a"
-"W9uOiByZWxhdGl2ZSAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlciB0YWJsZSB0ZC5jb2"
-"RlIC5jb250YWluZXIgdGV4dGFyZWEgewogIGJveC1zaXppbmc6IGJvcmRlci1ib3ggIWltcG9ydGF"
-"udDsKICBwb3NpdGlvbjogYWJzb2x1dGUgIWltcG9ydGFudDsKICBsZWZ0OiAwICFpbXBvcnRhbnQ7"
-"CiAgdG9wOiAwICFpbXBvcnRhbnQ7CiAgd2lkdGg6IDEwMCUgIWltcG9ydGFudDsKICBoZWlnaHQ6I"
-"DEwMCUgIWltcG9ydGFudDsKICBib3JkZXI6IG5vbmUgIWltcG9ydGFudDsKICBiYWNrZ3JvdW5kOi"
-"B3aGl0ZSAhaW1wb3J0YW50OwogIHBhZGRpbmctbGVmdDogMWVtICFpbXBvcnRhbnQ7CiAgb3ZlcmZ"
-"sb3c6IGhpZGRlbiAhaW1wb3J0YW50OwogIHdoaXRlLXNwYWNlOiBwcmUgIWltcG9ydGFudDsKfQou"
-"c3ludGF4aGlnaGxpZ2h0ZXIgdGFibGUgdGQuZ3V0dGVyIC5saW5lIHsKICB0ZXh0LWFsaWduOiBya"
-"WdodCAhaW1wb3J0YW50OwogIHBhZGRpbmc6IDAgMC41ZW0gMCAxZW0gIWltcG9ydGFudDsKfQouc3"
-"ludGF4aGlnaGxpZ2h0ZXIgdGFibGUgdGQuY29kZSAubGluZSB7CiAgcGFkZGluZzogMCAxZW0gIWl"
-"tcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIubm9ndXR0ZXIgdGQuY29kZSAuY29udGFpbmVy"
-"IHRleHRhcmVhLCAuc3ludGF4aGlnaGxpZ2h0ZXIubm9ndXR0ZXIgdGQuY29kZSAubGluZSB7CiAgc"
-"GFkZGluZy1sZWZ0OiAwZW0gIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIuc2hvdyB7Ci"
-"AgZGlzcGxheTogYmxvY2sgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIuY29sbGFwc2V"
-"kIHRhYmxlIHsKICBkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVy"
-"LmNvbGxhcHNlZCAudG9vbGJhciB7CiAgcGFkZGluZzogMC4xZW0gMC44ZW0gMGVtIDAuOGVtICFpb"
-"XBvcnRhbnQ7CiAgZm9udC1zaXplOiAxZW0gIWltcG9ydGFudDsKICBwb3NpdGlvbjogc3RhdGljIC"
-"FpbXBvcnRhbnQ7CiAgd2lkdGg6IGF1dG8gIWltcG9ydGFudDsKICBoZWlnaHQ6IGF1dG8gIWltcG9"
-"ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIuY29sbGFwc2VkIC50b29sYmFyIHNwYW4gewogIGRp"
-"c3BsYXk6IGlubGluZSAhaW1wb3J0YW50OwogIG1hcmdpbi1yaWdodDogMWVtICFpbXBvcnRhbnQ7C"
-"n0KLnN5bnRheGhpZ2hsaWdodGVyLmNvbGxhcHNlZCAudG9vbGJhciBzcGFuIGEgewogIHBhZGRpbm"
-"c6IDAgIWltcG9ydGFudDsKICBkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2h"
-"saWdodGVyLmNvbGxhcHNlZCAudG9vbGJhciBzcGFuIGEuZXhwYW5kU291cmNlIHsKICBkaXNwbGF5"
-"OiBpbmxpbmUgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLnRvb2xiYXIgewogIHBvc"
-"2l0aW9uOiBhYnNvbHV0ZSAhaW1wb3J0YW50OwogIHJpZ2h0OiAxcHggIWltcG9ydGFudDsKICB0b3"
-"A6IDFweCAhaW1wb3J0YW50OwogIHdpZHRoOiAxMXB4ICFpbXBvcnRhbnQ7CiAgaGVpZ2h0OiAxMXB"
-"4ICFpbXBvcnRhbnQ7CiAgZm9udC1zaXplOiAxMHB4ICFpbXBvcnRhbnQ7CiAgei1pbmRleDogMTAg"
-"IWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLnRvb2xiYXIgc3Bhbi50aXRsZSB7CiAgZ"
-"GlzcGxheTogaW5saW5lICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyIC50b29sYmFyIG"
-"EgewogIGRpc3BsYXk6IGJsb2NrICFpbXBvcnRhbnQ7CiAgdGV4dC1hbGlnbjogY2VudGVyICFpbXB"
-"vcnRhbnQ7CiAgdGV4dC1kZWNvcmF0aW9uOiBub25lICFpbXBvcnRhbnQ7CiAgcGFkZGluZy10b3A6"
-"IDFweCAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlciAudG9vbGJhciBhLmV4cGFuZFNvd"
-"XJjZSB7CiAgZGlzcGxheTogbm9uZSAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5pZS"
-"B7CiAgZm9udC1zaXplOiAuOWVtICFpbXBvcnRhbnQ7CiAgcGFkZGluZzogMXB4IDAgMXB4IDAgIWl"
-"tcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIuaWUgLnRvb2xiYXIgewogIGxpbmUtaGVpZ2h0"
-"OiA4cHggIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIuaWUgLnRvb2xiYXIgYSB7CiAgc"
-"GFkZGluZy10b3A6IDBweCAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5wcmludGluZy"
-"AubGluZS5hbHQxIC5jb250ZW50LAouc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnRpbmcgLmxpbmUuYWx"
-"0MiAuY29udGVudCwKLnN5bnRheGhpZ2hsaWdodGVyLnByaW50aW5nIC5saW5lLmhpZ2hsaWdodGVk"
-"IC5udW1iZXIsCi5zeW50YXhoaWdobGlnaHRlci5wcmludGluZyAubGluZS5oaWdobGlnaHRlZC5hb"
-"HQxIC5jb250ZW50LAouc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnRpbmcgLmxpbmUuaGlnaGxpZ2h0ZW"
-"QuYWx0MiAuY29udGVudCB7CiAgYmFja2dyb3VuZDogbm9uZSAhaW1wb3J0YW50Owp9Ci5zeW50YXh"
-"oaWdobGlnaHRlci5wcmludGluZyAubGluZSAubnVtYmVyIHsKICBjb2xvcjogI2JiYmJiYiAhaW1w"
-"b3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5wcmludGluZyAubGluZSAuY29udGVudCB7CiAgY"
-"29sb3I6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyLnByaW50aW5nIC50b2"
-"9sYmFyIHsKICBkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyLnB"
-"yaW50aW5nIGEgewogIHRleHQtZGVjb3JhdGlvbjogbm9uZSAhaW1wb3J0YW50Owp9Ci5zeW50YXho"
-"aWdobGlnaHRlci5wcmludGluZyAucGxhaW4sIC5zeW50YXhoaWdobGlnaHRlci5wcmludGluZyAuc"
-"GxhaW4gYSB7CiAgY29sb3I6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyLn"
-"ByaW50aW5nIC5jb21tZW50cywgLnN5bnRheGhpZ2hsaWdodGVyLnByaW50aW5nIC5jb21tZW50cyB"
-"hIHsKICBjb2xvcjogIzAwODIwMCAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5wcmlu"
-"dGluZyAuc3RyaW5nLCAuc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnRpbmcgLnN0cmluZyBhIHsKICBjb"
-"2xvcjogYmx1ZSAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5wcmludGluZyAua2V5d2"
-"9yZCB7CiAgY29sb3I6ICMwMDY2OTkgIWltcG9ydGFudDsKICBmb250LXdlaWdodDogYm9sZCAhaW1"
-"wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5wcmludGluZyAucHJlcHJvY2Vzc29yIHsKICBj"
-"b2xvcjogZ3JheSAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5wcmludGluZyAudmFya"
-"WFibGUgewogIGNvbG9yOiAjYWE3NzAwICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyLn"
-"ByaW50aW5nIC52YWx1ZSB7CiAgY29sb3I6ICMwMDk5MDAgIWltcG9ydGFudDsKfQouc3ludGF4aGl"
-"naGxpZ2h0ZXIucHJpbnRpbmcgLmZ1bmN0aW9ucyB7CiAgY29sb3I6ICNmZjE0OTMgIWltcG9ydGFu"
-"dDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnRpbmcgLmNvbnN0YW50cyB7CiAgY29sb3I6ICMwM"
-"DY2Y2MgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnRpbmcgLnNjcmlwdCB7Ci"
-"AgZm9udC13ZWlnaHQ6IGJvbGQgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnR"
-"pbmcgLmNvbG9yMSwgLnN5bnRheGhpZ2hsaWdodGVyLnByaW50aW5nIC5jb2xvcjEgYSB7CiAgY29s"
-"b3I6IGdyYXkgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnRpbmcgLmNvbG9yM"
-"iwgLnN5bnRheGhpZ2hsaWdodGVyLnByaW50aW5nIC5jb2xvcjIgYSB7CiAgY29sb3I6ICNmZjE0OT"
-"MgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIucHJpbnRpbmcgLmNvbG9yMywgLnN5bnR"
-"heGhpZ2hsaWdodGVyLnByaW50aW5nIC5jb2xvcjMgYSB7CiAgY29sb3I6IHJlZCAhaW1wb3J0YW50"
-"Owp9Ci5zeW50YXhoaWdobGlnaHRlci5wcmludGluZyAuYnJlYWssIC5zeW50YXhoaWdobGlnaHRlc"
-"i5wcmludGluZyAuYnJlYWsgYSB7CiAgY29sb3I6IGJsYWNrICFpbXBvcnRhbnQ7Cn0K")
-
-shthemedefault = ("data:text/css;base64,LyoqCiAqIFN5bnRheEhpZ2hsaWdodGVyCiAqIG"
-"h0dHA6Ly9hbGV4Z29yYmF0Y2hldi5jb20vU3ludGF4SGlnaGxpZ2h0ZXIKICoKICogU3ludGF4SGl"
-"naGxpZ2h0ZXIgaXMgZG9uYXRpb253YXJlLiBJZiB5b3UgYXJlIHVzaW5nIGl0LCBwbGVhc2UgZG9u"
-"YXRlLgogKiBodHRwOi8vYWxleGdvcmJhdGNoZXYuY29tL1N5bnRheEhpZ2hsaWdodGVyL2RvbmF0Z"
-"S5odG1sCiAqCiAqIEB2ZXJzaW9uCiAqIDMuMC44MyAoSnVseSAwMiAyMDEwKQogKiAKICogQGNvcH"
-"lyaWdodAogKiBDb3B5cmlnaHQgKEMpIDIwMDQtMjAxMCBBbGV4IEdvcmJhdGNoZXYuCiAqCiAqIEB"
-"saWNlbnNlCiAqIER1YWwgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBhbmQgR1BMIGxpY2Vuc2VzLgog"
-"Ki8KLnN5bnRheGhpZ2hsaWdodGVyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB3aGl0ZSAhaW1wb3J0Y"
-"W50Owp9Ci5zeW50YXhoaWdobGlnaHRlciAubGluZS5hbHQxIHsKICBiYWNrZ3JvdW5kLWNvbG9yOi"
-"B3aGl0ZSAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlciAubGluZS5hbHQyIHsKICBiYWN"
-"rZ3JvdW5kLWNvbG9yOiB3aGl0ZSAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlciAubGlu"
-"ZS5oaWdobGlnaHRlZC5hbHQxLCAuc3ludGF4aGlnaGxpZ2h0ZXIgLmxpbmUuaGlnaGxpZ2h0ZWQuY"
-"Wx0MiB7CiAgYmFja2dyb3VuZC1jb2xvcjogI2UwZTBlMCAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaW"
-"dobGlnaHRlciAubGluZS5oaWdobGlnaHRlZC5udW1iZXIgewogIGNvbG9yOiBibGFjayAhaW1wb3J"
-"0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlciB0YWJsZSBjYXB0aW9uIHsKICBjb2xvcjogYmxhY2sg"
-"IWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLmd1dHRlciB7CiAgY29sb3I6ICNhZmFmY"
-"WYgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLmd1dHRlciAubGluZSB7CiAgYm9yZG"
-"VyLXJpZ2h0OiAzcHggc29saWQgIzZjZTI2YyAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHR"
-"lciAuZ3V0dGVyIC5saW5lLmhpZ2hsaWdodGVkIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjNmNlMjZj"
-"ICFpbXBvcnRhbnQ7CiAgY29sb3I6IHdoaXRlICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdod"
-"GVyLnByaW50aW5nIC5saW5lIC5jb250ZW50IHsKICBib3JkZXI6IG5vbmUgIWltcG9ydGFudDsKfQ"
-"ouc3ludGF4aGlnaGxpZ2h0ZXIuY29sbGFwc2VkIHsKICBvdmVyZmxvdzogdmlzaWJsZSAhaW1wb3J"
-"0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlci5jb2xsYXBzZWQgLnRvb2xiYXIgewogIGNvbG9yOiBi"
-"bHVlICFpbXBvcnRhbnQ7CiAgYmFja2dyb3VuZDogd2hpdGUgIWltcG9ydGFudDsKICBib3JkZXI6I"
-"DFweCBzb2xpZCAjNmNlMjZjICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyLmNvbGxhcH"
-"NlZCAudG9vbGJhciBhIHsKICBjb2xvcjogYmx1ZSAhaW1wb3J0YW50Owp9Ci5zeW50YXhoaWdobGl"
-"naHRlci5jb2xsYXBzZWQgLnRvb2xiYXIgYTpob3ZlciB7CiAgY29sb3I6IHJlZCAhaW1wb3J0YW50"
-"Owp9Ci5zeW50YXhoaWdobGlnaHRlciAudG9vbGJhciB7CiAgY29sb3I6IHdoaXRlICFpbXBvcnRhb"
-"nQ7CiAgYmFja2dyb3VuZDogIzZjZTI2YyAhaW1wb3J0YW50OwogIGJvcmRlcjogbm9uZSAhaW1wb3"
-"J0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlciAudG9vbGJhciBhIHsKICBjb2xvcjogd2hpdGUgIWl"
-"tcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLnRvb2xiYXIgYTpob3ZlciB7CiAgY29sb3I6"
-"IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyIC5wbGFpbiwgLnN5bnRheGhpZ"
-"2hsaWdodGVyIC5wbGFpbiBhIHsKICBjb2xvcjogYmxhY2sgIWltcG9ydGFudDsKfQouc3ludGF4aG"
-"lnaGxpZ2h0ZXIgLmNvbW1lbnRzLCAuc3ludGF4aGlnaGxpZ2h0ZXIgLmNvbW1lbnRzIGEgewogIGN"
-"vbG9yOiAjMDA4MjAwICFpbXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyIC5zdHJpbmcsIC5z"
-"eW50YXhoaWdobGlnaHRlciAuc3RyaW5nIGEgewogIGNvbG9yOiBibHVlICFpbXBvcnRhbnQ7Cn0KL"
-"nN5bnRheGhpZ2hsaWdodGVyIC5rZXl3b3JkIHsKICBjb2xvcjogIzAwNjY5OSAhaW1wb3J0YW50Ow"
-"p9Ci5zeW50YXhoaWdobGlnaHRlciAucHJlcHJvY2Vzc29yIHsKICBjb2xvcjogZ3JheSAhaW1wb3J"
-"0YW50Owp9Ci5zeW50YXhoaWdobGlnaHRlciAudmFyaWFibGUgewogIGNvbG9yOiAjYWE3NzAwICFp"
-"bXBvcnRhbnQ7Cn0KLnN5bnRheGhpZ2hsaWdodGVyIC52YWx1ZSB7CiAgY29sb3I6ICMwMDk5MDAgI"
-"WltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLmZ1bmN0aW9ucyB7CiAgY29sb3I6ICNmZj"
-"E0OTMgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLmNvbnN0YW50cyB7CiAgY29sb3I"
-"6ICMwMDY2Y2MgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLnNjcmlwdCB7CiAgZm9u"
-"dC13ZWlnaHQ6IGJvbGQgIWltcG9ydGFudDsKICBjb2xvcjogIzAwNjY5OSAhaW1wb3J0YW50OwogI"
-"GJhY2tncm91bmQtY29sb3I6IG5vbmUgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLm"
-"NvbG9yMSwgLnN5bnRheGhpZ2hsaWdodGVyIC5jb2xvcjEgYSB7CiAgY29sb3I6IGdyYXkgIWltcG9"
-"ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0ZXIgLmNvbG9yMiwgLnN5bnRheGhpZ2hsaWdodGVyIC5j"
-"b2xvcjIgYSB7CiAgY29sb3I6ICNmZjE0OTMgIWltcG9ydGFudDsKfQouc3ludGF4aGlnaGxpZ2h0Z"
-"XIgLmNvbG9yMywgLnN5bnRheGhpZ2hsaWdodGVyIC5jb2xvcjMgYSB7CiAgY29sb3I6IHJlZCAhaW"
-"1wb3J0YW50Owp9Cgouc3ludGF4aGlnaGxpZ2h0ZXIgLmtleXdvcmQgewogIGZvbnQtd2VpZ2h0OiB"
-"ib2xkICFpbXBvcnRhbnQ7Cn0K")
diff --git a/pecan/middleware/recursive.py b/pecan/middleware/recursive.py
index 747dd1b..f7471ec 100644
--- a/pecan/middleware/recursive.py
+++ b/pecan/middleware/recursive.py
@@ -24,12 +24,12 @@ class CheckForRecursionMiddleware(object):
def __call__(self, environ, start_response):
path_info = environ.get('PATH_INFO', '')
- if path_info in self.env.get(
- 'pecan.recursive.old_path_info', []):
+ if path_info in self.env.get('pecan.recursive.old_path_info', []):
raise RecursionLoop(
"Forwarding loop detected; %r visited twice (internal "
"redirect path: %s)"
- % (path_info, self.env['pecan.recursive.old_path_info']))
+ % (path_info, self.env['pecan.recursive.old_path_info'])
+ )
old_path_info = self.env.setdefault(
'pecan.recursive.old_path_info', []
)
@@ -122,17 +122,20 @@ class ForwardRequestException(Exception):
def __init__(self, url=None, environ={}, factory=None, path_info=None):
# Check no incompatible options have been chosen
if factory and url:
- raise TypeError( # pragma: no cover
+ raise TypeError(
'You cannot specify factory and a url in '
- 'ForwardRequestException')
+ 'ForwardRequestException'
+ ) # pragma: nocover
elif factory and environ:
- raise TypeError( # pragma: no cover
+ raise TypeError(
'You cannot specify factory and environ in '
- 'ForwardRequestException')
+ 'ForwardRequestException'
+ ) # pragma: nocover
if url and environ:
- raise TypeError( # pragma: no cover
+ raise TypeError(
'You cannot specify environ and url in '
- 'ForwardRequestException')
+ 'ForwardRequestException'
+ ) # pragma: nocover
# set the path_info or warn about its use.
if path_info:
diff --git a/pecan/middleware/resources/__init__.py b/pecan/middleware/resources/__init__.py
new file mode 100644
index 0000000..5c43a5f
--- /dev/null
+++ b/pecan/middleware/resources/__init__.py
@@ -0,0 +1,26 @@
+import os
+import urllib
+from mimetypes import guess_type
+from contextlib import closing
+
+
+def load_resource(filename):
+ with closing(open(
+ os.path.join(
+ os.path.dirname(__file__),
+ filename
+ ),
+ 'rb'
+ )) as f:
+ return 'data:%s;base64,%s' % (
+ guess_type(filename)[0],
+ urllib.quote(
+ f.read().encode('base64').replace('\n', '')
+ )
+ )
+
+pecan_image = load_resource('pecan.png')
+syntax_js = load_resource('syntax.js')
+syntax_css = load_resource('syntax.css')
+theme = load_resource('theme.css')
+brush = load_resource('brush-python.js')
diff --git a/pecan/middleware/resources/brush-python.js b/pecan/middleware/resources/brush-python.js
new file mode 100644
index 0000000..30f0a8f
--- /dev/null
+++ b/pecan/middleware/resources/brush-python.js
@@ -0,0 +1,64 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+(function()
+{
+ // CommonJS
+ typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+ function Brush()
+ {
+ // Contributed by Gheorghe Milas and Ahmad Sherif
+
+ var keywords = 'and assert break class continue def del elif else ' +
+ 'except exec finally for from global if import in is ' +
+ 'lambda not or pass print raise return try yield while';
+
+ var funcs = '__import__ abs all any apply basestring bin bool buffer callable ' +
+ 'chr classmethod cmp coerce compile complex delattr dict dir ' +
+ 'divmod enumerate eval execfile file filter float format frozenset ' +
+ 'getattr globals hasattr hash help hex id input int intern ' +
+ 'isinstance issubclass iter len list locals long map max min next ' +
+ 'object oct open ord pow print property range raw_input reduce ' +
+ 'reload repr reversed round set setattr slice sorted staticmethod ' +
+ 'str sum super tuple type type unichr unicode vars xrange zip';
+
+ var special = 'None True False self cls class_';
+
+ this.regexList = [
+ { regex: SyntaxHighlighter.regexLib.singleLinePerlComments, css: 'comments' },
+ { regex: /^\s*@\w+/gm, css: 'decorator' },
+ { regex: /(['\"]{3})([^\1])*?\1/gm, css: 'comments' },
+ { regex: /"(?!")(?:\.|\\\"|[^\""\n])*"/gm, css: 'string' },
+ { regex: /'(?!')(?:\.|(\\\')|[^\''\n])*'/gm, css: 'string' },
+ { regex: /\+|\-|\*|\/|\%|=|==/gm, css: 'keyword' },
+ { regex: /\b\d+\.?\w*/g, css: 'value' },
+ { regex: new RegExp(this.getKeywords(funcs), 'gmi'), css: 'functions' },
+ { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' },
+ { regex: new RegExp(this.getKeywords(special), 'gm'), css: 'color1' }
+ ];
+
+ this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+ };
+
+ Brush.prototype = new SyntaxHighlighter.Highlighter();
+ Brush.aliases = ['py', 'python'];
+
+ SyntaxHighlighter.brushes.Python = Brush;
+
+ // CommonJS
+ typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/pecan/middleware/resources/pecan.png b/pecan/middleware/resources/pecan.png
new file mode 100644
index 0000000..16f2490
--- /dev/null
+++ b/pecan/middleware/resources/pecan.png
Binary files differ
diff --git a/pecan/middleware/resources/syntax.css b/pecan/middleware/resources/syntax.css
new file mode 100644
index 0000000..4f0021e
--- /dev/null
+++ b/pecan/middleware/resources/syntax.css
@@ -0,0 +1,226 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+ -moz-border-radius: 0 0 0 0 !important;
+ -webkit-border-radius: 0 0 0 0 !important;
+ background: none !important;
+ border: 0 !important;
+ bottom: auto !important;
+ float: none !important;
+ height: auto !important;
+ left: auto !important;
+ line-height: 1.1em !important;
+ margin: 0 !important;
+ outline: 0 !important;
+ overflow: visible !important;
+ padding: 0 !important;
+ position: static !important;
+ right: auto !important;
+ text-align: left !important;
+ top: auto !important;
+ vertical-align: baseline !important;
+ width: auto !important;
+ box-sizing: content-box !important;
+ font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+ font-weight: normal !important;
+ font-style: normal !important;
+ font-size: 1em !important;
+ min-height: inherit !important;
+ min-height: auto !important;
+}
+
+.syntaxhighlighter {
+ width: 100% !important;
+ margin: 1em 0 1em 0 !important;
+ position: relative !important;
+ overflow: auto !important;
+ font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+ overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+ font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+ font-style: italic !important;
+}
+.syntaxhighlighter .line {
+ white-space: pre !important;
+}
+.syntaxhighlighter table {
+ width: 100% !important;
+}
+.syntaxhighlighter table caption {
+ text-align: left !important;
+ padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+ width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+ position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+ box-sizing: border-box !important;
+ position: absolute !important;
+ left: 0 !important;
+ top: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+ border: none !important;
+ background: white !important;
+ padding-left: 1em !important;
+ overflow: hidden !important;
+ white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+ text-align: right !important;
+ padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+ padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+ padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+ display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ padding: 0.1em 0.8em 0em 0.8em !important;
+ font-size: 1em !important;
+ position: static !important;
+ width: auto !important;
+ height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+ display: inline !important;
+ margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+ padding: 0 !important;
+ display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+ position: absolute !important;
+ right: 1px !important;
+ top: 1px !important;
+ width: 11px !important;
+ height: 11px !important;
+ font-size: 10px !important;
+ z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+ display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+ display: block !important;
+ text-align: center !important;
+ text-decoration: none !important;
+ padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+ display: none !important;
+}
+.syntaxhighlighter.ie {
+ font-size: .9em !important;
+ padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+ line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+ padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+ background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+ color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+ color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+ display: none !important;
+}
+.syntaxhighlighter.printing a {
+ text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+ color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+ color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+ color: #006699 !important;
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+ font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+ color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+ color: black !important;
+}
diff --git a/pecan/middleware/resources/syntax.js b/pecan/middleware/resources/syntax.js
new file mode 100644
index 0000000..b47b645
--- /dev/null
+++ b/pecan/middleware/resources/syntax.js
@@ -0,0 +1,17 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a<f.L;a++)I(f[a]===e)H a;H-1}M=6(f,e){K a=[],b=M.1B,c=0,d,h;I(M.1R(f)){I(e!==1d)1S 3m("2a\'t 5r 5I 5F 5B 5C 15 5E 5p");H r(f)}I(v)1S 2U("2a\'t W 3l M 59 5m 5g 5x 5i");e=e||"";O(d={2N:11,19:[],2K:6(g){H e.1i(g)>-1},3d:6(g){e+=g}};c<f.L;)I(h=B(f,c,b,d)){a.U(h.3k);c+=h.1C[0].L||1}Y I(h=n.X.W(z[b],f.1a(c))){a.U(h[0]);c+=h[0].L}Y{h=f.3a(c);I(h==="[")b=M.2I;Y I(h==="]")b=M.1B;a.U(h);c++}a=15(a.1K(""),n.Q.W(e,w,""));a.1w={1m:f,19:d.2N?d.19:N};H a};M.3v="1.5.0";M.2I=1;M.1B=2;K C=/\\$(?:(\\d\\d?|[$&`\'])|{([$\\w]+)})/g,w=/[^5h]+|([\\s\\S])(?=[\\s\\S]*\\1)/g,A=/^(?:[?*+]|{\\d+(?:,\\d*)?})\\??/,v=11,u=[],n={X:15.Z.X,1A:15.Z.1A,1C:1r.Z.1C,Q:1r.Z.Q,1e:1r.Z.1e},x=n.X.W(/()??/,"")[1]===1d,D=6(){K f=/^/g;n.1A.W(f,"");H!f.12}(),y=6(){K f=/x/g;n.Q.W("x",f,"");H!f.12}(),E=15.Z.3n!==1d,z={};z[M.2I]=/^(?:\\\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S]))/;z[M.1B]=/^(?:\\\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\\d*|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S])|\\(\\?[:=!]|[?*+]\\?|{\\d+(?:,\\d*)?}\\??)/;M.1h=6(f,e,a,b){u.U({2q:r(f,"g"+(E?"y":"")),2b:e,3r:a||M.1B,2p:b||N})};M.2n=6(f,e){K a=f+"/"+(e||"");H M.2n[a]||(M.2n[a]=M(f,e))};M.3c=6(f){H r(f,"g")};M.5l=6(f){H f.Q(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g,"\\\\$&")};M.5e=6(f,e,a,b){e=r(e,"g"+(b&&E?"y":""));e.12=a=a||0;f=e.X(f);H b?f&&f.P===a?f:N:f};M.3q=6(){M.1h=6(){1S 2U("2a\'t 55 1h 54 3q")}};M.1R=6(f){H 53.Z.1q.W(f)==="[2m 15]"};M.3p=6(f,e,a,b){O(K c=r(e,"g"),d=-1,h;h=c.X(f);){a.W(b,h,++d,f,c);c.12===h.P&&c.12++}I(e.1J)e.12=0};M.57=6(f,e){H 6 a(b,c){K d=e[c].1I?e[c]:{1I:e[c]},h=r(d.1I,"g"),g=[],i;O(i=0;i<b.L;i++)M.3p(b[i],h,6(k){g.U(d.3j?k[d.3j]||"":k[0])});H c===e.L-1||!g.L?g:a(g,c+1)}([f],0)};15.Z.1p=6(f,e){H J.X(e[0])};15.Z.W=6(f,e){H J.X(e)};15.Z.X=6(f){K e=n.X.1p(J,14),a;I(e){I(!x&&e.L>1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;b<e.L;b++)I(a=J.1w.19[b-1])e[a]=e[b];!D&&J.1J&&!e[0].L&&J.12>e.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;d<b.L;d++)I(b[d])14[0][b[d]]=14[d+1]}I(a&&f.1J)f.12=14[14.L-2]+14[0].L;H e.1p(N,14)});Y{c=J+"";c=n.Q.W(c,f,6(){K d=14;H n.Q.W(e,C,6(h,g,i){I(g)5b(g){24"$":H"$";24"&":H d[0];24"`":H d[d.L-1].1a(0,d[d.L-2]);24"\'":H d[d.L-1].1a(d[d.L-2]+d[0].L);5a:i="";g=+g;I(!g)H h;O(;g>d.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P<a.L&&3b.Z.U.1p(b,d.1a(1));h=d[0].L;c=f.12;I(b.L>=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a<d.L&&i==N;a++)i=p(d[a],b,c);H i}6 C(a,b){K c={},d;O(d 2g a)c[d]=a[d];O(d 2g b)c[d]=b[d];H c}6 w(a,b,c,d){6 h(g){g=g||1P.5y;I(!g.1F){g.1F=g.52;g.3N=6(){J.5w=11}}c.W(d||1P,g)}a.3g?a.3g("4U"+b,h):a.4y(b,h,11)}6 A(a,b){K c=e.1Y.2j,d=N;I(c==N){c={};O(K h 2g e.1U){K g=e.1U[h];d=g.4x;I(d!=N){g.1V=h.4w();O(g=0;g<d.L;g++)c[d[g]]=h}}e.1Y.2j=c}d=e.1U[c[a]];d==N&&b!=11&&1P.1X(e.13.1x.1X+(e.13.1x.3E+a));H d}6 v(a,b){O(K c=a.1e("\\n"),d=0;d<c.L;d++)c[d]=b(c[d],d);H c.1K("\\n")}6 u(a,b){I(a==N||a.L==0||a=="\\n")H a;a=a.Q(/</g,"&1y;");a=a.Q(/ {2,}/g,6(c){O(K d="",h=0;h<c.L-1;h++)d+=e.13.1W;H d+" "});I(b!=N)a=v(a,6(c){I(c.L==0)H"";K d="";c=c.Q(/^(&2s;| )+/,6(h){d=h;H""});I(c.L==0)H d;H d+\'<17 1g="\'+b+\'">\'+c+"</17>"});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.P<b.P)H-1;Y I(a.P>b.P)H 1;Y I(a.L<b.L)H-1;Y I(a.L>b.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'<a 2h="\'+c+\'">\'+c+"</a>"+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<a.L;c++)a[c].3s=="20"&&b.U(a[c]);H b}6 f(a){a=a.1F;K b=p(a,".20",R);a=p(a,".3O",R);K c=1E.4i("3t");I(!(!a||!b||p(a,"3t"))){B(b.1c);r(b,"1m");O(K d=a.3G,h=[],g=0;g<d.L;g++)h.U(d[g].4z||d[g].4A);h=h.1K("\\r");c.39(1E.4D(h));a.39(c);c.2C();c.4C();w(c,"4u",6(){c.2G.4E(c);b.1l=b.1l.Q("1m","")})}}I(1j 3F!="1d"&&1j M=="1d")M=3F("M").M;K e={2v:{"1g-27":"","2i-1s":1,"2z-1s-2t":11,1M:N,1t:N,"42-45":R,"43-22":4,1u:R,16:R,"3V-17":R,2l:11,"41-40":R,2k:11,"1z-1k":11},13:{1W:"&2s;",2M:R,46:11,44:11,34:"4n",1x:{21:"4o 1m",2P:"?",1X:"1v\\n\\n",3E:"4r\'t 4t 1D O: ",4g:"4m 4B\'t 51 O 1z-1k 4F: ",37:\'<!4T 1z 4S "-//4V//3H 4W 1.0 4Z//4Y" "1Z://2y.3L.3K/4X/3I/3H/3I-4P.4J"><1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v</1t></3J><3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;"><T 1L="2O-3D:3C;3w-32:1.6z;"><T 1L="25-22:6A-6E;">1v</T><T 1L="25-22:.6C;3w-6B:6R;"><T>3v 3.0.76 (72 73 3x)</T><T><a 2h="1Z://3u.2w/1v" 1F="38" 1L="2f:#3y">1Z://3u.2w/1v</a></T><T>70 17 6U 71.</T><T>6T 6X-3x 6Y 6D.</T></T><T>6t 61 60 J 1k, 5Z <a 2h="6u://2y.62.2w/63-66/65?64=5X-5W&5P=5O" 1L="2f:#3y">5R</a> 5V <2R/>5U 5T 5S!</T></T></3B></1z>\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'<T 1g="16">\',d=e.16.2x,h=d.2X,g=0;g<h.L;g++)c+=(d[h[g]].1H||b)(a,h[g]);c+="</T>";H c},2o:6(a,b,c){H\'<2W><a 2h="#" 1g="6e 6h\'+b+" "+b+\'">\'+c+"</a></2W>"},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h<c.L;h++)d.U(c[h]);c=d}c=c;d=[];I(e.13.2M)c=c.1O(z());I(c.L===0)H d;O(h=0;h<c.L;h++){O(K g=c[h],i=a,k=c[h].1l,j=3W 0,l={},m=1f M("^\\\\[(?<2V>(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g<c.L;g++){b=c[g];K i=b.1F,k=b.1n,j=k.1D,l;I(j!=N){I(k["1z-1k"]=="R"||e.2v["1z-1k"]==R){d=1f e.4l(j);j="4O"}Y I(d=A(j))d=1f d;Y 6H;l=i.3X;I(h.2M){l=l;K m=x(l),s=11;I(m.1i("<![6G[")==0){m=m.4h(9);s=R}K o=m.L;I(m.1i("]]\\>")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;m<j.L;m++)j[m].P+=l}K c=A(a),d,h=1f e.1U.5Y,g=J,i="2F 1H 2Q".1e(" ");I(c!=N){d=1f c;O(K k=0;k<i.L;k++)(6(){K j=i[k];g[j]=6(){H h[j].1p(h,14)}})();d.28==N?1P.1X(e.13.1x.1X+(e.13.1x.4g+a)):h.2J.U({1I:d.28.17,2D:6(j){O(K l=j.17,m=[],s=d.2J,o=j.P+j.18.L,F=d.28,q,G=0;G<s.L;G++){q=y(l,s[G]);b(q,o);m=m.1O(q)}I(F.18!=N&&j.18!=N){q=y(j.18,F.18);b(q,j.P);m=m.1O(q)}I(F.1b!=N&&j.1b!=N){q=y(j.1b,F.1b);b(q,j.P+j[0].5Q(j.1b));m=m.1O(q)}O(j=0;j<m.L;j++)m[j].1V=c.1V;H m}})}};e.4j=6(){};e.4j.Z={V:6(a,b){K c=J.1n[a];c=c==N?b:c;K d={"R":R,"11":11}[c];H d==N?c:d},3Y:6(a){H 1E.4i(a)},4c:6(a,b){K c=[];I(a!=N)O(K d=0;d<a.L;d++)I(1j a[d]=="2m")c=c.1O(y(b,a[d]));H J.4e(c.6b(D))},4e:6(a){O(K b=0;b<a.L;b++)I(a[b]!==N)O(K c=a[b],d=c.P+c.L,h=b+1;h<a.L&&a[b]!==N;h++){K g=a[h];I(g!==N)I(g.P>d)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P<d)a[h]=N}H a},4d:6(a){K b=[],c=2u(J.V("2i-1s"));v(a,6(d,h){b.U(h+c)});H b},3U:6(a){K b=J.V("1M",[]);I(1j b!="2m"&&b.U==N)b=[b];a:{a=a.1q();K c=3W 0;O(c=c=1Q.6c(c||0,0);c<b.L;c++)I(b[c]==a){b=c;1N a}b=-1}H b!=-1},2r:6(a,b,c){a=["1s","6i"+b,"P"+a,"6r"+(b%2==0?1:2).1q()];J.3U(b)&&a.U("67");b==0&&a.U("1N");H\'<T 1g="\'+a.1K(" ")+\'">\'+c+"</T>"},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i<d;i++){K k=b?b[i]:h+i,j;I(k==0)j=e.13.1W;Y{j=g;O(K l=k.1q();l.L<j;)l="0"+l;j=l}a=j;c+=J.2r(i,k,a)}H c},49:6(a,b){a=x(a);K c=a.1e("\\n");J.V("2z-1s-2t");K d=2u(J.V("2i-1s"));a="";O(K h=J.V("1D"),g=0;g<c.L;g++){K i=c[g],k=/^(&2s;|\\s)+/.X(i),j=N,l=b?b[g]:d+g;I(k!=N){j=k[0].1q();i=i.1o(j.L);j=j.Q(" ",e.13.1W)}i=x(i);I(i.L==0)i=e.13.1W;a+=J.2r(g,l,(j!=N?\'<17 1g="\'+h+\' 5N">\'+j+"</17>":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"</4a>":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i<b.L;i++){K k=b[i],j;I(!(k===N||k.L===0)){j=c(k);h+=u(a.1o(d,k.P-d),j+"48")+u(k.1T,j+k.23);d=k.P+k.L+(k.75||0)}}h+=u(a.1o(d),c()+"48");H h},1H:6(a){K b="",c=["20"],d;I(J.V("2k")==R)J.1n.16=J.1n.1u=11;1l="20";J.V("2l")==R&&c.U("47");I((1u=J.V("1u"))==11)c.U("6S");c.U(J.V("1g-27"));c.U(J.V("1D"));a=a.Q(/^[ ]*[\\n]+|[\\n]*[ ]*$/g,"").Q(/\\r/g," ");b=J.V("43-22");I(J.V("42-45")==R)a=n(a,b);Y{O(K h="",g=0;g<b;g++)h+=" ";a=a.Q(/\\t/g,h)}a=a;a:{b=a=a;h=/<2R\\s*\\/?>|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i<b.L&&g>0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i<b.L;i++)b[i]=b[i].1o(g);a=b.1K("\\n")}I(1u)d=J.4d(a);b=J.4c(J.2J,a);b=J.4b(a,b);b=J.49(b,d);I(J.V("41-40"))b=E(b);1j 2H!="1d"&&2H.3S&&2H.3S.1C(/5s/)&&c.U("5t");H b=\'<T 1c="\'+t(J.1c)+\'" 1g="\'+c.1K(" ")+\'">\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"</2d>":"")+\'<2d 1g="17"><T 1g="3O">\'+b+"</T></2d></3P></3T></3Z></T>"},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{}))
diff --git a/pecan/middleware/resources/theme.css b/pecan/middleware/resources/theme.css
new file mode 100644
index 0000000..8a870e4
--- /dev/null
+++ b/pecan/middleware/resources/theme.css
@@ -0,0 +1,117 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ *
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+ background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+ background-color: #e0e0e0 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+ color: black !important;
+}
+.syntaxhighlighter table caption {
+ color: black !important;
+}
+.syntaxhighlighter .gutter {
+ color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+ border-right: 3px solid #6ce26c !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+ background-color: #6ce26c !important;
+ color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+ border: none !important;
+}
+.syntaxhighlighter.collapsed {
+ overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+ color: blue !important;
+ background: white !important;
+ border: 1px solid #6ce26c !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+ color: blue !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+ color: red !important;
+}
+.syntaxhighlighter .toolbar {
+ color: white !important;
+ background: #6ce26c !important;
+ border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+ color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+ color: black !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+ color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+ color: #008200 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+ color: blue !important;
+}
+.syntaxhighlighter .keyword {
+ color: #006699 !important;
+}
+.syntaxhighlighter .preprocessor {
+ color: gray !important;
+}
+.syntaxhighlighter .variable {
+ color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+ color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+ color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+ font-weight: bold !important;
+ color: #006699 !important;
+ background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+ color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+ color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+ color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+ font-weight: bold !important;
+}
diff --git a/pecan/rest.py b/pecan/rest.py
index fc6d5e5..2fda2a3 100644
--- a/pecan/rest.py
+++ b/pecan/rest.py
@@ -32,7 +32,7 @@ class RestController(object):
via a GET request).
'''
# convention uses "_method" to handle browser-unsupported methods
- if request.environ.get('pecan.validation_redirected', False) == True:
+ if request.environ.get('pecan.validation_redirected', False) is True:
#
# If the request has been internally redirected due to a validation
# exception, we want the request method to be enforced as GET, not
@@ -97,8 +97,8 @@ class RestController(object):
self._set_routing_args(remainder[:i])
return lookup_controller(controller, remainder[i + 1:])
elif fixed_args < len(remainder) and hasattr(
- self, remainder[fixed_args]
- ):
+ self, remainder[fixed_args]
+ ):
controller = getattr(self, remainder[fixed_args])
if not ismethod(controller):
self._set_routing_args(remainder[:fixed_args])
diff --git a/pecan/secure.py b/pecan/secure.py
index cf14f6f..c6f29cf 100644
--- a/pecan/secure.py
+++ b/pecan/secure.py
@@ -1,3 +1,4 @@
+from functools import wraps
from inspect import getmembers, ismethod, isfunction
from webob import exc
@@ -78,10 +79,11 @@ class _SecuredAttribute(object):
# helper for secure decorator
def _allowed_check_permissions_types(x):
- return (ismethod(x) or
- isfunction(x) or
- isinstance(x, basestring)
- )
+ return (
+ ismethod(x) or
+ isfunction(x) or
+ isinstance(x, basestring)
+ )
# methods that can either decorate functions or wrap classes
@@ -134,14 +136,20 @@ class SecureController(object):
unlocked=[]
)
- for name, value in getmembers(cls):
+ for name, value in getmembers(cls)[:]:
if ismethod(value):
if iscontroller(value) and value._pecan.get(
'secured'
) is None:
- value._pecan['secured'] = Protected
- value._pecan['check_permissions'] = \
+ # Wrap the function so that the security context is
+ # local to this class definition. This works around
+ # the fact that unbound method attributes are shared
+ # across classes with the same bases.
+ wrapped = _make_wrapper(value)
+ wrapped._pecan['secured'] = Protected
+ wrapped._pecan['check_permissions'] = \
cls.check_permissions
+ setattr(cls, name, wrapped)
elif hasattr(value, '__class__'):
if name.startswith('__') and name.endswith('__'):
continue
@@ -162,6 +170,15 @@ class SecureController(object):
return False
+def _make_wrapper(f):
+ """return a wrapped function with a copy of the _pecan context"""
+ @wraps(f)
+ def wrapper(*args, **kwargs):
+ return f(*args, **kwargs)
+ wrapper._pecan = f._pecan.copy()
+ return wrapper
+
+
# methods to evaluate security during routing
def handle_security(controller):
""" Checks the security of a controller. """
diff --git a/pecan/templating.py b/pecan/templating.py
index 5089e9e..d131909 100644
--- a/pecan/templating.py
+++ b/pecan/templating.py
@@ -32,7 +32,7 @@ _builtin_renderers['json'] = JsonRenderer
try:
from genshi.template import (TemplateLoader,
- TemplateError as gTemplateError)
+ TemplateError as gTemplateError)
class GenshiRenderer(object):
'''
@@ -71,8 +71,8 @@ except ImportError: # pragma no cover
try:
from mako.lookup import TemplateLookup
- from mako.exceptions import CompileException, SyntaxException, \
- html_error_template
+ from mako.exceptions import (CompileException, SyntaxException,
+ html_error_template)
class MakoRenderer(object):
'''
diff --git a/pecan/tests/middleware/test_debug.py b/pecan/tests/middleware/test_debug.py
index 6312d64..a4a9608 100644
--- a/pecan/tests/middleware/test_debug.py
+++ b/pecan/tests/middleware/test_debug.py
@@ -1,18 +1,32 @@
from unittest import TestCase
+from wsgiref.util import setup_testing_defaults
+
from webtest import TestApp
from pecan.middleware.debug import DebugMiddleware
+class StripPasteVar(object):
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ environ.pop('paste.testing')
+ return self.app(environ, start_response)
+
+
class TestDebugMiddleware(TestCase):
def setUp(self):
def conditional_error_app(environ, start_response):
+ setup_testing_defaults(environ)
if environ['PATH_INFO'] == '/error':
assert 1 == 2
start_response("200 OK", [('Content-type', 'text/plain')])
return ['requested page returned']
- self.app = TestApp(DebugMiddleware(conditional_error_app))
+ self.app = TestApp(StripPasteVar(DebugMiddleware(
+ conditional_error_app
+ )))
def test_middleware_passes_through_when_no_exception_raised(self):
r = self.app.get('/')
@@ -54,12 +68,10 @@ class TestDebugMiddleware(TestCase):
debugger = []
- app = TestApp(
- DebugMiddleware(
- self.app,
- patch_debugger(debugger)
- )
- )
+ app = TestApp(StripPasteVar(DebugMiddleware(
+ self.app,
+ patch_debugger(debugger)
+ )))
r = app.get('/error', expect_errors=True)
assert r.status_int == 400
diff --git a/pecan/tests/middleware/test_recursive.py b/pecan/tests/middleware/test_recursive.py
index 8c7888a..c52612d 100644
--- a/pecan/tests/middleware/test_recursive.py
+++ b/pecan/tests/middleware/test_recursive.py
@@ -99,9 +99,8 @@ class TestRecursiveMiddleware(TestCase):
self.app = app
def __call__(self, e, start_response):
- def keep_status_start_response(
- status, headers, exc_info=None
- ):
+ def keep_status_start_response(status, headers,
+ exc_info=None):
return start_response(
'404 Not Found', headers, exc_info
)
diff --git a/pecan/tests/middleware/test_static.py b/pecan/tests/middleware/test_static.py
index eaddf73..976a747 100644
--- a/pecan/tests/middleware/test_static.py
+++ b/pecan/tests/middleware/test_static.py
@@ -1,6 +1,6 @@
from unittest import TestCase
from pecan.middleware.static import (StaticFileMiddleware, FileWrapper,
- _dump_date)
+ _dump_date)
import os
@@ -50,7 +50,7 @@ class TestStaticFileMiddleware(TestCase):
def test_file_can_be_closed(self):
result = self._request('/static_fixtures/text.txt')
- assert result.close() == None
+ assert result.close() is None
def test_file_can_be_iterated_over(self):
result = self._request('/static_fixtures/text.txt')
diff --git a/pecan/tests/test_base.py b/pecan/tests/test_base.py
index 0b5bc88..13d1c94 100644
--- a/pecan/tests/test_base.py
+++ b/pecan/tests/test_base.py
@@ -908,6 +908,54 @@ class TestFileTypeExtensions(unittest.TestCase):
assert r.status_int == 404
+class TestContentTypeByAcceptHeaders(unittest.TestCase):
+
+ @property
+ def app_(self):
+ """
+ Test that content type is set appropriately based on Accept headers.
+ """
+ class RootController(object):
+
+ @expose(content_type='text/html')
+ @expose(content_type='application/json')
+ def index(self, *args):
+ return 'Foo'
+
+ return TestApp(Pecan(RootController()))
+
+ def test_quality(self):
+ r = self.app_.get('/', headers={
+ 'Accept': 'text/html,application/json;q=0.9,*/*;q=0.8'
+ })
+ assert r.status_int == 200
+ assert r.content_type == 'text/html'
+
+ r = self.app_.get('/', headers={
+ 'Accept': 'application/json,text/html;q=0.9,*/*;q=0.8'
+ })
+ assert r.status_int == 200
+ assert r.content_type == 'application/json'
+
+ def test_file_extension_has_higher_precedence(self):
+ r = self.app_.get('/index.html', headers={
+ 'Accept': 'application/json,text/html;q=0.9,*/*;q=0.8'
+ })
+ assert r.status_int == 200
+ assert r.content_type == 'text/html'
+
+ def test_not_acceptable(self):
+ r = self.app_.get('/', headers={
+ 'Accept': 'application/xml',
+ }, status=406)
+ assert r.status_int == 406
+
+ def test_accept_header_missing(self):
+ r = self.app_.get('/')
+ assert r.status_int == 200
+ assert r.content_type == 'text/html'
+
+
class TestCanonicalRouting(unittest.TestCase):
@property
@@ -1050,9 +1098,9 @@ class TestNonCanonical(unittest.TestCase):
@expose()
def index(self):
request.testing = True
- assert request.testing == True
+ assert request.testing is True
del request.testing
- assert hasattr(request, 'testing') == False
+ assert hasattr(request, 'testing') is False
return '/'
app = TestApp(make_app(RootController(), debug=True))
@@ -1295,8 +1343,7 @@ class TestEngines(unittest.TestCase):
return render('mako.html', dict(name=name))
app = TestApp(
- Pecan(RootController(),
- template_path=self.template_path)
+ Pecan(RootController(), template_path=self.template_path)
)
r = app.get('/')
assert r.status_int == 200
diff --git a/pecan/tests/test_conf.py b/pecan/tests/test_conf.py
index 74e9d2a..4b276cb 100644
--- a/pecan/tests/test_conf.py
+++ b/pecan/tests/test_conf.py
@@ -194,7 +194,7 @@ class TestConf(TestCase):
assert to_dict['server']['host'] == '0.0.0.0'
assert to_dict['server']['port'] == '8080'
assert to_dict['app']['modules'] == []
- assert to_dict['app']['root'] == None
+ assert to_dict['app']['root'] is None
assert to_dict['app']['static_root'] == 'public'
assert to_dict['app']['template_path'] == ''
@@ -211,7 +211,7 @@ class TestConf(TestCase):
assert to_dict['server']['host'] == '0.0.0.0'
assert to_dict['server']['port'] == '8080'
assert to_dict['app']['modules'] == []
- assert to_dict['app']['root'] == None
+ assert to_dict['app']['root'] is None
assert to_dict['app']['static_root'] == 'public'
assert to_dict['app']['template_path'] == ''
assert to_dict['nested']['one']['two'] == 2
@@ -229,7 +229,7 @@ class TestConf(TestCase):
assert to_dict['prefix_server']['prefix_host'] == '0.0.0.0'
assert to_dict['prefix_server']['prefix_port'] == '8080'
assert to_dict['prefix_app']['prefix_modules'] == []
- assert to_dict['prefix_app']['prefix_root'] == None
+ assert to_dict['prefix_app']['prefix_root'] is None
assert to_dict['prefix_app']['prefix_static_root'] == 'public'
assert to_dict['prefix_app']['prefix_template_path'] == ''
@@ -265,7 +265,8 @@ class TestGlobalConfig(TestCase):
def test_overwrite_from_file(self):
from pecan import configuration
- configuration.set_config(os.path.join(
+ configuration.set_config(
+ os.path.join(
__here__,
'config_fixtures/foobar.py',
),
diff --git a/pecan/tests/test_hooks.py b/pecan/tests/test_hooks.py
index 458e40b..ab0faea 100644
--- a/pecan/tests/test_hooks.py
+++ b/pecan/tests/test_hooks.py
@@ -1078,7 +1078,8 @@ class TestRequestViewerHook(TestCase):
return 'Hello, World!'
app = TestApp(
- make_app(RootController(),
+ make_app(
+ RootController(),
hooks=[
RequestViewerHook(
config={'items':['path']}, writer=_stdout
@@ -1112,7 +1113,8 @@ class TestRequestViewerHook(TestCase):
return 'Hello, World!'
app = TestApp(
- make_app(RootController(),
+ make_app(
+ RootController(),
hooks=[
RequestViewerHook(
config={'blacklist':['/']}, writer=_stdout
@@ -1138,7 +1140,8 @@ class TestRequestViewerHook(TestCase):
return 'Hello, World!'
app = TestApp(
- make_app(RootController(),
+ make_app(
+ RootController(),
hooks=[
RequestViewerHook(
config={'items':['date']}, writer=_stdout
diff --git a/pecan/tests/test_jsonify.py b/pecan/tests/test_jsonify.py
index e30eaa1..3c5d8b5 100644
--- a/pecan/tests/test_jsonify.py
+++ b/pecan/tests/test_jsonify.py
@@ -15,7 +15,7 @@ from pecan.jsonify import jsonify, encode, ResultProxy, RowProxy
from pecan import Pecan, expose
from webtest import TestApp
-from webob.multidict import MultiDict
+from webob.multidict import MultiDict
def make_person():
@@ -182,10 +182,13 @@ class TestJsonifySQLAlchemyGenericEncoder(TestCase):
# create the table and mapper
metadata = schema.MetaData()
- user_table = schema.Table('user', metadata,
+ user_table = schema.Table(
+ 'user',
+ metadata,
schema.Column('id', types.Integer, primary_key=True),
schema.Column('first_name', types.Unicode(25)),
- schema.Column('last_name', types.Unicode(25)))
+ schema.Column('last_name', types.Unicode(25))
+ )
class User(object):
pass
diff --git a/pecan/tests/test_scaffolds.py b/pecan/tests/test_scaffolds.py
index 3264549..0dc916c 100644
--- a/pecan/tests/test_scaffolds.py
+++ b/pecan/tests/test_scaffolds.py
@@ -4,10 +4,10 @@ import tempfile
import shutil
import subprocess
import pkg_resources
-import httplib
import urllib2
import time
from cStringIO import StringIO
+
import pecan
if sys.version_info < (2, 7):
@@ -82,9 +82,10 @@ class TestScaffoldUtils(unittest.TestCase):
def test_destination_directory_levels_deep(self):
from pecan.scaffolds import copy_dir
f = StringIO()
- copy_dir(('pecan', os.path.join(
- 'tests', 'scaffold_fixtures', 'simple'
- )),
+ copy_dir(
+ (
+ 'pecan', os.path.join('tests', 'scaffold_fixtures', 'simple')
+ ),
os.path.join(self.scaffold_destination, 'some', 'app'),
{},
out_=f
@@ -107,9 +108,10 @@ class TestScaffoldUtils(unittest.TestCase):
from pecan.scaffolds import copy_dir
from cStringIO import StringIO
f = StringIO()
- copy_dir(('pecan', os.path.join(
- 'tests', 'scaffold_fixtures', 'simple'
- )),
+ copy_dir(
+ (
+ 'pecan', os.path.join('tests', 'scaffold_fixtures', 'simple')
+ ),
os.path.join(self.scaffold_destination),
{},
out_=f
@@ -118,9 +120,10 @@ class TestScaffoldUtils(unittest.TestCase):
def test_copy_dir_with_filename_substitution(self):
from pecan.scaffolds import copy_dir
- copy_dir(('pecan', os.path.join(
- 'tests', 'scaffold_fixtures', 'file_sub'
- )),
+ copy_dir(
+ (
+ 'pecan', os.path.join('tests', 'scaffold_fixtures', 'file_sub')
+ ),
os.path.join(
self.scaffold_destination, 'someapp'
),
@@ -143,9 +146,11 @@ class TestScaffoldUtils(unittest.TestCase):
def test_copy_dir_with_file_content_substitution(self):
from pecan.scaffolds import copy_dir
- copy_dir(('pecan', os.path.join(
- 'tests', 'scaffold_fixtures', 'content_sub'
- )),
+ copy_dir(
+ (
+ 'pecan',
+ os.path.join('tests', 'scaffold_fixtures', 'content_sub'),
+ ),
os.path.join(
self.scaffold_destination, 'someapp'
),
@@ -215,9 +220,8 @@ class TestTemplateBuilds(unittest.TestCase):
])
def poll(self, proc):
- limit = 5
+ limit = 30
for i in range(limit):
- time.sleep(1)
proc.poll()
# Make sure it's running
@@ -225,6 +229,7 @@ class TestTemplateBuilds(unittest.TestCase):
break
elif i == limit: # pragma: no cover
raise RuntimeError("pecan serve config.py didn't start.")
+ time.sleep(.1)
@unittest.skipUnless(has_internet(), 'Internet connectivity unavailable.')
@unittest.skipUnless(
@@ -243,13 +248,23 @@ class TestTemplateBuilds(unittest.TestCase):
try:
self.poll(proc)
-
- # ...and that it's serving (valid) content...
- conn = httplib.HTTPConnection('localhost:8080')
- conn.request('GET', '/')
- resp = conn.getresponse()
- assert resp.status == 200
- assert 'This is a sample Pecan project.' in resp.read()
+ retries = 30
+ while True:
+ retries -= 1
+ if retries < 0: # pragma: nocover
+ raise RuntimeError(
+ "The HTTP server has not replied within 3 seconds."
+ )
+ try:
+ # ...and that it's serving (valid) content...
+ resp = urllib2.urlopen('http://localhost:8080/')
+ assert resp.getcode() == 200
+ assert 'This is a sample Pecan project.' in resp.read()
+ except urllib2.URLError:
+ pass
+ else:
+ break
+ time.sleep(.1)
finally:
proc.terminate()
diff --git a/pecan/tests/test_secure.py b/pecan/tests/test_secure.py
index 7aca4eb..f71e740 100644
--- a/pecan/tests/test_secure.py
+++ b/pecan/tests/test_secure.py
@@ -1,8 +1,14 @@
-from unittest import TestCase
+import sys
+
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest # noqa
+
+from webtest import TestApp
from pecan import expose, make_app
from pecan.secure import secure, unlocked, SecureController
-from webtest import TestApp
try:
set()
@@ -10,7 +16,7 @@ except:
from sets import Set as set
-class TestSecure(TestCase):
+class TestSecure(unittest.TestCase):
def test_simple_secure(self):
authorized = False
@@ -184,7 +190,7 @@ class TestSecure(TestCase):
assert isinstance(e, TypeError)
-class TestObjectPathSecurity(TestCase):
+class TestObjectPathSecurity(unittest.TestCase):
def setUp(self):
permissions_checked = set()
@@ -392,3 +398,32 @@ class TestObjectPathSecurity(TestCase):
response = self.app.get('/notsecret/unlocked/')
assert response.status_int == 200
assert response.body == 'Index unlocked'
+
+
+class SecureControllerSharedPermissionsRegression(unittest.TestCase):
+ """Regression tests for https://github.com/dreamhost/pecan/issues/131"""
+
+ def setUp(self):
+ class Parent(object):
+ @expose()
+ def index(self):
+ return 'hello'
+
+ class UnsecuredChild(Parent):
+ pass
+
+ class SecureChild(Parent, SecureController):
+ @classmethod
+ def check_permissions(cls):
+ return False
+
+ class RootController(object):
+
+ secured = SecureChild()
+ unsecured = UnsecuredChild()
+
+ self.app = TestApp(make_app(RootController()))
+
+ def test_inherited_security(self):
+ assert self.app.get('/secured/', status=401).status_int == 401
+ assert self.app.get('/unsecured/').status_int == 200
diff --git a/setup.py b/setup.py
index 32d5a0f..cb3bb6a 100644
--- a/setup.py
+++ b/setup.py
@@ -2,16 +2,16 @@ import sys
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
-version = '0.2.0a'
+version = '0.2.0b'
#
# determine requirements
#
requirements = [
- "WebOb >= 1.2dev", # py3 compat
- "simplegeneric >= 0.8", # py3 compat
- "Mako >= 0.4.0",
- "WebTest >= 1.3.1" # py3 compat
+ "WebOb >= 1.2dev", # py3 compat
+ "simplegeneric >= 0.8", # py3 compat
+ "Mako >= 0.4.0",
+ "WebTest >= 1.3.1" # py3 compat
]
try:
@@ -61,8 +61,8 @@ class test(TestCommand):
setup(
name='pecan',
version=version,
- description="A WSGI object-dispatching web framework, designed to be "\
- "lean and fast, with few dependancies.",
+ description="A WSGI object-dispatching web framework, designed to be "
+ "lean and fast, with few dependancies.",
long_description=None,
classifiers=[
'Development Status :: 5 - Production/Stable',
diff --git a/tox.ini b/tox.ini
index 16135c2..55e2996 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,9 @@
[tox]
-envlist = py26,py27
+envlist = py26,py27,pep8
[testenv]
commands=python setup.py test --functional
+
+[testenv:pep8]
+deps = pep8
+commands = pep8 --repeat --show-source --exclude *compat*,resources.py pecan setup.py