--- a/.hgignore Fri Aug 20 16:29:35 2010 +0200
+++ b/.hgignore Wed Jun 29 19:47:16 2011 -0400
@@ -9,3 +9,4 @@
*.swp
*.log
docs/_build
+*.un~
--- a/README.markdown Fri Aug 20 16:29:35 2010 +0200
+++ b/README.markdown Wed Jun 29 19:47:16 2011 -0400
@@ -1,9 +1,5 @@
-hg-review is close to being released!
-=====================================
-
-Feedback is appreciated, but things may still break before v1.0.0!
-==================================================================
-
+hg-review is still beta! Things may break!
+===========================================
Installing
==========
@@ -46,4 +42,10 @@
Check out the [documentation][docs] to learn more.
+If you've still got questions, there's a mailing list (hosted by
+[Librelist][]). Send an email (no subject/body necessary) to
+[hg.review@librelist.com][ml] to subscribe.
+
[docs]: http://sjl.bitbucket.org/hg-review/
+[Librelist]: http://librelist.com/
+[ml]: mailto:hg.review@librelist.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/.gitignore Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,10 @@
+.DS_Store
+*.pyc
+*.pyo
+env
+env*
+dist
+*.egg
+*.egg-info
+_mailinglist
+.tox
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/.gitmodules Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,3 @@
+[submodule "docs/_themes"]
+ path = docs/_themes
+ url = git://github.com/mitsuhiko/flask-sphinx-themes.git
--- a/bundled/flask/AUTHORS Fri Aug 20 16:29:35 2010 +0200
+++ b/bundled/flask/AUTHORS Wed Jun 29 19:47:16 2011 -0400
@@ -9,16 +9,22 @@
Patches and Suggestions
```````````````````````
+- Adam Zapletal
+- Ali Afshar
- Chris Edgemon
- Chris Grindstaff
+- Christopher Grebs
- Florent Xicluna
- Georg Brandl
- Justin Quick
- Kenneth Reitz
- Marian Sigler
+- Matt Campell
- Matthew Frazier
+- Michael van Tellingen
- Ron DuPlain
- Sebastien Estienne
- Simon Sapin
- Stephane Wirtel
+- Thomas Schranz
- Zhao Xiaohong
--- a/bundled/flask/CHANGES Fri Aug 20 16:29:35 2010 +0200
+++ b/bundled/flask/CHANGES Wed Jun 29 19:47:16 2011 -0400
@@ -3,10 +3,134 @@
Here you can see the full list of changes between each Flask release.
+Version 0.7
+-----------
+
+Release date to be announced, codename to be selected
+
+- Added :meth:`~flask.Flask.make_default_options_response`
+ which can be used by subclasses to alter the default
+ behaviour for `OPTIONS` responses.
+- Unbound locals now raise a proper :exc:`RuntimeError` instead
+ of an :exc:`AttributeError`.
+- Mimetype guessing and etag support based on file objects is now
+ deprecated for :func:`flask.send_file` because it was unreliable.
+ Pass filenames instead or attach your own etags and provide a
+ proper mimetype by hand.
+- Static file handling for modules now requires the name of the
+ static folder to be supplied explicitly. The previous autodetection
+ was not reliable and caused issues on Google's App Engine. Until
+ 1.0 the old behaviour will continue to work but issue dependency
+ warnings.
+- fixed a problem for Flask to run on jython.
+- added a `PROPAGATE_EXCEPTIONS` configuration variable that can be
+ used to flip the setting of exception propagation which previously
+ was linked to `DEBUG` alone and is now linked to either `DEBUG` or
+ `TESTING`.
+- Flask no longer internally depends on rules being added through the
+ `add_url_rule` function and can now also accept regular werkzeug
+ rules added to the url map.
+- Added an `endpoint` method to the flask application object which
+ allows one to register a callback to an arbitrary endpoint with
+ a decorator.
+
+Version 0.6.1
+-------------
+
+Bugfix release, released on December 31st 2010
+
+- Fixed an issue where the default `OPTIONS` response was
+ not exposing all valid methods in the `Allow` header.
+- Jinja2 template loading syntax now allows "./" in front of
+ a template load path. Previously this caused issues with
+ module setups.
+- Fixed an issue where the subdomain setting for modules was
+ ignored for the static folder.
+- Fixed a security problem that allowed clients to download arbitrary files
+ if the host server was a windows based operating system and the client
+ uses backslashes to escape the directory the files where exposed from.
+
+Version 0.6
+-----------
+
+Released on July 27th 2010, codename Whisky
+
+- after request functions are now called in reverse order of
+ registration.
+- OPTIONS is now automatically implemented by Flask unless the
+ application explicitly adds 'OPTIONS' as method to the URL rule.
+ In this case no automatic OPTIONS handling kicks in.
+- static rules are now even in place if there is no static folder
+ for the module. This was implemented to aid GAE which will
+ remove the static folder if it's part of a mapping in the .yml
+ file.
+- the :attr:`~flask.Flask.config` is now available in the templates
+ as `config`.
+- context processors will no longer override values passed directly
+ to the render function.
+- added the ability to limit the incoming request data with the
+ new ``MAX_CONTENT_LENGTH`` configuration value.
+- the endpoint for the :meth:`flask.Module.add_url_rule` method
+ is now optional to be consistent with the function of the
+ same name on the application object.
+- added a :func:`flask.make_response` function that simplifies
+ creating response object instances in views.
+- added signalling support based on blinker. This feature is currently
+ optional and supposed to be used by extensions and applications. If
+ you want to use it, make sure to have `blinker`_ installed.
+- refactored the way URL adapters are created. This process is now
+ fully customizable with the :meth:`~flask.Flask.create_url_adapter`
+ method.
+- modules can now register for a subdomain instead of just an URL
+ prefix. This makes it possible to bind a whole module to a
+ configurable subdomain.
+
+.. _blinker: http://pypi.python.org/pypi/blinker
+
+Version 0.5.2
+-------------
+
+Bugfix Release, released on July 15th 2010
+
+- fixed another issue with loading templates from directories when
+ modules were used.
+
+Version 0.5.1
+-------------
+
+Bugfix Release, released on July 6th 2010
+
+- fixes an issue with template loading from directories when modules
+ where used.
+
+Version 0.5
+-----------
+
+Released on July 6th 2010, codename Calvados
+
+- fixed a bug with subdomains that was caused by the inability to
+ specify the server name. The server name can now be set with
+ the `SERVER_NAME` config key. This key is now also used to set
+ the session cookie cross-subdomain wide.
+- autoescaping is no longer active for all templates. Instead it
+ is only active for ``.html``, ``.htm``, ``.xml`` and ``.xhtml``.
+ Inside templates this behaviour can be changed with the
+ ``autoescape`` tag.
+- refactored Flask internally. It now consists of more than a
+ single file.
+- :func:`flask.send_file` now emits etags and has the ability to
+ do conditional responses builtin.
+- (temporarily) dropped support for zipped applications. This was a
+ rarely used feature and led to some confusing behaviour.
+- added support for per-package template and static-file directories.
+- removed support for `create_jinja_loader` which is no longer used
+ in 0.5 due to the improved module support.
+- added a helper function to expose files from any directory.
+
Version 0.4
-----------
-Release date to be announced, codename to be selected.
+Released on June 18th 2010, codename Rakia
- added the ability to register application wide error handlers
from modules.
@@ -19,11 +143,12 @@
- because the Python standard library caches loggers, the name of
the logger is configurable now to better support unittests.
- added `TESTING` switch that can activate unittesting helpers.
+- the logger switches to `DEBUG` mode now if debug is enabled.
Version 0.3.1
-------------
-Bugfix release, released May 28th
+Bugfix release, released on May 28th 2010
- fixed a error reporting bug with :meth:`flask.Config.from_envvar`
- removed some unused code from flask
@@ -34,7 +159,7 @@
Version 0.3
-----------
-Released on May 28th, codename Schnaps
+Released on May 28th 2010, codename Schnaps
- added support for categories for flashed messages.
- the application now configures a :class:`logging.Handler` and will
@@ -50,7 +175,7 @@
Version 0.2
-----------
-Released on May 12th, codename Jägermeister
+Released on May 12th 2010, codename Jägermeister
- various bugfixes
- integrated JSON support
--- a/bundled/flask/LICENSE Fri Aug 20 16:29:35 2010 +0200
+++ b/bundled/flask/LICENSE Wed Jun 29 19:47:16 2011 -0400
@@ -3,9 +3,9 @@
Some rights reserved.
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
+Redistribution and use in source and binary forms of the software as well
+as documentation, with or without modification, are permitted provided
+that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
@@ -19,14 +19,15 @@
promote products derived from this software without specific
prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
--- a/bundled/flask/MANIFEST.in Fri Aug 20 16:29:35 2010 +0200
+++ b/bundled/flask/MANIFEST.in Wed Jun 29 19:47:16 2011 -0400
@@ -1,9 +1,12 @@
include Makefile CHANGES LICENSE AUTHORS
+recursive-include artwork *
recursive-include tests *
recursive-include examples *
recursive-include docs *
recursive-exclude docs *.pyc
recursive-exclude docs *.pyo
+recursive-exclude tests *.pyc
+recursive-exclude tests *.pyo
recursive-exclude examples *.pyc
recursive-exclude examples *.pyo
prune docs/_build
--- a/bundled/flask/Makefile Fri Aug 20 16:29:35 2010 +0200
+++ b/bundled/flask/Makefile Wed Jun 29 19:47:16 2011 -0400
@@ -1,10 +1,19 @@
-.PHONY: clean-pyc test upload-docs
+.PHONY: clean-pyc ext-test test upload-docs docs audit
all: clean-pyc test
test:
python setup.py test
+audit:
+ python setup.py audit
+
+tox-test:
+ PYTHONDONTWRITEBYTECODE= tox
+
+ext-test:
+ python tests/flaskext_test.py --browse
+
release:
python setup.py release sdist upload
@@ -20,3 +29,6 @@
scp -r docs/_build/dirhtml/* pocoo.org:/var/www/flask.pocoo.org/docs/
scp -r docs/_build/latex/Flask.pdf pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.pdf
scp -r docs/_build/flask-docs.zip pocoo.org:/var/www/flask.pocoo.org/docs/
+
+docs:
+ $(MAKE) -C docs html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/artwork/LICENSE Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,20 @@
+Copyright (c) 2010 by Armin Ronacher.
+
+Some rights reserved.
+
+This logo or a modified version may be used by anyone to refer to the
+Flask project, but does not indicate endorsement by the project.
+
+Redistribution and use in source (the SVG file) and binary forms (rendered
+PNG files etc.) of the image, with or without modification, are permitted
+provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright
+ notice and this list of conditions.
+
+* The names of the contributors to the Flask software (see AUTHORS) may
+ not be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+Note: we would appreciate that you make the image a link to
+http://flask.pocoo.org/ if you use it on a web page.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/artwork/logo-full.svg Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,290 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="460"
+ height="180"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="logo.svg">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective2824"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2840"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2878"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2894"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2910"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2926"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective2976"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3020"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3036"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3052"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <inkscape:perspective
+ id="perspective3866"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.98994949"
+ inkscape:cx="240.32415"
+ inkscape:cy="-37.836532"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1680"
+ inkscape:window-height="998"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-27.820801,-24.714976)">
+ <path
+ style="fill:#000000"
+ d="M 96.944917,182.03377 C 89.662681,176.30608 81.894549,170.81448 76.586317,163.08166 65.416842,149.44499 56.816875,133.6567 50.937585,117.06515 47.383955,106.27654 46.166898,94.709824 41.585799,84.338096 c -4.792287,-7.533044 0.821224,-15.767897 9.072722,-18.16242 3.673742,-0.705104 10.133327,-4.170258 2.335951,-1.693539 -6.990592,5.128871 -7.667129,-4.655603 -0.498823,-5.27517 4.892026,-0.650249 6.692895,-4.655044 5.019966,-8.260251 -5.251326,-3.424464 12.733737,-7.18801 3.684373,-12.297799 -9.426987,-10.170666 13.186339,-12.128546 7.607283,-0.577786 -1.335447,8.882061 15.801226,-1.627907 11.825117,8.628945 4.041283,4.925694 15.133562,1.1211 14.85838,8.031392 5.887092,0.404678 7.907562,5.358061 13.433992,5.738347 5.72759,2.586557 16.1108,4.624792 18.0598,11.079149 -5.68242,4.498756 -18.84089,-9.292674 -19.47305,3.160397 1.71659,18.396078 1.27926,37.346439 8.00986,54.864989 3.18353,10.60759 10.9012,18.95779 17.87109,27.21946 6.66875,8.09126 15.70186,13.78715 24.90885,18.58338 8.07647,3.80901 16.78383,6.33528 25.58583,7.92044 3.5701,-2.7307 9.87303,-12.8828 15.44238,-8.60188 0.26423,4.81007 -11.0541,10.05512 -0.53248,9.5235 6.17819,-1.86378 10.46336,4.77803 15.55099,-1.21289 4.68719,5.55206 19.48197,-3.54734 16.14693,7.80115 -4.50972,2.90955 -11.08689,1.15142 -15.60404,5.15397 -7.44757,-3.71979 -13.37691,3.32843 -21.6219,2.43707 -9.15641,1.64002 -18.4716,2.30204 -27.75473,2.31642 -15.22952,-1.20328 -30.78158,-1.71049 -45.26969,-7.01291 -8.16166,-2.37161 -16.12649,-7.01887 -23.299683,-11.66829 z m 12.862043,5.5729 c 7.9696,3.44651 15.76243,7.07889 24.49656,8.17457 13.85682,1.92727 28.16653,4.89163 42.07301,2.18757 -6.2939,-2.84199 -12.80077,1.10719 -19.07096,-2.0322 -7.52033,1.61821 -15.59049,-0.41223 -23.23574,-1.41189 -8.69395,-3.87259 -18.0762,-6.53549 -26.21772,-11.56219 -10.173155,-3.71578 5.26142,4.76524 8.00873,5.45214 6.35952,3.60969 -6.99343,-1.85044 -8.87589,-3.35101 -5.32648,-2.9879 -6.00529,-2.36357 -0.52745,0.67085 1.10332,0.64577 2.19359,1.32226 3.34946,1.87216 z M 94.642259,176.88976 c 7.722781,2.86052 -0.03406,-5.43082 -3.572941,-4.94904 -1.567906,-2.72015 -5.9903,-4.43854 -2.870721,-5.89973 -5.611524,1.9481 -5.878319,-7.40814 -8.516004,-6.07139 -5.936516,-1.87454 -2.310496,-8.51501 -9.381929,-12.59292 -0.645488,-4.29697 -7.02577,-8.02393 -9.060801,-14.50525 -0.898786,-3.31843 -7.208336,-12.84783 -3.332369,-3.97927 3.300194,8.53747 9.106618,15.84879 13.93868,23.15175 3.752083,6.95328 8.182497,14.22026 15.015767,18.55788 2.303436,2.20963 4.527452,5.59533 7.780318,6.28797 z M 72.39456,152.46355 c 0.26956,-1.16626 1.412424,2.52422 0,0 z m 31.49641,27.85526 c 1.71013,-0.76577 -2.45912,-0.96476 0,0 z m 4.19228,1.52924 c -0.43419,-2.1116 -1.91376,1.18074 0,0 z m 5.24749,2.18891 c 2.49828,-2.37871 -3.85009,-1.49983 0,0 z m 8.99389,5.01274 c 1.51811,-2.2439 -4.85872,-0.84682 0,0 z m -17.2707,-12.03933 c 3.88031,-2.51023 -5.01186,-0.0347 0,0 z m 3.9366,1.96293 c -0.11004,-1.32709 -1.40297,0.59432 0,0 z m 19.67473,12.28006 c 3.16281,1.99601 18.46961,4.3749 8.88477,0.81847 -1.60377,0.33811 -17.77263,-4.57336 -8.88477,-0.81847 z M 97.430958,166.92721 c -0.307503,-1.33094 -4.909341,-1.4694 0,0 z m 9.159302,5.33813 c 2.38371,-1.66255 -4.94757,-1.28235 0,0 z m 7.70426,4.72382 c 3.42065,-1.28963 -5.54907,-1.29571 0,0 z M 93.703927,162.86805 c 3.711374,2.84621 14.967683,0.36473 5.683776,-1.69906 -4.225516,-2.2524 -13.74889,-3.79415 -7.25757,1.35821 l 1.573785,0.34088 9e-6,-3e-5 z m 25.808723,15.75216 c 1.54595,-2.63388 -6.48298,-1.50411 0,0 z m -7.84249,-6.23284 c 9.0752,2.56719 -7.63142,-5.739 -2.23911,-0.94466 l 1.19513,0.54082 1.04399,0.4039 -1e-5,-6e-5 z m 15.72354,9.0878 c 8.59474,0.082 -7.76304,-1.18486 0,1e-5 l 0,-1e-5 z M 90.396984,157.89545 c -0.335695,-1.60094 -2.120962,0.13419 0,0 z m 51.535396,31.73502 c 0.2292,-2.89141 -2.80486,2.15157 0,0 z m -36.86817,-22.75299 c -0.51986,-1.52251 -2.68548,-0.0622 0,0 z m -13.852128,-9.98649 c 4.934237,-0.29629 -6.755322,-2.17418 0,0 z M 74.802387,146.28394 c -0.614146,-2.36536 -5.369213,-4.2519 0,0 z m 43.079323,27.33941 c -0.90373,-1.0307 -0.4251,0.22546 0,0 z m 26.81408,16.45475 c -0.086,-1.57503 -1.46039,0.59616 0,0 z m -29.18712,-18.90528 c 0.48266,-2.02932 -4.20741,-0.61442 0,0 z M 95.532612,158.51286 c 3.670785,-0.39305 -5.880434,-2.48161 0,0 z M 129.32396,179.51 c 5.72042,-2.26627 -5.57541,-1.10635 0,0 z m -17.57682,-11.93145 c 6.59278,0.85002 -7.84442,-4.48425 -1.44651,-0.4773 l 1.4465,0.47734 1e-5,-4e-5 z m 22.91296,14.0886 c 6.15514,-3.67975 4.12588,8.61677 10.44254,1.0388 6.23086,-4.54942 -5.38086,5.62451 2.29838,0.81116 5.55359,-3.71438 13.75643,1.76075 18.93848,3.5472 3.72659,-0.18307 7.34938,3.22236 11.16973,1.15059 7.3542,-1.98082 -14.38097,-2.93789 -8.68344,-6.4523 -6.72914,1.95848 -11.70093,-2.33483 -15.01213,-6.64508 -7.54812,-1.74298 -16.27548,-5.602 -20.04257,-12.28184 -1.5359,-2.50802 2.21884,0.35333 -1.32586,-3.74638 -4.54834,-4.04546 -6.81948,-8.63766 -9.87278,-13.5552 -3.64755,-1.94587 -4.07249,-7.67345 -4.44123,-0.19201 0.0289,-4.72164 -4.40393,-7.89964 -5.48589,-6.57859 -0.0194,-4.54721 4.74396,-2.26787 1.40945,-5.63228 -0.71771,-4.71302 -3.08085,-9.6241 -3.79115,-14.9453 -1.1036,-2.56502 -0.15541,-8.05863 -3.76662,-2.25204 -1.31566,6.13669 -0.43668,-7.54129 1.6093,-3.03083 2.68543,-4.60251 -0.9641,-4.0612 -1.11361,-3.42211 1.74931,-3.88333 1.10719,-9.39159 -0.45644,-7.29023 0.93213,-4.11586 1.47259,-15.147529 -1.3951,-13.192579 1.73833,-4.303958 3.29668,-19.694077 -4.24961,-13.826325 -3.058358,0.04294 -8.354541,1.110195 -10.858032,2.355243 7.849502,4.326857 -0.789543,1.562577 -3.984808,0.874879 -0.416343,4.003642 -3.58119,2.272086 -7.535123,2.311339 6.315273,0.781339 -3.075253,6.458962 -6.698132,4.253506 -4.705102,2.248756 4.060621,7.862038 0.0944,9.597586 0.487433,2.616581 -7.208227,-0.944906 -6.603832,5.097711 -4.56774,-1.92155 -0.628961,7.16796 1.656273,4.09382 7.768882,2.10261 5.469108,6.89631 5.666947,11.44992 -1.265833,2.6534 -6.249495,-6.23691 -1.109939,-5.82517 -4.054715,-6.58674 -4.485232,-2.38081 -7.854566,0.67911 -0.783857,0.22222 8.5944,4.35376 2.709059,6.3967 5.177884,0.79894 5.325199,5.33008 6.379284,8.19735 3.11219,3.24152 2.475226,-3.57931 6.199071,0.31623 -2.356488,-3.4705 -12.48183,-9.77839 -4.329567,-7.7553 -0.04358,-3.49291 -1.474412,-6.30951 1.02322,-6.24118 2.473367,-4.47926 -2.590385,11.044 2.984725,5.35124 1.543285,-0.67388 1.92554,-4.48494 4.699544,0.35989 4.029096,3.96363 1.45533,6.83577 -4.228162,3.20648 1.016828,3.44946 7.603062,4.68217 6.365348,10.07646 1.3121,4.7444 3.147844,2.99695 4.747999,2.72266 1.25523,4.60973 1.968016,1.2201 2.027559,-0.97355 5.747357,1.23033 4.401142,4.62773 6.199456,7.00134 3.960416,1.78761 -5.668696,-12.11713 1.130659,-4.18106 7.153577,6.4586 2.682797,9.15464 -3.736856,8.11995 4.063129,-0.32824 5.373423,5.49305 10.455693,5.28853 4.63456,2.20477 7.77237,10.67291 -0.21613,7.1478 -2.77074,-2.49821 -12.575734,-5.5801 -4.56731,-0.82823 7.39657,3.42523 13.27117,5.47432 20.40487,9.77384 5.10535,3.64464 7.31104,7.81908 9.24607,8.64541 -4.29084,2.04946 -12.93089,-1.63655 -6.51514,-2.76618 -4.00168,-0.72894 -8.50258,-2.75259 -4.66961,2.2333 3.25926,2.72127 11.54708,2.43298 13.0328,2.74132 -1.25934,2.77488 -3.4207,2.99556 0.0516,3.21078 -3.87375,2.06438 1.24216,2.38403 1.60114,3.56362 z m -7.9215,-22.36993 c -2.35682,-2.46475 -2.9662,-7.08134 -0.41852,-3.06426 1.30648,0.52466 4.18523,7.54428 0.41857,3.06426 l -5e-5,0 z m 25.79733,16.38693 c 1.47004,-0.0952 0.0427,1.11681 0,0 z m -29.51867,-22.43039 c -0.0904,-3.72637 0.8525,2.87419 0,0 z m -2.56392,-3.44965 c -2.96446,-5.72787 3.73721,1.62212 0,0 z M 89.382646,128.35916 c 1.7416,-0.46446 0.856841,2.97864 0,0 z m 24.728294,13.40357 c 1.06957,-4.01654 1.25692,3.37014 0,0 z M 96.64115,129.61525 c -1.231543,-2.21638 2.576009,2.07865 0,0 z m 14.99279,4.80618 c -2.80851,-6.29223 1.98836,-3.43699 0.62135,1.03124 l -0.62135,-1.03124 0,0 z M 85.778757,117.17864 c -1.255624,-2.06432 -3.332663,-8.12135 -2.663982,-9.97042 0.604935,3.0114 6.403914,12.95956 2.844571,4.12096 -3.933386,-7.40908 4.701805,2.40491 5.590052,4.2529 0.413624,1.83837 -2.426789,-0.50225 -0.502192,3.80828 -3.509809,-4.90766 -2.071967,2.71088 -5.268449,-2.21172 z m -7.990701,-5.50612 c 0.328938,-4.79981 1.829262,3.29132 0,0 z m 3.594293,1.23728 c 1.715175,-3.62282 2.908243,5.05052 0,0 z m -8.64616,-6.68847 c -2.974956,-2.95622 -5.127809,-5.68132 0.139193,-1.83474 2.029482,0.0792 -4.509002,-6.19705 0.488751,-1.99305 5.25531,0.95822 2.5951,8.61674 -0.627944,3.82779 z m 4.541717,-0.11873 c 1.727646,-1.71203 0.917172,1.6853 0,0 z m 2.794587,0.8959 c -2.619181,-4.9094 3.178801,2.05822 0,0 z m -5.55546,-5.30909 c -8.64844,-7.696511 10.867309,4.02451 1.4129,1.4269 l -1.412955,-1.42683 5.5e-5,-7e-5 z m 24.77908,14.39717 c -3.742506,-2.24398 -0.991777,-15.79747 0.284503,-6.52785 3.638294,-1.17695 -0.200879,4.78728 2.512784,4.73208 -0.42767,3.76305 -1.64169,5.11594 -2.797287,1.79577 z m 9.165207,5.41684 c 0.36705,-4.08462 0.77249,2.79262 0,0 z m -1.59198,-1.57295 c 0.41206,-1.74497 0.0426,2.05487 0,0 z M 76.213566,99.16032 c -5.556046,-7.665657 16.147323,7.75413 3.558556,1.9443 -1.315432,-0.34404 -2.898208,-0.46688 -3.558556,-1.9443 z m 17.649112,9.35749 c -0.525779,-6.45461 1.174169,1.06991 -1.92e-4,-2e-5 l 1.92e-4,2e-5 z m 13.399762,8.59585 c 1.03698,-3.67668 0.0773,2.43221 0,0 z M 77.064685,96.23472 c 3.302172,-0.706291 13.684695,5.79939 4.150224,1.85832 -1.059396,-1.17279 -3.317802,-0.63994 -4.150224,-1.85832 z m 28.356745,14.13312 c 0.35296,-6.60002 1.97138,-3.94233 0.0122,0.94474 l -0.0121,-0.94473 -5e-5,-1e-5 z M 79.52277,93.938099 c 1.345456,-1.97361 -3.571631,-8.923063 0.708795,-2.492797 1.849543,1.469605 5.355103,2.461959 2.260017,3.080216 4.867744,4.294162 -1.187244,1.163612 -2.968812,-0.587419 z m 24.49612,14.368161 c 0.92952,-7.51843 0.81971,4.40485 0,0 z M 76.712755,86.993902 c 1.027706,-0.439207 0.542746,1.369335 0,0 z m 6.389622,3.803092 c 1.644416,-3.450522 3.03351,3.848297 0,0 z m 18.023553,10.026276 c -0.0174,-1.3252 0.34003,1.92765 0,0 z m -1.04404,-2.31139 c -2.501612,-6.171646 2.32693,3.26759 0,0 z m -1.536003,-4.046372 c -0.419906,-2.550188 1.427129,3.203862 -7.3e-5,-9e-6 l 7.3e-5,9e-6 z m 2.499773,-4.063514 c -1.71663,-3.025123 2.16777,-13.331073 2.60122,-6.939418 -1.81185,4.980256 -0.52268,7.766309 0.74129,1.086388 2.33417,-5.257159 -0.50421,10.374054 -3.34255,5.853057 l 4e-5,-2.7e-5 z m 2.56889,-15.326649 c 0.74833,-0.918921 0.16609,1.107082 0,0 z m -4.290016,84.534235 c -1.017552,-0.88802 0.127775,0.56506 0,0 z m 8.837726,4.47065 c 4.91599,1.26135 4.89086,-0.76487 0.44782,-1.36683 -2.3898,-2.22316 -9.930475,-4.58124 -3.18119,-0.27586 0.44699,1.13227 1.85944,1.10589 2.73337,1.64269 z M 90.708067,152.48725 c 2.708244,2.01956 10.201213,5.72375 3.858186,0.76868 2.138588,-2.48467 -4.093336,-3.80722 -2.026067,-5.46927 -5.258175,-3.21755 -4.147962,-2.93133 -0.464111,-2.8301 -6.319385,-2.82462 0.912163,-2.61333 0.571661,-4.06067 -2.436706,-0.48126 -12.103074,-4.29664 -6.416395,0.31341 -5.780887,-2.94751 -1.377603,1.09799 -3.12488,0.67029 -5.911336,-1.61178 5.264392,4.50224 -0.938845,2.98448 3.391327,2.6875 9.128301,6.88393 1.433786,2.84407 -1.013816,1.45934 5.506273,3.67136 7.106665,4.77911 z m 9.243194,5.31013 c 11.238769,3.62163 -5.510018,-4.4246 0,0 z m 47.316399,28.66432 c 0.14496,-2.22965 -1.53604,1.90201 0,0 z m 4.86324,2.04679 c 2.59297,-2.51255 0.106,4.00222 4.29655,-0.61509 0.0453,-3.30544 -0.12904,-5.25783 -4.81563,-1.24252 -1.29194,0.71648 -1.86871,3.76288 0.51908,1.85761 z M 74.932378,140.02637 c -0.796355,-3.1304 -5.581949,-3.11418 0,0 z m 5.193029,3.40294 c -1.928397,-3.19739 -6.880525,-2.89469 0,0 z m 29.543373,17.81697 c 2.8844,2.56199 13.24761,1.87984 3.50331,0.31527 -1.44321,-2.13386 -9.16415,-1.6203 -3.50331,-0.31527 z m 40.61236,25.08153 c 4.43933,-3.72512 -4.30122,1.66183 0,0 z m 9.2328,6.34473 c 0.0277,-1.19543 -1.91352,0.52338 0,0 z m 0.0142,-1.6736 c 4.91602,-5.20866 -4.76346,0.30807 -4e-5,0 l 4e-5,0 z M 62.15981,129.33339 c -4.189944,-5.97826 -2.604586,-8.66544 -6.645136,-13.54677 -0.764913,-3.73279 -6.931672,-12.20326 -3.189579,-3.22947 3.42754,5.24836 4.446054,13.37434 9.834715,16.77624 z m 95.82635,60.00977 c 9.04429,-5.84575 -3.7125,-2.54641 0,0 z m 6.9041,2.70461 c 4.52911,-3.88867 -2.86491,-0.81334 0,0 z M 73.393094,133.41838 c 1.296204,-1.92838 -3.347642,-0.24666 0,0 z m 90.055596,56.78275 c 4.38526,-2.82746 -1.01036,-2.39335 -0.79483,0.26003 l 0.79484,-0.26003 -1e-5,0 z m -59.51386,-37.51178 c -0.15075,-1.90924 -2.31574,0.16206 0,0 z m 3.67794,2.11629 c -1.16888,-2.36318 -1.79716,0.37121 0,0 z m 62.8725,37.30625 c 5.61806,-4.05283 -3.4056,-0.77594 -1.17927,0.76785 l 1.17927,-0.76785 0,0 z m -2.15131,-1.03979 c 4.57663,-3.83506 -4.83183,1.69954 0,0 z m 10.99163,7.31983 c 3.0728,-2.05816 -3.73316,-0.66575 0,0 z M 76.211249,132.02781 c 4.118965,0.92286 16.460394,10.1439 9.179466,0.63772 -3.728991,-1.10384 -1.492605,-10.21906 -5.29621,-8.60579 2.552972,4.2649 2.100461,6.08018 -3.259642,3.3914 -6.736808,-3.28853 -3.785888,1.6297 -2.469293,2.98518 -1.794185,0.40772 2.373226,1.5572 1.845679,1.59149 z m -18.76588,-14.82026 c 0.737407,-3.04991 -6.789814,-16.77881 -3.554464,-6.87916 1.167861,2.07373 1.049123,6.00387 3.554464,6.87916 z m 34.443451,21.23513 c -2.120989,-1.77378 -0.100792,-0.25103 0,0 z m 5.222997,1.21548 c -0.0027,-3.23079 -5.77326,-1.31196 0,0 z m 45.261473,28.53321 c -0.86326,-2.20739 -3.41229,-0.0512 8e-5,4e-5 l -8e-5,-4e-5 z m 2.17351,1.58769 c -0.32087,-1.23546 -1.25399,0.23848 0,0 z m 17.94015,11.3001 c 1.72546,-1.27472 -2.15318,-0.1628 0,0 z M 66.819057,119.6006 c 4.935243,-1.91072 -5.28775,-1.36248 0,0 z m 71.569733,45.08937 c -0.0549,-3.19499 -3.14622,0.79264 0,0 z M 64.869152,115.05675 c 3.170167,-1.07084 -2.932663,-0.70531 0,0 z m 9.201532,4.45726 c -0.0575,-1.05014 -0.973336,0.39747 0,0 z m 112.231406,68.82181 c 4.0765,-0.8265 13.36606,2.07489 14.86752,-1.08086 -4.95044,-0.12019 -17.12734,-3.49263 -17.70346,0.80479 l 1.08368,0.17072 1.75226,0.10534 0,1e-5 z M 76.995161,120.25099 c 0.07087,-3.23755 -2.524669,-0.12092 0,0 z M 52.801998,103.4687 c -1.098703,-6.16843 -4.178791,-0.93357 0,0 z m 5.769195,1.45013 c 0.07087,-1.9807 -5.280562,-1.78224 0,0 z m 3.296917,1.61923 c -0.953019,-0.77196 -0.745959,0.97521 0,0 z m 20.744719,13.30775 c 0.976615,-0.89718 -2.312116,-0.66455 0,0 z M 59.672204,102.88617 c -0.557624,-4.65897 -6.681999,-0.69805 0,0 z M 47.844441,95.21166 c -0.168219,-2.150189 -1.152625,0.81111 0,0 z m 1.759336,-1.328672 c -0.28703,-2.549584 -1.510515,0.324387 0,0 z m 9.720792,5.802442 c 4.110486,-1.61465 -7.487254,-3.33984 -0.839893,-0.30506 l 0.839893,0.30506 z m 130.097601,80.35913 c 2.63092,-2.4121 -3.34373,-0.74577 0,0 z m 15.71669,8.14691 c 1.05433,-3.1186 -2.65452,0.41058 0,0 z M 60.318012,94.590436 c 0.433018,-3.016773 -3.258762,0.59902 0,0 z M 46.487687,85.324242 c -0.742965,-4.25911 -0.64134,-11.735065 6.465133,-9.208583 -9.485962,1.883339 6.56534,11.790095 4.538357,3.968363 3.988626,0.195294 7.802669,-2.357284 5.709487,1.516403 7.85876,-0.867958 13.307129,-7.682612 20.898169,-6.72768 5.913058,-0.782493 12.378182,-1.375955 18.750257,-3.756157 5.23905,-0.37743 10.28235,-6.018062 7.41068,-9.361383 -7.14456,-0.604513 -14.62339,0.289393 -22.520112,1.858993 -8.750559,1.819117 -16.699014,5.275307 -25.528125,6.758866 -8.605891,1.15604 1.730998,3.185165 -0.734074,3.637227 -4.490681,1.558136 5.355488,2.608852 -0.582182,4.251428 C 57.228283,77.56448 53.411411,76.304535 54.977788,72.440196 46.7341,73.50992 39.490264,76.931325 46.003276,85.320342 l 0.484402,0.0037 9e-6,-2.56e-4 z m 19.864291,-10.1168 c 1.932856,-7.120464 10.355229,5.859274 3.168052,0.945776 -0.858453,-0.642457 -2.2703,-1.166588 -3.168052,-0.945776 z m 0.376038,-3.452197 c 2.789661,-2.078257 1.482964,1.16516 0,0 z m 3.542213,0.05622 c 0.251833,-3.27648 8.108752,1.73455 1.295517,1.179499 l -1.295517,-1.179499 0,0 z m 4.84543,-1.948193 c 1.769481,-2.067535 0.50862,1.83906 0,0 z m 1.239563,-0.83005 c 2.946379,-3.540216 16.68561,-2.259413 6.628966,-0.34519 -2.695543,-2.029363 -4.761797,1.196575 -6.628966,0.34519 z m 17.930017,-2.763886 c -0.448199,-9.670222 8.907771,3.436477 0,0 z m 5.087539,-0.02784 c 1.860022,-4.873906 7.218072,-1.955774 0.860732,-0.979407 0.13805,0.518656 -0.18642,2.516173 -0.860732,0.979407 z M 58.311842,92.088739 c 5.55753,-3.403212 -5.899945,-2.952541 0,0 l 0,0 z m 4.109214,1.141866 c 1.948513,-2.071884 -4.233857,-0.840369 0,0 z M 50.313395,84.63767 c 3.175569,-2.439416 -3.757842,-0.927473 0,0 z M 214.41578,187.30012 c 0.0918,-2.83019 -2.42718,1.27537 0,0 z m -16.67487,-11.37935 c 0.47417,-3.25845 -2.14286,0.28408 0,0 z m 21.26022,12.47672 c 4.43994,0.015 13.45265,-1.37884 3.79217,-1.37442 -1.51594,0.23641 -8.83311,0.18571 -3.79216,1.37439 l -1e-5,3e-5 z M 66.035603,91.23339 c 3.593258,-0.246807 5.621861,-3.963629 -0.694932,-3.749977 -9.789949,-1.013541 8.637508,3.352129 -1.255898,2.10383 -1.329368,0.880346 1.873606,1.889721 1.95083,1.646147 z m 3.164618,1.601748 c -0.375177,-2.307063 -1.111156,1.225591 0,0 z m 3.753896,-10.009901 c 1.559281,-1.934055 -2.157697,-0.517053 0,0 z M 61.003998,62.84999 c 6.412879,-2.181631 15.182392,-4.633087 18.210335,1.074184 -3.081589,-3.70893 -1.24361,-7.360157 1.666959,-1.937407 4.115576,5.486669 6.175915,-2.495489 3.499086,-4.335821 3.050468,3.790246 6.520044,5.581281 2.042429,0.239564 4.865693,-5.852929 -9.742712,0.766433 -13.063105,0.699775 -1.597564,0.717062 -16.493576,3.79889 -12.355704,4.259705 z m 3.75831,-7.197834 c 3.657324,-2.760416 12.648968,1.641989 6.879078,-2.743367 -0.564117,-0.498292 -12.636077,3.325475 -6.879078,2.743367 z m 13.333489,0.550473 c 4.280389,0.109225 -1.84632,-5.750287 3.254304,-3.095159 -0.837696,-2.736627 -5.938558,-3.248956 -8.432316,-4.342312 -1.410474,2.502054 2.870977,7.471102 5.178012,7.437471 z M 67.100291,44.099162 c 1.480803,-2.007406 -2.59521,1.017699 0,0 z m 5.449586,1.304353 c 6.897867,-0.914901 -1.758292,-2.970542 -1.389954,-0.07352 l 1.389954,0.07352 0,-9e-6 z M 62.374386,37.441437 c -4.856866,-6.340205 9.133987,1.065769 4.199411,-5.572646 -4.153254,-3.307245 -8.144297,3.721775 -4.199411,5.572646 z m 62.330124,33.572802 c 2.22762,-3.948988 -9.19697,-5.323011 -1.5009,-1.399578 0.70858,0.236781 0.54821,1.6727 1.5009,1.399578 z"
+ id="path2900" />
+ <g
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ id="text3850">
+ <path
+ d="m 229.85182,43.77803 c -0.79695,3.140714 -1.28913,8.414146 -1.47656,15.820313 -7e-5,1.453199 -0.65632,2.179761 -1.96875,2.179687 -1.31257,7.4e-5 -2.22663,-0.632738 -2.74219,-1.898437 -1.40631,-3.421796 -2.74225,-5.812419 -4.00781,-7.171875 -1.50006,-1.593666 -3.49225,-2.554603 -5.97656,-2.882813 -2.67193,-0.421789 -9.32818,-0.632726 -19.96875,-0.632812 -2.43754,8.6e-5 -4.03129,0.257898 -4.78125,0.773437 -0.46879,0.32821 -0.70316,1.031335 -0.70313,2.109375 l 0,31.851563 c -3e-5,1.078175 0.67966,1.5938 2.03906,1.546875 4.17184,-0.04682 10.21871,-0.328075 18.14063,-0.84375 1.54682,-0.187449 2.58979,-0.691355 3.12891,-1.511719 0.539,-0.820259 1.06634,-2.941351 1.58203,-6.363281 0.32806,-1.87494 1.42963,-2.601502 3.30468,-2.179688 1.59369,0.328186 2.27338,1.054747 2.03907,2.179688 -1.31256,6.375052 -1.73444,14.671919 -1.26563,24.890627 0.0468,1.21878 -0.72662,1.87503 -2.32031,1.96875 -1.31256,0.14065 -2.13287,-0.56247 -2.46094,-2.10938 -1.2188,-5.859333 -3.48052,-8.988236 -6.78515,-9.386716 -3.30474,-0.398394 -8.68364,-0.597612 -16.13672,-0.597656 -0.84379,4.4e-5 -1.26566,0.304731 -1.26563,0.914062 l 0,31.64063 c -3e-5,2.34375 0.86716,3.9375 2.60156,4.78125 1.35934,0.70313 4.28903,1.33594 8.78907,1.89843 2.29683,0.23438 3.30464,1.24219 3.02343,3.02344 -0.28129,1.54688 -2.34379,2.15625 -6.1875,1.82813 -11.1094,-0.89063 -20.27345,-0.84375 -27.49218,0.14062 -2.01564,0.28125 -3.02345,-0.53906 -3.02344,-2.46094 -1e-5,-1.21874 1.0078,-1.92187 3.02344,-2.10937 4.59373,-0.51562 6.8906,-4.54687 6.89062,-12.09375 l 0,-60.187502 c -2e-5,-3.093671 -0.5508,-5.472575 -1.65234,-7.136719 -1.10158,-1.663977 -3.15236,-3.175695 -6.15235,-4.535156 -1.87501,-0.843661 -2.57813,-1.992098 -2.10937,-3.445313 0.23436,-0.890532 0.60936,-1.382719 1.125,-1.476562 0.46874,-0.140532 1.71092,-0.04678 3.72656,0.28125 2.95311,0.468842 9.91404,0.703217 20.88281,0.703125 12.93746,9.2e-5 24.11713,-0.281158 33.53907,-0.84375 3.14055,-0.187407 4.71086,0.07041 4.71093,0.773437 -7e-5,0.187592 -0.0235,0.375092 -0.0703,0.5625 z"
+ style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2830" />
+ <path
+ d="m 275.55495,133.14522 c -4e-5,1.875 -1.05473,2.69531 -3.16407,2.46094 -6.46877,-0.60938 -14.48439,-0.51563 -24.04687,0.28125 -1.92189,0.18749 -3.10548,0.14062 -3.55078,-0.14063 -0.44532,-0.28125 -0.66798,-1.05469 -0.66797,-2.32031 -1e-5,-1.125 1.27733,-2.07422 3.83203,-2.84766 2.55467,-0.77343 3.83202,-3.08202 3.83203,-6.92578 l 0,-63.632812 c -1e-5,-3.796796 -0.55079,-6.585856 -1.65234,-8.367188 -1.10158,-1.781164 -3.03517,-3.163975 -5.80078,-4.148437 -1.45313,-0.515537 -2.1797,-1.242099 -2.17969,-2.179688 -1e-5,-1.406158 1.05468,-2.460845 3.16406,-3.164062 3.18749,-1.031156 6.49217,-2.624904 9.91406,-4.78125 2.81248,-1.687401 4.59373,-2.53115 5.34375,-2.53125 1.73435,1e-4 2.60154,1.195412 2.60157,3.585937 -3e-5,-0.187403 -0.0938,2.156345 -0.28125,7.03125 -0.14065,4.64071 -0.18753,9.211018 -0.14063,13.710938 l 0.28125,62.789062 c -2e-5,2.85939 0.7031,4.9336 2.10938,6.22266 1.40622,1.28906 3.82028,2.14453 7.24218,2.5664 2.10934,0.23438 3.16403,1.03126 3.16407,2.39063 z"
+ style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2832" />
+ <path
+ d="m 339.67995,128.43428 c -7e-5,0.98438 -1.79303,2.47266 -5.37891,4.46484 -3.58599,1.99219 -6.45708,2.98828 -8.61328,2.98829 -1.82817,-10e-6 -3.44536,-0.89063 -4.85156,-2.67188 -1.40629,-1.78125 -2.39067,-2.67187 -2.95313,-2.67187 -0.42191,0 -2.64847,0.96094 -6.67969,2.88281 -4.03128,1.92187 -8.08596,2.88281 -12.16406,2.88281 -3.84377,0 -7.0547,-1.125 -9.63281,-3.375 -2.81251,-2.48437 -4.21876,-5.85937 -4.21875,-10.125 -1e-5,-8.10935 9.28123,-13.92185 27.84375,-17.4375 3.18746,-0.60934 4.80465,-1.89841 4.85156,-3.86719 l 0.14063,-4.499997 c 0.28121,-7.687454 -3.11723,-11.5312 -10.19532,-11.53125 -2.01565,5e-5 -3.9258,1.804735 -5.73046,5.414062 -1.80471,3.609416 -4.39456,5.554727 -7.76954,5.835938 -3.84376,0.375038 -5.76563,-1.242148 -5.76562,-4.851563 -1e-5,-2.249954 2.85936,-4.874951 8.57812,-7.875 5.99998,-3.14057 11.7656,-4.710881 17.29688,-4.710937 9.51558,5.6e-5 14.22651,4.523489 14.13281,13.570312 l -0.28125,28.968755 c -0.0469,3.04688 1.24214,4.57032 3.86719,4.57031 0.51557,1e-5 1.49994,-0.11718 2.95312,-0.35156 1.45307,-0.23437 2.29682,-0.35156 2.53125,-0.35157 1.35932,1e-5 2.039,0.91407 2.03907,2.74219 z M 318.0237,112.40303 c 0.0468,-1.17185 -0.2227,-1.94529 -0.8086,-2.32031 -0.58597,-0.37498 -1.51175,-0.44529 -2.77734,-0.21094 -11.2969,2.01565 -16.94533,5.69533 -16.94531,11.03906 -2e-5,5.39064 2.92966,8.08595 8.78906,8.08594 2.34372,1e-5 4.75778,-0.44531 7.24219,-1.33594 2.90621,-1.03124 4.35933,-2.27342 4.35937,-3.72656 z"
+ style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2834" />
+ <path
+ d="m 392.13307,120.06709 c -5e-5,4.96876 -1.9102,8.91798 -5.73047,11.84766 -3.82035,2.92969 -9.03519,4.39453 -15.64453,4.39453 -4.40627,0 -8.81252,-0.46875 -13.21875,-1.40625 -3.79688,-0.84375 -6.00001,-1.61719 -6.60937,-2.32031 -0.37501,-0.65625 -0.56251,-3.86718 -0.5625,-9.63281 -1e-5,-2.48436 0.56249,-3.77343 1.6875,-3.86719 1.12499,-0.14061 2.08592,0.46876 2.88281,1.82812 3.51561,6.14064 9.18748,9.21095 17.01562,9.21094 6.60934,1e-5 9.91403,-2.29687 9.91407,-6.89062 -4e-5,-2.01562 -0.75004,-3.70311 -2.25,-5.0625 -1.64066,-1.54686 -4.82816,-3.35155 -9.5625,-5.41407 -6.84377,-3.04685 -11.41408,-5.71872 -13.71094,-8.01562 -2.48439,-2.43747 -3.72657,-5.718716 -3.72656,-9.843752 -1e-5,-5.062455 1.9453,-8.999951 5.83593,-11.8125 3.60936,-2.718695 8.43748,-4.078069 14.48438,-4.078125 3.79684,5.6e-5 7.26559,0.304743 10.40625,0.914062 3.37496,0.60943 5.13277,1.359429 5.27344,2.25 0.37495,2.625051 1.14839,6.421922 2.32031,11.390625 0.14058,0.609416 -0.51567,1.101603 -1.96875,1.476563 -1.54692,0.328165 -2.57817,0.07035 -3.09375,-0.773438 -3.70317,-6.046828 -8.39066,-9.070262 -14.0625,-9.070312 -6.4219,5e-5 -9.63283,2.062548 -9.63281,6.1875 -2e-5,2.296916 0.86716,4.12504 2.60156,5.484375 1.54685,1.171912 5.17966,3.000035 10.89844,5.484372 5.99996,2.57816 10.07808,4.89847 12.23437,6.96094 2.81245,2.6719 4.2187,6.25783 4.21875,10.75781 z"
+ style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2836" />
+ <path
+ d="m 473.69557,132.72334 c -7e-5,1.64063 -1.10163,2.50782 -3.30469,2.60157 -3.28131,0.0469 -7.57037,0.28124 -12.86718,0.70312 -2.62506,0.51562 -4.50006,0.1875 -5.625,-0.98437 -7.4063,-7.96875 -13.68754,-16.31249 -18.84375,-25.03125 -0.42191,-0.74998 -0.96097,-1.12498 -1.61719,-1.125 -0.79691,2e-5 -2.17972,0.70315 -4.14844,2.10937 -2.20315,1.21877 -3.30471,2.95315 -3.30469,5.20313 -2e-5,1.59376 0.0469,3.89064 0.14063,6.89062 0.0937,3.00001 0.84372,4.96876 2.25,5.90625 0.98435,0.65626 3.25778,1.17188 6.82031,1.54688 2.20309,0.28125 3.30465,1.10156 3.30469,2.46093 -4e-5,1.07813 -0.17582,1.7461 -0.52734,2.00391 -0.3516,0.25781 -1.27738,0.31641 -2.77735,0.17578 -4.68753,-0.42187 -12.60939,-0.1875 -23.76562,0.70313 -2.81251,0.23437 -4.33595,-0.11719 -4.57032,-1.05469 -0.0937,-0.32813 -0.14063,-0.79688 -0.14062,-1.40625 -1e-5,-1.45312 1.42968,-2.55469 4.28906,-3.30469 2.57811,-0.65624 3.86718,-3.67968 3.86719,-9.07031 l 0,-61.453127 c -1e-5,-3.843671 -0.37501,-6.515543 -1.125,-8.015625 -1.03126,-1.92179 -3.18751,-3.421788 -6.46875,-4.5 -1.54688,-0.515536 -2.32032,-1.242098 -2.32031,-2.179688 -1e-5,-1.359283 1.10155,-2.413969 3.30468,-3.164062 3.51562,-1.17178 6.86718,-2.788966 10.05469,-4.851563 2.57811,-1.6874 4.17186,-2.531149 4.78125,-2.53125 1.92185,1.01e-4 2.88279,1.21885 2.88281,3.65625 -2e-5,-0.328027 -0.0235,1.992283 -0.0703,6.960938 -0.0469,3.421962 -0.0703,8.015707 -0.0703,13.78125 l 0.14062,44.015627 c -2e-5,1.21878 0.3281,1.82815 0.98438,1.82812 0.7031,3e-5 1.78122,-0.60934 3.23437,-1.82812 3.8906,-3.046842 8.67184,-7.031213 14.34375,-11.953127 1.12496,-1.17183 1.68746,-2.109329 1.6875,-2.8125 -4e-5,-1.265577 -1.89848,-2.156201 -5.69531,-2.671875 -1.64066,-0.18745 -2.4141,-1.101512 -2.32031,-2.742188 0.14059,-1.64057 0.9609,-2.343695 2.46094,-2.109375 3.37495,0.468805 8.29682,0.726617 14.76562,0.773438 4.49994,0.04693 8.9765,0.07037 13.42969,0.07031 1.45306,0.04693 2.17962,0.914116 2.17969,2.601563 -7e-5,1.5938 -1.14851,2.460986 -3.44532,2.601562 -3.60943,0.140674 -7.00787,0.960986 -10.19531,2.460938 -4.45317,2.015669 -9.21098,5.554728 -14.27344,10.617187 -0.37504,0.281286 -0.56254,0.632845 -0.5625,1.054685 -4e-5,0.65629 0.79684,2.2266 2.39063,4.71094 5.85933,8.90627 11.39057,15.63283 16.59375,20.17969 3.32806,2.85938 6.44525,4.28907 9.35156,4.28906 2.15618,1e-5 3.49212,0.15235 4.00781,0.45703 0.51556,0.30469 0.77337,1.11329 0.77344,2.42578 z"
+ style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2838" />
+ </g>
+ <g
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:89.99999762%;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ id="text2870"
+ transform="translate(0,4)">
+ <path
+ d="m 285.18048,153.2296 c -2e-5,0.16407 -0.31447,0.33269 -0.94336,0.50586 -0.41017,0.10938 -0.75653,0.48308 -1.03906,1.12109 -1.64064,3.64584 -3.10809,6.58529 -4.40234,8.81836 -0.0274,0.0547 -0.0957,0.0775 -0.20508,0.0684 -0.10028,-0.009 -0.16864,-0.041 -0.20508,-0.0957 -0.21876,-0.32813 -0.77931,-1.47201 -1.68164,-3.43164 -0.82944,-1.80469 -1.27605,-2.70703 -1.33985,-2.70703 -0.10027,0 -0.5879,0.87044 -1.46289,2.61132 -0.99349,1.94141 -1.58138,3.04883 -1.76367,3.32227 -0.0274,0.0638 -0.0912,0.0911 -0.1914,0.082 -0.10027,0 -0.17775,-0.0365 -0.23243,-0.10937 -0.66537,-1.03907 -2.19662,-3.88281 -4.59375,-8.53125 -0.27344,-0.54687 -0.57877,-0.89322 -0.91601,-1.03907 -0.58334,-0.2552 -0.875,-0.44204 -0.875,-0.56054 0,-0.35546 0.19596,-0.51497 0.58789,-0.47852 1.75911,0.1823 3.29947,0.14584 4.62109,-0.10937 0.30078,-0.0547 0.45117,0.0456 0.45117,0.30078 0,0.35548 -0.20508,0.58334 -0.61523,0.68359 -0.53777,0.12761 -0.80665,0.32814 -0.80664,0.60156 -1e-5,0.17319 0.0638,0.4284 0.19141,0.76563 0.32812,0.84766 0.8203,1.92318 1.47656,3.22656 0.65624,1.30339 1.03905,1.95508 1.14844,1.95508 0.0729,0 0.46027,-0.67903 1.16211,-2.03711 0.70181,-1.35807 1.05272,-2.11002 1.05273,-2.25586 -1e-5,-0.35546 -0.1185,-0.73827 -0.35547,-1.14844 -0.28256,-0.60155 -0.61069,-0.96158 -0.98437,-1.08008 -0.60157,-0.20962 -0.90236,-0.4147 -0.90235,-0.61523 -1e-5,-0.36457 0.20507,-0.51496 0.61524,-0.45117 1.40363,0.19142 2.88931,0.15496 4.45703,-0.10938 0.30077,-0.0547 0.45116,0.0365 0.45117,0.27344 -1e-5,0.34637 -0.22332,0.56056 -0.66992,0.64258 -0.52866,0.10027 -0.79298,0.36915 -0.79297,0.80664 -10e-6,0.20964 0.0592,0.45574 0.17773,0.73828 1.40364,3.4362 2.21483,5.1543 2.4336,5.1543 0.082,0 0.4466,-0.66992 1.09375,-2.00977 0.69269,-1.43098 1.17576,-2.58397 1.44922,-3.45898 0.0182,-0.0456 0.0273,-0.0957 0.0273,-0.15039 -2e-5,-0.39192 -0.30536,-0.69726 -0.91602,-0.91602 -0.47397,-0.15494 -0.71095,-0.32811 -0.71093,-0.51953 -2e-5,-0.319 0.15037,-0.44205 0.45117,-0.36914 0.41014,0.0912 1.14842,0.15952 2.21484,0.20508 1.00259,0.0365 1.66339,0.0319 1.98242,-0.0137 0.37368,-0.0456 0.56053,0.0592 0.56055,0.31445 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2877" />
+ <path
+ d="m 297.29376,160.98155 c -1e-5,0.39193 -0.52865,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43815,0.97071 -3.70507,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47266,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82812 0,-1.80469 0.56055,-3.2539 1.68164,-4.34766 1.04818,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90234,1e-5 1.74544,0.28712 2.5293,0.86133 0.70181,0.51042 1.16666,1.09376 1.39453,1.75 0.082,0.22787 0.20507,0.35547 0.36914,0.38281 0.23697,0.0456 0.35546,0.20509 0.35547,0.47852 -10e-6,0.3737 -0.44662,0.69271 -1.33984,0.95703 l -6.30274,1.87304 c 0.41016,2.16928 1.52213,3.25391 3.33594,3.25391 1.04817,0 2.24218,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39648,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24023,-5.48242 c -1e-5,-0.50129 -0.20281,-0.94107 -0.6084,-1.31934 -0.40561,-0.37824 -0.90463,-0.56737 -1.49707,-0.56738 -1.67709,10e-6 -2.51563,1.20769 -2.51563,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60156,-0.17317 0.90234,-0.48762 0.90235,-0.94336 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2879" />
+ <path
+ d="m 311.11603,157.13976 c -2e-5,1.80469 -0.62892,3.31771 -1.88672,4.53906 -1.20314,1.17578 -2.63412,1.76367 -4.29297,1.76367 -0.93881,0 -1.91862,-0.15495 -2.93945,-0.46484 -0.98438,-0.30078 -1.48112,-0.57422 -1.49024,-0.82032 -0.009,-0.20963 -0.005,-0.94335 0.0137,-2.20117 0.0273,-1.54036 0.041,-2.66145 0.041,-3.36328 0,-1.05728 -0.009,-2.59081 -0.0273,-4.60059 -0.0182,-2.00975 -0.0274,-3.34275 -0.0274,-3.99902 0,-0.65623 -0.10026,-1.1074 -0.30078,-1.35351 -0.18229,-0.21874 -0.57878,-0.39191 -1.18945,-0.51954 -0.23698,-0.082 -0.35547,-0.2324 -0.35547,-0.45117 0,-0.19139 0.17773,-0.35089 0.5332,-0.47851 0.51042,-0.18228 1.14388,-0.48762 1.90039,-0.91602 0.60156,-0.33722 0.97526,-0.50584 1.1211,-0.50586 0.28254,2e-5 0.42382,0.23244 0.42383,0.69727 -1e-5,0.0365 -0.0137,0.51043 -0.041,1.42187 -0.0182,0.85679 -0.0228,1.72723 -0.0137,2.61133 l 0.0273,4.73047 c 0,0.44662 0.15494,0.57423 0.46485,0.38281 1.09374,-0.62889 2.23306,-0.94335 3.41796,-0.94336 1.36718,1e-5 2.47916,0.41245 3.33594,1.23731 0.85676,0.82488 1.28514,1.90267 1.28516,3.2334 z m -2.1875,1.21679 c -1e-5,-1.24869 -0.34637,-2.27408 -1.03906,-3.07617 -0.65626,-0.7565 -1.45379,-1.13476 -2.39258,-1.13477 -0.64714,1e-5 -1.28517,0.16863 -1.91407,0.50586 -0.62891,0.33725 -0.94336,0.69272 -0.94335,1.06641 l 0,3.66406 c -1e-5,1.75912 0.97069,2.63868 2.9121,2.63867 1.01172,1e-5 1.82747,-0.32584 2.44727,-0.97753 0.61978,-0.65169 0.92968,-1.5472 0.92969,-2.68653 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2881" />
+ <path
+ d="m 333.23712,161.93858 c 0.0365,0.082 0.0547,0.14128 0.0547,0.17774 -2e-5,0.1914 -0.46486,0.50586 -1.39453,0.94336 -1.07553,0.5013 -1.709,0.75195 -1.90039,0.75195 -0.16408,0 -0.3532,-0.27572 -0.56739,-0.82715 -0.2142,-0.55143 -0.36231,-0.82715 -0.44433,-0.82715 0.009,0 -0.50815,0.23926 -1.55176,0.71778 -1.04363,0.47851 -1.83432,0.71777 -2.37207,0.71777 -1.20313,0 -2.24675,-0.43294 -3.13086,-1.29883 -1.06641,-1.02994 -1.59961,-2.4746 -1.59961,-4.33398 0,-1.55859 0.62891,-2.90299 1.88672,-4.03321 1.11198,-0.99347 2.24674,-1.49022 3.4043,-1.49023 0.8203,1e-5 1.80012,0.10483 2.93945,0.31445 0.35546,0.0638 0.53319,-0.041 0.5332,-0.31445 l 0,-4.67578 c -1e-5,-0.52863 -0.12761,-0.90233 -0.38281,-1.12109 -0.16407,-0.14582 -0.55144,-0.319 -1.16211,-0.51954 -0.29167,-0.10935 -0.43751,-0.28709 -0.4375,-0.5332 -10e-6,-0.22785 0.19596,-0.39647 0.58789,-0.50586 0.53775,-0.16404 1.18033,-0.43748 1.92774,-0.82031 0.61978,-0.32811 1.00259,-0.49217 1.14843,-0.49219 0.33723,2e-5 0.50585,0.23244 0.50586,0.69727 -1e-5,-0.009 -0.0137,0.44435 -0.041,1.36035 -0.0274,0.91603 -0.041,1.80698 -0.041,2.67285 l 0,12.42773 c -10e-6,0.51954 0.23241,0.7793 0.69727,0.7793 0.17316,0 0.40558,-0.0273 0.69726,-0.082 0.319,-0.0547 0.53319,0.0501 0.64258,0.31445 z m -4.22461,-1.35351 0,-4.89453 c -1e-5,-0.40104 -0.35548,-0.81575 -1.0664,-1.24414 -0.75652,-0.45572 -1.57683,-0.68359 -2.46094,-0.6836 -1.90495,1e-5 -2.85743,1.17579 -2.85742,3.52735 -10e-6,1.25781 0.37825,2.33333 1.13476,3.22656 0.75651,0.89323 1.64518,1.33984 2.66602,1.33984 0.46483,0 1.01171,-0.15494 1.64062,-0.46484 0.6289,-0.3099 0.94335,-0.57878 0.94336,-0.80664 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2883" />
+ <path
+ d="m 345.09064,160.98155 c -1e-5,0.39193 -0.52866,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47265,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82812 0,-1.80469 0.56054,-3.2539 1.68164,-4.34766 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.5293,0.86133 0.70181,0.51042 1.16665,1.09376 1.39453,1.75 0.082,0.22787 0.20506,0.35547 0.36914,0.38281 0.23697,0.0456 0.35545,0.20509 0.35547,0.47852 -2e-5,0.3737 -0.44663,0.69271 -1.33985,0.95703 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33594,3.25391 1.04816,0 2.24217,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39647,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24024,-5.48242 c -10e-6,-0.50129 -0.2028,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90462,-0.56737 -1.49707,-0.56738 -1.67708,10e-6 -2.51562,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2885" />
+ <path
+ d="m 360.08868,153.20226 c -1e-5,0.17318 -0.28712,0.35092 -0.86132,0.5332 -0.37371,0.10938 -0.69272,0.48308 -0.95704,1.12109 -0.44662,1.07553 -1.22592,2.65235 -2.33789,4.73047 -0.93881,1.75912 -1.66342,3.04427 -2.17382,3.85547 -0.0274,0.0638 -0.0866,0.0911 -0.17774,0.082 -0.0912,-0.009 -0.15951,-0.0456 -0.20508,-0.10937 -1.93229,-2.77995 -3.58659,-5.6237 -4.96289,-8.53125 -0.29167,-0.61979 -0.57878,-0.96614 -0.86133,-1.03907 -0.52864,-0.20962 -0.79296,-0.39647 -0.79296,-0.56054 0,-0.36458 0.17773,-0.52408 0.5332,-0.47852 0.55599,0.0729 1.38086,0.0912 2.47461,0.0547 1.05729,-0.0273 1.80012,-0.082 2.22851,-0.16406 0.26432,-0.0547 0.39648,0.0456 0.39649,0.30078 -10e-6,0.31902 -0.18686,0.5241 -0.56055,0.61523 -0.71094,0.19142 -1.06641,0.43751 -1.0664,0.73828 -1e-5,0.13673 0.0456,0.33269 0.13671,0.58789 0.30989,0.76564 0.86588,1.87078 1.66797,3.31543 0.80208,1.44467 1.25781,2.167 1.36719,2.167 0.082,0 0.46939,-0.69271 1.16211,-2.07813 0.72916,-1.46744 1.25324,-2.65234 1.57227,-3.55469 0.0456,-0.10025 0.0683,-0.20051 0.0684,-0.30078 -1e-5,-0.35546 -0.21876,-0.64712 -0.65625,-0.875 -0.42839,-0.17317 -0.64258,-0.33723 -0.64257,-0.49219 -10e-6,-0.319 0.14126,-0.45116 0.42382,-0.39648 0.36458,0.0638 1.00716,0.1185 1.92774,0.16406 0.92056,0.0456 1.51756,0.0547 1.79101,0.0274 0.33723,-0.0638 0.50585,0.0319 0.50586,0.28711 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2887" />
+ <path
+ d="m 372.21564,160.98155 c -1e-5,0.39193 -0.52866,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47265,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82812 0,-1.80469 0.56054,-3.2539 1.68164,-4.34766 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.5293,0.86133 0.70181,0.51042 1.16665,1.09376 1.39453,1.75 0.082,0.22787 0.20506,0.35547 0.36914,0.38281 0.23697,0.0456 0.35545,0.20509 0.35547,0.47852 -2e-5,0.3737 -0.44663,0.69271 -1.33985,0.95703 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33594,3.25391 1.04816,0 2.24217,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39647,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24024,-5.48242 c -10e-6,-0.50129 -0.2028,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90462,-0.56737 -1.49707,-0.56738 -1.67708,10e-6 -2.51562,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2889" />
+ <path
+ d="m 380.66486,162.88194 c -10e-6,0.36459 -0.20509,0.52409 -0.61524,0.47852 -1.25782,-0.11849 -2.81641,-0.10026 -4.67578,0.0547 -0.3737,0.0365 -0.60384,0.0273 -0.69043,-0.0274 -0.0866,-0.0547 -0.12988,-0.20508 -0.12988,-0.45117 0,-0.21875 0.24837,-0.40332 0.74512,-0.55371 0.49674,-0.15039 0.74511,-0.59928 0.74511,-1.34668 l 0,-12.37305 c 0,-0.73826 -0.1071,-1.28058 -0.32129,-1.62695 -0.21419,-0.34634 -0.59017,-0.61522 -1.12793,-0.80664 -0.28255,-0.10024 -0.42383,-0.24152 -0.42382,-0.42383 -1e-5,-0.27342 0.20507,-0.4785 0.61523,-0.61523 0.61979,-0.20051 1.26237,-0.5104 1.92773,-0.92969 0.54687,-0.32811 0.89323,-0.49217 1.03907,-0.49219 0.33723,2e-5 0.50585,0.23244 0.50586,0.69727 -10e-6,-0.0364 -0.0182,0.41929 -0.0547,1.36718 -0.0273,0.90236 -0.0365,1.79104 -0.0273,2.66602 l 0.0547,12.20898 c 0,0.556 0.13672,0.95932 0.41016,1.20997 0.27343,0.25065 0.74283,0.41699 1.4082,0.49902 0.41015,0.0456 0.61523,0.20052 0.61524,0.46484 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2891" />
+ <path
+ d="m 393.9129,157.76866 c -10e-6,1.58594 -0.5879,2.92806 -1.76367,4.02637 -1.17579,1.09831 -2.63412,1.64746 -4.375,1.64746 -1.74089,0 -3.11719,-0.48307 -4.12891,-1.44922 -0.97526,-0.94791 -1.46289,-2.20117 -1.46289,-3.75976 0,-1.61328 0.61524,-2.97135 1.84571,-4.07422 1.194,-1.0664 2.62043,-1.5996 4.27929,-1.59961 1.77734,1e-5 3.16275,0.47852 4.15625,1.43554 0.96614,0.9297 1.44921,2.18751 1.44922,3.77344 z m -2.40625,0.83399 c -10e-6,-1.43099 -0.34636,-2.5931 -1.03906,-3.48633 -0.67449,-0.86588 -1.53126,-1.29882 -2.57031,-1.29883 -0.96615,1e-5 -1.75456,0.33953 -2.36524,1.01855 -0.61068,0.67905 -0.91602,1.5062 -0.91601,2.48145 -1e-5,1.56771 0.35546,2.78907 1.0664,3.66406 0.65625,0.80209 1.51302,1.20313 2.57032,1.20313 1.00259,0 1.79556,-0.33268 2.3789,-0.99805 0.58333,-0.66536 0.87499,-1.52669 0.875,-2.58398 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2893" />
+ <path
+ d="m 408.20001,157.39952 c -1e-5,1.70443 -0.57195,3.15137 -1.71582,4.34082 -1.14389,1.18945 -2.4769,1.78418 -3.99902,1.78418 -0.92058,0 -1.80925,-0.16406 -2.66602,-0.49219 -0.0911,-0.0365 -0.13672,0.18685 -0.13672,0.66993 l 0,4.11523 c 0,0.66536 0.54232,1.09374 1.62696,1.28516 0.50129,0.0911 0.81802,0.17545 0.95019,0.25293 0.13216,0.0775 0.19824,0.20735 0.19824,0.38964 0,0.30989 -0.20508,0.45117 -0.61523,0.42383 -1.91407,-0.12761 -3.59571,-0.082 -5.04492,0.13672 -0.30078,0.0456 -0.48763,0.0456 -0.56055,0 -0.0729,-0.0456 -0.10937,-0.16863 -0.10937,-0.36914 0,-0.15495 0.25976,-0.3418 0.77929,-0.56055 0.44662,-0.18229 0.66992,-0.61979 0.66993,-1.3125 l 0,-12.05859 c -1e-5,-0.97525 -0.26889,-1.58593 -0.80665,-1.83203 -0.63802,-0.28254 -0.95703,-0.51497 -0.95703,-0.69727 0,-0.20051 0.18685,-0.3509 0.56055,-0.45117 0.48307,-0.11848 0.97982,-0.32811 1.49023,-0.62891 0.42839,-0.24608 0.68815,-0.36912 0.7793,-0.36914 0.27344,2e-5 0.49674,0.25067 0.66992,0.75196 0.25521,0.72917 0.42383,1.09376 0.50586,1.09375 0.0182,1e-5 0.41015,-0.21419 1.17578,-0.64258 0.83854,-0.46483 1.65885,-0.69726 2.46094,-0.69727 1.30338,1e-5 2.3789,0.36915 3.22656,1.10743 1.01171,0.86589 1.51757,2.11914 1.51758,3.75976 z m -2.32422,1.09375 c -10e-6,-1.48567 -0.36915,-2.64322 -1.10742,-3.47266 -0.60157,-0.69269 -1.30795,-1.03905 -2.11914,-1.03906 -0.62891,1e-5 -1.23047,0.21876 -1.80469,0.65625 -0.75651,0.56511 -1.13477,1.35353 -1.13476,2.36524 l 0,3.86914 c -1e-5,0.24609 0.34635,0.50586 1.03906,0.77929 0.74739,0.30079 1.55859,0.45118 2.43359,0.45118 1.79557,0 2.69335,-1.20313 2.69336,-3.60938 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2895" />
+ <path
+ d="m 432.90509,162.90929 c -2e-5,0.30078 -0.1413,0.44205 -0.42383,0.42382 -1.85939,-0.13671 -3.59116,-0.13671 -5.19531,0 -0.35549,0.0273 -0.53322,-0.13216 -0.5332,-0.47851 -2e-5,-0.27344 0.24152,-0.41927 0.72461,-0.4375 0.61065,-0.0365 0.91599,-0.5332 0.91601,-1.49024 l 0,-3.63671 c -2e-5,-2.04166 -0.82944,-3.0625 -2.48828,-3.0625 -0.87502,0 -1.6771,0.15951 -2.40625,0.47851 -0.66538,0.28256 -1.00262,0.56511 -1.01172,0.84766 l -0.0547,5.42773 c -10e-6,0.56511 0.0684,0.93881 0.20508,1.1211 0.10025,0.1276 0.32356,0.21875 0.66992,0.27343 0.80207,0.13672 1.20311,0.30534 1.20313,0.50586 -2e-5,0.17318 -0.0228,0.29167 -0.0684,0.35547 -0.0547,0.0911 -0.21877,0.13216 -0.49219,0.12305 -1.03907,-0.0365 -2.55209,-0.0182 -4.53906,0.0547 -0.28256,0.009 -0.46485,-0.0137 -0.54688,-0.0684 -0.082,-0.0547 -0.12305,-0.1823 -0.12304,-0.38282 -10e-6,-0.23697 0.25975,-0.38281 0.77929,-0.4375 0.5651,-0.0638 0.84765,-0.57877 0.84766,-1.54492 l 0,-3.71875 c -10e-6,-1.0026 -0.22787,-1.77733 -0.68359,-2.32422 -0.40105,-0.49218 -0.91147,-0.73827 -1.53125,-0.73828 -0.91147,1e-5 -1.73178,0.18458 -2.46094,0.55371 -0.72917,0.36915 -1.09376,0.76336 -1.09375,1.18262 l 0,4.99023 c -1e-5,0.56511 0.14583,0.95704 0.4375,1.17579 0.26432,0.20052 0.74283,0.32356 1.43555,0.36914 0.37369,0.0182 0.56054,0.14583 0.56054,0.38281 0,0.26432 -0.15039,0.39648 -0.45117,0.39648 -2.27865,0 -3.92839,0.0638 -4.94922,0.19141 -0.34635,0.0456 -0.56966,0.0456 -0.66992,0 -0.082,-0.0456 -0.12305,-0.15495 -0.12305,-0.32813 0,-0.22786 0.33724,-0.42382 1.01172,-0.58789 0.41016,-0.10937 0.61523,-0.64257 0.61524,-1.59961 l 0,-4.19726 c -1e-5,-1.0664 -0.28711,-1.68163 -0.86133,-1.8457 -0.48308,-0.13671 -0.77702,-0.23697 -0.88184,-0.30079 -0.10482,-0.0638 -0.15722,-0.16861 -0.15722,-0.31445 0,-0.16405 0.47395,-0.51041 1.42187,-1.03906 1.0026,-0.56509 1.61784,-0.84765 1.8457,-0.84766 0.19141,1e-5 0.35319,0.27573 0.48536,0.82715 0.13215,0.55144 0.23469,0.82716 0.30761,0.82715 0.10026,10e-6 0.37825,-0.14127 0.83399,-0.42383 0.5651,-0.35546 1.07551,-0.61978 1.53125,-0.79297 0.74739,-0.29165 1.51757,-0.43749 2.31054,-0.4375 0.63802,1e-5 1.194,0.13673 1.66797,0.41016 0.32812,0.1823 0.62434,0.43295 0.88868,0.75195 0.21873,0.27345 0.32811,0.41017 0.32812,0.41016 -10e-6,1e-5 0.23697,-0.13671 0.71094,-0.41016 0.55597,-0.319 1.09829,-0.56965 1.62695,-0.75195 0.77472,-0.27343 1.52668,-0.41015 2.25586,-0.41016 2.49738,1e-5 3.74607,1.37176 3.74609,4.11524 l 0,4.42968 c -2e-5,0.51954 0.12303,0.88868 0.36914,1.10743 0.21873,0.18229 0.64712,0.33724 1.28516,0.46484 0.48305,0.0911 0.72459,0.22787 0.72461,0.41016 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2897" />
+ <path
+ d="m 444.45782,160.98155 c -1e-5,0.39193 -0.52865,0.87956 -1.58593,1.46289 -1.20314,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47266,-1.35352 -1.0026,-0.95703 -1.5039,-2.23307 -1.5039,-3.82812 0,-1.80469 0.56054,-3.2539 1.68164,-4.34766 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.52929,0.86133 0.70182,0.51042 1.16666,1.09376 1.39453,1.75 0.082,0.22787 0.20507,0.35547 0.36914,0.38281 0.23697,0.0456 0.35546,0.20509 0.35547,0.47852 -10e-6,0.3737 -0.44662,0.69271 -1.33984,0.95703 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33593,3.25391 1.04817,0 2.24218,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39648,-0.17773 0.58789,-0.17773 0.14583,0 0.21874,0.13216 0.21875,0.39648 z m -3.24023,-5.48242 c -10e-6,-0.50129 -0.20281,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90463,-0.56737 -1.49707,-0.56738 -1.67709,10e-6 -2.51563,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2899" />
+ <path
+ d="m 460.86407,163.03233 c -1e-5,0.28256 -0.20509,0.41016 -0.61523,0.38282 -1.95965,-0.10026 -3.44532,-0.11849 -4.45703,-0.0547 -0.51954,0.0365 -0.80209,-0.0273 -0.84766,-0.19141 -0.0182,-0.0547 -0.0273,-0.13216 -0.0273,-0.23242 -1e-5,-0.18229 0.26431,-0.34635 0.79297,-0.49219 0.48306,-0.13672 0.7246,-0.60612 0.72461,-1.4082 l 0,-3.63672 c -1e-5,-2.08723 -0.90691,-3.13085 -2.72071,-3.13086 -0.8112,1e-5 -1.56315,0.19142 -2.25586,0.57422 -0.65625,0.37371 -0.98438,0.76108 -0.98437,1.16211 l 0,5.00391 c -1e-5,0.85677 0.56054,1.33528 1.68164,1.43554 0.42838,0.0365 0.64257,0.17318 0.64258,0.41016 -10e-6,0.22786 -0.0592,0.36458 -0.17774,0.41016 -0.0547,0.0182 -0.20964,0.0228 -0.46484,0.0137 -1.43099,-0.0547 -2.90756,0.0182 -4.42969,0.21875 -0.32812,0.0456 -0.5332,0.0592 -0.61523,0.041 -0.18229,-0.0365 -0.27344,-0.17773 -0.27344,-0.42383 0,-0.21874 0.25976,-0.40559 0.7793,-0.56054 0.48307,-0.14583 0.7246,-0.75195 0.72461,-1.81836 l 0,-4.14258 c -1e-5,-0.70182 -0.0912,-1.14843 -0.27344,-1.33984 -0.12761,-0.13671 -0.55143,-0.32812 -1.27148,-0.57422 -0.1823,-0.0638 -0.27344,-0.1914 -0.27344,-0.38281 0,-0.18229 0.18685,-0.34179 0.56054,-0.47852 0.51042,-0.1914 1.07097,-0.49674 1.68165,-0.91602 0.51041,-0.34634 0.83853,-0.51952 0.98437,-0.51953 0.24609,1e-5 0.41015,0.28029 0.49219,0.84082 0.082,0.56056 0.19596,0.84083 0.34179,0.84082 -0.0729,1e-5 0.39193,-0.26659 1.39454,-0.7998 1.00259,-0.53319 1.99152,-0.7998 2.96679,-0.79981 2.36067,1e-5 3.55012,1.34441 3.56836,4.03321 l 0.0273,4.40234 c -2e-5,0.56511 0.14582,0.97071 0.4375,1.2168 0.21873,0.18229 0.64256,0.33724 1.27148,0.46484 0.41014,0.082 0.61522,0.23242 0.61523,0.45117 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2901" />
+ <path
+ d="m 469.64142,161.99327 c -10e-6,0.4375 -0.40105,0.8112 -1.20313,1.12109 -0.71094,0.27344 -1.43099,0.41016 -2.16015,0.41016 -1.97787,0 -2.9668,-1.05273 -2.9668,-3.1582 l 0,-4.94922 c 0,-0.6289 -0.0501,-1.01399 -0.15039,-1.15527 -0.10026,-0.14127 -0.43294,-0.28027 -0.99805,-0.417 -0.14583,-0.0365 -0.21875,-0.17772 -0.21875,-0.42382 0,-0.26432 0.0547,-0.42382 0.16407,-0.47852 0.98437,-0.48306 1.84114,-1.34895 2.57031,-2.59766 0.10025,-0.17316 0.29622,-0.22785 0.58789,-0.16406 0.20051,0.0638 0.30533,0.18231 0.31445,0.35547 l 0.0547,1.70898 c -10e-6,0.12762 0.0228,0.21876 0.0684,0.27344 0.0638,0.082 0.20963,0.12306 0.4375,0.12305 l 3.04883,0 c 0.17317,1e-5 0.17317,0.2142 0,0.64258 -0.20965,0.51954 -0.51954,0.7793 -0.92969,0.77929 l -2.06445,0 c -0.35548,1e-5 -0.57423,0.0593 -0.65625,0.17774 -0.0638,0.082 -0.0957,0.30535 -0.0957,0.66992 l 0,4.4707 c 0,1.13021 0.10026,1.84571 0.30078,2.14649 0.26432,0.38281 0.87956,0.57422 1.84571,0.57422 0.319,0 0.70637,-0.0524 1.16211,-0.15723 0.45572,-0.10482 0.69725,-0.15723 0.72461,-0.15723 0.10936,0 0.16405,0.0684 0.16406,0.20508 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2903" />
+ <path
+ d="m 475.84845,162.60851 c -1e-5,2.36979 -0.99805,4.15168 -2.99414,5.3457 -0.18229,0.10937 -0.31446,0.16406 -0.39649,0.16406 -0.1276,0 -0.24609,-0.14584 -0.35546,-0.4375 -0.0365,-0.10026 -0.0547,-0.1823 -0.0547,-0.24609 0,-0.15495 0.25976,-0.41016 0.7793,-0.76563 0.89322,-0.61979 1.33984,-1.32161 1.33984,-2.10547 0,-0.40104 -0.25977,-0.76562 -0.7793,-1.09375 -0.72917,-0.45573 -1.09375,-1.01627 -1.09375,-1.68164 0,-0.44661 0.15039,-0.81803 0.45117,-1.11426 0.30078,-0.29622 0.70182,-0.44433 1.20313,-0.44433 0.60156,0 1.06868,0.21875 1.40137,0.65625 0.33267,0.4375 0.49901,1.01172 0.49902,1.72266 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2905" />
+ <path
+ d="m 278.95978,182.96866 c -1e-5,1.58594 -0.58791,2.92806 -1.76367,4.02637 -1.1758,1.09831 -2.63413,1.64746 -4.375,1.64746 -1.74089,0 -3.1172,-0.48307 -4.12891,-1.44922 -0.97526,-0.94791 -1.46289,-2.20117 -1.46289,-3.75977 0,-1.61327 0.61523,-2.97134 1.8457,-4.07421 1.19401,-1.0664 2.62044,-1.5996 4.2793,-1.59961 1.77733,1e-5 3.16275,0.47852 4.15625,1.43554 0.96613,0.9297 1.44921,2.18751 1.44922,3.77344 z m -2.40625,0.83399 c -1e-5,-1.43099 -0.34637,-2.5931 -1.03906,-3.48633 -0.67449,-0.86588 -1.53126,-1.29882 -2.57032,-1.29883 -0.96615,1e-5 -1.75456,0.33953 -2.36523,1.01855 -0.61068,0.67905 -0.91602,1.5062 -0.91602,2.48145 0,1.56771 0.35547,2.78906 1.06641,3.66406 0.65624,0.80209 1.51301,1.20313 2.57031,1.20313 1.0026,0 1.79557,-0.33268 2.37891,-0.99805 0.58332,-0.66536 0.87499,-1.52669 0.875,-2.58398 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2907" />
+ <path
+ d="m 295.59845,188.23233 c -2e-5,0.28255 -0.20509,0.41016 -0.61523,0.38282 -1.95965,-0.10027 -3.44533,-0.11849 -4.45704,-0.0547 -0.51954,0.0365 -0.80209,-0.0274 -0.84765,-0.19141 -0.0182,-0.0547 -0.0274,-0.13216 -0.0274,-0.23242 -1e-5,-0.18229 0.26432,-0.34635 0.79297,-0.49219 0.48306,-0.13672 0.7246,-0.60612 0.72461,-1.4082 l 0,-3.63672 c -10e-6,-2.08723 -0.90691,-3.13085 -2.7207,-3.13086 -0.81121,1e-5 -1.56316,0.19142 -2.25586,0.57422 -0.65626,0.37371 -0.98438,0.76107 -0.98438,1.16211 l 0,5.00391 c 0,0.85677 0.56055,1.33528 1.68165,1.43554 0.42837,0.0365 0.64257,0.17318 0.64257,0.41016 0,0.22786 -0.0593,0.36458 -0.17773,0.41015 -0.0547,0.0182 -0.20964,0.0228 -0.46484,0.0137 -1.431,-0.0547 -2.90756,0.0182 -4.42969,0.21875 -0.32813,0.0456 -0.53321,0.0592 -0.61524,0.041 -0.18229,-0.0365 -0.27344,-0.17773 -0.27343,-0.42383 -10e-6,-0.21875 0.25976,-0.40559 0.77929,-0.56054 0.48307,-0.14584 0.72461,-0.75195 0.72461,-1.81836 l 0,-4.14258 c 0,-0.70182 -0.0912,-1.14843 -0.27344,-1.33984 -0.1276,-0.13671 -0.55143,-0.32812 -1.27148,-0.57422 -0.18229,-0.0638 -0.27344,-0.1914 -0.27344,-0.38282 0,-0.18228 0.18685,-0.34178 0.56055,-0.47851 0.51041,-0.1914 1.07096,-0.49674 1.68164,-0.91602 0.51041,-0.34634 0.83854,-0.51952 0.98438,-0.51953 0.24608,1e-5 0.41015,0.28029 0.49218,0.84082 0.082,0.56056 0.19596,0.84083 0.3418,0.84082 -0.0729,1e-5 0.39192,-0.26659 1.39453,-0.7998 1.0026,-0.53319 1.99153,-0.7998 2.9668,-0.79981 2.36066,1e-5 3.55011,1.34441 3.56836,4.03321 l 0.0273,4.40234 c -10e-6,0.56511 0.14582,0.9707 0.4375,1.2168 0.21874,0.18229 0.64256,0.33724 1.27149,0.46484 0.41014,0.082 0.61521,0.23242 0.61523,0.45117 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2909" />
+ <path
+ d="m 307.02814,186.18155 c -1e-5,0.39193 -0.52866,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47265,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82813 0,-1.80468 0.56054,-3.25389 1.68164,-4.34765 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.5293,0.86133 0.70181,0.51042 1.16665,1.09376 1.39453,1.75 0.082,0.22787 0.20506,0.35547 0.36914,0.38281 0.23697,0.0456 0.35545,0.20508 0.35547,0.47851 -2e-5,0.37371 -0.44663,0.69272 -1.33985,0.95704 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33594,3.25391 1.04816,0 2.24217,-0.38281 3.58203,-1.14844 0.20051,-0.11849 0.39647,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24024,-5.48242 c -10e-6,-0.50129 -0.2028,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90462,-0.56737 -1.49707,-0.56738 -1.67708,1e-5 -2.51562,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2911" />
+ <path
+ d="m 328.91681,187.13858 c 0.0364,0.082 0.0547,0.14128 0.0547,0.17774 -2e-5,0.1914 -0.46486,0.50586 -1.39453,0.94336 -1.07554,0.5013 -1.709,0.75195 -1.9004,0.75195 -0.16407,0 -0.3532,-0.27572 -0.56738,-0.82715 -0.2142,-0.55143 -0.36231,-0.82715 -0.44433,-0.82715 0.009,0 -0.50815,0.23926 -1.55176,0.71778 -1.04363,0.47851 -1.83432,0.71777 -2.37207,0.71777 -1.20313,0 -2.24675,-0.43294 -3.13086,-1.29883 -1.06641,-1.02994 -1.59961,-2.4746 -1.59961,-4.33398 0,-1.55859 0.6289,-2.90299 1.88672,-4.03321 1.11197,-0.99348 2.24674,-1.49022 3.40429,-1.49023 0.82031,1e-5 1.80013,0.10483 2.93946,0.31445 0.35546,0.0638 0.53319,-0.041 0.5332,-0.31445 l 0,-4.67578 c -1e-5,-0.52863 -0.12761,-0.90233 -0.38281,-1.1211 -0.16407,-0.14581 -0.55144,-0.31899 -1.16211,-0.51953 -0.29168,-0.10935 -0.43751,-0.28709 -0.4375,-0.5332 -10e-6,-0.22785 0.19595,-0.39647 0.58789,-0.50586 0.53775,-0.16404 1.18033,-0.43748 1.92773,-0.82031 0.61978,-0.32811 1.0026,-0.49217 1.14844,-0.49219 0.33723,2e-5 0.50585,0.23244 0.50586,0.69727 -1e-5,-0.009 -0.0137,0.44435 -0.041,1.36035 -0.0274,0.91603 -0.041,1.80698 -0.041,2.67285 l 0,12.42773 c -1e-5,0.51954 0.23241,0.7793 0.69727,0.7793 0.17316,0 0.40558,-0.0273 0.69726,-0.082 0.319,-0.0547 0.53319,0.0501 0.64258,0.31445 z m -4.22461,-1.35351 0,-4.89453 c -1e-5,-0.40104 -0.35548,-0.81575 -1.06641,-1.24414 -0.75651,-0.45572 -1.57683,-0.68359 -2.46093,-0.6836 -1.90496,1e-5 -2.85743,1.17579 -2.85743,3.52735 0,1.25781 0.37826,2.33333 1.13477,3.22656 0.7565,0.89323 1.64518,1.33984 2.66602,1.33984 0.46483,0 1.01171,-0.15494 1.64062,-0.46484 0.6289,-0.3099 0.94335,-0.57878 0.94336,-0.80664 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2913" />
+ <path
+ d="m 339.23907,178.78507 c -1e-5,1.10287 -0.31902,1.6543 -0.95703,1.65429 -0.10027,1e-5 -0.43295,-0.0638 -0.99804,-0.1914 -0.56511,-0.1276 -0.97983,-0.1914 -1.24414,-0.19141 -1.02084,1e-5 -1.53126,0.68816 -1.53125,2.06445 l 0,4.03321 c -10e-6,0.47396 0.16405,0.8112 0.49218,1.01172 0.1914,0.11849 0.66992,0.27799 1.43555,0.47851 0.41015,0.10026 0.61523,0.25521 0.61523,0.46485 0,0.24609 -0.0342,0.3942 -0.10253,0.44433 -0.0684,0.0501 -0.23927,0.0615 -0.5127,0.0342 -2.00521,-0.20963 -3.59115,-0.21875 -4.75781,-0.0273 -0.36459,0.0638 -0.59701,0.0638 -0.69727,0 -0.082,-0.0547 -0.12305,-0.21419 -0.12304,-0.47852 -1e-5,-0.24609 0.25292,-0.44205 0.75878,-0.58789 0.50586,-0.14583 0.75879,-0.58333 0.75879,-1.3125 l 0,-4.38867 c 0,-0.57421 -0.0752,-0.96158 -0.22558,-1.16211 -0.1504,-0.20051 -0.56283,-0.46939 -1.23731,-0.80664 l -0.082,-0.041 c -0.18229,-0.0911 -0.27344,-0.21874 -0.27344,-0.38281 0,-0.18228 0.18685,-0.33723 0.56055,-0.46484 0.6289,-0.21874 1.20312,-0.52864 1.72266,-0.92969 0.42838,-0.33723 0.70182,-0.50585 0.82031,-0.50586 0.32812,1e-5 0.52408,0.28256 0.58789,0.84766 0.082,0.72006 0.17773,1.08008 0.28711,1.08008 0.009,0 0.51953,-0.34179 1.53125,-1.0254 0.66536,-0.4466 1.39452,-0.66991 2.1875,-0.66992 0.65624,1e-5 0.98436,0.35092 0.98437,1.05274 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2915" />
+ <path
+ d="m 352.7879,182.96866 c -10e-6,1.58594 -0.5879,2.92806 -1.76367,4.02637 -1.17579,1.09831 -2.63412,1.64746 -4.375,1.64746 -1.74089,0 -3.11719,-0.48307 -4.12891,-1.44922 -0.97526,-0.94791 -1.46289,-2.20117 -1.46289,-3.75977 0,-1.61327 0.61524,-2.97134 1.84571,-4.07421 1.194,-1.0664 2.62043,-1.5996 4.27929,-1.59961 1.77734,1e-5 3.16275,0.47852 4.15625,1.43554 0.96614,0.9297 1.44921,2.18751 1.44922,3.77344 z m -2.40625,0.83399 c -10e-6,-1.43099 -0.34636,-2.5931 -1.03906,-3.48633 -0.67449,-0.86588 -1.53126,-1.29882 -2.57031,-1.29883 -0.96615,1e-5 -1.75456,0.33953 -2.36524,1.01855 -0.61068,0.67905 -0.91602,1.5062 -0.91601,2.48145 -1e-5,1.56771 0.35546,2.78906 1.0664,3.66406 0.65625,0.80209 1.51302,1.20313 2.57032,1.20313 1.00259,0 1.79556,-0.33268 2.3789,-0.99805 0.58333,-0.66536 0.87499,-1.52669 0.875,-2.58398 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2917" />
+ <path
+ d="m 367.07501,182.59952 c -1e-5,1.70443 -0.57195,3.15137 -1.71582,4.34082 -1.14389,1.18945 -2.4769,1.78418 -3.99902,1.78418 -0.92058,0 -1.80925,-0.16406 -2.66602,-0.49219 -0.0911,-0.0365 -0.13672,0.18685 -0.13672,0.66992 l 0,4.11524 c 0,0.66536 0.54232,1.09374 1.62696,1.28516 0.50129,0.0911 0.81802,0.17544 0.95019,0.25292 0.13216,0.0775 0.19824,0.20736 0.19824,0.38965 0,0.30989 -0.20508,0.45117 -0.61523,0.42383 -1.91407,-0.12761 -3.59571,-0.082 -5.04492,0.13672 -0.30078,0.0456 -0.48763,0.0456 -0.56055,0 -0.0729,-0.0456 -0.10937,-0.16863 -0.10937,-0.36914 0,-0.15495 0.25976,-0.3418 0.77929,-0.56055 0.44662,-0.18229 0.66992,-0.61979 0.66993,-1.3125 l 0,-12.05859 c -1e-5,-0.97525 -0.26889,-1.58593 -0.80665,-1.83203 -0.63802,-0.28254 -0.95703,-0.51497 -0.95703,-0.69727 0,-0.20051 0.18685,-0.3509 0.56055,-0.45117 0.48307,-0.11848 0.97982,-0.32811 1.49023,-0.62891 0.42839,-0.24608 0.68815,-0.36913 0.7793,-0.36914 0.27344,1e-5 0.49674,0.25067 0.66992,0.75196 0.25521,0.72917 0.42383,1.09376 0.50586,1.09375 0.0182,1e-5 0.41015,-0.21419 1.17578,-0.64258 0.83854,-0.46483 1.65885,-0.69726 2.46094,-0.69727 1.30338,1e-5 2.3789,0.36915 3.22656,1.10742 1.01171,0.8659 1.51757,2.11915 1.51758,3.75977 z m -2.32422,1.09375 c -10e-6,-1.48567 -0.36915,-2.64322 -1.10742,-3.47266 -0.60157,-0.6927 -1.30795,-1.03905 -2.11914,-1.03906 -0.62891,1e-5 -1.23047,0.21876 -1.80469,0.65625 -0.75651,0.56511 -1.13477,1.35352 -1.13476,2.36524 l 0,3.86914 c -1e-5,0.24609 0.34635,0.50586 1.03906,0.77929 0.74739,0.30079 1.55859,0.45118 2.43359,0.45118 1.79557,0 2.69335,-1.20313 2.69336,-3.60938 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2919" />
+ <path
+ d="m 387.33673,187.16593 c -10e-6,0.1914 -0.34864,0.48079 -1.0459,0.86816 -0.69727,0.38737 -1.25554,0.58106 -1.6748,0.58106 -0.35548,0 -0.66993,-0.17318 -0.94336,-0.51954 -0.27345,-0.34635 -0.46485,-0.51953 -0.57422,-0.51953 -0.082,0 -0.51498,0.18685 -1.29883,0.56055 -0.78386,0.3737 -1.57227,0.56055 -2.36523,0.56055 -0.7474,0 -1.37175,-0.21875 -1.87305,-0.65625 -0.54688,-0.48308 -0.82031,-1.13932 -0.82031,-1.96875 0,-1.57682 1.80468,-2.70703 5.41406,-3.39063 0.61978,-0.11848 0.93424,-0.36913 0.94336,-0.75195 l 0.0273,-0.875 c 0.0547,-1.49478 -0.60612,-2.24218 -1.98242,-2.24219 -0.39193,1e-5 -0.76335,0.35092 -1.11426,1.05274 -0.35091,0.70183 -0.85449,1.08008 -1.51074,1.13476 -0.7474,0.0729 -1.12109,-0.24153 -1.12109,-0.94336 0,-0.43749 0.55598,-0.94791 1.66797,-1.53125 1.16666,-0.61067 2.28775,-0.916 3.36328,-0.91601 1.85025,1e-5 2.76626,0.87956 2.74804,2.63867 l -0.0547,5.63281 c -0.009,0.59245 0.24152,0.88867 0.75195,0.88867 0.10025,0 0.29166,-0.0228 0.57422,-0.0684 0.28254,-0.0456 0.4466,-0.0683 0.49219,-0.0684 0.26431,1e-5 0.39647,0.17774 0.39648,0.53321 z m -4.21094,-3.11719 c 0.009,-0.22786 -0.0433,-0.37825 -0.15722,-0.45117 -0.11394,-0.0729 -0.29396,-0.0866 -0.54004,-0.041 -2.19662,0.39193 -3.29493,1.10743 -3.29492,2.14649 -10e-6,1.04817 0.56965,1.57226 1.70898,1.57226 0.45572,0 0.92512,-0.0866 1.4082,-0.25976 0.5651,-0.20052 0.84765,-0.44206 0.84766,-0.72461 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2921" />
+ <path
+ d="m 396.52423,187.19327 c -10e-6,0.4375 -0.40105,0.8112 -1.20312,1.12109 -0.71095,0.27344 -1.431,0.41016 -2.16016,0.41016 -1.97787,0 -2.9668,-1.05273 -2.9668,-3.1582 l 0,-4.94922 c 0,-0.6289 -0.0501,-1.01399 -0.15039,-1.15528 -0.10026,-0.14126 -0.43294,-0.28026 -0.99804,-0.41699 -0.14584,-0.0365 -0.21876,-0.17772 -0.21875,-0.42383 -1e-5,-0.26431 0.0547,-0.42381 0.16406,-0.47851 0.98437,-0.48306 1.84114,-1.34895 2.57031,-2.59766 0.10026,-0.17316 0.29622,-0.22785 0.58789,-0.16406 0.20052,0.0638 0.30533,0.1823 0.31445,0.35547 l 0.0547,1.70898 c 0,0.12762 0.0228,0.21876 0.0684,0.27344 0.0638,0.082 0.20963,0.12306 0.4375,0.12305 l 3.04883,0 c 0.17317,1e-5 0.17317,0.2142 0,0.64258 -0.20964,0.51954 -0.51954,0.7793 -0.92969,0.77929 l -2.06445,0 c -0.35548,1e-5 -0.57422,0.0593 -0.65625,0.17774 -0.0638,0.082 -0.0957,0.30534 -0.0957,0.66992 l 0,4.4707 c -1e-5,1.13021 0.10025,1.84571 0.30078,2.14649 0.26431,0.38281 0.87955,0.57422 1.8457,0.57422 0.319,0 0.70637,-0.0524 1.16211,-0.15723 0.45572,-0.10482 0.69726,-0.15723 0.72461,-0.15723 0.10936,0 0.16405,0.0684 0.16406,0.20508 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2923" />
+ <path
+ d="m 416.32111,187.16593 c -2e-5,0.1914 -0.34865,0.48079 -1.0459,0.86816 -0.69728,0.38737 -1.25555,0.58106 -1.67481,0.58106 -0.35547,0 -0.66993,-0.17318 -0.94336,-0.51954 -0.27344,-0.34635 -0.46485,-0.51953 -0.57422,-0.51953 -0.082,0 -0.51498,0.18685 -1.29882,0.56055 -0.78386,0.3737 -1.57227,0.56055 -2.36524,0.56055 -0.7474,0 -1.37175,-0.21875 -1.87304,-0.65625 -0.54688,-0.48308 -0.82032,-1.13932 -0.82032,-1.96875 0,-1.57682 1.80469,-2.70703 5.41407,-3.39063 0.61978,-0.11848 0.93423,-0.36913 0.94335,-0.75195 l 0.0273,-0.875 c 0.0547,-1.49478 -0.60613,-2.24218 -1.98242,-2.24219 -0.39194,1e-5 -0.76335,0.35092 -1.11426,1.05274 -0.35092,0.70183 -0.8545,1.08008 -1.51074,1.13476 -0.7474,0.0729 -1.1211,-0.24153 -1.1211,-0.94336 0,-0.43749 0.55599,-0.94791 1.66797,-1.53125 1.16666,-0.61067 2.28776,-0.916 3.36328,-0.91601 1.85025,1e-5 2.76627,0.87956 2.74805,2.63867 l -0.0547,5.63281 c -0.009,0.59245 0.24153,0.88867 0.75196,0.88867 0.10025,0 0.29165,-0.0228 0.57421,-0.0684 0.28254,-0.0456 0.44661,-0.0683 0.49219,-0.0684 0.26431,1e-5 0.39647,0.17774 0.39649,0.53321 z m -4.21094,-3.11719 c 0.009,-0.22786 -0.0433,-0.37825 -0.15723,-0.45117 -0.11394,-0.0729 -0.29395,-0.0866 -0.54004,-0.041 -2.19662,0.39193 -3.29492,1.10743 -3.29492,2.14649 0,1.04817 0.56966,1.57226 1.70899,1.57226 0.45572,0 0.92512,-0.0866 1.4082,-0.25976 0.5651,-0.20052 0.84765,-0.44206 0.84765,-0.72461 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2925" />
+ <path
+ d="m 432.45392,187.19327 c -10e-6,0.4375 -0.40105,0.8112 -1.20313,1.12109 -0.71094,0.27344 -1.43099,0.41016 -2.16015,0.41016 -1.97787,0 -2.9668,-1.05273 -2.9668,-3.1582 l 0,-4.94922 c 0,-0.6289 -0.0501,-1.01399 -0.15039,-1.15528 -0.10026,-0.14126 -0.43294,-0.28026 -0.99805,-0.41699 -0.14583,-0.0365 -0.21875,-0.17772 -0.21875,-0.42383 0,-0.26431 0.0547,-0.42381 0.16407,-0.47851 0.98437,-0.48306 1.84114,-1.34895 2.57031,-2.59766 0.10025,-0.17316 0.29622,-0.22785 0.58789,-0.16406 0.20051,0.0638 0.30533,0.1823 0.31445,0.35547 l 0.0547,1.70898 c -10e-6,0.12762 0.0228,0.21876 0.0684,0.27344 0.0638,0.082 0.20963,0.12306 0.4375,0.12305 l 3.04883,0 c 0.17317,1e-5 0.17317,0.2142 0,0.64258 -0.20965,0.51954 -0.51954,0.7793 -0.92969,0.77929 l -2.06445,0 c -0.35548,1e-5 -0.57423,0.0593 -0.65625,0.17774 -0.0638,0.082 -0.0957,0.30534 -0.0957,0.66992 l 0,4.4707 c 0,1.13021 0.10026,1.84571 0.30078,2.14649 0.26432,0.38281 0.87956,0.57422 1.84571,0.57422 0.319,0 0.70637,-0.0524 1.16211,-0.15723 0.45572,-0.10482 0.69725,-0.15723 0.72461,-0.15723 0.10936,0 0.16405,0.0684 0.16406,0.20508 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2927" />
+ <path
+ d="m 439.30353,172.66007 c -1e-5,0.41928 -0.15723,0.80893 -0.47168,1.16894 -0.31446,0.36004 -0.65854,0.54006 -1.03223,0.54004 -0.42839,2e-5 -0.7793,-0.12759 -1.05273,-0.38281 -0.27344,-0.25519 -0.41016,-0.58788 -0.41016,-0.99805 0,-0.40102 0.16634,-0.77472 0.49902,-1.12109 0.33268,-0.34634 0.69043,-0.51952 1.07325,-0.51953 0.92968,1e-5 1.39452,0.43751 1.39453,1.3125 z m 1.51758,15.47656 c -0.0456,0.26432 -0.15496,0.41471 -0.32813,0.45117 -0.0456,0.009 -0.26433,0 -0.65625,-0.0273 -1.35808,-0.0911 -2.69336,-0.0729 -4.00586,0.0547 -0.35547,0.0365 -0.57878,0.0228 -0.66992,-0.041 -0.0912,-0.0638 -0.13672,-0.20964 -0.13672,-0.4375 0,-0.20964 0.24154,-0.38281 0.72461,-0.51953 0.52864,-0.15495 0.79297,-0.66081 0.79297,-1.51758 l 0,-3.58203 c 0,-0.72005 -0.0729,-1.22591 -0.21875,-1.51758 -0.20052,-0.40103 -0.61524,-0.70637 -1.24414,-0.91601 -0.28256,-0.10026 -0.42383,-0.25065 -0.42383,-0.45118 0,-0.26431 0.20508,-0.46027 0.61523,-0.58789 0.76563,-0.23697 1.44466,-0.55142 2.03711,-0.94336 0.47396,-0.32811 0.76562,-0.49217 0.875,-0.49218 0.36458,1e-5 0.54232,0.23699 0.53321,0.71093 -0.0365,2.38803 -0.0547,4.82162 -0.0547,7.30078 -10e-6,0.59245 0.0866,1.01628 0.25977,1.27149 0.1914,0.28255 0.55598,0.48307 1.09375,0.60156 0.59244,0.13672 0.86132,0.35091 0.80664,0.64258 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2929" />
+ <path
+ d="m 464.89728,188.10929 c -3e-5,0.30078 -0.1413,0.44205 -0.42383,0.42382 -1.8594,-0.13671 -3.59117,-0.13671 -5.19531,0 -0.35549,0.0274 -0.53322,-0.13216 -0.53321,-0.47851 -1e-5,-0.27344 0.24152,-0.41927 0.72461,-0.4375 0.61066,-0.0365 0.916,-0.5332 0.91602,-1.49024 l 0,-3.63671 c -2e-5,-2.04166 -0.82945,-3.0625 -2.48828,-3.0625 -0.87502,0 -1.6771,0.15951 -2.40625,0.47851 -0.66538,0.28256 -1.00262,0.56511 -1.01172,0.84766 l -0.0547,5.42773 c -10e-6,0.56511 0.0684,0.9388 0.20508,1.1211 0.10025,0.1276 0.32355,0.21875 0.66992,0.27343 0.80207,0.13672 1.20311,0.30534 1.20313,0.50586 -2e-5,0.17318 -0.0228,0.29167 -0.0684,0.35547 -0.0547,0.0911 -0.21877,0.13216 -0.49219,0.12305 -1.03908,-0.0365 -2.5521,-0.0182 -4.53906,0.0547 -0.28256,0.009 -0.46486,-0.0137 -0.54688,-0.0684 -0.082,-0.0547 -0.12305,-0.1823 -0.12304,-0.38282 -10e-6,-0.23698 0.25975,-0.38281 0.77929,-0.4375 0.5651,-0.0638 0.84765,-0.57877 0.84766,-1.54492 l 0,-3.71875 c -10e-6,-1.0026 -0.22788,-1.77733 -0.6836,-2.32422 -0.40105,-0.49218 -0.91146,-0.73827 -1.53125,-0.73828 -0.91146,1e-5 -1.73177,0.18458 -2.46093,0.55371 -0.72917,0.36915 -1.09376,0.76336 -1.09375,1.18262 l 0,4.99023 c -1e-5,0.56511 0.14583,0.95704 0.4375,1.17579 0.26431,0.20052 0.74283,0.32356 1.43554,0.36914 0.37369,0.0182 0.56054,0.14583 0.56055,0.38281 -10e-6,0.26432 -0.1504,0.39648 -0.45117,0.39648 -2.27865,0 -3.92839,0.0638 -4.94922,0.19141 -0.34636,0.0456 -0.56966,0.0456 -0.66992,0 -0.082,-0.0456 -0.12305,-0.15495 -0.12305,-0.32813 0,-0.22786 0.33724,-0.42382 1.01172,-0.58789 0.41015,-0.10937 0.61523,-0.64257 0.61523,-1.59961 l 0,-4.19726 c 0,-1.0664 -0.28711,-1.68163 -0.86132,-1.8457 -0.48308,-0.13672 -0.77702,-0.23698 -0.88184,-0.30079 -0.10482,-0.0638 -0.15723,-0.16861 -0.15723,-0.31445 0,-0.16405 0.47396,-0.51041 1.42188,-1.03906 1.0026,-0.5651 1.61783,-0.84765 1.8457,-0.84766 0.1914,1e-5 0.35319,0.27573 0.48535,0.82715 0.13216,0.55144 0.2347,0.82716 0.30762,0.82715 0.10026,1e-5 0.37825,-0.14127 0.83399,-0.42383 0.56509,-0.35546 1.07551,-0.61978 1.53125,-0.79297 0.74738,-0.29165 1.51756,-0.43749 2.31054,-0.4375 0.63801,1e-5 1.194,0.13673 1.66797,0.41016 0.32811,0.1823 0.62434,0.43295 0.88867,0.75195 0.21874,0.27345 0.32812,0.41017 0.32813,0.41016 -10e-6,1e-5 0.23696,-0.13671 0.71094,-0.41016 0.55597,-0.319 1.09829,-0.56965 1.62695,-0.75195 0.77472,-0.27343 1.52667,-0.41015 2.25586,-0.41016 2.49737,1e-5 3.74607,1.37176 3.74609,4.11524 l 0,4.42968 c -2e-5,0.51954 0.12303,0.88868 0.36914,1.10743 0.21873,0.18229 0.64712,0.33724 1.28516,0.46484 0.48305,0.0911 0.72458,0.22786 0.72461,0.41016 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2931" />
+ <path
+ d="m 476.45001,186.18155 c -1e-5,0.39193 -0.52865,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43815,0.97071 -3.70507,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47266,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82813 0,-1.80468 0.56055,-3.25389 1.68164,-4.34765 1.04818,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90234,1e-5 1.74544,0.28712 2.5293,0.86133 0.70181,0.51042 1.16666,1.09376 1.39453,1.75 0.082,0.22787 0.20507,0.35547 0.36914,0.38281 0.23697,0.0456 0.35546,0.20508 0.35547,0.47851 -10e-6,0.37371 -0.44662,0.69272 -1.33984,0.95704 l -6.30274,1.87304 c 0.41016,2.16928 1.52213,3.25391 3.33594,3.25391 1.04817,0 2.24218,-0.38281 3.58203,-1.14844 0.20051,-0.11849 0.39648,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24023,-5.48242 c -1e-5,-0.50129 -0.20281,-0.94107 -0.6084,-1.31934 -0.40561,-0.37824 -0.90463,-0.56737 -1.49707,-0.56738 -1.67709,1e-5 -2.51563,1.20769 -2.51563,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60156,-0.17317 0.90234,-0.48762 0.90235,-0.94336 z"
+ style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
+ id="path2933" />
+ </g>
+ </g>
+</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/.gitignore Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+_build
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/Makefile Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,118 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask"
+ @echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+
+latexpdf: latex
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C _build/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in _build/latex."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
Binary file bundled/flask/docs/_static/debugger.png has changed
Binary file bundled/flask/docs/_static/flask.png has changed
Binary file bundled/flask/docs/_static/flaskr.png has changed
Binary file bundled/flask/docs/_static/logo-full.png has changed
Binary file bundled/flask/docs/_static/no.png has changed
Binary file bundled/flask/docs/_static/touch-icon.png has changed
Binary file bundled/flask/docs/_static/yes.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/_templates/sidebarintro.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,21 @@
+<h3>About Flask</h3>
+<p>
+ Flask is a micro webdevelopment framework for Python. You are currently
+ looking at the documentation of the development version. Things are
+ not stable yet, but if you have some feedback,
+ <a href="mailto:armin.ronacher@active-4.com">let me know</a>.
+</p>
+<h3>Other Formats</h3>
+<p>
+ You can download the documentation in other formats as well:
+</p>
+<ul>
+ <li><a href="http://flask.pocoo.org/docs/flask-docs.pdf">as PDF</a>
+ <li><a href="http://flask.pocoo.org/docs/flask-docs.zip">as zipped HTML</a>
+</ul>
+<h3>Useful Links</h3>
+<ul>
+ <li><a href="http://flask.pocoo.org/">The Flask Website</a></li>
+ <li><a href="http://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
+ <li><a href="http://github.com/mitsuhiko/flask">Flask @ github</a></li>
+</ul>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/_templates/sidebarlogo.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,3 @@
+<p class="logo"><a href="{{ pathto(master_doc) }}">
+ <img class="logo" src="{{ pathto('_static/flask.png', 1) }}" alt="Logo"/>
+</a></p>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/api.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,441 @@
+.. _api:
+
+API
+===
+
+.. module:: flask
+
+This part of the documentation covers all the interfaces of Flask. For
+parts where Flask depends on external libraries, we document the most
+important right here and provide links to the canonical documentation.
+
+
+Application Object
+------------------
+
+.. autoclass:: Flask
+ :members:
+ :inherited-members:
+
+
+Module Objects
+--------------
+
+.. autoclass:: Module
+ :members:
+ :inherited-members:
+
+Incoming Request Data
+---------------------
+
+.. autoclass:: Request
+
+.. class:: request
+
+ To access incoming request data, you can use the global `request`
+ object. Flask parses incoming request data for you and gives you
+ access to it through that global object. Internally Flask makes
+ sure that you always get the correct data for the active thread if you
+ are in a multithreaded environment.
+
+ This is a proxy. See :ref:`notes-on-proxies` for more information.
+
+ The request object is an instance of a :class:`~werkzeug.Request`
+ subclass and provides all of the attributes Werkzeug defines. This
+ just shows a quick overview of the most important ones.
+
+ .. attribute:: form
+
+ A :class:`~werkzeug.MultiDict` with the parsed form data from `POST`
+ or `PUT` requests. Please keep in mind that file uploads will not
+ end up here, but instead in the :attr:`files` attribute.
+
+ .. attribute:: args
+
+ A :class:`~werkzeug.MultiDict` with the parsed contents of the query
+ string. (The part in the URL after the question mark).
+
+ .. attribute:: values
+
+ A :class:`~werkzeug.CombinedMultiDict` with the contents of both
+ :attr:`form` and :attr:`args`.
+
+ .. attribute:: cookies
+
+ A :class:`dict` with the contents of all cookies transmitted with
+ the request.
+
+ .. attribute:: stream
+
+ If the incoming form data was not encoded with a known mimetype
+ the data is stored unmodified in this stream for consumption. Most
+ of the time it is a better idea to use :attr:`data` which will give
+ you that data as a string. The stream only returns the data once.
+
+ .. attribute:: data
+
+ Contains the incoming request data as string in case it came with
+ a mimetype Flask does not handle.
+
+ .. attribute:: files
+
+ A :class:`~werkzeug.MultiDict` with files uploaded as part of a
+ `POST` or `PUT` request. Each file is stored as
+ :class:`~werkzeug.FileStorage` object. It basically behaves like a
+ standard file object you know from Python, with the difference that
+ it also has a :meth:`~werkzeug.FileStorage.save` function that can
+ store the file on the filesystem.
+
+ .. attribute:: environ
+
+ The underlying WSGI environment.
+
+ .. attribute:: method
+
+ The current request method (``POST``, ``GET`` etc.)
+
+ .. attribute:: path
+ .. attribute:: script_root
+ .. attribute:: url
+ .. attribute:: base_url
+ .. attribute:: url_root
+
+ Provides different ways to look at the current URL. Imagine your
+ application is listening on the following URL::
+
+ http://www.example.com/myapplication
+
+ And a user requests the following URL::
+
+ http://www.example.com/myapplication/page.html?x=y
+
+ In this case the values of the above mentioned attributes would be
+ the following:
+
+ ============= ======================================================
+ `path` ``/page.html``
+ `script_root` ``/myapplication``
+ `base_url` ``http://www.example.com/myapplication/page.html``
+ `url` ``http://www.example.com/myapplication/page.html?x=y``
+ `url_root` ``http://www.example.com/myapplication/``
+ ============= ======================================================
+
+ .. attribute:: is_xhr
+
+ `True` if the request was triggered via a JavaScript
+ `XMLHttpRequest`. This only works with libraries that support the
+ ``X-Requested-With`` header and set it to `XMLHttpRequest`.
+ Libraries that do that are prototype, jQuery and Mochikit and
+ probably some more.
+
+ .. attribute:: json
+
+ Contains the parsed body of the JSON request if the mimetype of
+ the incoming data was `application/json`. This requires Python 2.6
+ or an installed version of simplejson.
+
+Response Objects
+----------------
+
+.. autoclass:: flask.Response
+ :members: set_cookie, data, mimetype
+
+ .. attribute:: headers
+
+ A :class:`Headers` object representing the response headers.
+
+ .. attribute:: status_code
+
+ The response status as integer.
+
+
+Sessions
+--------
+
+If you have the :attr:`Flask.secret_key` set you can use sessions in Flask
+applications. A session basically makes it possible to remember
+information from one request to another. The way Flask does this is by
+using a signed cookie. So the user can look at the session contents, but
+not modify it unless he knows the secret key, so make sure to set that to
+something complex and unguessable.
+
+To access the current session you can use the :class:`session` object:
+
+.. class:: session
+
+ The session object works pretty much like an ordinary dict, with the
+ difference that it keeps track on modifications.
+
+ This is a proxy. See :ref:`notes-on-proxies` for more information.
+
+ The following attributes are interesting:
+
+ .. attribute:: new
+
+ `True` if the session is new, `False` otherwise.
+
+ .. attribute:: modified
+
+ `True` if the session object detected a modification. Be advised
+ that modifications on mutable structures are not picked up
+ automatically, in that situation you have to explicitly set the
+ attribute to `True` yourself. Here an example::
+
+ # this change is not picked up because a mutable object (here
+ # a list) is changed.
+ session['objects'].append(42)
+ # so mark it as modified yourself
+ session.modified = True
+
+ .. attribute:: permanent
+
+ If set to `True` the session lives for
+ :attr:`~flask.Flask.permanent_session_lifetime` seconds. The
+ default is 31 days. If set to `False` (which is the default) the
+ session will be deleted when the user closes the browser.
+
+
+Application Globals
+-------------------
+
+To share data that is valid for one request only from one function to
+another, a global variable is not good enough because it would break in
+threaded environments. Flask provides you with a special object that
+ensures it is only valid for the active request and that will return
+different values for each request. In a nutshell: it does the right
+thing, like it does for :class:`request` and :class:`session`.
+
+.. data:: g
+
+ Just store on this whatever you want. For example a database
+ connection or the user that is currently logged in.
+
+ This is a proxy. See :ref:`notes-on-proxies` for more information.
+
+
+Useful Functions and Classes
+----------------------------
+
+.. data:: current_app
+
+ Points to the application handling the request. This is useful for
+ extensions that want to support multiple applications running side
+ by side.
+
+ This is a proxy. See :ref:`notes-on-proxies` for more information.
+
+.. autofunction:: url_for
+
+.. function:: abort(code)
+
+ Raises an :exc:`~werkzeug.exception.HTTPException` for the given
+ status code. For example to abort request handling with a page not
+ found exception, you would call ``abort(404)``.
+
+ :param code: the HTTP error code.
+
+.. autofunction:: redirect
+
+.. autofunction:: make_response
+
+.. autofunction:: send_file
+
+.. autofunction:: send_from_directory
+
+.. autofunction:: escape
+
+.. autoclass:: Markup
+ :members: escape, unescape, striptags
+
+Message Flashing
+----------------
+
+.. autofunction:: flash
+
+.. autofunction:: get_flashed_messages
+
+Returning JSON
+--------------
+
+.. autofunction:: jsonify
+
+.. data:: json
+
+ If JSON support is picked up, this will be the module that Flask is
+ using to parse and serialize JSON. So instead of doing this yourself::
+
+ try:
+ import simplejson as json
+ except ImportError:
+ import json
+
+ You can instead just do this::
+
+ from flask import json
+
+ For usage examples, read the :mod:`json` documentation.
+
+ The :func:`~json.dumps` function of this json module is also available
+ as filter called ``|tojson`` in Jinja2. Note that inside `script`
+ tags no escaping must take place, so make sure to disable escaping
+ with ``|safe`` if you intend to use it inside `script` tags:
+
+ .. sourcecode:: html+jinja
+
+ <script type=text/javascript>
+ doSomethingWith({{ user.username|tojson|safe }});
+ </script>
+
+ Note that the ``|tojson`` filter escapes forward slashes properly.
+
+Template Rendering
+------------------
+
+.. autofunction:: render_template
+
+.. autofunction:: render_template_string
+
+.. autofunction:: get_template_attribute
+
+Configuration
+-------------
+
+.. autoclass:: Config
+ :members:
+
+Useful Internals
+----------------
+
+.. data:: _request_ctx_stack
+
+ The internal :class:`~werkzeug.LocalStack` that is used to implement
+ all the context local objects used in Flask. This is a documented
+ instance and can be used by extensions and application code but the
+ use is discouraged in general.
+
+ The following attributes are always present on each layer of the
+ stack:
+
+ `app`
+ the active Flask application.
+
+ `url_adapter`
+ the URL adapter that was used to match the request.
+
+ `request`
+ the current request object.
+
+ `session`
+ the active session object.
+
+ `g`
+ an object with all the attributes of the :data:`flask.g` object.
+
+ `flashes`
+ an internal cache for the flashed messages.
+
+ Example usage::
+
+ from flask import _request_ctx_stack
+
+ def get_session():
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.session
+
+ .. versionchanged:: 0.4
+
+ The request context is automatically popped at the end of the request
+ for you. In debug mode the request context is kept around if
+ exceptions happen so that interactive debuggers have a chance to
+ introspect the data. With 0.4 this can also be forced for requests
+ that did not fail and outside of `DEBUG` mode. By setting
+ ``'flask._preserve_context'`` to `True` on the WSGI environment the
+ context will not pop itself at the end of the request. This is used by
+ the :meth:`~flask.Flask.test_client` for example to implement the
+ deferred cleanup functionality.
+
+ You might find this helpful for unittests where you need the
+ information from the context local around for a little longer. Make
+ sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in
+ that situation, otherwise your unittests will leak memory.
+
+Signals
+-------
+
+.. when modifying this list, also update the one in signals.rst
+
+.. versionadded:: 0.6
+
+.. data:: signals_available
+
+ `True` if the signalling system is available. This is the case
+ when `blinker`_ is installed.
+
+.. data:: template_rendered
+
+ This signal is sent when a template was successfully rendered. The
+ signal is invoked with the instance of the template as `template`
+ and the context as dictionary (named `context`).
+
+.. data:: request_started
+
+ This signal is sent before any request processing started but when the
+ request context was set up. Because the request context is already
+ bound, the subscriber can access the request with the standard global
+ proxies such as :class:`~flask.request`.
+
+.. data:: request_finished
+
+ This signal is sent right before the response is sent to the client.
+ It is passed the response to be sent named `response`.
+
+.. data:: got_request_exception
+
+ This signal is sent when an exception happens during request processing.
+ It is sent *before* the standard exception handling kicks in and even
+ in debug mode, where no exception handling happens. The exception
+ itself is passed to the subscriber as `exception`.
+
+.. currentmodule:: None
+
+.. class:: flask.signals.Namespace
+
+ An alias for :class:`blinker.base.Namespace` if blinker is available,
+ otherwise a dummy class that creates fake signals. This class is
+ available for Flask extensions that want to provide the same fallback
+ system as Flask itself.
+
+ .. method:: signal(name, doc=None)
+
+ Creates a new signal for this namespace if blinker is available,
+ otherwise returns a fake signal that has a send method that will
+ do nothing but will fail with a :exc:`RuntimeError` for all other
+ operations, including connecting.
+
+.. _blinker: http://pypi.python.org/pypi/blinker
+
+.. _notes-on-proxies:
+
+Notes On Proxies
+----------------
+
+Some of the objects provided by Flask are proxies to other objects. The
+reason behind this is that these proxies are shared between threads and
+they have to dispatch to the actual object bound to a thread behind the
+scenes as necessary.
+
+Most of the time you don't have to care about that, but there are some
+exceptions where it is good to know that this object is an actual proxy:
+
+- The proxy objects do not fake their inherited types, so if you want to
+ perform actual instance checks, you have to do that on the instance
+ that is being proxied (see `_get_current_object` below).
+- if the object reference is important (so for example for sending
+ :ref:`signals`)
+
+If you need to get access to the underlying object that is proxied, you
+can use the :meth:`~werkzeug.LocalProxy._get_current_object` method::
+
+ app = current_app._get_current_object()
+ my_signal.send(app)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/becomingbig.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,88 @@
+.. _becomingbig:
+
+Becoming Big
+============
+
+Your application is becoming more and more complex? If you suddenly
+realize that Flask does things in a way that does not work out for your
+application there are ways to deal with that.
+
+Flask is powered by Werkzeug and Jinja2, two libraries that are in use at
+a number of large websites out there and all Flask does is bring those
+two together. Being a microframework Flask does not do much more than
+combining existing libraries - there is not a lot of code involved.
+What that means for large applications is that it's very easy to take the
+code from Flask and put it into a new module within the applications and
+expand on that.
+
+Flask is designed to be extended and modified in a couple of different
+ways:
+
+- Flask extensions. For a lot of reusable functionality you can create
+ extensions. For extensions a number of hooks exist throughout Flask
+ with signals and callback functions.
+
+- Subclassing. The majority of functionality can be changed by creating
+ a new subclass of the :class:`~flask.Flask` class and overriding
+ methods provided for this exact purpose.
+
+- Forking. If nothing else works out you can just take the Flask
+ codebase at a given point and copy/paste it into your application
+ and change it. Flask is designed with that in mind and makes this
+ incredible easy. You just have to take the package and copy it
+ into your application's code and rename it (for example to
+ `framework`). Then you can start modifying the code in there.
+
+Why consider Forking?
+---------------------
+
+The majority of code of Flask is within Werkzeug and Jinja2. These
+libraries do the majority of the work. Flask is just the paste that glues
+those together. For every project there is the point where the underlying
+framework gets in the way (due to assumptions the original developers
+had). This is natural because if this would not be the case, the
+framework would be a very complex system to begin with which causes a
+steep learning curve and a lot of user frustration.
+
+This is not unique to Flask. Many people use patched and modified
+versions of their framework to counter shortcomings. This idea is also
+reflected in the license of Flask. You don't have to contribute any
+changes back if you decide to modify the framework.
+
+The downside of forking is of course that Flask extensions will most
+likely break because the new framework has a different import name.
+Furthermore integrating upstream changes can be a complex process,
+depending on the number of changes. Because of that, forking should be
+the very last resort.
+
+Scaling like a Pro
+------------------
+
+For many web applications the complexity of the code is less an issue than
+the scaling for the number of users or data entries expected. Flask by
+itself is only limited in terms of scaling by your application code, the
+data store you want to use and the Python implementation and webserver you
+are running on.
+
+Scaling well means for example that if you double the amount of servers
+you get about twice the performance. Scaling bad means that if you add a
+new server the application won't perform any better or would not even
+support a second server.
+
+There is only one limiting factor regarding scaling in Flask which are
+the context local proxies. They depend on context which in Flask is
+defined as being either a thread, process or greenlet. If your server
+uses some kind of concurrency that is not based on threads or greenlets,
+Flask will no longer be able to support these global proxies. However the
+majority of servers are using either threads, greenlets or separate
+processes to achieve concurrency which are all methods well supported by
+the underlying Werkzeug library.
+
+Dialogue with the Community
+---------------------------
+
+The Flask developers are very interested to keep everybody happy, so as
+soon as you find an obstacle in your way, caused by Flask, don't hesitate
+to contact the developers on the mailinglist or IRC channel. The best way
+for the Flask and Flask-extension developers to improve it for larger
+applications is getting feedback from users.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/changelog.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+.. include:: ../CHANGES
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/conf.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,267 @@
+# -*- coding: utf-8 -*-
+#
+# Flask documentation build configuration file, created by
+# sphinx-quickstart on Tue Apr 6 15:24:58 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.append(os.path.abspath('_themes'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Flask'
+copyright = u'2010, Armin Ronacher'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+import pkg_resources
+try:
+ release = pkg_resources.get_distribution('Flask').version
+except pkg_resources.DistributionNotFound:
+ print 'To build the documentation, The distribution information of Flask'
+ print 'Has to be available. Either install the package into your'
+ print 'development environment or run "setup.py develop" to setup the'
+ print 'metadata. A virtualenv is recommended!'
+ sys.exit(1)
+del pkg_resources
+
+if 'dev' in release:
+ release = release.split('dev')[0] + 'dev'
+version = '.'.join(release.split('.')[:2])
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'flask'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+html_theme_options = {
+ 'touch_icon': 'touch-icon.png'
+}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ['_themes']
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar. Do not set, template magic!
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+html_sidebars = {
+ 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
+ '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
+ 'sourcelink.html', 'searchbox.html']
+}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+html_use_modindex = False
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+html_show_sphinx = False
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Flaskdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('latexindex', 'Flask.tex', u'Flask Documentation',
+ u'Armin Ronacher', 'manual'),
+]
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+latex_use_modindex = False
+
+latex_elements = {
+ 'fontpkg': r'\usepackage{mathpazo}',
+ 'papersize': 'a4paper',
+ 'pointsize': '12pt',
+ 'preamble': r'\usepackage{flaskstyle}'
+}
+latex_use_parts = True
+
+latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
+
+
+# -- Options for Epub output ---------------------------------------------------
+
+# Bibliographic Dublin Core info.
+#epub_title = ''
+#epub_author = ''
+#epub_publisher = ''
+#epub_copyright = ''
+
+# The language of the text. It defaults to the language option
+# or en if the language is not set.
+#epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+#epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#epub_identifier = ''
+
+# A unique identification for the text.
+#epub_uid = ''
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_pre_files = []
+
+# HTML files shat should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+#epub_exclude_files = []
+
+# The depth of the table of contents in toc.ncx.
+#epub_tocdepth = 3
+
+intersphinx_mapping = {
+ 'http://docs.python.org/dev': None,
+ 'http://werkzeug.pocoo.org/docs/': None,
+ 'http://www.sqlalchemy.org/docs/': None,
+ 'http://wtforms.simplecodes.com/docs/0.5/': None,
+ 'http://discorporate.us/projects/Blinker/docs/1.1/': None
+}
+
+pygments_style = 'flask_theme_support.FlaskyStyle'
+
+# fall back if theme is not there
+try:
+ __import__('flask_theme_support')
+except ImportError, e:
+ print '-' * 74
+ print 'Warning: Flask themes unavailable. Building with default theme'
+ print 'If you want the Flask themes, run this command and build again:'
+ print
+ print ' git submodule update --init'
+ print '-' * 74
+
+ pygments_style = 'tango'
+ html_theme = 'default'
+ html_theme_options = {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/config.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,236 @@
+.. _config:
+
+Configuration Handling
+======================
+
+.. versionadded:: 0.3
+
+Applications need some kind of configuration. There are different things
+you might want to change like toggling debug mode, the secret key, and a
+lot of very similar things.
+
+The way Flask is designed usually requires the configuration to be
+available when the application starts up. You can hardcode the
+configuration in the code, which for many small applications is not
+actually that bad, but there are better ways.
+
+Independent of how you load your config, there is a config object
+available which holds the loaded configuration values:
+The :attr:`~flask.Flask.config` attribute of the :class:`~flask.Flask`
+object. This is the place where Flask itself puts certain configuration
+values and also where extensions can put their configuration values. But
+this is also where you can have your own configuration.
+
+Configuration Basics
+--------------------
+
+The :attr:`~flask.Flask.config` is actually a subclass of a dictionary and
+can be modified just like any dictionary::
+
+ app = Flask(__name__)
+ app.config['DEBUG'] = True
+
+Certain configuration values are also forwarded to the
+:attr:`~flask.Flask` object so that you can read and write them from
+there::
+
+ app.debug = True
+
+To update multiple keys at once you can use the :meth:`dict.update`
+method::
+
+ app.config.update(
+ DEBUG=True,
+ SECRET_KEY='...'
+ )
+
+Builtin Configuration Values
+----------------------------
+
+The following configuration values are used internally by Flask:
+
+.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
+
+=============================== =========================================
+``DEBUG`` enable/disable debug mode
+``TESTING`` enable/disable testing mode
+``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
+ propagation of exceptions. If not set or
+ explicitly set to `None` this is
+ implicitly true if either `TESTING` or
+ `DEBUG` is true.
+``SECRET_KEY`` the secret key
+``SESSION_COOKIE_NAME`` the name of the session cookie
+``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
+ :class:`datetime.timedelta` object.
+``USE_X_SENDFILE`` enable/disable x-sendfile
+``LOGGER_NAME`` the name of the logger
+``SERVER_NAME`` the name of the server. Required for
+ subdomain support (e.g.: ``'localhost'``)
+``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will
+ reject incoming requests with a
+ content length greater than this by
+ returning a 413 status code.
+=============================== =========================================
+
+.. admonition:: More on ``SERVER_NAME``
+
+ The ``SERVER_NAME`` key is used for the subdomain support. Because
+ Flask cannot guess the subdomain part without the knowledge of the
+ actual server name, this is required if you want to work with
+ subdomains. This is also used for the session cookie.
+
+ Please keep in mind that not only Flask has the problem of not knowing
+ what subdomains are, your web browser does as well. Most modern web
+ browsers will not allow cross-subdomain cookies to be set on a
+ server name without dots in it. So if your server name is
+ ``'localhost'`` you will not be able to set a cookie for
+ ``'localhost'`` and every subdomain of it. Please chose a different
+ server name in that case, like ``'myapplication.local'`` and add
+ this name + the subdomains you want to use into your host config
+ or setup a local `bind`_.
+
+.. _bind: https://www.isc.org/software/bind
+
+.. versionadded:: 0.4
+ ``LOGGER_NAME``
+
+.. versionadded:: 0.5
+ ``SERVER_NAME``
+
+.. versionadded:: 0.6
+ ``MAX_CONTENT_LENGTH``
+
+.. versionadded:: 0.7
+ ``PROPAGATE_EXCEPTIONS``
+
+Configuring from Files
+----------------------
+
+Configuration becomes more useful if you can configure from a file, and
+ideally that file would be outside of the actual application package so that
+you can install the package with distribute (:ref:`distribute-deployment`)
+and still modify that file afterwards.
+
+So a common pattern is this::
+
+ app = Flask(__name__)
+ app.config.from_object('yourapplication.default_settings')
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+This first loads the configuration from the
+`yourapplication.default_settings` module and then overrides the values
+with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
+environment variable points to. This environment variable can be set on
+Linux or OS X with the export command in the shell before starting the
+server::
+
+ $ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
+ $ python run-app.py
+ * Running on http://127.0.0.1:5000/
+ * Restarting with reloader...
+
+On Windows systems use the `set` builtin instead::
+
+ >set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
+
+The configuration files themselves are actual Python files. Only values
+in uppercase are actually stored in the config object later on. So make
+sure to use uppercase letters for your config keys.
+
+Here is an example configuration file::
+
+ DEBUG = False
+ SECRET_KEY = '?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83'
+
+Make sure to load the configuration very early on so that extensions have
+the ability to access the configuration when starting up. There are other
+methods on the config object as well to load from individual files. For a
+complete reference, read the :class:`~flask.Config` object's
+documentation.
+
+
+Configuration Best Practices
+----------------------------
+
+The downside with the approach mentioned earlier is that it makes testing
+a little harder. There is no one 100% solution for this problem in
+general, but there are a couple of things you can do to improve that
+experience:
+
+1. create your application in a function and register modules on it.
+ That way you can create multiple instances of your application with
+ different configurations attached which makes unittesting a lot
+ easier. You can use this to pass in configuration as needed.
+
+2. Do not write code that needs the configuration at import time. If you
+ limit yourself to request-only accesses to the configuration you can
+ reconfigure the object later on as needed.
+
+
+Development / Production
+------------------------
+
+Most applications need more than one configuration. There will at least
+be a separate configuration for a production server and one used during
+development. The easiest way to handle this is to use a default
+configuration that is always loaded and part of version control, and a
+separate configuration that overrides the values as necessary as mentioned
+in the example above::
+
+ app = Flask(__name__)
+ app.config.from_object('yourapplication.default_settings')
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+Then you just have to add a separate `config.py` file and export
+``YOURAPPLICATION_SETTINGS=/path/to/config.py`` and you are done. However
+there are alternative ways as well. For example you could use imports or
+subclassing.
+
+What is very popular in the Django world is to make the import explicit in
+the config file by adding an ``from yourapplication.default_settings
+import *`` to the top of the file and then overriding the changes by hand.
+You could also inspect an environment variable like
+``YOURAPPLICATION_MODE`` and set that to `production`, `development` etc
+and import different hardcoded files based on that.
+
+An interesting pattern is also to use classes and inheritance for
+configuration::
+
+ class Config(object):
+ DEBUG = False
+ TESTING = False
+ DATABASE_URI = 'sqlite://:memory:'
+
+ class ProductionConfig(Config):
+ DATABASE_URI = 'mysql://user@localhost/foo'
+
+ class DevelopmentConfig(Config):
+ DEBUG = True
+
+ class TestinConfig(Config):
+ TESTING = True
+
+To enable such a config you just have to call into
+:meth:`~flask.Config.from_object`::
+
+ app.config.from_object('configmodule.ProductionConfig')
+
+There are many different ways and it's up to you how you want to manage
+your configuration files. However here a list of good recommendations:
+
+- keep a default configuration in version control. Either populate the
+ config with this default configuration or import it in your own
+ configuration files before overriding values.
+- use an environment variable to switch between the configurations.
+ This can be done from outside the Python interpreter and makes
+ development and deployment much easier because you can quickly and
+ easily switch between different configs without having to touch the
+ code at all. If you are working often on different projects you can
+ even create your own script for sourcing that activates a virtualenv
+ and exports the development configuration for you.
+- Use a tool like `fabric`_ in production to push code and
+ configurations separately to the production server(s). For some
+ details about how to do that, head over to the :ref:`deploy` pattern.
+
+.. _fabric: http://fabfile.org/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/contents.rst.inc Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,52 @@
+User's Guide
+------------
+
+This part of the documentation, which is mostly prose, begins with some
+background information about Flask, then focuses on step-by-step
+instructions for web development with Flask.
+
+.. toctree::
+ :maxdepth: 2
+
+ foreword
+ installation
+ quickstart
+ tutorial/index
+ templating
+ testing
+ errorhandling
+ config
+ signals
+ shell
+ patterns/index
+ deploying/index
+ becomingbig
+
+API Reference
+-------------
+
+If you are looking for information on a specific function, class or
+method, this part of the documentation is for you.
+
+.. toctree::
+ :maxdepth: 2
+
+ api
+
+Additional Notes
+----------------
+
+Design notes, legal information and changelog are here for the interested.
+
+.. toctree::
+ :maxdepth: 2
+
+ design
+ htmlfaq
+ security
+ unicode
+ extensiondev
+ styleguide
+ upgrading
+ changelog
+ license
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/deploying/cgi.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,47 @@
+CGI
+===
+
+If all other deployment methods do not work, CGI will work for sure. CGI
+is supported by all major servers but usually has a less-than-optimal
+performance.
+
+This is also the way you can use a Flask application on Google's
+`App Engine`_, there however the execution does happen in a CGI-like
+environment. The application's performance is unaffected because of that.
+
+.. admonition:: Watch Out
+
+ Please make sure in advance that your ``app.run()`` call you might
+ have in your application file, is inside an ``if __name__ ==
+ '__main__':`` or moved to a separate file. Just make sure it's not
+ called because this will always start a local WSGI server which we do
+ not want if we deploy that application to CGI / app engine.
+
+.. _App Engine: http://code.google.com/appengine/
+
+Creating a `.cgi` file
+----------------------
+
+First you need to create the CGI application file. Let's call it
+`yourapplication.cgi`::
+
+ #!/usr/bin/python
+ from wsgiref.handlers import CGIHandler
+ from yourapplication import app
+
+ CGIHandler().run(app)
+
+Server Setup
+------------
+
+Usually there are two ways to configure the server. Either just copy the
+`.cgi` into a `cgi-bin` (and use `mod_rewrite` or something similar to
+rewrite the URL) or let the server point to the file directly.
+
+In Apache for example you can put a like like this into the config:
+
+.. sourcecode:: apache
+
+ ScriptAlias /app /path/to/the/application.cgi
+
+For more information consult the documentation of your webserver.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/deploying/fastcgi.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,139 @@
+FastCGI
+=======
+
+A very popular deployment setup on servers like `lighttpd`_ and `nginx`_
+is FastCGI. To use your WSGI application with any of them you will need
+a FastCGI server first.
+
+The most popular one is `flup`_ which we will use for this guide. Make
+sure to have it installed.
+
+.. admonition:: Watch Out
+
+ Please make sure in advance that your ``app.run()`` call you might
+ have in your application file, is inside an ``if __name__ ==
+ '__main__':`` or moved to a separate file. Just make sure it's not
+ called because this will always start a local WSGI server which we do
+ not want if we deploy that application to FastCGI.
+
+Creating a `.fcgi` file
+-----------------------
+
+First you need to create the FastCGI server file. Let's call it
+`yourapplication.fcgi`::
+
+ #!/usr/bin/python
+ from flup.server.fcgi import WSGIServer
+ from yourapplication import app
+
+ WSGIServer(app).run()
+
+This is enough for Apache to work, however lighttpd and nginx need a
+socket to communicate with the FastCGI server. For that to work you
+need to pass the path to the socket to the
+:class:`~flup.server.fcgi.WSGIServer`::
+
+ WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
+
+The path has to be the exact same path you define in the server
+config.
+
+Save the `yourapplication.fcgi` file somewhere you will find it again.
+It makes sense to have that in `/var/www/yourapplication` or something
+similar.
+
+Make sure to set the executable bit on that file so that the servers
+can execute it:
+
+.. sourcecode:: text
+
+ # chmod +x /var/www/yourapplication/yourapplication.fcgi
+
+Configuring lighttpd
+--------------------
+
+A basic FastCGI configuration for lighttpd looks like that::
+
+ fastcgi.server = ("/yourapplication" =>
+ "yourapplication" => (
+ "socket" => "/tmp/yourapplication-fcgi.sock",
+ "bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
+ "check-local" => "disable"
+ )
+ )
+
+This configuration binds the application to `/yourapplication`. If you
+want the application to work in the URL root you have to work around a
+lighttpd bug with the :class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix`
+middleware.
+
+Make sure to apply it only if you are mounting the application the URL
+root.
+
+Configuring nginx
+-----------------
+
+Installing FastCGI applications on nginx is a bit different because by default
+no FastCGI parameters are forwarded.
+
+A basic flask FastCGI configuration for nginx looks like this::
+
+ location = /yourapplication { rewrite ^ /yourapplication/ last; }
+ location /yourapplication { try_files $uri @yourapplication; }
+ location @yourapplication {
+ include fastcgi_params;
+ fastcgi_split_path_info ^(/yourapplication)(.*)$;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+ fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
+ }
+
+This configuration binds the application to `/yourapplication`. If you want
+to have it in the URL root it's a bit simpler because you don't have to figure
+out how to calculate `PATH_INFO` and `SCRIPT_NAME`::
+
+ location / { try_files $uri @yourapplication; }
+ location @yourapplication {
+ include fastcgi_params;
+ fastcgi_param PATH_INFO $fastcgi_script_name;
+ fastcgi_param SCRIPT_NAME "";
+ fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
+ }
+
+Since Nginx doesn't load FastCGI apps, you have to do it by yourself. You
+can either write an `init.d` script for that or execute it inside a screen
+session::
+
+ $ screen
+ $ /var/www/yourapplication/yourapplication.fcgi
+
+Debugging
+---------
+
+FastCGI deployments tend to be hard to debug on most webservers. Very often the
+only thing the server log tells you is something along the lines of "premature
+end of headers". In order to debug the application the only thing that can
+really give you ideas why it breaks is switching to the correct user and
+executing the application by hand.
+
+This example assumes your application is called `application.fcgi` and that your
+webserver user is `www-data`::
+
+ $ su www-data
+ $ cd /var/www/yourapplication
+ $ python application.fcgi
+ Traceback (most recent call last):
+ File "yourapplication.fcgi", line 4, in <module>
+ ImportError: No module named yourapplication
+
+In this case the error seems to be "yourapplication" not being on the python
+path. Common problems are:
+
+- relative paths being used. Don't rely on the current working directory
+- the code depending on environment variables that are not set by the
+ web server.
+- different python interpreters being used.
+
+.. _lighttpd: http://www.lighttpd.net/
+.. _nginx: http://nginx.net/
+.. _flup: http://trac.saddi.com/flup
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/deploying/index.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,19 @@
+Deployment Options
+==================
+
+Depending on what you have available there are multiple ways to run Flask
+applications. A very common method is to use the builtin server during
+development and maybe behind a proxy for simple applications, but there
+are more options available.
+
+If you have a different WSGI server look up the server documentation about
+how to use a WSGI app with it. Just remember that your application object
+is the actual WSGI application.
+
+.. toctree::
+ :maxdepth: 2
+
+ mod_wsgi
+ cgi
+ fastcgi
+ others
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/deploying/mod_wsgi.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,166 @@
+.. _mod_wsgi-deployment:
+
+mod_wsgi (Apache)
+=================
+
+If you are using the `Apache`_ webserver you should consider using `mod_wsgi`_.
+
+.. admonition:: Watch Out
+
+ Please make sure in advance that your ``app.run()`` call you might
+ have in your application file, is inside an ``if __name__ ==
+ '__main__':`` or moved to a separate file. Just make sure it's not
+ called because this will always start a local WSGI server which we do
+ not want if we deploy that application to mod_wsgi.
+
+.. _Apache: http://httpd.apache.org/
+
+Installing `mod_wsgi`
+---------------------
+
+If you don't have `mod_wsgi` installed yet you have to either install it using
+a package manager or compile it yourself.
+
+The mod_wsgi `installation instructions`_ cover source installations on UNIX
+systems.
+
+If you are using Ubuntu/Debian you can apt-get it and activate it as follows:
+
+.. sourcecode:: text
+
+ # apt-get install libapache2-mod-wsgi
+
+On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by using
+pkg_add:
+
+.. sourcecode:: text
+
+ # pkg_add -r mod_wsgi
+
+If you are using pkgsrc you can install `mod_wsgi` by compiling the
+`www/ap2-wsgi` package.
+
+If you encounter segfaulting child processes after the first apache reload you
+can safely ignore them. Just restart the server.
+
+Creating a `.wsgi` file
+-----------------------
+
+To run your application you need a `yourapplication.wsgi` file. This file
+contains the code `mod_wsgi` is executing on startup to get the application
+object. The object called `application` in that file is then used as
+application.
+
+For most applications the following file should be sufficient::
+
+ from yourapplication import app as application
+
+If you don't have a factory function for application creation but a singleton
+instance you can directly import that one as `application`.
+
+Store that file somewhere that you will find it again (e.g.:
+`/var/www/yourapplication`) and make sure that `yourapplication` and all
+the libraries that are in use are on the python load path. If you don't
+want to install it system wide consider using a `virtual python`_ instance.
+
+Configuring Apache
+------------------
+
+The last thing you have to do is to create an Apache configuration file for
+your application. In this example we are telling `mod_wsgi` to execute the
+application under a different user for security reasons:
+
+.. sourcecode:: apache
+
+ <VirtualHost *>
+ ServerName example.com
+
+ WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
+ WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
+
+ <Directory /var/www/yourapplication>
+ WSGIProcessGroup yourapplication
+ WSGIApplicationGroup %{GLOBAL}
+ Order deny,allow
+ Allow from all
+ </Directory>
+ </VirtualHost>
+
+For more information consult the `mod_wsgi wiki`_.
+
+.. _mod_wsgi: http://code.google.com/p/modwsgi/
+.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
+.. _virtual python: http://pypi.python.org/pypi/virtualenv
+.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/
+
+Troubleshooting
+---------------
+
+If your application does not run, follow this guide to troubleshoot:
+
+**Problem:** application does not run, errorlog shows SystemExit ignored
+ You have a ``app.run()`` call in your application file that is not
+ guarded by an ``if __name__ == '__main__':`` condition. Either remove
+ that :meth:`~flask.Flask.run` call from the file and move it into a
+ separate `run.py` file or put it into such an if block.
+
+**Problem:** application gives permission errors
+ Probably caused by your application running as the wrong user. Make
+ sure the folders the application needs access to have the proper
+ privileges set and the application runs as the correct user (``user``
+ and ``group`` parameter to the `WSGIDaemonProcess` directive)
+
+**Problem:** application dies with an error on print
+ Keep in mind that mod_wsgi disallows doing anything with
+ :data:`sys.stdout` and :data:`sys.stderr`. You can disable this
+ protection from the config by setting the `WSGIRestrictStdout` to
+ ``off``:
+
+ .. sourcecode:: apache
+
+ WSGIRestrictStdout Off
+
+ Alternatively you can also replace the standard out in the .wsgi file
+ with a different stream::
+
+ import sys
+ sys.stdout = sys.stderr
+
+**Problem:** accessing resources gives IO errors
+ Your application probably is a single .py file you symlinked into the
+ site-packages folder. Please be aware that this does not work,
+ instead you either have to put the folder into the pythonpath the file
+ is stored in, or convert your application into a package.
+
+ The reason for this is that for non-installed packages, the module
+ filename is used to locate the resources and for symlinks the wrong
+ filename is picked up.
+
+Support for Automatic Reloading
+-------------------------------
+
+To help deployment tools you can activate support for automatic reloading.
+Whenever something changes the `.wsgi` file, `mod_wsgi` will reload all
+the daemon processes for us.
+
+For that, just add the following directive to your `Directory` section:
+
+.. sourcecode:: apache
+
+ WSGIScriptReloading On
+
+Working with Virtual Environments
+---------------------------------
+
+Virtual environments have the advantage that they never install the
+required dependencies system wide so you have a better control over what
+is used where. If you want to use a virtual environment with mod_wsgi you
+have to modify your `.wsgi` file slightly.
+
+Add the following lines to the top of your `.wsgi` file::
+
+ activate_this = '/path/to/env/bin/activate_this.py'
+ execfile(activate_this, dict(__file__=activate_this))
+
+This sets up the load paths according to the settings of the virtual
+environment. Keep in mind that the path has to be absolute.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/deploying/others.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,98 @@
+Other Servers
+=============
+
+There are popular servers written in Python that allow the execution of
+WSGI applications as well. Keep in mind though that some of these servers
+were written for very specific applications and might not work as well for
+standard WSGI application such as Flask powered ones.
+
+
+Tornado
+--------
+
+`Tornado`_ is an open source version of the scalable, non-blocking web
+server and tools that power `FriendFeed`_. Because it is non-blocking and
+uses epoll, it can handle thousands of simultaneous standing connections,
+which means it is ideal for real-time web services. Integrating this
+service with Flask is a trivial task::
+
+ from tornado.wsgi import WSGIContainer
+ from tornado.httpserver import HTTPServer
+ from tornado.ioloop import IOLoop
+ from yourapplication import app
+
+ http_server = HTTPServer(WSGIContainer(app))
+ http_server.listen(5000)
+ IOLoop.instance().start()
+
+
+.. _Tornado: http://www.tornadoweb.org/
+.. _FriendFeed: http://friendfeed.com/
+
+
+Gevent
+-------
+
+`Gevent`_ is a coroutine-based Python networking library that uses
+`greenlet`_ to provide a high-level synchronous API on top of `libevent`_
+event loop::
+
+ from gevent.wsgi import WSGIServer
+ from yourapplication import app
+
+ http_server = WSGIServer(('', 5000), app)
+ http_server.serve_forever()
+
+.. _Gevent: http://www.gevent.org/
+.. _greenlet: http://codespeak.net/py/0.9.2/greenlet.html
+.. _libevent: http://monkey.org/~provos/libevent/
+
+
+Gunicorn
+--------
+
+`Gunicorn`_ 'Green Unicorn' is a WSGI HTTP Server for UNIX. It's a pre-fork
+worker model ported from Ruby's Unicorn project. It supports both `eventlet`_
+and `greenlet`_. Running a Flask application on this server is quite simple::
+
+ gunicorn myproject:app
+
+.. _Gunicorn: http://gunicorn.org/
+.. _eventlet: http://eventlet.net/
+.. _greenlet: http://codespeak.net/py/0.9.2/greenlet.html
+
+
+Proxy Setups
+------------
+
+If you deploy your application behind an HTTP proxy you will need to
+rewrite a few headers in order for the application to work. The two
+problematic values in the WSGI environment usually are `REMOTE_ADDR` and
+`HTTP_HOST`. Werkzeug ships a fixer that will solve some common setups,
+but you might want to write your own WSGI middleware for specific setups.
+
+The most common setup invokes the host being set from `X-Forwarded-Host`
+and the remote address from `X-Forward-For`::
+
+ from werkzeug.contrib.fixers import ProxyFix
+ app.wsgi_app = ProxyFix(app.wsgi_app)
+
+Please keep in mind that it is a security issue to use such a middleware
+in a non-proxy setup because it will blindly trust the incoming
+headers which might be forged by malicious clients.
+
+If you want to rewrite the headers from another header, you might want to
+use a fixer like this::
+
+ class CustomProxyFix(object):
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ host = environ.get('HTTP_X_FHOST', '')
+ if host:
+ environ['HTTP_HOST'] = host
+ return self.app(environ, start_response)
+
+ app.wsgi_app = CustomProxyFix(app.wsgi_app)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/design.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,175 @@
+.. _design:
+
+Design Decisions in Flask
+=========================
+
+If you are curious why Flask does certain things the way it does and not
+differently, this section is for you. This should give you an idea about
+some of the design decisions that may appear arbitrary and surprising at
+first, especially in direct comparison with other frameworks.
+
+
+The Explicit Application Object
+-------------------------------
+
+A Python web application based on WSGI has to have one central callable
+object that implements the actual application. In Flask this is an
+instance of the :class:`~flask.Flask` class. Each Flask application has
+to create an instance of this class itself and pass it the name of the
+module, but why can't Flask do that itself?
+
+Without such an explicit application object the following code::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ @app.route('/')
+ def index():
+ return 'Hello World!'
+
+Would look like this instead::
+
+ from hypothetical_flask import route
+
+ @route('/')
+ def index():
+ return 'Hello World!'
+
+There are three major reasons for this. The most important one is that
+implicit application objects require that there may only be one instance at
+the time. There are ways to fake multiple applications with a single
+application object, like maintaining a stack of applications, but this
+causes some problems I won't outline here in detail. Now the question is:
+when does a microframework need more than one application at the same
+time? A good example for this is unittesting. When you want to test
+something it can be very helpful to create a minimal application to test
+specific behavior. When the application object is deleted everything it
+allocated will be freed again.
+
+Another thing that becomes possible when you have an explicit object lying
+around in your code is that you can subclass the base class
+(:class:`~flask.Flask`) to alter specific behaviour. This would not be
+possible without hacks if the object were created ahead of time for you
+based on a class that is not exposed to you.
+
+But there is another very important reason why Flask depends on an
+explicit instantiation of that class: the package name. Whenever you
+create a Flask instance you usually pass it `__name__` as package name.
+Flask depends on that information to properly load resources relative
+to your module. With Python's outstanding support for reflection it can
+then access the package to figure out where the templates and static files
+are stored (see :meth:`~flask.Flask.open_resource`). Now obviously there
+are frameworks around that do not need any configuration and will still be
+able to load templates relative to your application module. But they have
+to use the current working directory for that, which is a very unreliable
+way to determine where the application is. The current working directory
+is process-wide and if you are running multiple applications in one
+process (which could happen in a webserver without you knowing) the paths
+will be off. Worse: many webservers do not set the working directory to
+the directory of your application but to the document root which does not
+have to be the same folder.
+
+The third reason is "explicit is better than implicit". That object is
+your WSGI application, you don't have to remember anything else. If you
+want to apply a WSGI middleware, just wrap it and you're done (though
+there are better ways to do that so that you do not lose the reference
+to the application object :meth:`~flask.Flask.wsgi_app`).
+
+Furthermore this design makes it possible to use a factory function to
+create the application which is very helpful for unittesting and similar
+things (:ref:`app-factories`).
+
+One Template Engine
+-------------------
+
+Flask decides on one template engine: Jinja2. Why doesn't Flask have a
+pluggable template engine interface? You can obviously use a different
+template engine, but Flask will still configure Jinja2 for you. While
+that limitation that Jinja2 is *always* configured will probably go away,
+the decision to bundle one template engine and use that will not.
+
+Template engines are like programming languages and each of those engines
+has a certain understanding about how things work. On the surface they
+all work the same: you tell the engine to evaluate a template with a set
+of variables and take the return value as string.
+
+But that's about where similarities end. Jinja2 for example has an
+extensive filter system, a certain way to do template inheritance, support
+for reusable blocks (macros) that can be used from inside templates and
+also from Python code, uses Unicode for all operations, supports
+iterative template rendering, configurable syntax and more. On the other
+hand an engine like Genshi is based on XML stream evaluation, template
+inheritance by taking the availability of XPath into account and more.
+Mako on the other hand treats templates similar to Python modules.
+
+When it comes to connecting a template engine with an application or
+framework there is more than just rendering templates. For instance,
+Flask uses Jinja2's extensive autoescaping support. Also it provides
+ways to access macros from Jinja2 templates.
+
+A template abstraction layer that would not take the unique features of
+the template engines away is a science on its own and a too large
+undertaking for a microframework like Flask.
+
+Furthermore extensions can then easily depend on one template language
+being present. You can easily use your own templating language, but an
+extension could still depend on Jinja itself.
+
+
+Micro with Dependencies
+-----------------------
+
+Why does Flask call itself a microframework and yet it depends on two
+libraries (namely Werkzeug and Jinja2). Why shouldn't it? If we look
+over to the Ruby side of web development there we have a protocol very
+similar to WSGI. Just that it's called Rack there, but besides that it
+looks very much like a WSGI rendition for Ruby. But nearly all
+applications in Ruby land do not work with Rack directly, but on top of a
+library with the same name. This Rack library has two equivalents in
+Python: WebOb (formerly Paste) and Werkzeug. Paste is still around but
+from my understanding it's sort of deprecated in favour of WebOb. The
+development of WebOb and Werkzeug started side by side with similar ideas
+in mind: be a good implementation of WSGI for other applications to take
+advantage.
+
+Flask is a framework that takes advantage of the work already done by
+Werkzeug to properly interface WSGI (which can be a complex task at
+times). Thanks to recent developments in the Python package
+infrastructure, packages with dependencies are no longer an issue and
+there are very few reasons against having libraries that depend on others.
+
+
+Thread Locals
+-------------
+
+Flask uses thread local objects (context local objects in fact, they
+support greenlet contexts as well) for request, session and an extra
+object you can put your own things on (:data:`~flask.g`). Why is that and
+isn't that a bad idea?
+
+Yes it is usually not such a bright idea to use thread locals. They cause
+troubles for servers that are not based on the concept of threads and make
+large applications harder to maintain. However Flask is just not designed
+for large applications or asynchronous servers. Flask wants to make it
+quick and easy to write a traditional web application.
+
+Also see the :ref:`becomingbig` section of the documentation for some
+inspiration for larger applications based on Flask.
+
+
+What Flask is, What Flask is Not
+--------------------------------
+
+Flask will never have a database layer. It will not have a form library
+or anything else in that direction. Flask itself just bridges to Werkzeug
+to implement a proper WSGI application and to Jinja2 to handle templating.
+It also binds to a few common standard library packages such as logging.
+Everything else is up for extensions.
+
+Why is this the case? Because people have different preferences and
+requirements and Flask could not meet those if it would force any of this
+into the core. The majority of web applications will need a template
+engine in some sort. However not every application needs a SQL database.
+
+The idea of Flask is to build a good foundation for all applications.
+Everything else is up to you or extensions.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/errorhandling.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,237 @@
+.. _application-errors:
+
+Handling Application Errors
+===========================
+
+.. versionadded:: 0.3
+
+Applications fail, servers fail. Sooner or later you will see an exception
+in production. Even if your code is 100% correct, you will still see
+exceptions from time to time. Why? Because everything else involved will
+fail. Here some situations where perfectly fine code can lead to server
+errors:
+
+- the client terminated the request early and the application was still
+ reading from the incoming data.
+- the database server was overloaded and could not handle the query.
+- a filesystem is full
+- a harddrive crashed
+- a backend server overloaded
+- a programming error in a library you are using
+- network connection of the server to another system failed.
+
+And that's just a small sample of issues you could be facing. So how do we
+deal with that sort of problem? By default if your application runs in
+production mode, Flask will display a very simple page for you and log the
+exception to the :attr:`~flask.Flask.logger`.
+
+But there is more you can do, and we will cover some better setups to deal
+with errors.
+
+Error Mails
+-----------
+
+If the application runs in production mode (which it will do on your
+server) you won't see any log messages by default. Why is that? Flask
+tries to be a zero-configuration framework. Where should it drop the logs
+for you if there is no configuration? Guessing is not a good idea because
+chances are, the place it guessed is not the place where the user has
+permission to create a logfile. Also, for most small applications nobody
+will look at the logs anyways.
+
+In fact, I promise you right now that if you configure a logfile for the
+application errors you will never look at it except for debugging an issue
+when a user reported it for you. What you want instead is a mail the
+second the exception happened. Then you get an alert and you can do
+something about it.
+
+Flask uses the Python builtin logging system, and it can actually send
+you mails for errors which is probably what you want. Here is how you can
+configure the Flask logger to send you mails for exceptions::
+
+ ADMINS = ['yourname@example.com']
+ if not app.debug:
+ import logging
+ from logging.handlers import SMTPHandler
+ mail_handler = SMTPHandler('127.0.0.1',
+ 'server-error@example.com',
+ ADMINS, 'YourApplication Failed')
+ mail_handler.setLevel(logging.ERROR)
+ app.logger.addHandler(mail_handler)
+
+So what just happened? We created a new
+:class:`~logging.handlers.SMTPHandler` that will send mails with the mail
+server listening on ``127.0.0.1`` to all the `ADMINS` from the address
+*server-error@example.com* with the subject "YourApplication Failed". If
+your mail server requires credentials, these can also be provided. For
+that check out the documentation for the
+:class:`~logging.handlers.SMTPHandler`.
+
+We also tell the handler to only send errors and more critical messages.
+Because we certainly don't want to get a mail for warnings or other
+useless logs that might happen during request handling.
+
+Before you run that in production, please also look at :ref:`logformat` to
+put more information into that error mail. That will save you from a lot
+of frustration.
+
+
+Logging to a File
+-----------------
+
+Even if you get mails, you probably also want to log warnings. It's a
+good idea to keep as much information around that might be required to
+debug a problem. Please note that Flask itself will not issue any
+warnings in the core system, so it's your responsibility to warn in the
+code if something seems odd.
+
+There are a couple of handlers provided by the logging system out of the
+box but not all of them are useful for basic error logging. The most
+interesting are probably the following:
+
+- :class:`~logging.FileHandler` - logs messages to a file on the
+ filesystem.
+- :class:`~logging.handlers.RotatingFileHandler` - logs messages to a file
+ on the filesystem and will rotate after a certain number of messages.
+- :class:`~logging.handlers.NTEventLogHandler` - will log to the system
+ event log of a Windows system. If you are deploying on a Windows box,
+ this is what you want to use.
+- :class:`~logging.handlers.SysLogHandler` - sends logs to a UNIX
+ syslog.
+
+Once you picked your log handler, do like you did with the SMTP handler
+above, just make sure to use a lower setting (I would recommend
+`WARNING`)::
+
+ if not app.debug:
+ import logging
+ from themodule import TheHandler YouWant
+ file_handler = TheHandlerYouWant(...)
+ file_handler.setLevel(logging.WARNING)
+ app.logger.addHandler(file_handler)
+
+.. _logformat:
+
+Controlling the Log Format
+--------------------------
+
+By default a handler will only write the message string into a file or
+send you that message as mail. A log record stores more information,
+and it makes a lot of sense to configure your logger to also contain that
+information so that you have a better idea of why that error happened, and
+more importantly, where it did.
+
+A formatter can be instantiated with a format string. Note that
+tracebacks are appended to the log entry automatically. You don't have to
+do that in the log formatter format string.
+
+Here some example setups:
+
+Email
+`````
+
+::
+
+ from logging import Formatter
+ mail_handler.setFormatter(Formatter('''
+ Message type: %(levelname)s
+ Location: %(pathname)s:%(lineno)d
+ Module: %(module)s
+ Function: %(funcName)s
+ Time: %(asctime)s
+
+ Message:
+
+ %(message)s
+ '''))
+
+File logging
+````````````
+
+::
+
+ from logging import Formatter
+ file_handler.setFormatter(Formatter(
+ '%(asctime)s %(levelname)s: %(message)s '
+ '[in %(pathname)s:%(lineno)d]'
+ ))
+
+
+Complex Log Formatting
+``````````````````````
+
+Here is a list of useful formatting variables for the format string. Note
+that this list is not complete, consult the official documentation of the
+:mod:`logging` package for a full list.
+
+.. tabularcolumns:: |p{3cm}|p{12cm}|
+
++------------------+----------------------------------------------------+
+| Format | Description |
++==================+====================================================+
+| ``%(levelname)s``| Text logging level for the message |
+| | (``'DEBUG'``, ``'INFO'``, ``'WARNING'``, |
+| | ``'ERROR'``, ``'CRITICAL'``). |
++------------------+----------------------------------------------------+
+| ``%(pathname)s`` | Full pathname of the source file where the |
+| | logging call was issued (if available). |
++------------------+----------------------------------------------------+
+| ``%(filename)s`` | Filename portion of pathname. |
++------------------+----------------------------------------------------+
+| ``%(module)s`` | Module (name portion of filename). |
++------------------+----------------------------------------------------+
+| ``%(funcName)s`` | Name of function containing the logging call. |
++------------------+----------------------------------------------------+
+| ``%(lineno)d`` | Source line number where the logging call was |
+| | issued (if available). |
++------------------+----------------------------------------------------+
+| ``%(asctime)s`` | Human-readable time when the LogRecord` was |
+| | created. By default this is of the form |
+| | ``"2003-07-08 16:49:45,896"`` (the numbers after |
+| | the comma are millisecond portion of the time). |
+| | This can be changed by subclassing the formatter |
+| | and overriding the |
+| | :meth:`~logging.Formatter.formatTime` method. |
++------------------+----------------------------------------------------+
+| ``%(message)s`` | The logged message, computed as ``msg % args`` |
++------------------+----------------------------------------------------+
+
+If you want to further customize the formatting, you can subclass the
+formatter. The formatter has three interesting methods:
+
+:meth:`~logging.Formatter.format`:
+ handles the actual formatting. It is passed a
+ :class:`~logging.LogRecord` object and has to return the formatted
+ string.
+:meth:`~logging.Formatter.formatTime`:
+ called for `asctime` formatting. If you want a different time format
+ you can override this method.
+:meth:`~logging.Formatter.formatException`
+ called for exception formatting. It is passed an :attr:`~sys.exc_info`
+ tuple and has to return a string. The default is usually fine, you
+ don't have to override it.
+
+For more information, head over to the official documentation.
+
+
+Other Libraries
+---------------
+
+So far we only configured the logger your application created itself.
+Other libraries might log themselves as well. For example, SQLAlchemy uses
+logging heavily in its core. While there is a method to configure all
+loggers at once in the :mod:`logging` package, I would not recommend using
+it. There might be a situation in which you want to have multiple
+separate applications running side by side in the same Python interpreter
+and then it becomes impossible to have different logging setups for those.
+
+Instead, I would recommend figuring out which loggers you are interested
+in, getting the loggers with the :func:`~logging.getLogger` function and
+iterating over them to attach handlers::
+
+ from logging import getLogger
+ loggers = [app.logger, getLogger('sqlalchemy'),
+ getLogger('otherlibrary')]
+ for logger in loggers:
+ logger.addHandler(mail_handler)
+ logger.addHandler(file_handler)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/extensiondev.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,327 @@
+Flask Extension Development
+===========================
+
+Flask, being a microframework, often requires some repetitive steps to get
+a third party library working. Because very often these steps could be
+abstracted to support multiple projects the `Flask Extension Registry`_
+was created.
+
+If you want to create your own Flask extension for something that does not
+exist yet, this guide to extension development will help you get your
+extension running in no time and to feel like users would expect your
+extension to behave.
+
+.. _Flask Extension Registry: http://flask.pocoo.org/extensions/
+
+Anatomy of an Extension
+-----------------------
+
+Extensions are all located in a package called ``flaskext.something``
+where "something" is the name of the library you want to bridge. So for
+example if you plan to add support for a library named `simplexml` to
+Flask, you would name your extension's package ``flaskext.simplexml``.
+
+The name of the actual extension (the human readable name) however would
+be something like "Flask-SimpleXML". Make sure to include the name
+"Flask" somewhere in that name and that you check the capitalization.
+This is how users can then register dependencies to your extension in
+their `setup.py` files.
+
+The magic that makes it possible to have your library in a package called
+``flaskext.something`` is called a "namespace package". Check out the
+guide below how to create something like that.
+
+But how do extensions look like themselves? An extension has to ensure
+that it works with multiple Flask application instances at once. This is
+a requirement because many people will use patterns like the
+:ref:`app-factories` pattern to create their application as needed to aid
+unittests and to support multiple configurations. Because of that it is
+crucial that your application supports that kind of behaviour.
+
+Most importantly the extension must be shipped with a `setup.py` file and
+registered on PyPI. Also the development checkout link should work so
+that people can easily install the development version into their
+virtualenv without having to download the library by hand.
+
+Flask extensions must be licensed as BSD or MIT or a more liberal license
+to be enlisted on the Flask Extension Registry. Keep in mind that the
+Flask Extension Registry is a moderated place and libraries will be
+reviewed upfront if they behave as required.
+
+"Hello Flaskext!"
+-----------------
+
+So let's get started with creating such a Flask extension. The extension
+we want to create here will provide very basic support for SQLite3.
+
+There is a script on github called `Flask Extension Wizard`_ which helps
+you create the initial folder structure. But for this very basic example
+we want to create all by hand to get a better feeling for it.
+
+First we create the following folder structure::
+
+ flask-sqlite3/
+ flaskext/
+ __init__.py
+ sqlite3.py
+ setup.py
+ LICENSE
+
+Here's the contents of the most important files:
+
+flaskext/__init__.py
+````````````````````
+
+The only purpose of this file is to mark the package as namespace package.
+This is required so that multiple modules from different PyPI packages can
+reside in the same Python package::
+
+ __import__('pkg_resources').declare_namespace(__name__)
+
+If you want to know exactly what is happening there, checkout the
+distribute or setuptools docs which explain how this works.
+
+Just make sure to not put anything else in there!
+
+setup.py
+````````
+
+The next file that is absolutely required is the `setup.py` file which is
+used to install your Flask extension. The following contents are
+something you can work with::
+
+ """
+ Flask-SQLite3
+ -------------
+
+ This is the description for that library
+ """
+ from setuptools import setup
+
+
+ setup(
+ name='Flask-SQLite3',
+ version='1.0',
+ url='http://example.com/flask-sqlite3/',
+ license='BSD',
+ author='Your Name',
+ author_email='your-email@example.com',
+ description='Very short description',
+ long_description=__doc__,
+ packages=['flaskext'],
+ namespace_packages=['flaskext'],
+ zip_safe=False,
+ platforms='any',
+ install_requires=[
+ 'Flask'
+ ],
+ classifiers=[
+ 'Environment :: Web Environment',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+ 'Topic :: Software Development :: Libraries :: Python Modules'
+ ]
+ )
+
+That's a lot of code but you can really just copy/paste that from existing
+extensions and adapt. This is also what the wizard creates for you if you
+use it.
+
+flaskext/sqlite3.py
+```````````````````
+
+Now this is where your extension code goes. But how exactly should such
+an extension look like? What are the best practices? Continue reading
+for some insight.
+
+
+Initializing Extensions
+-----------------------
+
+Many extensions will need some kind of initialization step. For example,
+consider your application is currently connecting to SQLite like the
+documentation suggests (:ref:`sqlite3`) you will need to provide a few
+functions and before / after request handlers. So how does the extension
+know the name of the application object?
+
+Quite simple: you pass it to it.
+
+There are two recommended ways for an extension to initialize:
+
+initialization functions:
+ If your extension is called `helloworld` you might have a function
+ called ``init_helloworld(app[, extra_args])`` that initializes the
+ extension for that application. It could attach before / after
+ handlers etc.
+
+classes:
+ Classes work mostly like initialization functions but can later be
+ used to further change the behaviour. For an example look at how the
+ `OAuth extension`_ works: there is an `OAuth` object that provides
+ some helper functions like `OAuth.remote_app` to create a reference to
+ a remote application that uses OAuth.
+
+What to use depends on what you have in mind. For the SQLite 3 extension
+we will need to use the class based approach because we have to use a
+controller object that can be used to connect to the database.
+
+The Extension Code
+------------------
+
+Here's the contents of the `flaskext/sqlite3.py` for copy/paste::
+
+ from __future__ import absolute_import
+ import sqlite3
+ from flask import g
+
+ class SQLite3(object):
+
+ def __init__(self, app):
+ self.app = app
+ self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
+
+ self.app.before_request(self.before_request)
+ self.app.after_request(self.after_request)
+
+ def connect(self):
+ return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
+
+ def before_request(self):
+ g.sqlite3_db = self.connect()
+
+ def after_request(self, response):
+ g.sqlite3_db.close()
+ return response
+
+So here's what the lines of code do:
+
+1. the ``__future__`` import is necessary to activate absolute imports.
+ This is needed because otherwise we could not call our module
+ `sqlite3.py` and import the top-level `sqlite3` module which actually
+ implements the connection to SQLite.
+2. We create a class for our extension that sets a default configuration
+ for the SQLite 3 database if it's not there (:meth:`dict.setdefault`)
+ and connects two functions as before and after request handlers.
+3. Then it implements a `connect` function that returns a new database
+ connection and the two handlers.
+
+So why did we decide on a class based approach here? Because using that
+extension looks something like this::
+
+ from flask import Flask, g
+ from flaskext.sqlite3 import SQLite3
+
+ app = Flask(__name__)
+ app.config.from_pyfile('the-config.cfg')
+ db = SQLite(app)
+
+Either way you can use the database from the views like this::
+
+ @app.route('/')
+ def show_all():
+ cur = g.sqlite3_db.cursor()
+ cur.execute(...)
+
+But how would you open a database connection from outside a view function?
+This is where the `db` object now comes into play:
+
+>>> from yourapplication import db
+>>> con = db.connect()
+>>> cur = con.cursor()
+
+If you don't need that, you can go with initialization functions.
+
+Initialization Functions
+------------------------
+
+Here's what the module would look like with initialization functions::
+
+ from __future__ import absolute_import
+ import sqlite3
+ from flask import g
+
+ def init_sqlite3(app):
+ app = app
+ app.config.setdefault('SQLITE3_DATABASE', ':memory:')
+
+ @app.before_request
+ def before_request():
+ g.sqlite3_db = sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
+
+ @app.after_request
+ def after_request(response):
+ g.sqlite3_db.close()
+ return response
+
+Learn from Others
+-----------------
+
+This documentation only touches the bare minimum for extension
+development. If you want to learn more, it's a very good idea to check
+out existing extensions on the `Flask Extension Registry`_. If you feel
+lost there is still the `mailinglist`_ and the `IRC channel`_ to get some
+ideas for nice looking APIs. Especially if you do something nobody before
+you did, it might be a very good idea to get some more input. This not
+only to get an idea about what people might want to have from an
+extension, but also to avoid having multiple developers working on pretty
+much the same side by side.
+
+Remember: good API design is hard, so introduce your project on the
+mailinglist, and let other developers give you a helping hand with
+designing the API.
+
+The best Flask extensions are extensions that share common idioms for the
+API. And this can only work if collaboration happens early.
+
+
+Approved Extensions
+-------------------
+
+Flask also has the concept of approved extensions. Approved extensions
+are tested as part of Flask itself to ensure extensions do not break on
+new releases. These approved extensions are listed on the `Flask
+Extension Registry`_ and marked appropriately. If you want your own
+extension to be approved you have to follow these guidelines:
+
+1. An approved Flask extension must provide exactly one package or module
+ inside the `flaskext` namespace package.
+2. It must ship a testsuite that can either be invoked with ``make test``
+ or ``python setup.py test``. For testsuites invoked with ``make
+ test`` the extension has to ensure that all dependencies for the test
+ are installed automatically, in case of ``python setup.py test``
+ dependencies for tests alone can be specified in the `setup.py`
+ file. The testsuite also has to be part of the distribution.
+3. APIs of approved extensions will be checked for the following
+ characteristics:
+
+ - an approved extension has to support multiple applications
+ running in the same Python process.
+ - it must be possible to use the factory pattern for creating
+ applications.
+
+4. The license must be BSD/MIT/WTFPL licensed.
+5. The naming scheme for official extensions is *Flask-ExtensionName* or
+ *ExtensionName-Flask*.
+6. Approved extensions must define all their dependencies in the
+ `setup.py` file unless a dependency cannot be met because it is not
+ available on PyPI.
+7. The extension must have documentation that uses one of the two Flask
+ themes for Sphinx documentation.
+8. The setup.py description (and thus the PyPI description) has to
+ link to the documentation, website (if there is one) and there
+ must be a link to automatically install the development version
+ (``PackageName==dev``).
+9. The ``zip_safe`` flag in the setup script must be set to ``False``,
+ even if the extension would be safe for zipping.
+10. An extension currently has to support Python 2.5, 2.6 as well as
+ Python 2.7
+
+
+.. _Flask Extension Wizard:
+ http://github.com/mitsuhiko/flask-extension-wizard
+.. _OAuth extension: http://packages.python.org/Flask-OAuth/
+.. _mailinglist: http://flask.pocoo.org/mailinglist/
+.. _IRC channel: http://flask.pocoo.org/community/irc/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/flaskext.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,86 @@
+# flasky extensions. flasky pygments style based on tango style
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
+
+
+class FlaskyStyle(Style):
+ background_color = "#f8f8f8"
+ default_style = ""
+
+ styles = {
+ # No corresponding class for the following:
+ #Text: "", # class: ''
+ Whitespace: "underline #f8f8f8", # class: 'w'
+ Error: "#a40000 border:#ef2929", # class: 'err'
+ Other: "#000000", # class 'x'
+
+ Comment: "italic #8f5902", # class: 'c'
+ Comment.Preproc: "noitalic", # class: 'cp'
+
+ Keyword: "bold #004461", # class: 'k'
+ Keyword.Constant: "bold #004461", # class: 'kc'
+ Keyword.Declaration: "bold #004461", # class: 'kd'
+ Keyword.Namespace: "bold #004461", # class: 'kn'
+ Keyword.Pseudo: "bold #004461", # class: 'kp'
+ Keyword.Reserved: "bold #004461", # class: 'kr'
+ Keyword.Type: "bold #004461", # class: 'kt'
+
+ Operator: "#582800", # class: 'o'
+ Operator.Word: "bold #004461", # class: 'ow' - like keywords
+
+ Punctuation: "bold #000000", # class: 'p'
+
+ # because special names such as Name.Class, Name.Function, etc.
+ # are not recognized as such later in the parsing, we choose them
+ # to look the same as ordinary variables.
+ Name: "#000000", # class: 'n'
+ Name.Attribute: "#c4a000", # class: 'na' - to be revised
+ Name.Builtin: "#004461", # class: 'nb'
+ Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
+ Name.Class: "#000000", # class: 'nc' - to be revised
+ Name.Constant: "#000000", # class: 'no' - to be revised
+ Name.Decorator: "#888", # class: 'nd' - to be revised
+ Name.Entity: "#ce5c00", # class: 'ni'
+ Name.Exception: "bold #cc0000", # class: 'ne'
+ Name.Function: "#000000", # class: 'nf'
+ Name.Property: "#000000", # class: 'py'
+ Name.Label: "#f57900", # class: 'nl'
+ Name.Namespace: "#000000", # class: 'nn' - to be revised
+ Name.Other: "#000000", # class: 'nx'
+ Name.Tag: "bold #004461", # class: 'nt' - like a keyword
+ Name.Variable: "#000000", # class: 'nv' - to be revised
+ Name.Variable.Class: "#000000", # class: 'vc' - to be revised
+ Name.Variable.Global: "#000000", # class: 'vg' - to be revised
+ Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
+
+ Number: "#990000", # class: 'm'
+
+ Literal: "#000000", # class: 'l'
+ Literal.Date: "#000000", # class: 'ld'
+
+ String: "#4e9a06", # class: 's'
+ String.Backtick: "#4e9a06", # class: 'sb'
+ String.Char: "#4e9a06", # class: 'sc'
+ String.Doc: "italic #8f5902", # class: 'sd' - like a comment
+ String.Double: "#4e9a06", # class: 's2'
+ String.Escape: "#4e9a06", # class: 'se'
+ String.Heredoc: "#4e9a06", # class: 'sh'
+ String.Interpol: "#4e9a06", # class: 'si'
+ String.Other: "#4e9a06", # class: 'sx'
+ String.Regex: "#4e9a06", # class: 'sr'
+ String.Single: "#4e9a06", # class: 's1'
+ String.Symbol: "#4e9a06", # class: 'ss'
+
+ Generic: "#000000", # class: 'g'
+ Generic.Deleted: "#a40000", # class: 'gd'
+ Generic.Emph: "italic #000000", # class: 'ge'
+ Generic.Error: "#ef2929", # class: 'gr'
+ Generic.Heading: "bold #000080", # class: 'gh'
+ Generic.Inserted: "#00A000", # class: 'gi'
+ Generic.Output: "#888", # class: 'go'
+ Generic.Prompt: "#745334", # class: 'gp'
+ Generic.Strong: "bold #000000", # class: 'gs'
+ Generic.Subheading: "bold #800080", # class: 'gu'
+ Generic.Traceback: "bold #a40000", # class: 'gt'
+ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/flaskstyle.sty Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,118 @@
+\definecolor{TitleColor}{rgb}{0,0,0}
+\definecolor{InnerLinkColor}{rgb}{0,0,0}
+
+\renewcommand{\maketitle}{%
+ \begin{titlepage}%
+ \let\footnotesize\small
+ \let\footnoterule\relax
+ \ifsphinxpdfoutput
+ \begingroup
+ % This \def is required to deal with multi-line authors; it
+ % changes \\ to ', ' (comma-space), making it pass muster for
+ % generating document info in the PDF file.
+ \def\\{, }
+ \pdfinfo{
+ /Author (\@author)
+ /Title (\@title)
+ }
+ \endgroup
+ \fi
+ \begin{flushright}%
+ %\sphinxlogo%
+ {\center
+ \vspace*{3cm}
+ \includegraphics{logo.pdf}
+ \vspace{3cm}
+ \par
+ {\rm\Huge \@title \par}%
+ {\em\LARGE \py@release\releaseinfo \par}
+ {\large
+ \@date \par
+ \py@authoraddress \par
+ }}%
+ \end{flushright}%\par
+ \@thanks
+ \end{titlepage}%
+ \cleardoublepage%
+ \setcounter{footnote}{0}%
+ \let\thanks\relax\let\maketitle\relax
+ %\gdef\@thanks{}\gdef\@author{}\gdef\@title{}
+}
+
+\fancypagestyle{normal}{
+ \fancyhf{}
+ \fancyfoot[LE,RO]{{\thepage}}
+ \fancyfoot[LO]{{\nouppercase{\rightmark}}}
+ \fancyfoot[RE]{{\nouppercase{\leftmark}}}
+ \fancyhead[LE,RO]{{ \@title, \py@release}}
+ \renewcommand{\headrulewidth}{0.4pt}
+ \renewcommand{\footrulewidth}{0.4pt}
+}
+
+\fancypagestyle{plain}{
+ \fancyhf{}
+ \fancyfoot[LE,RO]{{\thepage}}
+ \renewcommand{\headrulewidth}{0pt}
+ \renewcommand{\footrulewidth}{0.4pt}
+}
+
+\titleformat{\section}{\Large}%
+ {\py@TitleColor\thesection}{0.5em}{\py@TitleColor}{\py@NormalColor}
+\titleformat{\subsection}{\large}%
+ {\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
+\titleformat{\subsubsection}{}%
+ {\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
+\titleformat{\paragraph}{\large}%
+ {\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor}
+
+\ChNameVar{\raggedleft\normalsize}
+\ChNumVar{\raggedleft \bfseries\Large}
+\ChTitleVar{\raggedleft \rm\Huge}
+
+\renewcommand\thepart{\@Roman\c@part}
+\renewcommand\part{%
+ \pagestyle{empty}
+ \if@noskipsec \leavevmode \fi
+ \cleardoublepage
+ \vspace*{6cm}%
+ \@afterindentfalse
+ \secdef\@part\@spart}
+
+\def\@part[#1]#2{%
+ \ifnum \c@secnumdepth >\m@ne
+ \refstepcounter{part}%
+ \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
+ \else
+ \addcontentsline{toc}{part}{#1}%
+ \fi
+ {\parindent \z@ %\center
+ \interlinepenalty \@M
+ \normalfont
+ \ifnum \c@secnumdepth >\m@ne
+ \rm\Large \partname~\thepart
+ \par\nobreak
+ \fi
+ \MakeUppercase{\rm\Huge #2}%
+ \markboth{}{}\par}%
+ \nobreak
+ \vskip 8ex
+ \@afterheading}
+\def\@spart#1{%
+ {\parindent \z@ %\center
+ \interlinepenalty \@M
+ \normalfont
+ \huge \bfseries #1\par}%
+ \nobreak
+ \vskip 3ex
+ \@afterheading}
+
+% use inconsolata font
+\usepackage{inconsolata}
+
+% fix single quotes, for inconsolata. (does not work)
+%%\usepackage{textcomp}
+%%\begingroup
+%% \catcode`'=\active
+%% \g@addto@macro\@noligs{\let'\textsinglequote}
+%% \endgroup
+%%\endinput
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/foreword.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,109 @@
+Foreword
+========
+
+Read this before you get started with Flask. This hopefully answers some
+questions about the purpose and goals of the project, and when you
+should or should not be using it.
+
+What does "micro" mean?
+-----------------------
+
+To me, the "micro" in microframework refers not only to the simplicity and
+small size of the framework, but also to the typically limited complexity
+and size of applications that are written with the framework. Also the
+fact that you can have an entire application in a single Python file. To
+be approachable and concise, a microframework sacrifices a few features
+that may be necessary in larger or more complex applications.
+
+For example, Flask uses thread-local objects internally so that you don't
+have to pass objects around from function to function within a request in
+order to stay threadsafe. While this is a really easy approach and saves
+you a lot of time, it might also cause some troubles for very large
+applications because changes on these thread-local objects can happen
+anywhere in the same thread.
+
+Flask provides some tools to deal with the downsides of this approach but
+it might be an issue for larger applications because in theory
+modifications on these objects might happen anywhere in the same thread.
+
+Flask is also based on convention over configuration, which means that
+many things are preconfigured. For example, by convention, templates and
+static files are in subdirectories within the Python source tree of the
+application.
+
+The main reason however why Flask is called a "microframework" is the idea
+to keep the core simple but extensible. There is no database abstraction
+layer, no form validation or anything else where different libraries
+already exist that can handle that. However Flask knows the concept of
+extensions that can add this functionality into your application as if it
+was implemented in Flask itself. There are currently extensions for
+object relational mappers, form validation, upload handling, various open
+authentication technologies and more.
+
+However Flask is not much code and it is built on a very solid foundation
+and with that it is very easy to adapt for large applications. If you are
+interested in that, check out the :ref:`becomingbig` chapter.
+
+If you are curious about the Flask design principles, head over to the
+section about :ref:`design`.
+
+A Framework and an Example
+--------------------------
+
+Flask is not only a microframework; it is also an example. Based on
+Flask, there will be a series of blog posts that explain how to create a
+framework. Flask itself is just one way to implement a framework on top
+of existing libraries. Unlike many other microframeworks, Flask does not
+try to implement everything on its own; it reuses existing code.
+
+Web Development is Dangerous
+----------------------------
+
+I'm not joking. Well, maybe a little. If you write a web
+application, you are probably allowing users to register and leave their
+data on your server. The users are entrusting you with data. And even if
+you are the only user that might leave data in your application, you still
+want that data to be stored securely.
+
+Unfortunately, there are many ways the security of a web application can be
+compromised. Flask protects you against one of the most common security
+problems of modern web applications: cross-site scripting (XSS). Unless
+you deliberately mark insecure HTML as secure, Flask and the underlying
+Jinja2 template engine have you covered. But there are many more ways to
+cause security problems.
+
+The documentation will warn you about aspects of web development that
+require attention to security. Some of these security concerns
+are far more complex than one might think, and we all sometimes underestimate
+the likelihood that a vulnerability will be exploited, until a clever
+attacker figures out a way to exploit our applications. And don't think
+that your application is not important enough to attract an attacker.
+Depending on the kind of attack, chances are that automated bots are
+probing for ways to fill your database with spam, links to malicious
+software, and the like.
+
+So always keep security in mind when doing web development.
+
+The Status of Python 3
+----------------------
+
+Currently the Python community is in the process of improving libraries to
+support the new iteration of the Python programming language.
+Unfortunately there are a few problems with Python 3, namely the missing
+consent on what WSGI for Python 3 should look like. These problems are
+partially caused by changes in the language that went unreviewed for too
+long, also partially the ambitions of everyone involved to drive the WSGI
+standard forward.
+
+Because of that we strongly recommend against using Python 3 for web
+development of any kind and wait until the WSGI situation is resolved.
+You will find a couple of frameworks and web libraries on PyPI that claim
+Python 3 support, but this support is based on the broken WSGI
+implementation provided by Python 3.0 and 3.1 which will most likely
+change in the near future.
+
+Werkzeug and Flask will be ported to Python 3 as soon as a solution for
+WSGI is found, and we will provide helpful tips how to upgrade existing
+applications to Python 3. Until then, we strongly recommend using Python
+2.6 and 2.7 with activated Python 3 warnings during development, as well
+as the Unicode literals `__future__` feature.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/htmlfaq.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,207 @@
+HTML/XHTML FAQ
+==============
+
+The Flask documentation and example applications are using HTML5. You
+may notice that in many situations, when end tags are optional they are
+not used, so that the HTML is cleaner and faster to load. Because there
+is much confusion about HTML and XHTML among developers, this document tries
+to answer some of the major questions.
+
+
+History of XHTML
+----------------
+
+For a while, it appeared that HTML was about to be replaced by XHTML.
+However, barely any websites on the Internet are actual XHTML (which is
+HTML processed using XML rules). There are a couple of major reasons
+why this is the case. One of them is Internet Explorer's lack of proper
+XHTML support. The XHTML spec states that XHTML must be served with the MIME
+type `application/xhtml+xml`, but Internet Explorer refuses to read files
+with that MIME type.
+While it is relatively easy to configure Web servers to serve XHTML properly,
+few people do. This is likely because properly using XHTML can be quite
+painful.
+
+One of the most important causes of pain is XML's draconian (strict and
+ruthless) error handling. When an XML parsing error is encountered,
+the browser is supposed to show the user an ugly error message, instead
+of attempting to recover from the error and display what it can. Most of
+the (X)HTML generation on the web is based on non-XML template engines
+(such as Jinja, the one used in Flask) which do not protect you from
+accidentally creating invalid XHTML. There are XML based template engines,
+such as Kid and the popular Genshi, but they often come with a larger
+runtime overhead and, are not as straightforward to use because they have
+to obey XML rules.
+
+The majority of users, however, assumed they were properly using XHTML.
+They wrote an XHTML doctype at the top of the document and self-closed all
+the necessary tags (``<br>`` becomes ``<br/>`` or ``<br></br>`` in XHTML).
+However, even if the document properly validates as XHTML, what really
+determines XHTML/HTML processing in browsers is the MIME type, which as
+said before is often not set properly. So the valid XHTML was being treated
+as invalid HTML.
+
+XHTML also changed the way JavaScript is used. To properly work with XHTML,
+programmers have to use the namespaced DOM interface with the XHTML
+namespace to query for HTML elements.
+
+History of HTML5
+----------------
+
+Development of the HTML5 specification was started in 2004 under the name
+"Web Applications 1.0" by the Web Hypertext Application Technology Working
+Group, or WHATWG (which was formed by the major browser vendors Apple,
+Mozilla, and Opera) with the goal of writing a new and improved HTML
+specification, based on existing browser behaviour instead of unrealistic
+and backwards-incompatible specifications.
+
+For example, in HTML4 ``<title/Hello/`` theoretically parses exactly the
+same as ``<title>Hello</title>``. However, since people were using
+XHTML-like tags along the lines of ``<link />``, browser vendors implemented
+the XHTML syntax over the syntax defined by the specification.
+
+In 2007, the specification was adopted as the basis of a new HTML
+specification under the umbrella of the W3C, known as HTML5. Currently,
+it appears that XHTML is losing traction, as the XHTML 2 working group has
+been disbanded and HTML5 is being implemented by all major browser vendors.
+
+HTML versus XHTML
+-----------------
+
+The following table gives you a quick overview of features available in
+HTML 4.01, XHTML 1.1 and HTML5. (XHTML 1.0 is not included, as it was
+superseded by XHTML 1.1 and the barely-used XHTML5.)
+
+.. tabularcolumns:: |p{9cm}|p{2cm}|p{2cm}|p{2cm}|
+
++-----------------------------------------+----------+----------+----------+
+| | HTML4.01 | XHTML1.1 | HTML5 |
++=========================================+==========+==========+==========+
+| ``<tag/value/`` == ``<tag>value</tag>`` | |Y| [1]_ | |N| | |N| |
++-----------------------------------------+----------+----------+----------+
+| ``<br/>`` supported | |N| | |Y| | |Y| [2]_ |
++-----------------------------------------+----------+----------+----------+
+| ``<script/>`` supported | |N| | |Y| | |N| |
++-----------------------------------------+----------+----------+----------+
+| should be served as `text/html` | |Y| | |N| [3]_ | |Y| |
++-----------------------------------------+----------+----------+----------+
+| should be served as | |N| | |Y| | |N| |
+| `application/xhtml+xml` | | | |
++-----------------------------------------+----------+----------+----------+
+| strict error handling | |N| | |Y| | |N| |
++-----------------------------------------+----------+----------+----------+
+| inline SVG | |N| | |Y| | |Y| |
++-----------------------------------------+----------+----------+----------+
+| inline MathML | |N| | |Y| | |Y| |
++-----------------------------------------+----------+----------+----------+
+| ``<video>`` tag | |N| | |N| | |Y| |
++-----------------------------------------+----------+----------+----------+
+| ``<audio>`` tag | |N| | |N| | |Y| |
++-----------------------------------------+----------+----------+----------+
+| New semantic tags like ``<article>`` | |N| | |N| | |Y| |
++-----------------------------------------+----------+----------+----------+
+
+.. [1] This is an obscure feature inherited from SGML. It is usually not
+ supported by browsers, for reasons detailed above.
+.. [2] This is for compatibility with server code that generates XHTML for
+ tags such as ``<br>``. It should not be used in new code.
+.. [3] XHTML 1.0 is the last XHTML standard that allows to be served
+ as `text/html` for backwards compatibility reasons.
+
+.. |Y| image:: _static/yes.png
+ :alt: Yes
+.. |N| image:: _static/no.png
+ :alt: No
+
+What does "strict" mean?
+------------------------
+
+HTML5 has strictly defined parsing rules, but it also specifies exactly
+how a browser should react to parsing errors - unlike XHTML, which simply
+states parsing should abort. Some people are confused by apparently
+invalid syntax that still generates the expected results (for example,
+missing end tags or unquoted attribute values).
+
+Some of these work because of the lenient error handling most browsers use
+when they encounter a markup error, others are actually specified. The
+following constructs are optional in HTML5 by standard, but have to be
+supported by browsers:
+
+- Wrapping the document in an ``<html>`` tag
+- Wrapping header elements in ``<head>`` or the body elements in
+ ``<body>``
+- Closing the ``<p>``, ``<li>``, ``<dt>``, ``<dd>``, ``<tr>``,
+ ``<td>``, ``<th>``, ``<tbody>``, ``<thead>``, or ``<tfoot>`` tags.
+- Quoting attributes, so long as they contain no whitespace or
+ special characters (like ``<``, ``>``, ``'``, or ``"``).
+- Requiring boolean attributes to have a value.
+
+This means the following page in HTML5 is perfectly valid:
+
+.. sourcecode:: html
+
+ <!doctype html>
+ <title>Hello HTML5</title>
+ <div class=header>
+ <h1>Hello HTML5</h1>
+ <p class=tagline>HTML5 is awesome
+ </div>
+ <ul class=nav>
+ <li><a href=/index>Index</a>
+ <li><a href=/downloads>Downloads</a>
+ <li><a href=/about>About</a>
+ </ul>
+ <div class=body>
+ <h2>HTML5 is probably the future</h2>
+ <p>
+ There might be some other things around but in terms of
+ browser vendor support, HTML5 is hard to beat.
+ <dl>
+ <dt>Key 1
+ <dd>Value 1
+ <dt>Key 2
+ <dd>Value 2
+ </dl>
+ </div>
+
+
+New technologies in HTML5
+-------------------------
+
+HTML5 adds many new features that make Web applications easier to write
+and to use.
+
+- The ``<audio>`` and ``<video>`` tags provide a way to embed audio and
+ video without complicated add-ons like QuickTime or Flash.
+- Semantic elements like ``<article>``, ``<header>``, ``<nav>``, and
+ ``<time>`` that make content easier to understand.
+- The ``<canvas>`` tag, which supports a powerful drawing API, reducing
+ the need for server-generated images to present data graphically.
+- New form control types like ``<input type="date">`` that allow user
+ agents to make entering and validating values easier.
+- Advanced JavaScript APIs like Web Storage, Web Workers, Web Sockets,
+ geolocation, and offline applications.
+
+Many other features have been added, as well. A good guide to new features
+in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_.
+Not all of them are supported in browsers yet, however, so use caution.
+
+.. _Dive Into HTML5: http://www.diveintohtml5.org/
+
+What should be used?
+--------------------
+
+Currently, the answer is HTML5. There are very few reasons to use XHTML
+considering the latest developments in Web browsers. To summarize the
+reasons given above:
+
+- Internet Explorer (which, sadly, currently leads in market share)
+ has poor support for XHTML.
+- Many JavaScript libraries also do not support XHTML, due to the more
+ complicated namespacing API it requires.
+- HTML5 adds several new features, including semantic tags and the
+ long-awaited ``<audio>`` and ``<video>`` tags.
+- It has the support of most browser vendors behind it.
+- It is much easier to write, and more compact.
+
+For most applications, it is undoubtedly better to use HTML5 than XHTML.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/index.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,30 @@
+:orphan:
+
+Welcome to Flask
+================
+
+.. image:: _static/logo-full.png
+ :alt: Flask: web development, one drop at a time
+ :class: floatingflask
+
+Welcome to Flask's documentation. This documentation is divided into
+different parts. I recommend that you get started with
+:ref:`installation` and then head over to the :ref:`quickstart`.
+Besides the quickstart there is also a more detailed :ref:`tutorial` that
+shows how to create a complete (albeit small) application with Flask. If
+you'd rather dive into the internals of Flask, check out
+the :ref:`api` documentation. Common patterns are described in the
+:ref:`patterns` section.
+
+Flask depends on two external libraries: the `Jinja2`_ template
+engine and the `Werkzeug`_ WSGI toolkit. These libraries are not documented
+here. If you want to dive into their documentation check out the
+following links:
+
+- `Jinja2 Documentation <http://jinja.pocoo.org/2/documentation/>`_
+- `Werkzeug Documentation <http://werkzeug.pocoo.org/documentation/>`_
+
+.. _Jinja2: http://jinja.pocoo.org/2/
+.. _Werkzeug: http://werkzeug.pocoo.org/
+
+.. include:: contents.rst.inc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/installation.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,175 @@
+.. _installation:
+
+Installation
+============
+
+Flask depends on two external libraries, `Werkzeug
+<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/2/>`_.
+Werkzeug is a toolkit for WSGI, the standard Python interface between web
+applications and a variety of servers for both development and deployment.
+Jinja2 renders templates.
+
+So how do you get all that on your computer quickly? There are many ways
+which this section will explain, but the most kick-ass method is
+virtualenv, so let's look at that first.
+
+Either way, you will need Python 2.5 or higher to get started, so be sure
+to have an up to date Python 2.x installation. At the time of writing,
+the WSGI specification is not yet finalized for Python 3, so Flask cannot
+support the 3.x series of Python.
+
+.. _virtualenv:
+
+virtualenv
+----------
+
+Virtualenv is probably what you want to use during development, and in
+production too if you have shell access there.
+
+What problem does virtualenv solve? If you like Python as I do,
+chances are you want to use it for other projects besides Flask-based
+web applications. But the more projects you have, the more likely it is
+that you will be working with different versions of Python itself, or at
+least different versions of Python libraries. Let's face it; quite often
+libraries break backwards compatibility, and it's unlikely that any serious
+application will have zero dependencies. So what do you do if two or more
+of your projects have conflicting dependencies?
+
+Virtualenv to the rescue! It basically enables multiple side-by-side
+installations of Python, one for each project. It doesn't actually
+install separate copies of Python, but it does provide a clever way
+to keep different project environments isolated.
+
+So let's see how virtualenv works!
+
+If you are on Mac OS X or Linux, chances are that one of the following two
+commands will work for you::
+
+ $ sudo easy_install virtualenv
+
+or even better::
+
+ $ sudo pip install virtualenv
+
+One of these will probably install virtualenv on your system. Maybe it's
+even in your package manager. If you use Ubuntu, try::
+
+ $ sudo apt-get install python-virtualenv
+
+If you are on Windows and don't have the `easy_install` command, you must
+install it first. Check the :ref:`windows-easy-install` section for more
+information about how to do that. Once you have it installed, run the
+same commands as above, but without the `sudo` prefix.
+
+Once you have virtualenv installed, just fire up a shell and create
+your own environment. I usually create a project folder and an `env`
+folder within::
+
+ $ mkdir myproject
+ $ cd myproject
+ $ virtualenv env
+ New python executable in env/bin/python
+ Installing setuptools............done.
+
+Now, whenever you want to work on a project, you only have to activate
+the corresponding environment. On OS X and Linux, do the following::
+
+ $ . env/bin/activate
+
+(Note the space between the dot and the script name. The dot means that
+this script should run in the context of the current shell. If this command
+does not work in your shell, try replacing the dot with ``source``)
+
+If you are a Windows user, the following command is for you::
+
+ $ env\scripts\activate
+
+Either way, you should now be using your virtualenv (see how the prompt of
+your shell has changed to show the virtualenv).
+
+Now you can just enter the following command to get Flask activated in
+your virtualenv::
+
+ $ easy_install Flask
+
+A few seconds later you are good to go.
+
+
+System Wide Installation
+------------------------
+
+This is possible as well, but I do not recommend it. Just run
+`easy_install` with root rights::
+
+ $ sudo easy_install Flask
+
+(Run it in an Admin shell on Windows systems and without `sudo`).
+
+
+Living on the Edge
+------------------
+
+If you want to work with the latest version of Flask, there are two ways: you
+can either let `easy_install` pull in the development version, or tell it
+to operate on a git checkout. Either way, virtualenv is recommended.
+
+Get the git checkout in a new virtualenv and run in development mode::
+
+ $ git clone http://github.com/mitsuhiko/flask.git
+ Initialized empty Git repository in ~/dev/flask/.git/
+ $ cd flask
+ $ virtualenv env
+ $ . env/bin/activate
+ New python executable in env/bin/python
+ Installing setuptools............done.
+ $ python setup.py develop
+ ...
+ Finished processing dependencies for Flask
+
+This will pull in the dependencies and activate the git head as the current
+version inside the virtualenv. Then you just have to ``git pull origin``
+to get the latest version.
+
+To just get the development version without git, do this instead::
+
+ $ mkdir flask
+ $ cd flask
+ $ virtualenv env
+ $ . env/bin/activate
+ New python executable in env/bin/python
+ Installing setuptools............done.
+ $ easy_install Flask==dev
+ ...
+ Finished processing dependencies for Flask==dev
+
+.. _windows-easy-install:
+
+`easy_install` on Windows
+-------------------------
+
+On Windows, installation of `easy_install` is a little bit trickier because
+slightly different rules apply on Windows than on Unix-like systems, but
+it's not difficult. The easiest way to do it is to download the
+`ez_setup.py`_ file and run it. The easiest way to run the file is to
+open your downloads folder and double-click on the file.
+
+Next, add the `easy_install` command and other Python scripts to the
+command search path, by adding your Python installation's Scripts folder
+to the `PATH` environment variable. To do that, right-click on the
+"Computer" icon on the Desktop or in the Start menu, and choose
+"Properties". Then, on Windows Vista and Windows 7 click on "Advanced System
+settings"; on Windows XP, click on the "Advanced" tab instead. Then click
+on the "Environment variables" button and double click on the "Path"
+variable in the "System variables" section. There append the path of your
+Python interpreter's Scripts folder; make sure you delimit it from
+existing values with a semicolon. Assuming you are using Python 2.6 on
+the default path, add the following value::
+
+ ;C:\Python26\Scripts
+
+Then you are done. To check that it worked, open the Command Prompt and
+execute ``easy_install``. If you have User Account Control enabled on
+Windows Vista or Windows 7, it should prompt you for admin privileges.
+
+
+.. _ez_setup.py: http://peak.telecommunity.com/dist/ez_setup.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/latexindex.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,6 @@
+:orphan:
+
+Flask Documentation
+===================
+
+.. include:: contents.rst.inc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/license.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,48 @@
+License
+=======
+
+Flask is licensed under a three clause BSD License. It basically means:
+do whatever you want with it as long as the copyright in Flask sticks
+around, the conditions are not modified and the disclaimer is present.
+Furthermore you must not use the names of the authors to promote derivatives
+of the software without written consent.
+
+The full license text can be found below (:ref:`flask-license`). For the
+documentation and artwork different licenses apply.
+
+.. _authors:
+
+Authors
+-------
+
+.. include:: ../AUTHORS
+
+General License Definitions
+---------------------------
+
+The following section contains the full license texts for Flask and the
+documentation.
+
+- "AUTHORS" hereby refers to all the authors listed in the
+ :ref:`authors` section.
+
+- The ":ref:`flask-license`" applies to all the sourcecode shipped as
+ part of Flask (Flask itself as well as the examples and the unittests)
+ as well as documentation.
+
+- The ":ref:`artwork-license`" applies to the project's Horn-Logo.
+
+.. _flask-license:
+
+Flask License
+-------------
+
+.. include:: ../LICENSE
+
+
+.. _artwork-license:
+
+Flask Artwork License
+---------------------
+
+.. include:: ../artwork/LICENSE
Binary file bundled/flask/docs/logo.pdf has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/make.bat Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,139 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Flask.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Flask.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% _build/devhelp
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/appfactories.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,75 @@
+.. _app-factories:
+
+Application Factories
+=====================
+
+If you are already using packages and modules for your application
+(:ref:`packages`) there are a couple of really nice ways to further improve
+the experience. A common pattern is creating the application object when
+the module is imported. But if you move the creation of this object,
+into a function, you can then create multiple instances of this and later.
+
+So why would you want to do this?
+
+1. Testing. You can have instances of the application with different
+ settings to test every case.
+2. Multiple instances. Imagine you want to run different versions of the
+ same application. Of course you could have multiple instances with
+ different configs set up in your webserver, but if you use factories,
+ you can have multiple instances of the same application running in the
+ same application process which can be handy.
+
+So how would you then actually implement that?
+
+Basic Factories
+---------------
+
+The idea is to set up the application in a function. Like this::
+
+ def create_app(config_filename):
+ app = Flask(__name__)
+ app.config.from_pyfile(config_filename)
+
+ from yourapplication.views.admin import admin
+ from yourapplication.views.frontend import frontend
+ app.register_module(admin)
+ app.register_module(frontend)
+
+ return app
+
+The downside is that you cannot use the application object in the modules
+at import time. You can however use it from within a request. How do you
+get access the application with the config? Use
+:data:`~flask.current_app`::
+
+ from flask import current_app, Module, render_template
+ admin = Module(__name__, url_prefix='/admin')
+
+ @admin.route('/')
+ def index():
+ return render_template(current_app.config['INDEX_TEMPLATE'])
+
+Here we look up the name of a template in the config.
+
+Using Applications
+------------------
+
+So to use such an application you then have to create the application
+first. Here an example `run.py` file that runs such an application::
+
+ from yourapplication import create_app
+ app = create_app('/path/to/config.cfg')
+ app.run()
+
+Factory Improvements
+--------------------
+
+The factory function from above is not very clever so far, you can improve
+it. The following changes are straightforward and possible:
+
+1. make it possible to pass in configuration values for unittests so that
+ you don't have to create config files on the filesystem
+2. call a function from a module when the application is setting up so
+ that you have a place to modify attributes of the application (like
+ hooking in before / after request handlers etc.)
+3. Add in WSGI middlewares when the application is creating if necessary.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/caching.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,69 @@
+.. _caching-pattern:
+
+Caching
+=======
+
+When your application runs slow, throw some caches in. Well, at least
+it's the easiest way to speed up things. What does a cache do? Say you
+have a function that takes some time to complete but the results would
+still be good enough if they were 5 minutes old. So then the idea is that
+you actually put the result of that calculation into a cache for some
+time.
+
+Flask itself does not provide caching for you, but Werkzeug, one of the
+libraries it is based on, has some very basic cache support. It supports
+multiple cache backends, normally you want to use a memcached server.
+
+Setting up a Cache
+------------------
+
+You create a cache object once and keep it around, similar to how
+:class:`~flask.Flask` objects are created. If you are using the
+development server you can create a
+:class:`~werkzeug.contrib.cache.SimpleCache` object, that one is a simple
+cache that keeps the item stored in the memory of the Python interpreter::
+
+ from werkzeug.contrib.cache import SimpleCache
+ cache = SimpleCache()
+
+If you want to use memcached, make sure to have one of the memcache modules
+supported (you get them from `PyPI <http://pypi.python.org/>`_) and a
+memcached server running somewhere. This is how you connect to such an
+memcached server then::
+
+ from werkzeug.contrib.cache import MemcachedCache
+ cache = MemcachedCache(['127.0.0.1:11211'])
+
+If you are using App Engine, you can connect to the App Engine memcache
+server easily::
+
+ from werkzeug.contrib.cache import GAEMemcachedCache
+ cache = GAEMemcachedCache()
+
+Using a Cache
+-------------
+
+Now how can one use such a cache? There are two very important
+operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and
+:meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them:
+
+To get an item from the cache call
+:meth:`~werkzeug.contrib.cache.BaseCache.get` with a string as key name.
+If something is in the cache, it is returned. Otherwise that function
+will return `None`::
+
+ rv = cache.get('my-item')
+
+To add items to the cache, use the :meth:`~werkzeug.contrib.cache.BaseCache.set`
+method instead. The first argument is the key and the second the value
+that should be set. Also a timeout can be provided after which the cache
+will automatically remove item.
+
+Here a full example how this looks like normally::
+
+ def get_my_item():
+ rv = cache.get('my-item')
+ if rv is None:
+ rv = calculate_value()
+ cache.set('my-item', rv, timeout=5 * 60)
+ return rv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/distribute.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,166 @@
+.. _distribute-deployment:
+
+Deploying with Distribute
+=========================
+
+`distribute`_, formerly setuptools, is an extension library that is
+commonly used to (like the name says) distribute Python libraries and
+extensions. It extends distutils, a basic module installation system
+shipped with Python to also support various more complex constructs that
+make larger applications easier to distribute:
+
+- **support for dependencies**: a library or application can declare a
+ list of other libraries it depends on which will be installed
+ automatically for you.
+- **package registry**: setuptools registers your package with your
+ Python installation. This makes it possible to query information
+ provided by one package from another package. The best known feature of
+ this system is the entry point support which allows one package to
+ declare an "entry point" another package can hook into to extend the
+ other package.
+- **installation manager**: `easy_install`, which comes with distribute
+ can install other libraries for you. You can also use `pip`_ which
+ sooner or later will replace `easy_install` which does more than just
+ installing packages for you.
+
+Flask itself, and all the libraries you can find on the cheeseshop
+are distributed with either distribute, the older setuptools or distutils.
+
+In this case we assume your application is called
+`yourapplication.py` and you are not using a module, but a :ref:`package
+<larger-applications>`. Distributing resources with standard modules is
+not supported by `distribute`_ so we will not bother with it. If you have
+not yet converted your application into a package, head over to the
+:ref:`larger-applications` pattern to see how this can be done.
+
+A working deployment with distribute is the first step into more complex
+and more automated deployment scenarios. If you want to fully automate
+the process, also read the :ref:`fabric-deployment` chapter.
+
+Basic Setup Script
+------------------
+
+Because you have Flask running, you either have setuptools or distribute
+available on your system anyways. If you do not, fear not, there is a
+script to install it for you: `distribute_setup.py`_. Just download and
+run with your Python interpreter.
+
+Standard disclaimer applies: :ref:`you better use a virtualenv
+<virtualenv>`.
+
+Your setup code always goes into a file named `setup.py` next to your
+application. The name of the file is only convention, but because
+everybody will look for a file with that name, you better not change it.
+
+Yes, even if you are using `distribute`, you are importing from a package
+called `setuptools`. `distribute` is fully backwards compatible with
+`setuptools`, so it also uses the same import name.
+
+A basic `setup.py` file for a Flask application looks like this::
+
+ from setuptools import setup
+
+ setup(
+ name='Your Application',
+ version='1.0',
+ long_description=__doc__,
+ packages=['yourapplication'],
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=['Flask']
+ )
+
+Please keep in mind that you have to list subpackages explicitly. If you
+want distribute to lookup the packages for you automatically, you can use
+the `find_packages` function::
+
+ from setuptools import setup, find_packages
+
+ setup(
+ ...
+ packages=find_packages()
+ )
+
+Most parameters to the `setup` function should be self explanatory,
+`include_package_data` and `zip_safe` might not be.
+`include_package_data` tells distribute to look for a `MANIFEST.in` file
+and install all the entries that match as package data. We will use this
+to distribute the static files and templates along with the Python module
+(see :ref:`distributing-resources`). The `zip_safe` flag can be used to
+force or prevent zip Archive creation. In general you probably don't want
+your packages to be installed as zip files because some tools do not
+support them and they make debugging a lot harder.
+
+
+.. _distributing-resources:
+
+Distributing Resources
+----------------------
+
+If you try to install the package you just created, you will notice that
+folders like `static` or `templates` are not installed for you. The
+reason for this is that distribute does not know which files to add for
+you. What you should do, is to create a `MANIFEST.in` file next to your
+`setup.py` file. This file lists all the files that should be added to
+your tarball::
+
+ recursive-include yourapplication/templates *
+ recursive-include yourapplication/static *
+
+Don't forget that even if you enlist them in your `MANIFEST.in` file, they
+won't be installed for you unless you set the `include_package_data`
+parameter of the `setup` function to `True`!
+
+
+Declaring Dependencies
+----------------------
+
+Dependencies are declared in the `install_requires` parameter as list.
+Each item in that list is the name of a package that should be pulled from
+PyPI on installation. By default it will always use the most recent
+version, but you can also provide minimum and maximum version
+requirements. Here some examples::
+
+ install_requires=[
+ 'Flask>=0.2',
+ 'SQLAlchemy>=0.6',
+ 'BrokenPackage>=0.7,<=1.0'
+ ]
+
+I mentioned earlier that dependencies are pulled from PyPI. What if you
+want to depend on a package that cannot be found on PyPI and won't be
+because it is an internal package you don't want to share with anyone?
+Just still do as if there was a PyPI entry for it and provide a list of
+alternative locations where distribute should look for tarballs::
+
+ dependency_links=['http://example.com/yourfiles']
+
+Make sure that page has a directory listing and the links on the page are
+pointing to the actual tarballs with their correct filenames as this is
+how distribute will find the files. If you have an internal company
+server that contains the packages, provide the URL to that server there.
+
+
+Installing / Developing
+-----------------------
+
+To install your application (ideally into a virtualenv) just run the
+`setup.py` script with the `install` parameter. It will install your
+application into the virtualenv's site-packages folder and also download
+and install all dependencies::
+
+ $ python setup.py install
+
+If you are developing on the package and also want the requirements to be
+installed, you can use the `develop` command instead::
+
+ $ python setup.py develop
+
+This has the advantage of just installing a link to the site-packages
+folder instead of copying the data over. You can then continue to work on
+the code without having to run `install` again after each change.
+
+
+.. _distribute: http://pypi.python.org/pypi/distribute
+.. _pip: http://pypi.python.org/pypi/pip
+.. _distribute_setup.py: http://python-distribute.org/distribute_setup.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/errorpages.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,77 @@
+Custom Error Pages
+==================
+
+Flask comes with a handy :func:`~flask.abort` function that aborts a
+request with an HTTP error code early. It will also provide a plain black
+and white error page for you with a basic description, but nothing fancy.
+
+Depending on the error code it is less or more likely for the user to
+actually see such an error.
+
+Common Error Codes
+------------------
+
+The following error codes are some that are often displayed to the user,
+even if the application behaves correctly:
+
+*404 Not Found*
+ The good old "chap, you made a mistake typing that URL" message. So
+ common that even novices to the internet know that 404 means: damn,
+ the thing I was looking for is not there. It's a very good idea to
+ make sure there is actually something useful on a 404 page, at least a
+ link back to the index.
+
+*403 Forbidden*
+ If you have some kind of access control on your website, you will have
+ to send a 403 code for disallowed resources. So make sure the user
+ is not lost when he tries to access a resource he cannot access.
+
+*410 Gone*
+ Did you know that there the "404 Not Found" has a brother named "410
+ Gone"? Few people actually implement that, but the idea is that
+ resources that previously existed and got deleted answer with 410
+ instead of 404. If you are not deleting documents permanently from
+ the database but just mark them as deleted, do the user a favour and
+ use the 410 code instead and display a message that what he was
+ looking for was deleted for all eternity.
+
+*500 Internal Server Error*
+ Usually happens on programming errors or if the server is overloaded.
+ A terrible good idea to have a nice page there, because your
+ application *will* fail sooner or later (see also:
+ :ref:`application-errors`).
+
+
+Error Handlers
+--------------
+
+An error handler is a function, just like a view function, but it is
+called when an error happens and is passed that error. The error is most
+likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it
+can be a different error: a handler for internal server errors will be
+passed other exception instances as well if they are uncaught.
+
+An error handler is registered with the :meth:`~flask.Flask.errorhandler`
+decorator and the error code of the exception. Keep in mind that Flask
+will *not* set the error code for you, so make sure to also provide the
+HTTP status code when returning a response.
+
+Here an example implementation for a "404 Page Not Found" exception::
+
+ from flask import render_template
+
+ @app.errorhandler(404)
+ def page_not_found(e):
+ return render_template('404.html'), 404
+
+An example template might be this:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block title %}Page Not Found{% endblock %}
+ {% block body %}
+ <h1>Page Not Found</h1>
+ <p>What you were looking for is just not there.
+ <p><a href="{{ url_for('index') }}">go somewhere nice</a>
+ {% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/fabric.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,196 @@
+.. _fabric-deployment:
+
+Deploying with Fabric
+=====================
+
+`Fabric`_ is a tool for Python similar to Makefiles but with the ability
+to execute commands on a remote server. In combination with a properly
+set up Python package (:ref:`larger-applications`) and a good concept for
+configurations (:ref:`config`) it is very easy to deploy Flask
+applications to external servers.
+
+Before we get started, here a quick checklist of things we have to ensure
+upfront:
+
+- Fabric 1.0 has to be installed locally. This tutorial assumes the
+ latest version of Fabric.
+- The application already has to be a package and requires a working
+ `setup.py` file (:ref:`distribute-deployment`).
+- In the following example we are using `mod_wsgi` for the remote
+ servers. You can of course use your own favourite server there, but
+ for this example we chose Apache + `mod_wsgi` because it's very easy
+ to setup and has a simple way to reload applications without root
+ access.
+
+Creating the first Fabfile
+--------------------------
+
+A fabfile is what controls what Fabric executes. It is named `fabfile.py`
+and executed by the `fab` command. All the functions defined in that file
+will show up as `fab` subcommands. They are executed on one or more
+hosts. These hosts can be defined either in the fabfile or on the command
+line. In this case we will add them to the fabfile.
+
+This is a basic first example that has the ability to upload the current
+sourcecode to the server and install it into a already existing
+virtual environment::
+
+ from fabric.api import *
+
+ # the user to use for the remote commands
+ env.user = 'appuser'
+ # the servers where the commands are executed
+ env.hosts = ['server1.example.com', 'server2.example.com']
+
+ def pack():
+ # create a new source distribution as tarball
+ local('python setup.py sdist --formats=gztar', capture=False)
+
+ def deploy():
+ # figure out the release name and version
+ dist = local('python setup.py --fullname').strip()
+ # upload the source tarball to the temporary folder on the server
+ put('dist/%s.tar.gz' % dist, '/tmp/yourapplication.tar.gz')
+ # create a place where we can unzip the tarball, then enter
+ # that directory and unzip it
+ run('mkdir yourapplication')
+ with cd('/tmp/yourapplication'):
+ run('tar xzf /tmp/yourapplication.tar.gz')
+ # now setup the package with our virtual environment's
+ # python interpreter
+ run('/var/www/yourapplication/env/bin/python setup.py install')
+ # now that all is set up, delete the folder again
+ run('rm -rf /tmp/yourapplication /tmp/yourapplication.tar.gz')
+ # and finally touch the .wsgi file so that mod_wsgi triggers
+ # a reload of the application
+ run('touch /var/www/yourapplication.wsgi')
+
+The example above is well documented and should be straightforward. Here
+a recap of the most common commands fabric provides:
+
+- `run` - executes a command on a remote server
+- `local` - executes a command on the local machine
+- `put` - uploads a file to the remote server
+- `cd` - changes the directory on the serverside. This has to be used
+ in combination with the `with` statement.
+
+Running Fabfiles
+----------------
+
+Now how do you execute that fabfile? You use the `fab` command. To
+deploy the current version of the code on the remote server you would use
+this command::
+
+ $ fab pack deploy
+
+However this requires that our server already has the
+``/var/www/yourapplication`` folder created and
+``/var/www/yourapplication/env`` to be a virtual environment. Furthermore
+are we not creating the configuration or `.wsgi` file on the server. So
+how do we bootstrap a new server into our infrastructure?
+
+This now depends on the number of servers we want to set up. If we just
+have one application server (which the majority of applications will
+have), creating a command in the fabfile for this is overkill. But
+obviously you can do that. In that case you would probably call it
+`setup` or `bootstrap` and then pass the servername explicitly on the
+command line::
+
+ $ fab -H newserver.example.com bootstrap
+
+To setup a new server you would roughly do these steps:
+
+1. Create the directory structure in ``/var/www``::
+
+ $ mkdir /var/www/yourapplication
+ $ cd /var/www/yourapplication
+ $ virtualenv --distribute env
+
+2. Upload a new `application.wsgi` file to the server and the
+ configuration file for the application (eg: `application.cfg`)
+
+3. Create a new Apache config for `yourapplication` and activate it.
+ Make sure to activate watching for changes of the `.wsgi` file so
+ that we can automatically reload the application by touching it.
+ (See :ref:`mod_wsgi-deployment` for more information)
+
+So now the question is, where do the `application.wsgi` and
+`application.cfg` files come from?
+
+The WSGI File
+-------------
+
+The WSGI file has to import the application and also to set an environment
+variable so that the application knows where to look for the config. This
+is a short example that does exactly that::
+
+ import os
+ os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg'
+ from yourapplication import app
+
+The application itself then has to initialize itself like this to look for
+the config at that environment variable::
+
+ app = Flask(__name__)
+ app.config.from_object('yourapplication.default_config')
+ app.config.from_envvar('YOURAPPLICATION_CONFIG')
+
+This approach is explained in detail in the :ref:`config` section of the
+documentation.
+
+The Configuration File
+----------------------
+
+Now as mentioned above, the application will find the correct
+configuration file by looking up the `YOURAPPLICATION_CONFIG` environment
+variable. So we have to put the configuration in a place where the
+application will able to find it. Configuration files have the unfriendly
+quality of being different on all computers, so you do not version them
+usually.
+
+A popular approach is to store configuration files for different servers
+in a separate version control repository and check them out on all
+servers. Then symlink the file that is active for the server into the
+location where it's expected (eg: ``/var/www/yourapplication``).
+
+Either way, in our case here we only expect one or two servers and we can
+upload them ahead of time by hand.
+
+First Deployment
+----------------
+
+Now we can do our first deployment. We have set up the servers so that
+they have their virtual environments and activated apache configs. Now we
+can pack up the application and deploy it::
+
+ $ fab pack deploy
+
+Fabric will now connect to all servers and run the commands as written
+down in the fabfile. First it will execute pack so that we have our
+tarball ready and then it will execute deploy and upload the source code
+to all servers and install it there. Thanks to the `setup.py` file we
+will automatically pull in the required libraries into our virtual
+environment.
+
+Next Steps
+----------
+
+From that point onwards there is so much that can be done to make
+deployment actually fun:
+
+- Create a `bootstrap` command that initializes new servers. It could
+ initialize a new virtual environment, setup apache appropriately etc.
+- Put configuration files into a separate version control repository
+ and symlink the active configs into place.
+- You could also put your application code into a repository and check
+ out the latest version on the server and then install. That way you
+ can also easily go back to older versions.
+- hook in testing functionality so that you can deploy to an external
+ server and run the testsuite.
+
+Working with Fabric is fun and you will notice that it's quite magical to
+type ``fab deploy`` and see your application being deployed automatically
+to one or more remote servers.
+
+
+.. _Fabric: http://fabfile.org/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/favicon.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,53 @@
+Adding a favicon
+================
+
+A "favicon" is an icon used by browsers for tabs and bookmarks. This helps
+to distinguish your website and to give it a unique brand.
+
+A common question is how to add a favicon to a flask application. First, of
+course, you need an icon. It should be 16 × 16 pixels and in the ICO file
+format. This is not a requirement but a de-facto standard supported by all
+relevant browsers. Put the icon in your static directory as
+:file:`favicon.ico`.
+
+Now, to get browsers to find your icon, the correct way is to add a link
+tag in your HTML. So, for example:
+
+.. sourcecode:: html+jinja
+
+ <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
+
+That's all you need for most browsers, however some really old ones do not
+support this standard. The old de-facto standard is to serve this file,
+with this name, at the website root. If your application is not mounted at
+the root path of the domain you either need to configure the webserver to
+serve the icon at the root or if you can't do that you're out of luck. If
+however your application is the root you can simply route a redirect::
+
+ app.add_url_rule('/favicon.ico',
+ redirect_to=url_for('static', filename='favicon.ico'))
+
+If you want to save the extra redirect request you can also write a view
+using :func:`~flask.send_from_directory`::
+
+ import os
+ from flask import send_from_directory
+
+ @app.route('/favicon.ico')
+ def favicon():
+ return send_from_directory(os.path.join(app.root_path, 'static'),
+ 'favicon.ico', mimetype='image/vnd.microsoft.icon')
+
+We can leave out the explicit mimetype and it will be guessed, but we may
+as well specify it to avoid the extra guessing, as it will always be the
+same.
+
+The above will serve the icon via your application and if possible it's
+better to configure your dedicated web server to serve it; refer to the
+webserver's documentation.
+
+See also
+--------
+
+* The `Favicon <http://en.wikipedia.org/wiki/Favicon>`_ article on
+ Wikipedia
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/fileuploads.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,180 @@
+.. _uploading-files:
+
+Uploading Files
+===============
+
+Ah yes, the good old problem of file uploads. The basic idea of file
+uploads is actually quite simple. It basically works like this:
+
+1. A ``<form>`` tag is marked with ``enctype=multipart/form-data``
+ and an ``<input type=file>`` is placed in that form.
+2. The application accesses the file from the :attr:`~flask.request.files`
+ dictionary on the request object.
+3. use the :meth:`~werkzeug.FileStorage.save` method of the file to save
+ the file permanently somewhere on the filesystem.
+
+A Gentle Introduction
+---------------------
+
+Let's start with a very basic application that uploads a file to a
+specific upload folder and displays a file to the user. Let's look at the
+bootstrapping code for our application::
+
+ import os
+ from flask import Flask, request, redirect, url_for
+ from werkzeug import secure_filename
+
+ UPLOAD_FOLDER = '/path/to/the/uploads'
+ ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
+
+ app = Flask(__name__)
+
+So first we need a couple of imports. Most should be straightforward, the
+:func:`werkzeug.secure_filename` is explained a little bit later. The
+`UPLOAD_FOLDER` is where we will store the uploaded files and the
+`ALLOWED_EXTENSIONS` is the set of allowed file extensions. Then we add a
+URL rule by hand to the application. Now usually we're not doing that, so
+why here? The reasons is that we want the webserver (or our development
+server) to serve these files for us and so we only need a rule to generate
+the URL to these files.
+
+Why do we limit the extensions that are allowed? You probably don't want
+your users to be able to upload everything there if the server is directly
+sending out the data to the client. That way you can make sure that users
+are not able to upload HTML files that would cause XSS problems (see
+:ref:`xss`). Also make sure to disallow `.php` files if the server
+executes them, but who has PHP installed on his server, right? :)
+
+Next the functions that check if an extension is valid and that uploads
+the file and redirects the user to the URL for the uploaded file::
+
+ def allowed_file(filename):
+ return '.' in filename and \
+ filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
+
+ @app.route('/', methods=['GET', 'POST'])
+ def upload_file():
+ if request.method == 'POST':
+ file = request.files['file']
+ if file and allowed_file(file.filename):
+ filename = secure_filename(file.filename)
+ file.save(os.path.join(UPLOAD_FOLDER, filename))
+ return redirect(url_for('uploaded_file',
+ filename=filename))
+ return '''
+ <!doctype html>
+ <title>Upload new File</title>
+ <h1>Upload new File</h1>
+ <form action="" method=post enctype=multipart/form-data>
+ <p><input type=file name=file>
+ <input type=submit value=Upload>
+ </form>
+ '''
+
+So what does that :func:`~werkzeug.secure_filename` function actually do?
+Now the problem is that there is that principle called "never trust user
+input". This is also true for the filename of an uploaded file. All
+submitted form data can be forged, and filenames can be dangerous. For
+the moment just remember: always use that function to secure a filename
+before storing it directly on the filesystem.
+
+.. admonition:: Information for the Pros
+
+ So you're interested in what that :func:`~werkzeug.secure_filename`
+ function does and what the problem is if you're not using it? So just
+ imagine someone would send the following information as `filename` to
+ your application::
+
+ filename = "../../../../home/username/.bashrc"
+
+ Assuming the number of ``../`` is correct and you would join this with
+ the `UPLOAD_FOLDER` the user might have the ability to modify a file on
+ the server's filesystem he should not modify. This does require some
+ knowledge about how the application looks like, but trust me, hackers
+ are patient :)
+
+ Now let's look how that function works:
+
+ >>> secure_filename('../../../../home/username/.bashrc')
+ 'home_username_.bashrc'
+
+Now one last thing is missing: the serving of the uploaded files. As of
+Flask 0.5 we can use a function that does that for us::
+
+ from flask import send_from_directory
+
+ @app.route('/uploads/<filename>')
+ def uploaded_file(filename):
+ return send_from_directory(app.config['UPLOAD_FOLDER'],
+ filename)
+
+Alternatively you can register `uploaded_file` as `build_only` rule and
+use the :class:`~werkzeug.SharedDataMiddleware`. This also works with
+older versions of Flask::
+
+ from werkzeug import SharedDataMiddleware
+ app.add_url_rule('/uploads/<filename>', 'uploaded_file',
+ build_only=True)
+ app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
+ '/uploads': UPLOAD_FOLDER
+ })
+
+If you now run the application everything should work as expected.
+
+
+Improving Uploads
+-----------------
+
+.. versionadded:: 0.6
+
+So how exactly does Flask handle uploads? Well it will store them in the
+webserver's memory if the files are reasonable small otherwise in a
+temporary location (as returned by :func:`tempfile.gettempdir`). But how
+do you specify the maximum file size after which an upload is aborted? By
+default Flask will happily accept file uploads to an unlimited amount of
+memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH``
+config key::
+
+ from flask import Flask, Request
+
+ app = Flask(__name__)
+ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
+
+The code above will limited the maximum allowed payload to 16 megabytes.
+If a larger file is transmitted, Flask will raise an
+:exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception.
+
+This feature was added in Flask 0.6 but can be achieved in older versions
+as well by subclassing the request object. For more information on that
+consult the Werkzeug documentation on file handling.
+
+
+Upload Progress Bars
+--------------------
+
+A while ago many developers had the idea to read the incoming file in
+small chunks and store the upload progress in the database to be able to
+poll the progress with JavaScript from the client. Long story short: the
+client asks the server every 5 seconds how much he has transmitted
+already. Do you realize the irony? The client is asking for something he
+should already know.
+
+Now there are better solutions to that work faster and more reliable. The
+web changed a lot lately and you can use HTML5, Java, Silverlight or Flash
+to get a nicer uploading experience on the client side. Look at the
+following libraries for some nice examples how to do that:
+
+- `Plupload <http://www.plupload.com/>`_ - HTML5, Java, Flash
+- `SWFUpload <http://www.swfupload.org/>`_ - Flash
+- `JumpLoader <http://jumploader.com/>`_ - Java
+
+
+An Easier Solution
+------------------
+
+Because the common pattern for file uploads exists almost unchanged in all
+applications dealing with uploads, there is a Flask extension called
+`Flask-Uploads`_ that implements a full fledged upload mechanism with
+white and blacklisting of extensions and more.
+
+.. _Flask-Uploads: http://packages.python.org/Flask-Uploads/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/flashing.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,119 @@
+.. _message-flashing-pattern:
+
+Message Flashing
+================
+
+Good applications and user interfaces are all about feedback. If the user
+does not get enough feedback he will probably end up hating the
+application. Flask provides a really simple way to give feedback to a
+user with the flashing system. The flashing system basically makes it
+possible to record a message at the end of a request and access it next
+request and only next request. This is usually combined with a layout
+template that does this.
+
+Simple Flashing
+---------------
+
+So here is a full example::
+
+ from flask import flash, redirect, url_for, render_template
+
+ @app.route('/')
+ def index():
+ return render_template('index.html')
+
+ @app.route('/login', methods=['GET', 'POST'])
+ def login():
+ error = None
+ if request.method == 'POST':
+ if request.form['username'] != 'admin' or \
+ request.form['password'] != 'secret':
+ error = 'Invalid credentials'
+ else:
+ flash('You were successfully logged in')
+ return redirect(url_for('index'))
+ return render_template('login.html', error=error)
+
+And here the ``layout.html`` template which does the magic:
+
+.. sourcecode:: html+jinja
+
+ <!doctype html>
+ <title>My Application</title>
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+ <ul class=flashes>
+ {% for message in messages %}
+ <li>{{ message }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ {% endwith %}
+ {% block body %}{% endblock %}
+
+And here the index.html template:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block body %}
+ <h1>Overview</h1>
+ <p>Do you want to <a href="{{ url_for('login') }}">log in?</a>
+ {% endblock %}
+
+And of course the login template:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block body %}
+ <h1>Login</h1>
+ {% if error %}
+ <p class=error><strong>Error:</strong> {{ error }}
+ {% endif %}
+ <form action="" method=post>
+ <dl>
+ <dt>Username:
+ <dd><input type=text name=username value="{{
+ request.form.username }}">
+ <dt>Password:
+ <dd><input type=password name=password>
+ </dl>
+ <p><input type=submit value=Login>
+ </form>
+ {% endblock %}
+
+Flashing With Categories
+------------------------
+
+.. versionadded:: 0.3
+
+It is also possible to provide categories when flashing a message. The
+default category if nothing is provided is ``'message'``. Alternative
+categories can be used to give the user better feedback. For example
+error messages could be displayed with a red background.
+
+To flash a message with a different category, just use the second argument
+to the :func:`~flask.flash` function::
+
+ flash(u'Invalid password provided', 'error')
+
+Inside the template you then have to tell the
+:func:`~flask.get_flashed_messages` function to also return the
+categories. The loop looks slightly different in that situation then:
+
+.. sourcecode:: html+jinja
+
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+ <ul class=flashes>
+ {% for category, message in messages %}
+ <li class="{{ category }}">{{ message }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ {% endwith %}
+
+This is just one example of how to render these flashed messages. One
+might also use the category to add a prefix such as
+``<strong>Error:</strong>`` to the message.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/index.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,35 @@
+.. _patterns:
+
+Patterns for Flask
+==================
+
+Certain things are common enough that the chances are high you will find
+them in most web applications. For example quite a lot of applications
+are using relational databases and user authentication. In that case,
+chances are they will open a database connection at the beginning of the
+request and get the information of the currently logged in user. At the
+end of the request, the database connection is closed again.
+
+There are more user contributed snippets and patterns in the `Flask
+Snippet Archives <http://flask.pocoo.org/snippets/>`_.
+
+.. toctree::
+ :maxdepth: 2
+
+ packages
+ appfactories
+ distribute
+ fabric
+ sqlite3
+ sqlalchemy
+ fileuploads
+ caching
+ viewdecorators
+ wtforms
+ templateinheritance
+ flashing
+ jquery
+ errorpages
+ lazyloading
+ mongokit
+ favicon
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/jquery.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,167 @@
+AJAX with jQuery
+================
+
+`jQuery`_ is a small JavaScript library commonly used to simplify working
+with the DOM and JavaScript in general. It is the perfect tool to make
+web applications more dynamic by exchanging JSON between server and
+client.
+
+JSON itself is a very lightweight transport format, very similar to how
+Python primitives (numbers, strings, dicts and lists) look like which is
+widely supported and very easy to parse. It became popular a few years
+ago and quickly replaced XML as transport format in web applications.
+
+If you have Python 2.6 JSON will work out of the box, in Python 2.5 you
+will have to install the `simplejson`_ library from PyPI.
+
+.. _jQuery: http://jquery.com/
+.. _simplejson: http://pypi.python.org/pypi/simplejson
+
+Loading jQuery
+--------------
+
+In order to use jQuery, you have to download it first and place it in the
+static folder of your application and then ensure it's loaded. Ideally
+you have a layout template that is used for all pages where you just have
+to add a script statement to your `head` to load jQuery:
+
+.. sourcecode:: html
+
+ <script type=text/javascript src="{{
+ url_for('static', filename='jquery.js') }}"></script>
+
+Another method is using Google's `AJAX Libraries API
+<http://code.google.com/apis/ajaxlibs/documentation/>`_ to load jQuery:
+
+.. sourcecode:: html
+
+ <script type=text/javascript
+ src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
+
+In this case you don't have to put jQuery into your static folder, it will
+instead be loaded from Google directly. This has the advantage that your
+website will probably load faster for users if they went to at least one
+other website before using the same jQuery version from Google because it
+will already be in the browser cache. Downside is that if you don't have
+network connectivity during development jQuery will not load.
+
+Where is My Site?
+-----------------
+
+Do you know where your application is? If you are developing the answer
+is quite simple: it's on localhost port something and directly on the root
+of that server. But what if you later decide to move your application to
+a different location? For example to ``http://example.com/myapp``? On
+the server side this never was a problem because we were using the handy
+:func:`~flask.url_for` function that could answer that question for
+us, but if we are using jQuery we should not hardcode the path to
+the application but make that dynamic, so how can we do that?
+
+A simple method would be to add a script tag to our page that sets a
+global variable to the prefix to the root of the application. Something
+like this:
+
+.. sourcecode:: html+jinja
+
+ <script type=text/javascript>
+ $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
+ </script>
+
+The ``|safe`` is necessary so that Jinja does not escape the JSON encoded
+string with HTML rules. Usually this would be necessary, but we are
+inside a `script` block here where different rules apply.
+
+.. admonition:: Information for Pros
+
+ In HTML the `script` tag is declared `CDATA` which means that entities
+ will not be parsed. Everything until ``</script>`` is handled as script.
+ This also means that there must never be any ``</`` between the script
+ tags. ``|tojson`` is kind enough to do the right thing here and
+ escape slashes for you (``{{ "</script>"|tojson|safe }}`` is rendered as
+ ``"<\/script>"``).
+
+
+JSON View Functions
+-------------------
+
+Now let's create a server side function that accepts two URL arguments of
+numbers which should be added together and then sent back to the
+application in a JSON object. This is a really ridiculous example and is
+something you usually would do on the client side alone, but a simple
+example that shows how you would use jQuery and Flask nonetheless::
+
+ from flask import Flask, jsonify, render_template, request
+ app = Flask(__name__)
+
+ @app.route('/_add_numbers')
+ def add_numbers():
+ a = request.args.get('a', 0, type=int)
+ b = request.args.get('b', 0, type=int)
+ return jsonify(result=a + b)
+
+ @app.route('/')
+ def index():
+ return render_template('index.html')
+
+As you can see I also added an `index` method here that renders a
+template. This template will load jQuery as above and have a little form
+we can add two numbers and a link to trigger the function on the server
+side.
+
+Note that we are using the :meth:`~werkzeug.MultiDict.get` method here
+which will never fail. If the key is missing a default value (here ``0``)
+is returned. Furthermore it can convert values to a specific type (like
+in our case `int`). This is especially handy for code that is
+triggered by a script (APIs, JavaScript etc.) because you don't need
+special error reporting in that case.
+
+The HTML
+--------
+
+Your index.html template either has to extend a `layout.html` template with
+jQuery loaded and the `$SCRIPT_ROOT` variable set, or do that on the top.
+Here's the HTML code needed for our little application (`index.html`).
+Notice that we also drop the script directly into the HTML here. It is
+usually a better idea to have that in a separate script file:
+
+.. sourcecode:: html
+
+ <script type=text/javascript>
+ $(function() {
+ $('a#calculate').bind('click', function() {
+ $.getJSON($SCRIPT_ROOT + '/_add_numbers', {
+ a: $('input[name="a"]').val(),
+ b: $('input[name="b"]').val()
+ }, function(data) {
+ $("#result").text(data.result);
+ });
+ return false;
+ });
+ });
+ </script>
+ <h1>jQuery Example</h1>
+ <p><input type=text size=5 name=a> +
+ <input type=text size=5 name=b> =
+ <span id=result>?</span>
+ <p><a href=# id=calculate>calculate server side</a>
+
+I won't got into detail here about how jQuery works, just a very quick
+explanation of the little bit of code above:
+
+1. ``$(function() { ... })`` specifies code that should run once the
+ browser is done loading the basic parts of the page.
+2. ``$('selector')`` selects an element and lets you operate on it.
+3. ``element.bind('event', func)`` specifies a function that should run
+ when the user clicked on the element. If that function returns
+ `false`, the default behaviour will not kick in (in this case, navigate
+ to the `#` URL).
+4. ``$.getJSON(url, data, func)`` sends a `GET` request to `url` and will
+ send the contents of the `data` object as query parameters. Once the
+ data arrived, it will call the given function with the return value as
+ argument. Note that we can use the `$SCRIPT_ROOT` variable here that
+ we set earlier.
+
+If you don't get the whole picture, download the `sourcecode
+for this example
+<http://github.com/mitsuhiko/flask/tree/master/examples/jqueryexample>`_
+from github.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/lazyloading.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,104 @@
+Lazily Loading Views
+====================
+
+Flask is usually used with the decorators. Decorators are simple and you
+have the URL right next to the function that is called for that specific
+URL. However there is a downside to this approach: it means all your code
+that uses decorators has to be imported upfront or Flask will never
+actually find your function.
+
+This can be a problem if your application has to import quick. It might
+have to do that on systems like Google's App Engine or other systems. So
+if you suddenly notice that your application outgrows this approach you
+can fall back to a centralized URL mapping.
+
+The system that enables having a central URL map is the
+:meth:`~flask.Flask.add_url_rule` function. Instead of using decorators,
+you have a file that sets up the application with all URLs.
+
+Converting to Centralized URL Map
+---------------------------------
+
+Imagine the current application looks somewhat like this::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ @app.route('/')
+ def index():
+ pass
+
+ @app.route('/user/<username>')
+ def user(username):
+ pass
+
+Then the centralized approach you would have one file with the views
+(`views.py`) but without any decorator::
+
+ def index():
+ pass
+
+ def user(username):
+ pass
+
+And then a file that sets up an application which maps the functions to
+URLs::
+
+ from flask import Flask
+ from yourapplication import views
+ app = Flask(__name__)
+ app.add_url_rule('/', view_func=views.index)
+ app.add_url_rule('/user/<username>', view_func=views.user)
+
+Loading Late
+------------
+
+So far we only split up the views and the routing, but the module is still
+loaded upfront. The trick to actually load the view function as needed.
+This can be accomplished with a helper class that behaves just like a
+function but internally imports the real function on first use::
+
+ from werkzeug import import_string, cached_property
+
+ class LazyView(object):
+
+ def __init__(self, import_name):
+ self.__module__, self.__name__ = import_name.rsplit('.', 1)
+ self.import_name = import_name
+
+ @cached_property
+ def view(self):
+ return import_string(self.import_name)
+
+ def __call__(self, *args, **kwargs):
+ return self.view(*args, **kwargs)
+
+What's important here is is that `__module__` and `__name__` are properly
+set. This is used by Flask internally to figure out how to name the
+URL rules in case you don't provide a name for the rule yourself.
+
+Then you can define your central place to combine the views like this::
+
+ from flask import Flask
+ from yourapplication.helpers import LazyView
+ app = Flask(__name__)
+ app.add_url_rule('/',
+ view_func=LazyView('yourapplication.views.index'))
+ app.add_url_rule('/user/<username>',
+ view_func=LazyView('yourapplication.views.user'))
+
+You can further optimize this in terms of amount of keystrokes needed to
+write this by having a function that calls into
+:meth:`~flask.Flask.add_url_rule` by prefixing a string with the project
+name and a dot, and by wrapping `view_func` in a `LazyView` as needed::
+
+ def url(url_rule, import_name, **options):
+ view = LazyView('yourapplication.' + import_name)
+ app.add_url_rule(url_rule, view_func=view, **options)
+
+ url('/', 'views.index')
+ url('/user/<username>', 'views.user')
+
+One thing to keep in mind is that before and after request handlers have
+to be in a file that is imported upfront to work properly on the first
+request. The same goes for any kind of remaining decorator.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/mongokit.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,144 @@
+.. mongokit-pattern:
+
+MongoKit in Flask
+=================
+
+Using a document database rather than a full DBMS gets more common these days.
+This pattern shows how to use MongoKit, a document mapper library, to
+integrate with MongoDB.
+
+This pattern requires a running MongoDB server and the MongoKit library
+installed.
+
+There are two very common ways to use MongoKit. I will outline each of them
+here:
+
+
+Declarative
+-----------
+
+The default behaviour of MongoKit is the declarative one that is based on
+common ideas from Django or the SQLAlchemy declarative extension.
+
+Here an example `app.py` module for your application::
+
+ from flask import Flask
+ from mongokit import Connection, Document
+
+ # configuration
+ MONGODB_HOST = 'localhost'
+ MONGODB_PORT = 27017
+
+ # create the little application object
+ app = Flask(__name__)
+ app.config.from_object(__name__)
+
+ # connect to the database
+ connection = Connection(app.config['MONGODB_HOST'],
+ app.config['MONGODB_PORT'])
+
+
+To define your models, just subclass the `Document` class that is imported
+from MongoKit. If you've seen the SQLAlchemy pattern you may wonder why we do
+not have a session and even do not define a `init_db` function here. On the
+one hand, MongoKit does not have something like a session. This sometimes
+makes it more to type but also makes it blazingly fast. On the other hand,
+MongoDB is schemaless. This means you can modify the data structure from one
+insert query to the next without any problem. MongoKit is just schemaless
+too, but implements some validation to ensure data integrity.
+
+Here is an example document (put this also into `app.py`, e.g.)::
+
+ def max_length(length):
+ def validate(value):
+ if len(value) <= length:
+ return True
+ raise Exception('%s must be at most %s characters long' % length)
+ return validate
+
+ class User(Document):
+ structure = {
+ 'name': unicode,
+ 'email': unicode,
+ }
+ validators = {
+ 'name': max_length(50),
+ 'email': max_length(120)
+ }
+ use_dot_notation = True
+ def __repr__(self):
+ return '<User %r>' % (self.name)
+
+ # register the User document with our current connection
+ connection.register([User])
+
+
+This example shows you how to define your schema (named structure), a
+validator for the maximum character length and uses a special MongoKit feature
+called `use_dot_notation`. Per default MongoKit behaves like a python
+dictionary but with `use_dot_notation` set to `True` you can use your
+documents like you use models in nearly any other ORM by using dots to
+separate between attributes.
+
+You can insert entries into the database like this:
+
+>>> from yourapplication.database import connection
+>>> from yourapplication.models import User
+>>> collection = connection['test'].users
+>>> user = collection.User()
+>>> user['name'] = u'admin'
+>>> user['email'] = u'admin@localhost'
+>>> user.save()
+
+Note that MongoKit is kinda strict with used column types, you must not use a
+common `str` type for either `name` or `email` but unicode.
+
+Querying is simple as well:
+
+>>> list(collection.User.find())
+[<User u'admin'>]
+>>> collection.User.find_one({'name': u'admin'})
+<User u'admin'>
+
+.. _MongoKit: http://bytebucket.org/namlook/mongokit/
+
+
+PyMongo Compatibility Layer
+---------------------------
+
+If you just want to use PyMongo, you can do that with MongoKit as well. You
+may use this process if you need the best performance to get. Note that this
+example does not show how to couple it with Flask, see the above MongoKit code
+for examples::
+
+ from MongoKit import Connection
+
+ connection = Connection()
+
+To insert data you can use the `insert` method. We have to get a
+collection first, this is somewhat the same as a table in the SQL world.
+
+>>> collection = connection['test'].users
+>>> user = {'name': u'admin', 'email': u'admin@localhost'}
+>>> collection.insert(user)
+
+print list(collection.find())
+print collection.find_one({'name': u'admin'})
+
+MongoKit will automatically commit for us.
+
+To query your database, you use the collection directly:
+
+>>> list(collection.find())
+[{u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'}]
+>>> collection.find_one({'name': u'admin'})
+{u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'}
+
+These results are also dict-like objects:
+
+>>> r = collection.find_one({'name': u'admin'})
+>>> r['email']
+u'admin@localhost'
+
+For more information about MongoKit, head over to the
+`website <http://bytebucket.org/namlook/mongokit/>`_.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/packages.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,286 @@
+.. _larger-applications:
+
+Larger Applications
+===================
+
+For larger applications it's a good idea to use a package instead of a
+module. That is quite simple. Imagine a small application looks like
+this::
+
+ /yourapplication
+ /yourapplication.py
+ /static
+ /style.css
+ /templates
+ layout.html
+ index.html
+ login.html
+ ...
+
+Simple Packages
+---------------
+
+To convert that into a larger one, just create a new folder
+`yourapplication` inside the existing one and move everything below it.
+Then rename `yourapplication.py` to `__init__.py`. (Make sure to delete
+all `.pyc` files first, otherwise things would most likely break)
+
+You should then end up with something like that::
+
+ /yourapplication
+ /yourapplication
+ /__init__.py
+ /static
+ /style.css
+ /templates
+ layout.html
+ index.html
+ login.html
+ ...
+
+But how do you run your application now? The naive ``python
+yourapplication/__init__.py`` will not work. Let's just say that Python
+does not want modules in packages to be the startup file. But that is not
+a big problem, just add a new file called `runserver.py` next to the inner
+`yourapplication` folder with the following contents::
+
+ from yourapplication import app
+ app.run(debug=True)
+
+What did we gain from this? Now we can restructure the application a bit
+into multiple modules. The only thing you have to remember is the
+following quick checklist:
+
+1. the `Flask` application object creation has to be in the
+ `__init__.py` file. That way each module can import it safely and the
+ `__name__` variable will resolve to the correct package.
+2. all the view functions (the ones with a :meth:`~flask.Flask.route`
+ decorator on top) have to be imported when in the `__init__.py` file.
+ Not the object itself, but the module it is in. Do the importing at
+ the *bottom* of the file.
+
+Here's an example `__init__.py`::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ import yourapplication.views
+
+And this is what `views.py` would look like::
+
+ from yourapplication import app
+
+ @app.route('/')
+ def index():
+ return 'Hello World!'
+
+You should then end up with something like that::
+
+ /yourapplication
+ /yourapplication
+ /__init__.py
+ /views.py
+ /static
+ /style.css
+ /templates
+ layout.html
+ index.html
+ login.html
+ ...
+
+.. admonition:: Circular Imports
+
+ Every Python programmer hates them, and yet we just added some:
+ circular imports (That's when two modules depend on each other. In this
+ case `views.py` depends on `__init__.py`). Be advised that this is a
+ bad idea in general but here it is actually fine. The reason for this is
+ that we are not actually using the views in `__init__.py` and just
+ ensuring the module is imported and we are doing that at the bottom of
+ the file.
+
+ There are still some problems with that approach but if you want to use
+ decorators there is no way around that. Check out the
+ :ref:`becomingbig` section for some inspiration how to deal with that.
+
+
+.. _working-with-modules:
+
+Working with Modules
+--------------------
+
+For larger applications with more than a dozen views it makes sense to
+split the views into modules. First let's look at the typical structure of
+such an application::
+
+ /yourapplication
+ /yourapplication
+ /__init__.py
+ /views
+ __init__.py
+ admin.py
+ frontend.py
+ /static
+ /style.css
+ /templates
+ layout.html
+ index.html
+ login.html
+ ...
+
+The views are stored in the `yourapplication.views` package. Just make
+sure to place an empty `__init__.py` file in there. Let's start with the
+`admin.py` file in the view package.
+
+First we have to create a :class:`~flask.Module` object with the name of
+the package. This works very similar to the :class:`~flask.Flask` object
+you have already worked with, it just does not support all of the methods,
+but most of them are the same.
+
+Long story short, here's a nice and concise example::
+
+ from flask import Module
+
+ admin = Module(__name__)
+
+ @admin.route('/')
+ def index():
+ pass
+
+ @admin.route('/login')
+ def login():
+ pass
+
+ @admin.route('/logout')
+ def logout():
+ pass
+
+Do the same with the `frontend.py` and then make sure to register the
+modules in the application (`__init__.py`) like this::
+
+ from flask import Flask
+ from yourapplication.views.admin import admin
+ from yourapplication.views.frontend import frontend
+
+ app = Flask(__name__)
+ app.register_module(admin, url_prefix='/admin')
+ app.register_module(frontend)
+
+We register the modules with the app so that it can add them to the
+URL map for our application. Note the prefix argument to the admin
+module: by default when we register a module, that module's end-points
+will be registered on `/` unless we specify this argument.
+
+So what is different when working with modules? It mainly affects URL
+generation. Remember the :func:`~flask.url_for` function? When not
+working with modules it accepts the name of the function as first
+argument. This first argument is called the "endpoint". When you are
+working with modules you can use the name of the function like you did
+without, when generating modules from a function or template in the same
+module. If you want to generate the URL to another module, prefix it with
+the name of the module and a dot.
+
+Confused? Let's clear that up with some examples. Imagine you have a
+method in one module (say `admin`) and you want to redirect to a
+different module (say `frontend`). This would look like this::
+
+ @admin.route('/to_frontend')
+ def to_frontend():
+ return redirect(url_for('frontend.index'))
+
+ @frontend.route('/')
+ def index():
+ return "I'm the frontend index"
+
+Now let's say we only want to redirect to a different function in the same
+module. Then we can either use the full qualified endpoint name like we
+did in the example above, or we just use the function name::
+
+ @frontend.route('/to_index')
+ def to_index():
+ return redirect(url_for('index'))
+
+ @frontend.route('/')
+ def index():
+ return "I'm the index"
+
+.. _modules-and-resources:
+
+Modules and Resources
+---------------------
+
+.. versionadded:: 0.5
+
+If a module is located inside an actual Python package it may contain
+static files and templates. Imagine you have an application like this::
+
+
+ /yourapplication
+ __init__.py
+ /apps
+ __init__.py
+ /frontend
+ __init__.py
+ views.py
+ /static
+ style.css
+ /templates
+ index.html
+ about.html
+ ...
+ /admin
+ __init__.py
+ views.py
+ /static
+ style.css
+ /templates
+ list_items.html
+ show_item.html
+ ...
+
+The static folders automatically become exposed as URLs. For example if
+the `admin` module is exported with an URL prefix of ``/admin`` you can
+access the style css from its static folder by going to
+``/admin/static/style.css``. The URL endpoint for the static files of the
+admin would be ``'admin.static'``, similar to how you refer to the regular
+static folder of the whole application as ``'static'``.
+
+If you want to refer to the templates you just have to prefix it with the
+name of the module. So for the admin it would be
+``render_template('admin/list_items.html')`` and so on. It is not
+possible to refer to templates without the prefixed module name. This is
+explicit unlike URL rules.
+
+You also need to explicitly pass the ``url_prefix`` argument when
+registering your modules this way::
+
+ # in yourapplication/__init__.py
+ from flask import Flask
+ from yourapplication.apps.admin.views import admin
+ from yourapplication.apps.frontend.views import frontend
+
+
+ app = Flask(__name__)
+ app.register_module(admin, url_prefix='/admin')
+ app.register_module(frontend, url_prefix='/frontend')
+
+This is because Flask cannot infer the prefix from the package names.
+
+.. admonition:: References to Static Folders
+
+ Please keep in mind that if you are using unqualified endpoints by
+ default Flask will always assume the module's static folder, even if
+ there is no such folder.
+
+ If you want to refer to the application's static folder, use a leading
+ dot::
+
+ # this refers to the application's static folder
+ url_for('.static', filename='static.css')
+
+ # this refers to the current module's static folder
+ url_for('static', filename='static.css')
+
+ This is the case for all endpoints, not just static folders, but for
+ static folders it's more common that you will stumble upon this because
+ most applications will have a static folder in the application and not
+ a specific module.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/sqlalchemy.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,216 @@
+.. _sqlalchemy-pattern:
+
+SQLAlchemy in Flask
+===================
+
+Many people prefer `SQLAlchemy`_ for database access. In this case it's
+encouraged to use a package instead of a module for your flask application
+and drop the models into a separate module (:ref:`larger-applications`).
+While that is not necessary, it makes a lot of sense.
+
+There are four very common ways to use SQLAlchemy. I will outline each
+of them here:
+
+Flask-SQLAlchemy Extension
+--------------------------
+
+Because SQLAlchemy is a common database abstraction layer and object
+relational mapper that requires a little bit of configuration effort,
+there is a Flask extension that handles that for you. This is recommended
+if you want to get started quickly.
+
+You can download `Flask-SQLAlchemy`_ from `PyPI
+<http://pypi.python.org/pypi/Flask-SQLAlchemy>`_.
+
+.. _Flask-SQLAlchemy: http://packages.python.org/Flask-SQLAlchemy/
+
+
+Declarative
+-----------
+
+The declarative extension in SQLAlchemy is the most recent method of using
+SQLAlchemy. It allows you to define tables and models in one go, similar
+to how Django works. In addition to the following text I recommend the
+official documentation on the `declarative`_ extension.
+
+Here the example `database.py` module for your application::
+
+ from sqlalchemy import create_engine
+ from sqlalchemy.orm import scoped_session, sessionmaker
+ from sqlalchemy.ext.declarative import declarative_base
+
+ engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
+ db_session = scoped_session(sessionmaker(autocommit=False,
+ autoflush=False,
+ bind=engine))
+ Base = declarative_base()
+ Base.query = db_session.query_property()
+
+ def init_db():
+ # import all modules here that might define models so that
+ # they will be registered properly on the metadata. Otherwise
+ # you will have to import them first before calling init_db()
+ import yourapplication.models
+ Base.metadata.create_all(bind=engine)
+
+To define your models, just subclass the `Base` class that was created by
+the code above. If you are wondering why we don't have to care about
+threads here (like we did in the SQLite3 example above with the
+:data:`~flask.g` object): that's because SQLAlchemy does that for us
+already with the :class:`~sqlalchemy.orm.scoped_session`.
+
+To use SQLAlchemy in a declarative way with your application, you just
+have to put the following code into your application module. Flask will
+automatically remove database sessions at the end of the request for you::
+
+ from yourapplication.database import db_session
+
+ @app.after_request
+ def shutdown_session(response):
+ db_session.remove()
+ return response
+
+Here is an example model (put this into `models.py`, e.g.)::
+
+ from sqlalchemy import Column, Integer, String
+ from yourapplication.database import Base
+
+ class User(Base):
+ __tablename__ = 'users'
+ id = Column(Integer, primary_key=True)
+ name = Column(String(50), unique=True)
+ email = Column(String(120), unique=True)
+
+ def __init__(self, name=None, email=None):
+ self.name = name
+ self.email = email
+
+ def __repr__(self):
+ return '<User %r>' % (self.name)
+
+To create the database you can use the `init_db` function:
+
+>>> from yourapplication.database import init_db
+>>> init_db()
+
+You can insert entries into the database like this:
+
+>>> from yourapplication.database import db_session
+>>> from yourapplication.models import User
+>>> u = User('admin', 'admin@localhost')
+>>> db_session.add(u)
+>>> db_session.commit()
+
+Querying is simple as well:
+
+>>> User.query.all()
+[<User u'admin'>]
+>>> User.query.filter(User.name == 'admin').first()
+<User u'admin'>
+
+.. _SQLAlchemy: http://www.sqlalchemy.org/
+.. _declarative:
+ http://www.sqlalchemy.org/docs/reference/ext/declarative.html
+
+Manual Object Relational Mapping
+--------------------------------
+
+Manual object relational mapping has a few upsides and a few downsides
+versus the declarative approach from above. The main difference is that
+you define tables and classes separately and map them together. It's more
+flexible but a little more to type. In general it works like the
+declarative approach, so make sure to also split up your application into
+multiple modules in a package.
+
+Here is an example `database.py` module for your application::
+
+ from sqlalchemy import create_engine, MetaData
+ from sqlalchemy.orm import scoped_session, sessionmaker
+
+ engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
+ metadata = MetaData()
+ db_session = scoped_session(sessionmaker(autocommit=False,
+ autoflush=False,
+ bind=engine))
+ def init_db():
+ metadata.create_all(bind=engine)
+
+As for the declarative approach you need to close the session after
+each request. Put this into your application module::
+
+ from yourapplication.database import db_session
+
+ @app.after_request
+ def shutdown_session(response):
+ db_session.remove()
+ return response
+
+Here is an example table and model (put this into `models.py`)::
+
+ from sqlalchemy import Table, Column, Integer, String
+ from sqlalchemy.orm import mapper
+ from yourapplication.database import metadata, db_session
+
+ class User(object):
+ query = db_session.query_property()
+
+ def __init__(self, name=None, email=None):
+ self.name = name
+ self.email = email
+
+ def __repr__(self):
+ return '<User %r>' % (self.name, self.email)
+
+ users = Table('users', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('name', String(50), unique=True),
+ Column('email', String(120), unique=True)
+ )
+ mapper(User, users)
+
+Querying and inserting works exactly the same as in the example above.
+
+
+SQL Abstraction Layer
+---------------------
+
+If you just want to use the database system (and SQL) abstraction layer
+you basically only need the engine::
+
+ from sqlalchemy import create_engine, MetaData
+
+ engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
+ metadata = MetaData(bind=engine)
+
+Then you can either declare the tables in your code like in the examples
+above, or automatically load them::
+
+ users = Table('users', metadata, autoload=True)
+
+To insert data you can use the `insert` method. We have to get a
+connection first so that we can use a transaction:
+
+>>> con = engine.connect()
+>>> con.execute(users.insert(name='admin', email='admin@localhost'))
+
+SQLAlchemy will automatically commit for us.
+
+To query your database, you use the engine directly or use a connection:
+
+>>> users.select(users.c.id == 1).execute().first()
+(1, u'admin', u'admin@localhost')
+
+These results are also dict-like tuples:
+
+>>> r = users.select(users.c.id == 1).execute().first()
+>>> r['name']
+u'admin'
+
+You can also pass strings of SQL statements to the
+:meth:`~sqlalchemy.engine.base.Connection.execute` method:
+
+>>> engine.execute('select * from users where id = :1', [1]).first()
+(1, u'admin', u'admin@localhost')
+
+For more information about SQLAlchemy, head over to the
+`website <http://sqlalchemy.org/>`_.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/sqlite3.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,87 @@
+.. _sqlite3:
+
+Using SQLite 3 with Flask
+=========================
+
+In Flask you can implement the opening of database connections at the
+beginning of the request and closing at the end with the
+:meth:`~flask.Flask.before_request` and :meth:`~flask.Flask.after_request`
+decorators in combination with the special :class:`~flask.g` object.
+
+So here is a simple example of how you can use SQLite 3 with Flask::
+
+ import sqlite3
+ from flask import g
+
+ DATABASE = '/path/to/database.db'
+
+ def connect_db():
+ return sqlite3.connect(DATABASE)
+
+ @app.before_request
+ def before_request():
+ g.db = connect_db()
+
+ @app.after_request
+ def after_request(response):
+ g.db.close()
+ return response
+
+.. _easy-querying:
+
+Easy Querying
+-------------
+
+Now in each request handling function you can access `g.db` to get the
+current open database connection. To simplify working with SQLite, a
+helper function can be useful::
+
+ def query_db(query, args=(), one=False):
+ cur = g.db.execute(query, args)
+ rv = [dict((cur.description[idx][0], value)
+ for idx, value in enumerate(row)) for row in cur.fetchall()]
+ return (rv[0] if rv else None) if one else rv
+
+This handy little function makes working with the database much more
+pleasant than it is by just using the raw cursor and connection objects.
+
+Here is how you can use it::
+
+ for user in query_db('select * from users'):
+ print user['username'], 'has the id', user['user_id']
+
+Or if you just want a single result::
+
+ user = query_db('select * from users where username = ?',
+ [the_username], one=True)
+ if user is None:
+ print 'No such user'
+ else:
+ print the_username, 'has the id', user['user_id']
+
+To pass variable parts to the SQL statement, use a question mark in the
+statement and pass in the arguments as a list. Never directly add them to
+the SQL statement with string formatting because this makes it possible
+to attack the application using `SQL Injections
+<http://en.wikipedia.org/wiki/SQL_injection>`_.
+
+Initial Schemas
+---------------
+
+Relational databases need schemas, so applications often ship a
+`schema.sql` file that creates the database. It's a good idea to provide
+a function that creates the database based on that schema. This function
+can do that for you::
+
+ from contextlib import closing
+
+ def init_db():
+ with closing(connect_db()) as db:
+ with app.open_resource('schema.sql') as f:
+ db.cursor().executescript(f.read())
+ db.commit()
+
+You can then create such a database from the python shell:
+
+>>> from yourapplication import init_db
+>>> init_db()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/templateinheritance.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,69 @@
+.. _template-inheritance:
+
+Template Inheritance
+====================
+
+The most powerful part of Jinja is template inheritance. Template inheritance
+allows you to build a base "skeleton" template that contains all the common
+elements of your site and defines **blocks** that child templates can override.
+
+Sounds complicated but is very basic. It's easiest to understand it by starting
+with an example.
+
+
+Base Template
+-------------
+
+This template, which we'll call ``layout.html``, defines a simple HTML skeleton
+document that you might use for a simple two-column page. It's the job of
+"child" templates to fill the empty blocks with content:
+
+.. sourcecode:: html+jinja
+
+ <!doctype html>
+ <html>
+ <head>
+ {% block head %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+ <title>{% block title %}{% endblock %} - My Webpage</title>
+ {% endblock %}
+ </head>
+ <body>
+ <div id="content">{% block content %}{% endblock %}</div>
+ <div id="footer">
+ {% block footer %}
+ © Copyright 2010 by <a href="http://domain.invalid/">you</a>.
+ {% endblock %}
+ </div>
+ </body>
+
+In this example, the ``{% block %}`` tags define four blocks that child templates
+can fill in. All the `block` tag does is to tell the template engine that a
+child template may override those portions of the template.
+
+Child Template
+--------------
+
+A child template might look like this:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block title %}Index{% endblock %}
+ {% block head %}
+ {{ super() }}
+ <style type="text/css">
+ .important { color: #336699; }
+ </style>
+ {% endblock %}
+ {% block content %}
+ <h1>Index</h1>
+ <p class="important">
+ Welcome on my awesome homepage.
+ {% endblock %}
+
+The ``{% extends %}`` tag is the key here. It tells the template engine that
+this template "extends" another template. When the template system evaluates
+this template, first it locates the parent. The extends tag must be the
+first tag in the template. To render the contents of a block defined in
+the parent template, use ``{{ super() }}``.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/viewdecorators.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,168 @@
+View Decorators
+===============
+
+Python has a really interesting feature called function decorators. This
+allow some really neat things for web applications. Because each view in
+Flask is a function decorators can be used to inject additional
+functionality to one or more functions. The :meth:`~flask.Flask.route`
+decorator is the one you probably used already. But there are use cases
+for implementing your own decorator. For instance, imagine you have a
+view that should only be used by people that are logged in to. If a user
+goes to the site and is not logged in, he should be redirected to the
+login page. This is a good example of a use case where a decorator is an
+excellent solution.
+
+Login Required Decorator
+------------------------
+
+So let's implement such a decorator. A decorator is a function that
+returns a function. Pretty simple actually. The only thing you have to
+keep in mind when implementing something like this is to update the
+`__name__`, `__module__` and some other attributes of a function. This is
+often forgotten, but you don't have to do that by hand, there is a
+function for that that is used like a decorator (:func:`functools.wraps`).
+
+This example assumes that the login page is called ``'login'`` and that
+the current user is stored as `g.user` and `None` if there is no-one
+logged in::
+
+ from functools import wraps
+ from flask import g, request, redirect, url_for
+
+ def login_required(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ if g.user is None:
+ return redirect(url_for('login', next=request.url))
+ return f(*args, **kwargs)
+ return decorated_function
+
+So how would you use that decorator now? Apply it as innermost decorator
+to a view function. When applying further decorators, always remember
+that the :meth:`~flask.Flask.route` decorator is the outermost::
+
+ @app.route('/secret_page')
+ @login_required
+ def secret_page():
+ pass
+
+Caching Decorator
+-----------------
+
+Imagine you have a view function that does an expensive calculation and
+because of that you would like to cache the generated results for a
+certain amount of time. A decorator would be nice for that. We're
+assuming you have set up a cache like mentioned in :ref:`caching-pattern`.
+
+Here an example cache function. It generates the cache key from a
+specific prefix (actually a format string) and the current path of the
+request. Notice that we are using a function that first creates the
+decorator that then decorates the function. Sounds awful? Unfortunately
+it is a little bit more complex, but the code should still be
+straightforward to read.
+
+The decorated function will then work as follows
+
+1. get the unique cache key for the current request base on the current
+ path.
+2. get the value for that key from the cache. If the cache returned
+ something we will return that value.
+3. otherwise the original function is called and the return value is
+ stored in the cache for the timeout provided (by default 5 minutes).
+
+Here the code::
+
+ from functools import wraps
+ from flask import request
+
+ def cached(timeout=5 * 60, key='view/%s'):
+ def decorator(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ cache_key = key % request.path
+ rv = cache.get(cache_key)
+ if rv is not None:
+ return rv
+ rv = f(*args, **kwargs)
+ cache.set(cache_key, rv, timeout=timeout)
+ return rv
+ return decorated_function
+ return decorator
+
+Notice that this assumes an instantiated `cache` object is available, see
+:ref:`caching-pattern` for more information.
+
+
+Templating Decorator
+--------------------
+
+A common pattern invented by the TurboGears guys a while back is a
+templating decorator. The idea of that decorator is that you return a
+dictionary with the values passed to the template from the view function
+and the template is automatically rendered. With that, the following
+three examples do exactly the same::
+
+ @app.route('/')
+ def index():
+ return render_template('index.html', value=42)
+
+ @app.route('/')
+ @templated('index.html')
+ def index():
+ return dict(value=42)
+
+ @app.route('/')
+ @templated()
+ def index():
+ return dict(value=42)
+
+As you can see, if no template name is provided it will use the endpoint
+of the URL map with dots converted to slashes + ``'.html'``. Otherwise
+the provided template name is used. When the decorated function returns,
+the dictionary returned is passed to the template rendering function. If
+`None` is returned, an empty dictionary is assumed, if something else than
+a dictionary is returned we return it from the function unchanged. That
+way you can still use the redirect function or return simple strings.
+
+Here the code for that decorator::
+
+ from functools import wraps
+ from flask import request
+
+ def templated(template=None):
+ def decorator(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ template_name = template
+ if template_name is None:
+ template_name = request.endpoint \
+ .replace('.', '/') + '.html'
+ ctx = f(*args, **kwargs)
+ if ctx is None:
+ ctx = {}
+ elif not isinstance(ctx, dict):
+ return ctx
+ return render_template(template_name, **ctx)
+ return decorated_function
+ return decorator
+
+
+Endpoint Decorator
+------------------
+
+When you want to use the werkzeug routing system for more flexibility you
+need to map the endpoint as defined in the :class:`~werkzeug.routing.Rule`
+to a view function. This is possible with this decorator. For example::
+
+ from flask import Flask
+ from werkzeug.routing import Rule
+
+ app = Flask(__name__)
+ app.url_map.add(Rule('/', endpoint='index'))
+
+ @app.endpoint('index')
+ def my_index():
+ return "Hello world"
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/patterns/wtforms.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,124 @@
+Form Validation with WTForms
+============================
+
+When you have to work with form data submitted by a browser view code
+quickly becomes very hard to read. There are libraries out there designed
+to make this process easier to manage. One of them is `WTForms`_ which we
+will handle here. If you find yourself in the situation of having many
+forms, you might want to give it a try.
+
+When you are working with WTForms you have to define your forms as classes
+first. I recommend breaking up the application into multiple modules
+(:ref:`larger-applications`) for that and adding a separate module for the
+forms.
+
+.. admonition:: Getting most of WTForms with an Extension
+
+ The `Flask-WTF`_ extension expands on this pattern and adds a few
+ handful little helpers that make working with forms and Flask more
+ fun. You can get it from `PyPI
+ <http://pypi.python.org/pypi/Flask-WTF>`_.
+
+.. _Flask-WTF: http://packages.python.org/Flask-WTF/
+
+The Forms
+---------
+
+This is an example form for a typical registration page::
+
+ from wtforms import Form, BooleanField, TextField, validators
+
+ class RegistrationForm(Form):
+ username = TextField('Username', [validators.Length(min=4, max=25)])
+ email = TextField('Email Address', [validators.Length(min=6, max=35)])
+ password = PasswordField('New Password', [
+ validators.Required(),
+ validators.EqualTo('confirm', message='Passwords must match')
+ ])
+ confirm = PasswordField('Repeat Password')
+ accept_tos = BooleanField('I accept the TOS', [validators.Required()])
+
+In the View
+-----------
+
+In the view function, the usage of this form looks like this::
+
+ @app.route('/register', methods=['GET', 'POST'])
+ def register():
+ form = RegistrationForm(request.form)
+ if request.method == 'POST' and form.validate():
+ user = User(form.username.data, form.email.data,
+ form.password.data)
+ db_session.add(user)
+ flash('Thanks for registering')
+ return redirect(url_for('login'))
+ return render_template('register.html', form=form)
+
+Notice that we are implying that the view is using SQLAlchemy here
+(:ref:`sqlalchemy-pattern`) but this is no requirement of course. Adapt
+the code as necessary.
+
+Things to remember:
+
+1. create the form from the request :attr:`~flask.request.form` value if
+ the data is submitted via the HTTP `POST` method and
+ :attr:`~flask.request.args` if the data is submitted as `GET`.
+2. to validate the data, call the :func:`~wtforms.form.Form.validate`
+ method which will return `True` if the data validates, `False`
+ otherwise.
+3. to access individual values from the form, access `form.<NAME>.data`.
+
+Forms in Templates
+------------------
+
+Now to the template side. When you pass the form to the templates you can
+easily render them there. Look at the following example template to see
+how easy this is. WTForms does half the form generation for us already.
+To make it even nicer, we can write a macro that renders a field with
+label and a list of errors if there are any.
+
+Here's an example `_formhelpers.html` template with such a macro:
+
+.. sourcecode:: html+jinja
+
+ {% macro render_field(field) %}
+ <dt>{{ field.label }}
+ <dd>{{ field(**kwargs)|safe }}
+ {% if field.errors %}
+ <ul class="errors">
+ {% for error in field.errors %}<li>{{ error }}{% endfor %}
+ </ul>
+ {% endif %}
+ </dd>
+ {% endmacro %}
+
+This macro accepts a couple of keyword arguments that are forwarded to
+WTForm's field function that renders the field for us. The keyword
+arguments will be inserted as HTML attributes. So for example you can
+call ``render_field(form.username, class='username')`` to add a class to
+the input element. Note that WTForms returns standard Python unicode
+strings, so we have to tell Jinja2 that this data is already HTML escaped
+with the `|safe` filter.
+
+Here the `register.html` template for the function we used above which
+takes advantage of the `_formhelpers.html` template:
+
+.. sourcecode:: html+jinja
+
+ {% from "_formhelpers.html" import render_field %}
+ <form method="post" action="/register">
+ <dl>
+ {{ render_field(form.username) }}
+ {{ render_field(form.email) }}
+ {{ render_field(form.password) }}
+ {{ render_field(form.confirm) }}
+ {{ render_field(form.accept_tos) }}
+ </dl>
+ <p><input type=submit value=Register>
+ </form>
+
+For more information about WTForms, head over to the `WTForms
+website`_.
+
+.. _WTForms: http://wtforms.simplecodes.com/
+.. _WTForms website: http://wtforms.simplecodes.com/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/quickstart.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,745 @@
+.. _quickstart:
+
+Quickstart
+==========
+
+Eager to get started? This page gives a good introduction in how to get
+started with Flask. This assumes you already have Flask installed. If
+you do not, head over to the :ref:`installation` section.
+
+
+A Minimal Application
+---------------------
+
+A minimal Flask application looks something like that::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ @app.route('/')
+ def hello_world():
+ return "Hello World!"
+
+ if __name__ == '__main__':
+ app.run()
+
+Just save it as `hello.py` or something similar and run it with your
+Python interpreter. Make sure to not call your application `flask.py`
+because this would conflict with Flask itself.
+
+::
+
+ $ python hello.py
+ * Running on http://127.0.0.1:5000/
+
+Head over to `http://127.0.0.1:5000/ <http://127.0.0.1:5000/>`_, you should
+see your hello world greeting.
+
+So what did that code do?
+
+1. First we imported the :class:`~flask.Flask` class. An instance of this
+ class will be our WSGI application. The first argument is the name of
+ the application's module. If you are using a single module (like here)
+ you should use `__name__` because depending on if it's started as
+ application or imported as module the name will be different
+ (``'__main__'`` versus the actual import name). For more information
+ on that, have a look at the :class:`~flask.Flask` documentation.
+2. Next we create an instance of it. We pass it the name of the module /
+ package. This is needed so that Flask knows where it should look for
+ templates, static files and so on.
+3. Then we use the :meth:`~flask.Flask.route` decorator to tell Flask
+ what URL should trigger our function.
+4. The function then has a name which is also used to generate URLs to
+ that particular function, and returns the message we want to display in
+ the user's browser.
+5. Finally we use the :meth:`~flask.Flask.run` function to run the
+ local server with our application. The ``if __name__ == '__main__':``
+ makes sure the server only runs if the script is executed directly from
+ the Python interpreter and not used as imported module.
+
+To stop the server, hit control-C.
+
+.. _public-server:
+
+.. admonition:: Externally Visible Server
+
+ If you run the server you will notice that the server is only available
+ from your own computer, not from any other in the network. This is the
+ default because in debugging mode a user of the application can execute
+ arbitrary Python code on your computer. If you have `debug` disabled
+ or trust the users on your network, you can make the server publicly
+ available.
+
+ Just change the call of the :meth:`~flask.Flask.run` method to look
+ like this::
+
+ app.run(host='0.0.0.0')
+
+ This tells your operating system to listen on a public IP.
+
+
+Debug Mode
+----------
+
+The :meth:`~flask.Flask.run` method is nice to start a local
+development server, but you would have to restart it manually after each
+change you do to code. That is not very nice and Flask can do better. If
+you enable the debug support the server will reload itself on code changes
+and also provide you with a helpful debugger if things go wrong.
+
+There are two ways to enable debugging. Either set that flag on the
+application object::
+
+ app.debug = True
+ app.run()
+
+Or pass it to run::
+
+ app.run(debug=True)
+
+Both will have exactly the same effect.
+
+.. admonition:: Attention
+
+ Even though the interactive debugger does not work in forking environments
+ (which makes it nearly impossible to use on production servers), it still
+ allows the execution of arbitrary code. That makes it a major security
+ risk and therefore it **must never be used on production machines**.
+
+Screenshot of the debugger in action:
+
+.. image:: _static/debugger.png
+ :align: center
+ :class: screenshot
+ :alt: screenshot of debugger in action
+
+
+Routing
+-------
+
+Modern web applications have beautiful URLs. This helps people remember
+the URLs which is especially handy for applications that are used from
+mobile devices with slower network connections. If the user can directly
+go to the desired page without having to hit the index page it is more
+likely he will like the page and come back next time.
+
+As you have seen above, the :meth:`~flask.Flask.route` decorator is used
+to bind a function to a URL. Here are some basic examples::
+
+ @app.route('/')
+ def index():
+ return 'Index Page'
+
+ @app.route('/hello')
+ def hello():
+ return 'Hello World'
+
+But there is more to it! You can make certain parts of the URL dynamic
+and attach multiple rules to a function.
+
+Variable Rules
+``````````````
+
+To add variable parts to a URL you can mark these special sections as
+``<variable_name>``. Such a part is then passed as keyword argument to
+your function. Optionally a converter can be specified by specifying a
+rule with ``<converter:variable_name>``. Here are some nice examples::
+
+ @app.route('/user/<username>')
+ def show_user_profile(username):
+ # show the user profile for that user
+ pass
+
+ @app.route('/post/<int:post_id>')
+ def show_post(post_id):
+ # show the post with the given id, the id is an integer
+ pass
+
+The following converters exist:
+
+=========== ===========================================
+`int` accepts integers
+`float` like `int` but for floating point values
+`path` like the default but also accepts slashes
+=========== ===========================================
+
+.. admonition:: Unique URLs / Redirection Behaviour
+
+ Flask's URL rules are based on Werkzeug's routing module. The idea
+ behind that module is to ensure nice looking and also unique URLs based
+ on behaviour Apache and earlier servers coined.
+
+ Take these two rules::
+
+ @app.route('/projects/')
+ def projects():
+ pass
+
+ @app.route('/about')
+ def about():
+ pass
+
+ They look rather similar, the difference is the trailing slash in the
+ URL *definition*. In the first case, the canonical URL for the
+ `projects` endpoint has a trailing slash. It's similar to a folder in
+ that sense. Accessing it without a trailing slash will cause Flask to
+ redirect to the canonical URL with the trailing slash.
+
+ However in the second case the URL is defined without a slash so it
+ behaves similar to a file and accessing the URL with a trailing slash
+ will be a 404 error.
+
+ Why is this? This allows relative URLs to continue working if users
+ access the page when they forget a trailing slash. This behaviour is
+ also consistent with how Apache and other servers work. Also, the URLs
+ will stay unique which helps search engines not indexing the same page
+ twice.
+
+
+.. _url-building:
+
+URL Building
+````````````
+
+If it can match URLs, can it also generate them? Of course it can. To
+build a URL to a specific function you can use the :func:`~flask.url_for`
+function. It accepts the name of the function as first argument and a
+number of keyword arguments, each corresponding to the variable part of
+the URL rule. Unknown variable parts are appended to the URL as query
+parameter. Here are some examples:
+
+>>> from flask import Flask, url_for
+>>> app = Flask(__name__)
+>>> @app.route('/')
+... def index(): pass
+...
+>>> @app.route('/login')
+... def login(): pass
+...
+>>> @app.route('/user/<username>')
+... def profile(username): pass
+...
+>>> with app.test_request_context():
+... print url_for('index')
+... print url_for('login')
+... print url_for('login', next='/')
+... print url_for('profile', username='John Doe')
+...
+/
+/login
+/login?next=/
+/user/John%20Doe
+
+(This also uses the :meth:`~flask.Flask.test_request_context` method
+explained below. It basically tells Flask to think we are handling a
+request even though we are not, we are in an interactive Python shell.
+Have a look at the explanation below. :ref:`context-locals`).
+
+Why would you want to build URLs instead of hardcoding them in your
+templates? There are three good reasons for this:
+
+1. reversing is often more descriptive than hardcoding the URLs. Also and
+ more importantly you can change URLs in one go without having to change
+ the URLs all over the place.
+2. URL building will handle escaping of special characters and Unicode
+ data transparently for you, you don't have to deal with that.
+3. If your application is placed outside the URL root (so say in
+ ``/myapplication`` instead of ``/``), :func:`~flask.url_for` will
+ handle that properly for you.
+
+
+HTTP Methods
+````````````
+
+HTTP (the protocol web applications are speaking) knows different methods
+to access URLs. By default a route only answers to `GET` requests, but
+that can be changed by providing the `methods` argument to the
+:meth:`~flask.Flask.route` decorator. Here are some examples::
+
+ @app.route('/login', methods=['GET', 'POST'])
+ def login():
+ if request.method == 'POST':
+ do_the_login()
+ else:
+ show_the_login_form()
+
+If `GET` is present, `HEAD` will be added automatically for you. You
+don't have to deal with that. It will also make sure that `HEAD` requests
+are handled like the `HTTP RFC`_ (the document describing the HTTP
+protocol) demands, so you can completely ignore that part of the HTTP
+specification. Likewise as of Flask 0.6, `OPTIONS` is implemented for you
+as well automatically.
+
+You have no idea what an HTTP method is? Worry not, here is a quick
+introduction to HTTP methods and why they matter:
+
+The HTTP method (also often called "the verb") tells the server what the
+clients wants to *do* with the requested page. The following methods are
+very common:
+
+`GET`
+ The browser tells the server to just *get* the information stored on
+ that page and send it. This is probably the most common method.
+
+`HEAD`
+ The browser tells the server to get the information, but it is only
+ interested in the *headers*, not the content of the page. An
+ application is supposed to handle that as if a `GET` request was
+ received but to not deliver the actual content. In Flask you don't
+ have to deal with that at all, the underlying Werkzeug library handles
+ that for you.
+
+`POST`
+ The browser tells the server that it wants to *post* some new
+ information to that URL and that the server must ensure the data is
+ stored and only stored once. This is how HTML forms are usually
+ transmitting data to the server.
+
+`PUT`
+ Similar to `POST` but the server might trigger the store procedure
+ multiple times by overwriting the old values more than once. Now you
+ might be asking why is this useful, but there are some good reasons
+ to do it this way. Consider that the connection gets lost during
+ transmission: in this situation a system between the browser and the
+ server might receive the request safely a second time without breaking
+ things. With `POST` that would not be possible because it must only
+ be triggered once.
+
+`DELETE`
+ Remove the information at the given location.
+
+`OPTIONS`
+ Provides a quick way for a client to figure out which methods are
+ supported by this URL. Starting with Flask 0.6, this is implemented
+ for you automatically.
+
+Now the interesting part is that in HTML4 and XHTML1, the only methods a
+form can submit to the server are `GET` and `POST`. But with JavaScript
+and future HTML standards you can use the other methods as well. Furthermore
+HTTP has become quite popular lately and browsers are no longer the only
+clients that are using HTTP. For instance, many revision control system
+use it.
+
+.. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt
+
+Static Files
+------------
+
+Dynamic web applications need static files as well. That's usually where
+the CSS and JavaScript files are coming from. Ideally your web server is
+configured to serve them for you, but during development Flask can do that
+as well. Just create a folder called `static` in your package or next to
+your module and it will be available at `/static` on the application.
+
+To generate URLs to that part of the URL, use the special ``'static'`` URL
+name::
+
+ url_for('static', filename='style.css')
+
+The file has to be stored on the filesystem as ``static/style.css``.
+
+Rendering Templates
+-------------------
+
+Generating HTML from within Python is not fun, and actually pretty
+cumbersome because you have to do the HTML escaping on your own to keep
+the application secure. Because of that Flask configures the `Jinja2
+<http://jinja.pocoo.org/2/>`_ template engine for you automatically.
+
+To render a template you can use the :func:`~flask.render_template`
+method. All you have to do is to provide the name of the template and the
+variables you want to pass to the template engine as keyword arguments.
+Here's a simple example of how to render a template::
+
+ from flask import render_template
+
+ @app.route('/hello/')
+ @app.route('/hello/<name>')
+ def hello(name=None):
+ return render_template('hello.html', name=name)
+
+Flask will look for templates in the `templates` folder. So if your
+application is a module, that folder is next to that module, if it's a
+package it's actually inside your package:
+
+**Case 1**: a module::
+
+ /application.py
+ /templates
+ /hello.html
+
+**Case 2**: a package::
+
+ /application
+ /__init__.py
+ /templates
+ /hello.html
+
+For templates you can use the full power of Jinja2 templates. Head over
+to the :ref:`templating` section of the documentation or the official
+`Jinja2 Template Documentation
+<http://jinja.pocoo.org/2/documentation/templates>`_ for more information.
+
+Here is an example template:
+
+.. sourcecode:: html+jinja
+
+ <!doctype html>
+ <title>Hello from Flask</title>
+ {% if name %}
+ <h1>Hello {{ name }}!</h1>
+ {% else %}
+ <h1>Hello World!</h1>
+ {% endif %}
+
+Inside templates you also have access to the :class:`~flask.request`,
+:class:`~flask.session` and :class:`~flask.g` [#]_ objects
+as well as the :func:`~flask.get_flashed_messages` function.
+
+Templates are especially useful if inheritance is used. If you want to
+know how that works, head over to the :ref:`template-inheritance` pattern
+documentation. Basically template inheritance makes it possible to keep
+certain elements on each page (like header, navigation and footer).
+
+Automatic escaping is enabled, so if name contains HTML it will be escaped
+automatically. If you can trust a variable and you know that it will be
+safe HTML (because for example it came from a module that converts wiki
+markup to HTML) you can mark it as safe by using the
+:class:`~jinja2.Markup` class or by using the ``|safe`` filter in the
+template. Head over to the Jinja 2 documentation for more examples.
+
+Here is a basic introduction to how the :class:`~jinja2.Markup` class works:
+
+>>> from flask import Markup
+>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
+Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
+>>> Markup.escape('<blink>hacker</blink>')
+Markup(u'<blink>hacker</blink>')
+>>> Markup('<em>Marked up</em> » HTML').striptags()
+u'Marked up \xbb HTML'
+
+.. versionchanged:: 0.5
+
+ Autoescaping is no longer enabled for all templates. The following
+ extensions for templates trigger autoescaping: ``.html``, ``.htm``,
+ ``.xml``, ``.xhtml``. Templates loaded from a string will have
+ autoescaping disabled.
+
+.. [#] Unsure what that :class:`~flask.g` object is? It's something in which
+ you can store information for your own needs, check the documentation of
+ that object (:class:`~flask.g`) and the :ref:`sqlite3` for more
+ information.
+
+
+Accessing Request Data
+----------------------
+
+For web applications it's crucial to react to the data a client sent to
+the server. In Flask this information is provided by the global
+:class:`~flask.request` object. If you have some experience with Python
+you might be wondering how that object can be global and how Flask
+manages to still be threadsafe. The answer are context locals:
+
+
+.. _context-locals:
+
+Context Locals
+``````````````
+
+.. admonition:: Insider Information
+
+ If you want to understand how that works and how you can implement
+ tests with context locals, read this section, otherwise just skip it.
+
+Certain objects in Flask are global objects, but not of the usual kind.
+These objects are actually proxies to objects that are local to a specific
+context. What a mouthful. But that is actually quite easy to understand.
+
+Imagine the context being the handling thread. A request comes in and the
+webserver decides to spawn a new thread (or something else, the
+underlying object is capable of dealing with other concurrency systems
+than threads as well). When Flask starts its internal request handling it
+figures out that the current thread is the active context and binds the
+current application and the WSGI environments to that context (thread).
+It does that in an intelligent way that one application can invoke another
+application without breaking.
+
+So what does this mean to you? Basically you can completely ignore that
+this is the case unless you are doing something like unittesting. You
+will notice that code that depends on a request object will suddenly break
+because there is no request object. The solution is creating a request
+object yourself and binding it to the context. The easiest solution for
+unittesting is by using the :meth:`~flask.Flask.test_request_context`
+context manager. In combination with the `with` statement it will bind a
+test request so that you can interact with it. Here is an example::
+
+ from flask import request
+
+ with app.test_request_context('/hello', method='POST'):
+ # now you can do something with the request until the
+ # end of the with block, such as basic assertions:
+ assert request.path == '/hello'
+ assert request.method == 'POST'
+
+The other possibility is passing a whole WSGI environment to the
+:meth:`~flask.Flask.request_context` method::
+
+ from flask import request
+
+ with app.request_context(environ):
+ assert request.method == 'POST'
+
+The Request Object
+``````````````````
+
+The request object is documented in the API section and we will not cover
+it here in detail (see :class:`~flask.request`). Here is a broad overview of
+some of the most common operations. First of all you have to import it from
+the `flask` module::
+
+ from flask import request
+
+The current request method is available by using the
+:attr:`~flask.request.method` attribute. To access form data (data
+transmitted in a `POST` or `PUT` request) you can use the
+:attr:`~flask.request.form` attribute. Here is a full example of the two
+attributes mentioned above::
+
+ @app.route('/login', methods=['POST', 'GET'])
+ def login():
+ error = None
+ if request.method == 'POST':
+ if valid_login(request.form['username'],
+ request.form['password']):
+ return log_the_user_in(request.form['username'])
+ else:
+ error = 'Invalid username/password'
+ # this is executed if the request method was GET or the
+ # credentials were invalid
+
+What happens if the key does not exist in the `form` attribute? In that
+case a special :exc:`KeyError` is raised. You can catch it like a
+standard :exc:`KeyError` but if you don't do that, a HTTP 400 Bad Request
+error page is shown instead. So for many situations you don't have to
+deal with that problem.
+
+To access parameters submitted in the URL (``?key=value``) you can use the
+:attr:`~flask.request.args` attribute::
+
+ searchword = request.args.get('q', '')
+
+We recommend accessing URL parameters with `get` or by catching the
+`KeyError` because users might change the URL and presenting them a 400
+bad request page in that case is not user friendly.
+
+For a full list of methods and attributes of the request object, head over
+to the :class:`~flask.request` documentation.
+
+
+File Uploads
+````````````
+
+You can handle uploaded files with Flask easily. Just make sure not to
+forget to set the ``enctype="multipart/form-data"`` attribute on your HTML
+form, otherwise the browser will not transmit your files at all.
+
+Uploaded files are stored in memory or at a temporary location on the
+filesystem. You can access those files by looking at the
+:attr:`~flask.request.files` attribute on the request object. Each
+uploaded file is stored in that dictionary. It behaves just like a
+standard Python :class:`file` object, but it also has a
+:meth:`~werkzeug.FileStorage.save` method that allows you to store that
+file on the filesystem of the server. Here is a simple example showing how
+that works::
+
+ from flask import request
+
+ @app.route('/upload', methods=['GET', 'POST'])
+ def upload_file():
+ if request.method == 'POST':
+ f = request.files['the_file']
+ f.save('/var/www/uploads/uploaded_file.txt')
+ ...
+
+If you want to know how the file was named on the client before it was
+uploaded to your application, you can access the
+:attr:`~werkzeug.FileStorage.filename` attribute. However please keep in
+mind that this value can be forged so never ever trust that value. If you
+want to use the filename of the client to store the file on the server,
+pass it through the :func:`~werkzeug.secure_filename` function that
+Werkzeug provides for you::
+
+ from flask import request
+ from werkzeug import secure_filename
+
+ @app.route('/upload', methods=['GET', 'POST'])
+ def upload_file():
+ if request.method == 'POST':
+ f = request.files['the_file']
+ f.save('/var/www/uploads/' + secure_filename(f.filename))
+ ...
+
+For some better examples, checkout the :ref:`uploading-files` pattern.
+
+Cookies
+```````
+
+To access cookies you can use the :attr:`~flask.request.cookies`
+attribute. Again this is a dictionary with all the cookies the client
+transmits. If you want to use sessions, do not use the cookies directly
+but instead use the :ref:`sessions` in Flask that add some security on top
+of cookies for you.
+
+
+Redirects and Errors
+--------------------
+
+To redirect a user to somewhere else you can use the
+:func:`~flask.redirect` function. To abort a request early with an error
+code use the :func:`~flask.abort` function. Here an example how this works::
+
+ from flask import abort, redirect, url_for
+
+ @app.route('/')
+ def index():
+ return redirect(url_for('login'))
+
+ @app.route('/login')
+ def login():
+ abort(401)
+ this_is_never_executed()
+
+This is a rather pointless example because a user will be redirected from
+the index to a page he cannot access (401 means access denied) but it
+shows how that works.
+
+By default a black and white error page is shown for each error code. If
+you want to customize the error page, you can use the
+:meth:`~flask.Flask.errorhandler` decorator::
+
+ from flask import render_template
+
+ @app.errorhandler(404)
+ def page_not_found(error):
+ return render_template('page_not_found.html'), 404
+
+Note the ``404`` after the :func:`~flask.render_template` call. This
+tells Flask that the status code of that page should be 404 which means
+not found. By default 200 is assumed which translates to: all went well.
+
+.. _sessions:
+
+Sessions
+--------
+
+Besides the request object there is also a second object called
+:class:`~flask.session` that allows you to store information specific to a
+user from one request to the next. This is implemented on top of cookies
+for you and signs the cookies cryptographically. What this means is that
+the user could look at the contents of your cookie but not modify it,
+unless he knows the secret key used for signing.
+
+In order to use sessions you have to set a secret key. Here is how
+sessions work::
+
+ from flask import Flask, session, redirect, url_for, escape, request
+
+ app = Flask(__name__)
+
+ @app.route('/')
+ def index():
+ if 'username' in session:
+ return 'Logged in as %s' % escape(session['username'])
+ return 'You are not logged in'
+
+ @app.route('/login', methods=['GET', 'POST'])
+ def login():
+ if request.method == 'POST':
+ session['username'] = request.form['username']
+ return redirect(url_for('index'))
+ return '''
+ <form action="" method="post">
+ <p><input type=text name=username>
+ <p><input type=submit value=Login>
+ </form>
+ '''
+
+ @app.route('/logout')
+ def logout():
+ # remove the username from the session if its there
+ session.pop('username', None)
+ return redirect(url_for('index'))
+
+ # set the secret key. keep this really secret:
+ app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
+
+The here mentioned :func:`~flask.escape` does escaping for you if you are
+not using the template engine (like in this example).
+
+.. admonition:: How to generate good secret keys
+
+ The problem with random is that it's hard to judge what random is. And
+ a secret key should be as random as possible. Your operating system
+ has ways to generate pretty random stuff based on a cryptographic
+ random generator which can be used to get such a key:
+
+ >>> import os
+ >>> os.urandom(24)
+ '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
+
+ Just take that thing and copy/paste it into your code and you're done.
+
+Message Flashing
+----------------
+
+Good applications and user interfaces are all about feedback. If the user
+does not get enough feedback he will probably end up hating the
+application. Flask provides a really simple way to give feedback to a
+user with the flashing system. The flashing system basically makes it
+possible to record a message at the end of a request and access it next
+request and only next request. This is usually combined with a layout
+template that does this.
+
+To flash a message use the :func:`~flask.flash` method, to get hold of the
+messages you can use :func:`~flask.get_flashed_messages` which is also
+available in the templates. Check out the :ref:`message-flashing-pattern`
+for a full example.
+
+Logging
+-------
+
+.. versionadded:: 0.3
+
+Sometimes you might be in a situation where you deal with data that
+should be correct, but actually is not. For example you may have some client
+side code that sends an HTTP request to the server but it's obviously
+malformed. This might be caused by a user tempering with the data, or the
+client code failing. Most of the time, it's okay to reply with ``400 Bad
+Request`` in that situation, but sometimes that won't do and the code has
+to continue working.
+
+You may still want to log that something fishy happened. This is where
+loggers come in handy. As of Flask 0.3 a logger is preconfigured for you
+to use.
+
+Here are some example log calls::
+
+ app.logger.debug('A value for debugging')
+ app.logger.warning('A warning occurred (%d apples)', 42)
+ app.logger.error('An error occurred')
+
+The attached :attr:`~flask.Flask.logger` is a standard logging
+:class:`~logging.Logger`, so head over to the official `logging
+documentation <http://docs.python.org/library/logging.html>`_ for more
+information.
+
+Hooking in WSGI Middlewares
+---------------------------
+
+If you want to add a WSGI middleware to your application you can wrap the
+internal WSGI application. For example if you want to use one of the
+middlewares from the Werkzeug package to work around bugs in lighttpd, you
+can do it like this::
+
+ from werkzeug.contrib.fixers import LighttpdCGIRootFix
+ app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/security.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,168 @@
+Security Considerations
+=======================
+
+Web applications usually face all kinds of security problems and it's very
+hard to get everything right. Flask tries to solve a few of these things
+for you, but there are a couple more you have to take care of yourself.
+
+.. _xss:
+
+Cross-Site Scripting (XSS)
+--------------------------
+
+Cross site scripting is the concept of injecting arbitrary HTML (and with
+it JavaScript) into the context of a website. To remedy this, developers
+have to properly escape text so that it cannot include arbitrary HTML
+tags. For more information on that have a look at the Wikipedia article
+on `Cross-Site Scripting
+<http://en.wikipedia.org/wiki/Cross-site_scripting>`_.
+
+Flask configures Jinja2 to automatically escape all values unless
+explicitly told otherwise. This should rule out all XSS problems caused
+in templates, but there are still other places where you have to be
+careful:
+
+- generating HTML without the help of Jinja2
+- calling :class:`~flask.Markup` on data submitted by users
+- sending out HTML from uploaded files, never do that, use the
+ `Content-Disposition: attachment` header to prevent that problem.
+- sending out textfiles from uploaded files. Some browsers are using
+ content-type guessing based on the first few bytes so users could
+ trick a browser to execute HTML.
+
+Another thing that is very important are unquoted attributes. While
+Jinja2 can protect you from XSS issues by escaping HTML, there is one
+thing it cannot protect you from: XSS by attribute injection. To counter
+this possible attack vector, be sure to always quote your attributes with
+either double or single quotes when using Jinja expressions in them:
+
+.. sourcecode:: html+jinja
+
+ <a href="{{ href }}">the text</a>
+
+Why is this necessary? Because if you would not be doing that, an
+attacker could easily inject custom JavaScript handlers. For example an
+attacker could inject this piece of HTML+JavaScript:
+
+.. sourcecode:: html
+
+ onmouseover=alert(document.cookie)
+
+When the user would then move with the mouse over the link, the cookie
+would be presented to the user in an alert window. But instead of showing
+the cookie to the user, a good attacker might also execute any other
+JavaScript code. In combination with CSS injections the attacker might
+even make the element fill out the entire page so that the user would
+just have to have the mouse anywhere on the page to trigger the attack.
+
+Cross-Site Request Forgery (CSRF)
+---------------------------------
+
+Another big problem is CSRF. This is a very complex topic and I won't
+outline it here in detail just mention what it is and how to theoretically
+prevent it.
+
+If your authentication information is stored in cookies, you have implicit
+state management. The state of "being logged in" is controlled by a
+cookie, and that cookie is sent with each request to a page.
+Unfortunately that includes requests triggered by 3rd party sites. If you
+don't keep that in mind, some people might be able to trick your
+application's users with social engineering to do stupid things without
+them knowing.
+
+Say you have a specific URL that, when you sent `POST` requests to will
+delete a user's profile (say `http://example.com/user/delete`). If an
+attacker now creates a page that sends a post request to that page with
+some JavaScript he just has to trick some users to load that page and
+their profiles will end up being deleted.
+
+Imagine you were to run Facebook with millions of concurrent users and
+someone would send out links to images of little kittens. When users
+would go to that page, their profiles would get deleted while they are
+looking at images of fluffy cats.
+
+How can you prevent that? Basically for each request that modifies
+content on the server you would have to either use a one-time token and
+store that in the cookie **and** also transmit it with the form data.
+After receiving the data on the server again, you would then have to
+compare the two tokens and ensure they are equal.
+
+Why does Flask not do that for you? The ideal place for this to happen is
+the form validation framework, which does not exist in Flask.
+
+.. _json-security:
+
+JSON Security
+-------------
+
+JSON itself is a high-level serialization format, so there is barely
+anything that could cause security problems, right? You can't declare
+recursive structures that could cause problems and the only thing that
+could possibly break are very large responses that can cause some kind of
+denial of service at the receiver's side.
+
+However there is a catch. Due to how browsers work the CSRF issue comes
+up with JSON unfortunately. Fortunately there is also a weird part of the
+JavaScript specification that can be used to solve that problem easily and
+Flask is kinda doing that for you by preventing you from doing dangerous
+stuff. Unfortunately that protection is only there for
+:func:`~flask.jsonify` so you are still at risk when using other ways to
+generate JSON.
+
+So what is the issue and how to avoid it? The problem are arrays at
+toplevel in JSON. Imagine you send the following data out in a JSON
+request. Say that's exporting the names and email addresses of all your
+friends for a part of the user interface that is written in JavaScript.
+Not very uncommon:
+
+.. sourcecode:: javascript
+
+ [
+ {"username": "admin",
+ "email": "admin@localhost"}
+ ]
+
+And it is doing that of course only as long as you are logged in and only
+for you. And it is doing that for all `GET` requests to a certain URL,
+say the URL for that request is
+``http://example.com/api/get_friends.json``.
+
+So now what happens if a clever hacker is embedding this to his website
+and social engineers a victim to visiting his site:
+
+.. sourcecode:: html
+
+ <script type=text/javascript>
+ var captured = [];
+ var oldArray = Array;
+ function Array() {
+ var obj = this, id = 0, capture = function(value) {
+ obj.__defineSetter__(id++, capture);
+ if (value)
+ captured.push(value);
+ };
+ capture();
+ }
+ </script>
+ <script type=text/javascript
+ src=http://example.com/api/get_friends.json></script>
+ <script type=text/javascript>
+ Array = oldArray;
+ // now we have all the data in the captured array.
+ </script>
+
+If you know a bit of JavaScript internals you might know that it's
+possible to patch constructors and register callbacks for setters. An
+attacker can use this (like above) to get all the data you exported in
+your JSON file. The browser will totally ignore the ``application/json``
+mimetype if ``text/javascript`` is defined as content type in the script
+tag and evaluate that as JavaScript. Because toplevel array elements are
+allowed (albeit useless) and we hooked in our own constructor, after that
+page loaded the data from the JSON response is in the `captured` array.
+
+Because it is a syntax error in JavaScript to have an object literal
+(``{...}``) toplevel an attacker could not just do a request to an
+external URL with the script tag to load up the data. So what Flask does
+is to only allow objects as toplevel elements when using
+:func:`~flask.jsonify`. Make sure to do the same when using an ordinary
+JSON generate function.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/shell.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,110 @@
+Working with the Shell
+======================
+
+.. versionadded:: 0.3
+
+One of the reasons everybody loves Python is the interactive shell. It
+basically allows you to execute Python commands in real time and
+immediately get results back. Flask itself does not come with an
+interactive shell, because it does not require any specific setup upfront,
+just import your application and start playing around.
+
+There are however some handy helpers to make playing around in the shell a
+more pleasant experience. The main issue with interactive console
+sessions is that you're not triggering a request like a browser does which
+means that :data:`~flask.g`, :data:`~flask.request` and others are not
+available. But the code you want to test might depend on them, so what
+can you do?
+
+This is where some helper functions come in handy. Keep in mind however
+that these functions are not only there for interactive shell usage, but
+also for unittesting and other situations that require a faked request
+context.
+
+Diving into Context Locals
+--------------------------
+
+Say you have a utility function that returns the URL the user should be
+redirected to. Imagine it would always redirect to the URL's ``next``
+parameter or the HTTP referrer or the index page::
+
+ from flask import request, url_for
+
+ def redirect_url():
+ return request.args.get('next') or \
+ request.referrer or \
+ url_for('index')
+
+As you can see, it accesses the request object. If you try to run this
+from a plain Python shell, this is the exception you will see:
+
+>>> redirect_url()
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+AttributeError: 'NoneType' object has no attribute 'request'
+
+That makes a lot of sense because we currently do not have a request we
+could access. So we have to make a request and bind it to the current
+context. The :attr:`~flask.Flask.test_request_context` method can create
+us a request context:
+
+>>> ctx = app.test_request_context('/?next=http://example.com/')
+
+This context can be used in two ways. Either with the `with` statement
+(which unfortunately is not very handy for shell sessions). The
+alternative way is to call the `push` and `pop` methods:
+
+>>> ctx.push()
+
+From that point onwards you can work with the request object:
+
+>>> redirect_url()
+u'http://example.com/'
+
+Until you call `pop`:
+
+>>> ctx.pop()
+>>> redirect_url()
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+AttributeError: 'NoneType' object has no attribute 'request'
+
+
+Firing Before/After Request
+---------------------------
+
+By just creating a request context, you still don't have run the code that
+is normally run before a request. This probably results in your database
+being unavailable, the current user not being stored on the
+:data:`~flask.g` object etc.
+
+This however can easily be done yourself. Just call
+:meth:`~flask.Flask.preprocess_request`:
+
+>>> ctx = app.test_request_context()
+>>> ctx.push()
+>>> app.preprocess_request()
+
+Keep in mind that the :meth:`~flask.Flask.preprocess_request` function
+might return a response object, in that case just ignore it.
+
+To shutdown a request, you need to trick a bit before the after request
+functions (triggered by :meth:`~flask.Flask.process_response`) operate on
+a response object:
+
+>>> app.process_response(app.response_class())
+<Response 0 bytes [200 OK]>
+>>> ctx.pop()
+
+
+Further Improving the Shell Experience
+--------------------------------------
+
+If you like the idea of experimenting in a shell, create yourself a module
+with stuff you want to star import into your interactive session. There
+you could also define some more helper methods for common things such as
+initializing the database, dropping tables etc.
+
+Just put them into a module (like `shelltools` and import from there):
+
+>>> from shelltools import *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/signals.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,239 @@
+.. _signals:
+
+Signals
+=======
+
+.. versionadded:: 0.6
+
+Starting with Flask 0.6, there is integrated support for signalling in
+Flask. This support is provided by the excellent `blinker`_ library and
+will gracefully fall back if it is not available.
+
+What are signals? Signals help you decouple applications by sending
+notifications when actions occur elsewhere in the core framework or
+another Flask extensions. In short, signals allow certain senders to
+notify subscribers that something happened.
+
+Flask comes with a couple of signals and other extensions might provide
+more. Also keep in mind that signals are intended to notify subscribers
+and should not encourage subscribers to modify data. You will notice that
+there are signals that appear to do the same thing like some of the
+builtin decorators do (eg: :data:`~flask.request_started` is very similar
+to :meth:`~flask.Flask.before_request`). There are however difference in
+how they work. The core :meth:`~flask.Flask.before_request` handler for
+example is executed in a specific order and is able to abort the request
+early by returning a response. In contrast all signal handlers are
+executed in undefined order and do not modify any data.
+
+The big advantage of signals over handlers is that you can safely
+subscribe to them for the split of a second. These temporary
+subscriptions are helpful for unittesting for example. Say you want to
+know what templates were rendered as part of a request: signals allow you
+to do exactly that.
+
+Subscribing to Signals
+----------------------
+
+To subscribe to a signal, you can use the
+:meth:`~blinker.base.Signal.connect` method of a signal. The first
+argument is the function that should be called when the signal is emitted,
+the optional second argument specifies a sender. To unsubscribe from a
+signal, you can use the :meth:`~blinker.base.Signal.disconnect` method.
+
+For all core Flask signals, the sender is the application that issued the
+signal. When you subscribe to a signal, be sure to also provide a sender
+unless you really want to listen for signals of all applications. This is
+especially true if you are developing an extension.
+
+Here for example a helper context manager that can be used to figure out
+in a unittest which templates were rendered and what variables were passed
+to the template::
+
+ from flask import template_rendered
+ from contextlib import contextmanager
+
+ @contextmanager
+ def captured_templates(app):
+ recorded = []
+ def record(sender, template, context):
+ recorded.append((template, context))
+ template_rendered.connect(record, app)
+ try:
+ yield recorded
+ finally:
+ template_rendered.disconnect(record, app)
+
+This can now easily be paired with a test client::
+
+ with captured_templates(app) as templates:
+ rv = app.test_client().get('/')
+ assert rv.status_code == 200
+ assert len(templates) == 1
+ template, context = templates[0]
+ assert template.name == 'index.html'
+ assert len(context['items']) == 10
+
+All the template rendering in the code issued by the application `app`
+in the body of the `with` block will now be recorded in the `templates`
+variable. Whenever a template is rendered, the template object as well as
+context are appended to it.
+
+Additionally there is a convenient helper method
+(:meth:`~blinker.base.Signal.connected_to`). that allows you to
+temporarily subscribe a function to a signal with is a context manager on
+its own. Because the return value of the context manager cannot be
+specified that way one has to pass the list in as argument::
+
+ from flask import template_rendered
+
+ def captured_templates(app, recorded):
+ def record(sender, template, context):
+ recorded.append((template, context))
+ return template_rendered.connected_to(record, app)
+
+The example above would then look like this::
+
+ templates = []
+ with captured_templates(app, templates):
+ ...
+ template, context = templates[0]
+
+.. admonition:: Blinker API Changes
+
+ The :meth:`~blinker.base.Signal.connected_to` method arrived in Blinker
+ with version 1.1.
+
+Creating Signals
+----------------
+
+If you want to use signals in your own application, you can use the
+blinker library directly. The most common use case are named signals in a
+custom :class:`~blinker.base.Namespace`.. This is what is recommended
+most of the time::
+
+ from blinker import Namespace
+ my_signals = Namespace()
+
+Now you can create new signals like this::
+
+ model_saved = my_signals.signal('model-saved')
+
+The name for the signal here makes it unique and also simplifies
+debugging. You can access the name of the signal with the
+:attr:`~blinker.base.NamedSignal.name` attribute.
+
+.. admonition:: For Extension Developers
+
+ If you are writing a Flask extension and you to gracefully degrade for
+ missing blinker installations, you can do so by using the
+ :class:`flask.signals.Namespace` class.
+
+Sending Signals
+---------------
+
+If you want to emit a signal, you can do so by calling the
+:meth:`~blinker.base.Signal.send` method. It accepts a sender as first
+argument and optionally some keyword arguments that are forwarded to the
+signal subscribers::
+
+ class Model(object):
+ ...
+
+ def save(self):
+ model_saved.send(self)
+
+Try to always pick a good sender. If you have a class that is emitting a
+signal, pass `self` as sender. If you emitting a signal from a random
+function, you can pass ``current_app._get_current_object()`` as sender.
+
+.. admonition:: Passing Proxies as Senders
+
+ Never pass :data:`~flask.current_app` as sender to a signal. Use
+ ``current_app._get_current_object()`` instead. The reason for this is
+ that :data:`~flask.current_app` is a proxy and not the real application
+ object.
+
+Decorator Based Signal Subscriptions
+------------------------------------
+
+With Blinker 1.1 you can also easily subscribe to signals by using the new
+:meth:`~blinker.base.NamedSignal.connect_via` decorator::
+
+ from flask import template_rendered
+
+ @template_rendered.connect_via(app)
+ def when_template_rendered(sender, template, context):
+ print 'Template %s is rendered with %s' % (template.name, context)
+
+Core Signals
+------------
+
+.. when modifying this list, also update the one in api.rst
+
+The following signals exist in Flask:
+
+.. data:: flask.template_rendered
+ :noindex:
+
+ This signal is sent when a template was successfully rendered. The
+ signal is invoked with the instance of the template as `template`
+ and the context as dictionary (named `context`).
+
+ Example subscriber::
+
+ def log_template_renders(sender, template, context):
+ sender.logger.debug('Rendering template "%s" with context %s',
+ template.name or 'string template',
+ context)
+
+ from flask import template_rendered
+ template_rendered.connect(log_template_renders, app)
+
+.. data:: flask.request_started
+ :noindex:
+
+ This signal is sent before any request processing started but when the
+ request context was set up. Because the request context is already
+ bound, the subscriber can access the request with the standard global
+ proxies such as :class:`~flask.request`.
+
+ Example subscriber::
+
+ def log_request(sender):
+ sender.logger.debug('Request context is set up')
+
+ from flask import request_started
+ request_started.connect(log_request, app)
+
+.. data:: flask.request_finished
+ :noindex:
+
+ This signal is sent right before the response is sent to the client.
+ It is passed the response to be sent named `response`.
+
+ Example subscriber::
+
+ def log_response(sender, response):
+ sender.logger.debug('Request context is about to close down. '
+ 'Response: %s', response)
+
+ from flask import request_finished
+ request_finished.connect(log_response, app)
+
+.. data:: flask.got_request_exception
+ :noindex:
+
+ This signal is sent when an exception happens during request processing.
+ It is sent *before* the standard exception handling kicks in and even
+ in debug mode, where no exception handling happens. The exception
+ itself is passed to the subscriber as `exception`.
+
+ Example subscriber::
+
+ def log_exception(sender, exception):
+ sender.logger.debug('Got exception during processing: %s', exception)
+
+ from flask import got_request_exception
+ got_request_exception.connect(log_exception, app)
+
+.. _blinker: http://pypi.python.org/pypi/blinker
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/styleguide.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,200 @@
+Pocoo Styleguide
+================
+
+The Pocoo styleguide is the styleguide for all Pocoo Projects, including
+Flask. This styleguide is a requirement for Patches to Flask and a
+recommendation for Flask extensions.
+
+In general the Pocoo Styleguide closely follows :pep:`8` with some small
+differences and extensions.
+
+General Layout
+--------------
+
+Indentation:
+ 4 real spaces. No tabs, no exceptions.
+
+Maximum line length:
+ 79 characters with a soft limit for 84 if absolutely necessary. Try
+ to avoid too nested code by cleverly placing `break`, `continue` and
+ `return` statements.
+
+Continuing long statements:
+ To continue a statement you can use backslashes in which case you should
+ align the next line with the last dot or equal sign, or indent four
+ spaces::
+
+ this_is_a_very_long(function_call, 'with many parameters') \
+ .that_returns_an_object_with_an_attribute
+
+ MyModel.query.filter(MyModel.scalar > 120) \
+ .order_by(MyModel.name.desc()) \
+ .limit(10)
+
+ If you break in a statement with parentheses or braces, align to the
+ braces::
+
+ this_is_a_very_long(function_call, 'with many parameters',
+ 23, 42, 'and even more')
+
+ For lists or tuples with many items, break immediately after the
+ opening brace::
+
+ items = [
+ 'this is the first', 'set of items', 'with more items',
+ 'to come in this line', 'like this'
+ ]
+
+Blank lines:
+ Top level functions and classes are separated by two lines, everything
+ else by one. Do not use too many blank lines to separate logical
+ segments in code. Example::
+
+ def hello(name):
+ print 'Hello %s!' % name
+
+
+ def goodbye(name):
+ print 'See you %s.' % name
+
+
+ class MyClass(object):
+ """This is a simple docstring"""
+
+ def __init__(self, name):
+ self.name = name
+
+ def get_annoying_name(self):
+ return self.name.upper() + '!!!!111'
+
+Expressions and Statements
+--------------------------
+
+General whitespace rules:
+ - No whitespace for unary operators that are not words
+ (e.g.: ``-``, ``~`` etc.) as well on the inner side of parentheses.
+ - Whitespace is placed between binary operators.
+
+ Good::
+
+ exp = -1.05
+ value = (item_value / item_count) * offset / exp
+ value = my_list[index]
+ value = my_dict['key']
+
+ Bad::
+
+ exp = - 1.05
+ value = ( item_value / item_count ) * offset / exp
+ value = (item_value/item_count)*offset/exp
+ value=( item_value/item_count ) * offset/exp
+ value = my_list[ index ]
+ value = my_dict ['key']
+
+Yoda statements are a nogo:
+ Never compare constant with variable, always variable with constant:
+
+ Good::
+
+ if method == 'md5':
+ pass
+
+ Bad::
+
+ if 'md5' == method:
+ pass
+
+Comparisons:
+ - against arbitrary types: ``==`` and ``!=``
+ - against singletons with ``is`` and ``is not`` (eg: ``foo is not
+ None``)
+ - never compare something with `True` or `False` (for example never
+ do ``foo == False``, do ``not foo`` instead)
+
+Negated containment checks:
+ use ``foo not in bar`` instead of ``not foo in bar``
+
+Instance checks:
+ ``isinstance(a, C)`` instead of ``type(A) is C``, but try to avoid
+ instance checks in general. Check for features.
+
+
+Naming Conventions
+------------------
+
+- Class names: ``CamelCase``, with acronyms kept uppercase (``HTTPWriter``
+ and not ``HttpWriter``)
+- Variable names: ``lowercase_with_underscores``
+- Method and function names: ``lowercase_with_underscores``
+- Constants: ``UPPERCASE_WITH_UNDERSCORES``
+- precompiled regular expressions: ``name_re``
+
+Protected members are prefixed with a single underscore. Double
+underscores are reserved for mixin classes.
+
+On classes with keywords, trailing underscores are appended. Clashes with
+builtins are allowed and **must not** be resolved by appending an
+underline to the variable name. If the function needs to access a
+shadowed builtin, rebind the builtin to a different name instead.
+
+Function and method arguments:
+ - class methods: ``cls`` as first parameter
+ - instance methods: ``self`` as first parameter
+ - lambdas for properties might have the first parameter replaced
+ with ``x`` like in ``display_name = property(lambda x: x.real_name
+ or x.username)``
+
+
+Docstrings
+----------
+
+Docstring conventions:
+ All docstrings are formatted with reStructuredText as understood by
+ Sphinx. Depending on the number of lines in the docstring, they are
+ laid out differently. If it's just one line, the closing triple
+ quote is on the same line as the opening, otherwise the text is on
+ the same line as the opening quote and the triple quote that closes
+ the string on its own line::
+
+ def foo():
+ """This is a simple docstring"""
+
+
+ def bar():
+ """This is a longer docstring with so much information in there
+ that it spans three lines. In this case the closing triple quote
+ is on its own line.
+ """
+
+Module header:
+ The module header consists of an utf-8 encoding declaration (if non
+ ASCII letters are used, but it is recommended all the time) and a
+ standard docstring::
+
+ # -*- coding: utf-8 -*-
+ """
+ package.module
+ ~~~~~~~~~~~~~~
+
+ A brief description goes here.
+
+ :copyright: (c) YEAR by AUTHOR.
+ :license: LICENSE_NAME, see LICENSE_FILE for more details.
+ """
+
+ Please keep in mind that proper copyrights and license files are a
+ requirement for approved Flask extensions.
+
+
+Comments
+--------
+
+Rules for comments are similar to docstrings. Both are formatted with
+reStructuredText. If a comment is used to document an attribute, put a
+colon after the opening pound sign (``#``)::
+
+ class User(object):
+ #: the name of the user as unicode string
+ name = Column(String)
+ #: the sha1 hash of the password + inline salt
+ pw_hash = Column(String)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/templating.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,188 @@
+Templates
+=========
+
+Flask leverages Jinja2 as template engine. You are obviously free to use
+a different template engine, but you still have to install Jinja2 to run
+Flask itself. This requirement is necessary to enable rich extensions.
+An extension can depend on Jinja2 being present.
+
+This section only gives a very quick introduction into how Jinja2
+is integrated into Flask. If you want information on the template
+engine's syntax itself, head over to the official `Jinja2 Template
+Documentation <http://jinja.pocoo.org/2/documentation/templates>`_ for
+more information.
+
+Jinja Setup
+-----------
+
+Unless customized, Jinja2 is configured by Flask as follows:
+
+- autoescaping is enabled for all templates ending in ``.html``,
+ ``.htm``, ``.xml`` as well as ``.xhtml``
+- a template has the ability to opt in/out autoescaping with the
+ ``{% autoescape %}`` tag.
+- Flask inserts a couple of global functions and helpers into the
+ Jinja2 context, additionally to the values that are present by
+ default.
+
+Standard Context
+----------------
+
+The following global variables are available within Jinja2 templates
+by default:
+
+.. data:: config
+ :noindex:
+
+ The current configuration object (:data:`flask.config`)
+
+ .. versionadded:: 0.6
+
+.. data:: request
+ :noindex:
+
+ The current request object (:class:`flask.request`)
+
+.. data:: session
+ :noindex:
+
+ The current session object (:class:`flask.session`)
+
+.. data:: g
+ :noindex:
+
+ The request-bound object for global variables (:data:`flask.g`)
+
+.. function:: url_for
+ :noindex:
+
+ The :func:`flask.url_for` function.
+
+.. function:: get_flashed_messages
+ :noindex:
+
+ The :func:`flask.get_flashed_messages` function.
+
+.. admonition:: The Jinja Context Behaviour
+
+ These variables are added to the context of variables, they are not
+ global variables. The difference is that by default these will not
+ show up in the context of imported templates. This is partially caused
+ by performance considerations, partially to keep things explicit.
+
+ What does this mean for you? If you have a macro you want to import,
+ that needs to access the request object you have two possibilities:
+
+ 1. you explicitly pass the request to the macro as parameter, or
+ the attribute of the request object you are interested in.
+ 2. you import the macro "with context".
+
+ Importing with context looks like this:
+
+ .. sourcecode:: jinja
+
+ {% from '_helpers.html' import my_macro with context %}
+
+Standard Filters
+----------------
+
+These filters are available in Jinja2 additionally to the filters provided
+by Jinja2 itself:
+
+.. function:: tojson
+ :noindex:
+
+ This function converts the given object into JSON representation. This
+ is for example very helpful if you try to generate JavaScript on the
+ fly.
+
+ Note that inside `script` tags no escaping must take place, so make
+ sure to disable escaping with ``|safe`` if you intend to use it inside
+ `script` tags:
+
+ .. sourcecode:: html+jinja
+
+ <script type=text/javascript>
+ doSomethingWith({{ user.username|tojson|safe }});
+ </script>
+
+ That the ``|tojson`` filter escapes forward slashes properly for you.
+
+Controlling Autoescaping
+------------------------
+
+Autoescaping is the concept of automatically escaping special characters
+of you. Special characters in the sense of HTML (or XML, and thus XHTML)
+are ``&``, ``>``, ``<``, ``"`` as well as ``'``. Because these characters
+carry specific meanings in documents on their own you have to replace them
+by so called "entities" if you want to use them for text. Not doing so
+would not only cause user frustration by the inability to use these
+characters in text, but can also lead to security problems. (see
+:ref:`xss`)
+
+Sometimes however you will need to disable autoescaping in templates.
+This can be the case if you want to explicitly inject HTML into pages, for
+example if they come from a system that generate secure HTML like a
+markdown to HTML converter.
+
+There are three ways to accomplish that:
+
+- In the Python code, wrap the HTML string in a :class:`~flask.Markup`
+ object before passing it to the template. This is in general the
+ recommended way.
+- Inside the template, use the ``|safe`` filter to explicitly mark a
+ string as safe HTML (``{{ myvariable|safe }}``)
+- Temporarily disable the autoescape system altogether.
+
+To disable the autoescape system in templates, you can use the ``{%
+autoescape %}`` block:
+
+.. sourcecode:: html+jinja
+
+ {% autoescape false %}
+ <p>autoescaping is disabled here
+ <p>{{ will_not_be_escaped }}
+ {% endautoescape %}
+
+Whenever you do this, please be very cautious about the variables you are
+using in this block.
+
+Registering Filters
+-------------------
+
+If you want to register your own filters in Jinja2 you have two ways to do
+that. You can either put them by hand into the
+:attr:`~flask.Flask.jinja_env` of the application or use the
+:meth:`~flask.Flask.template_filter` decorator.
+
+The two following examples work the same and both reverse an object::
+
+ @app.template_filter('reverse')
+ def reverse_filter(s):
+ return s[::-1]
+
+ def reverse_filter(s):
+ return s[::-1]
+ app.jinja_env.filters['reverse'] = reverse_filter
+
+In case of the decorator the argument is optional if you want to use the
+function name as name of the filter.
+
+Context Processors
+------------------
+
+To inject new variables automatically into the context of a template
+context processors exist in Flask. Context processors run before the
+template is rendered and have the ability to inject new values into the
+template context. A context processor is a function that returns a
+dictionary. The keys and values of this dictionary are then merged with
+the template context::
+
+ @app.context_processor
+ def inject_user():
+ return dict(user=g.user)
+
+The context processor above makes a variable called `user` available in
+the template with the value of `g.user`. This example is not very
+interesting because `g` is available in templates anyways, but it gives an
+idea how this works.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/testing.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,244 @@
+.. _testing:
+
+Testing Flask Applications
+==========================
+
+ **Something that is untested is broken.**
+
+Not sure where that is coming from, and it's not entirely correct, but
+also not that far from the truth. Untested applications make it hard to
+improve existing code and developers of untested applications tend to
+become pretty paranoid. If an application has automated tests, you can
+safely change things, and you will instantly know if your change broke
+something.
+
+Flask gives you a couple of ways to test applications. It mainly does
+that by exposing the Werkzeug test :class:`~werkzeug.Client` class to your
+code and handling the context locals for you. You can then use that with
+your favourite testing solution. In this documentation we will use the
+:mod:`unittest` package that comes preinstalled with each Python
+installation.
+
+The Application
+---------------
+
+First we need an application to test for functionality. For the testing
+we will use the application from the :ref:`tutorial`. If you don't have
+that application yet, get the sources from `the examples`_.
+
+.. _the examples:
+ http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
+
+The Testing Skeleton
+--------------------
+
+In order to test that, we add a second module (
+`flaskr_tests.py`) and create a unittest skeleton there::
+
+ import os
+ import flaskr
+ import unittest
+ import tempfile
+
+ class FlaskrTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
+ self.app = flaskr.app.test_client()
+ flaskr.init_db()
+
+ def tearDown(self):
+ os.close(self.db_fd)
+ os.unlink(flaskr.app.config['DATABASE'])
+
+ if __name__ == '__main__':
+ unittest.main()
+
+The code in the :meth:`~unittest.TestCase.setUp` method creates a new test
+client and initializes a new database. That function is called before
+each individual test function. To delete the database after the test, we
+close the file and remove it from the filesystem in the
+:meth:`~unittest.TestCase.tearDown` method. What the test client does is
+give us a simple interface to the application. We can trigger test
+requests to the application, and the client will also keep track of cookies
+for us.
+
+Because SQLite3 is filesystem-based we can easily use the tempfile module
+to create a temporary database and initialize it. The
+:func:`~tempfile.mkstemp` function does two things for us: it returns a
+low-level file handle and a random file name, the latter we use as
+database name. We just have to keep the `db_fd` around so that we can use
+the :func:`os.close` function to close the file.
+
+If we now run that test suite, we should see the following output::
+
+ $ python flaskr_tests.py
+
+ ----------------------------------------------------------------------
+ Ran 0 tests in 0.000s
+
+ OK
+
+Even though it did not run any tests, we already know that our flaskr
+application is syntactically valid, otherwise the import would have died
+with an exception.
+
+The First Test
+--------------
+
+Now we can add the first test. Let's check that the application shows
+"No entries here so far" if we access the root of the application (``/``).
+For that we modify our created test case class so that it looks like
+this::
+
+ class FlaskrTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
+ self.app = flaskr.app.test_client()
+ flaskr.init_db()
+
+ def tearDown(self):
+ os.close(self.db_fd)
+ os.unlink(flaskr.DATABASE)
+
+ def test_empty_db(self):
+ rv = self.app.get('/')
+ assert 'No entries here so far' in rv.data
+
+Test functions begin with the word `test`. Every function named like that
+will be picked up automatically. By using `self.app.get` we can send an
+HTTP `GET` request to the application with the given path. The return
+value will be a :class:`~flask.Flask.response_class` object. We can now
+use the :attr:`~werkzeug.BaseResponse.data` attribute to inspect the
+return value (as string) from the application. In this case, we ensure
+that ``'No entries here so far'`` is part of the output.
+
+Run it again and you should see one passing test::
+
+ $ python flaskr_tests.py
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.034s
+
+ OK
+
+Of course you can submit forms with the test client as well, which we will
+use now to log our user in.
+
+Logging In and Out
+------------------
+
+The majority of the functionality of our application is only available for
+the administrative user, so we need a way to log our test client in to the
+application and out of it again. For that we fire some requests to the
+login and logout pages with the required form data (username and
+password). Because the login and logout pages redirect, we tell the
+client to `follow_redirects`.
+
+Add the following two methods to your `FlaskrTestCase` class::
+
+ def login(self, username, password):
+ return self.app.post('/login', data=dict(
+ username=username,
+ password=password
+ ), follow_redirects=True)
+
+ def logout(self):
+ return self.app.get('/logout', follow_redirects=True)
+
+Now we can easily test if logging in and out works and that it fails with
+invalid credentials. Add this new test to the class::
+
+ def test_login_logout(self):
+ rv = self.login('admin', 'default')
+ assert 'You were logged in' in rv.data
+ rv = self.logout()
+ assert 'You were logged out' in rv.data
+ rv = self.login('adminx', 'default')
+ assert 'Invalid username' in rv.data
+ rv = self.login('admin', 'defaultx')
+ assert 'Invalid password' in rv.data
+
+Test Adding Messages
+--------------------
+
+Now we can also test that adding messages works. Add a new test method
+like this::
+
+ def test_messages(self):
+ self.login('admin', 'default')
+ rv = self.app.post('/add', data=dict(
+ title='<Hello>',
+ text='<strong>HTML</strong> allowed here'
+ ), follow_redirects=True)
+ assert 'No entries here so far' not in rv.data
+ assert '<Hello>' in rv.data
+ assert '<strong>HTML</strong> allowed here' in rv.data
+
+Here we check that HTML is allowed in the text but not in the title,
+which is the intended behavior.
+
+Running that should now give us three passing tests::
+
+ $ python flaskr_tests.py
+ ...
+ ----------------------------------------------------------------------
+ Ran 3 tests in 0.332s
+
+ OK
+
+For more complex tests with headers and status codes, check out the
+`MiniTwit Example`_ from the sources. That one contains a larger test
+suite.
+
+
+.. _MiniTwit Example:
+ http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/
+
+
+Other Testing Tricks
+--------------------
+
+Besides using the test client we used above, there is also the
+:meth:`~flask.Flask.test_request_context` method that in combination with
+the `with` statement can be used to activate a request context
+temporarily. With that you can access the :class:`~flask.request`,
+:class:`~flask.g` and :class:`~flask.session` objects like in view
+functions. Here's a full example that showcases this::
+
+ app = flask.Flask(__name__)
+
+ with app.test_request_context('/?name=Peter'):
+ assert flask.request.path == '/'
+ assert flask.request.args['name'] == 'Peter'
+
+All the other objects that are context bound can be used the same.
+
+If you want to test your application with different configurations and
+there does not seem to be a good way to do that, consider switching to
+application factories (see :ref:`app-factories`).
+
+
+Keeping the Context Around
+--------------------------
+
+.. versionadded:: 0.4
+
+Sometimes it can be helpful to trigger a regular request but keep the
+context around for a little longer so that additional introspection can
+happen. With Flask 0.4 this is possible by using the
+:meth:`~flask.Flask.test_client` with a `with` block::
+
+ app = flask.Flask(__name__)
+
+ with app.test_client() as c:
+ rv = c.get('/?tequila=42')
+ assert request.args['tequila'] == '42'
+
+If you would just be using the :meth:`~flask.Flask.test_client` without
+the `with` block, the `assert` would fail with an error because `request`
+is no longer available (because used outside of an actual request).
+Keep in mind however that :meth:`~flask.Flask.after_request` functions
+are already called at that point so your database connection and
+everything involved is probably already closed down.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/css.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,31 @@
+.. _tutorial-css:
+
+Step 7: Adding Style
+====================
+
+Now that everything else works, it's time to add some style to the
+application. Just create a stylesheet called `style.css` in the `static`
+folder we created before:
+
+.. sourcecode:: css
+
+ body { font-family: sans-serif; background: #eee; }
+ a, h1, h2 { color: #377BA8; }
+ h1, h2 { font-family: 'Georgia', serif; margin: 0; }
+ h1 { border-bottom: 2px solid #eee; }
+ h2 { font-size: 1.2em; }
+
+ .page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
+ padding: 0.8em; background: white; }
+ .entries { list-style: none; margin: 0; padding: 0; }
+ .entries li { margin: 0.8em 1.2em; }
+ .entries li h2 { margin-left: -1em; }
+ .add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
+ .add-entry dl { font-weight: bold; }
+ .metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
+ margin-bottom: 1em; background: #fafafa; }
+ .flash { background: #CEE5F5; padding: 0.5em;
+ border: 1px solid #AACBE2; }
+ .error { background: #F0D6D6; padding: 0.5em; }
+
+Continue with :ref:`tutorial-testing`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/dbcon.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,37 @@
+.. _tutorial-dbcon:
+
+Step 4: Request Database Connections
+------------------------------------
+
+Now we know how we can open database connections and use them for scripts,
+but how can we elegantly do that for requests? We will need the database
+connection in all our functions so it makes sense to initialize them
+before each request and shut them down afterwards.
+
+Flask allows us to do that with the :meth:`~flask.Flask.before_request` and
+:meth:`~flask.Flask.after_request` decorators::
+
+ @app.before_request
+ def before_request():
+ g.db = connect_db()
+
+ @app.after_request
+ def after_request(response):
+ g.db.close()
+ return response
+
+Functions marked with :meth:`~flask.Flask.before_request` are called before
+a request and passed no arguments, functions marked with
+:meth:`~flask.Flask.after_request` are called after a request and
+passed the response that will be sent to the client. They have to return
+that response object or a different one. In this case we just return it
+unchanged.
+
+We store our current database connection on the special :data:`~flask.g`
+object that flask provides for us. This object stores information for one
+request only and is available from within each function. Never store such
+things on other objects because this would not work with threaded
+environments. That special :data:`~flask.g` object does some magic behind
+the scenes to ensure it does the right thing.
+
+Continue to :ref:`tutorial-views`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/dbinit.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,67 @@
+.. _tutorial-dbinit:
+
+Step 3: Creating The Database
+=============================
+
+Flaskr is a database powered application as outlined earlier, and more
+precisely, an application powered by a relational database system. Such
+systems need a schema that tells them how to store that information. So
+before starting the server for the first time it's important to create
+that schema.
+
+Such a schema can be created by piping the `schema.sql` file into the
+`sqlite3` command as follows::
+
+ sqlite3 /tmp/flaskr.db < schema.sql
+
+The downside of this is that it requires the sqlite3 command to be
+installed which is not necessarily the case on every system. Also one has
+to provide the path to the database there which leaves some place for
+errors. It's a good idea to add a function that initializes the database
+for you to the application.
+
+If you want to do that, you first have to import the
+:func:`contextlib.closing` function from the contextlib package. If you
+want to use Python 2.5 it's also necessary to enable the `with` statement
+first (`__future__` imports must be the very first import)::
+
+ from __future__ import with_statement
+ from contextlib import closing
+
+Next we can create a function called `init_db` that initializes the
+database. For this we can use the `connect_db` function we defined
+earlier. Just add that function below the `connect_db` function::
+
+ def init_db():
+ with closing(connect_db()) as db:
+ with app.open_resource('schema.sql') as f:
+ db.cursor().executescript(f.read())
+ db.commit()
+
+The :func:`~contextlib.closing` helper function allows us to keep a
+connection open for the duration of the `with` block. The
+:func:`~flask.Flask.open_resource` method of the application object
+supports that functionality out of the box, so it can be used in the
+`with` block directly. This function opens a file from the resource
+location (your `flaskr` folder) and allows you to read from it. We are
+using this here to execute a script on the database connection.
+
+When we connect to a database we get a connection object (here called
+`db`) that can give us a cursor. On that cursor there is a method to
+execute a complete script. Finally we only have to commit the changes.
+SQLite 3 and other transactional databases will not commit unless you
+explicitly tell it to.
+
+Now it is possible to create a database by starting up a Python shell and
+importing and calling that function::
+
+>>> from flaskr import init_db
+>>> init_db()
+
+.. admonition:: Troubleshooting
+
+ If you get an exception later that a table cannot be found check that
+ you did call the `init_db` function and that your table names are
+ correct (singular vs. plural for example).
+
+Continue with :ref:`tutorial-dbcon`
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/folders.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,23 @@
+.. _tutorial-folders:
+
+Step 0: Creating The Folders
+============================
+
+Before we get started, let's create the folders needed for this
+application::
+
+ /flaskr
+ /static
+ /templates
+
+The `flaskr` folder is not a python package, but just something where we
+drop our files. Directly into this folder we will then put our database
+schema as well as main module in the following steps. The files inside
+the `static` folder are available to users of the application via `HTTP`.
+This is the place where css and javascript files go. Inside the
+`templates` folder Flask will look for `Jinja2`_ templates. The
+templates you create later in the tutorial will go in this directory.
+
+Continue with :ref:`tutorial-schema`.
+
+.. _Jinja2: http://jinja.pocoo.org/2/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/index.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,32 @@
+.. _tutorial:
+
+Tutorial
+========
+
+You want to develop an application with Python and Flask? Here you have
+the chance to learn that by example. In this tutorial we will create a
+simple microblog application. It only supports one user that can create
+text-only entries and there are no feeds or comments, but it still
+features everything you need to get started. We will use Flask and SQLite
+as database which comes out of the box with Python, so there is nothing
+else you need.
+
+If you want the full sourcecode in advance or for comparison, check out
+the `example source`_.
+
+.. _example source:
+ http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
+
+.. toctree::
+ :maxdepth: 2
+
+ introduction
+ folders
+ schema
+ setup
+ dbinit
+ dbcon
+ views
+ templates
+ css
+ testing
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/introduction.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,33 @@
+.. _tutorial-introduction:
+
+Introducing Flaskr
+==================
+
+We will call our blogging application flaskr here, feel free to chose a
+less web-2.0-ish name ;) Basically we want it to do the following things:
+
+1. let the user sign in and out with credentials specified in the
+ configuration. Only one user is supported.
+2. when the user is logged in he or she can add new entries to the page
+ consisting of a text-only title and some HTML for the text. This HTML
+ is not sanitized because we trust the user here.
+3. the page shows all entries so far in reverse order (newest on top) and
+ the user can add new ones from there if logged in.
+
+We will be using SQLite3 directly for that application because it's good
+enough for an application of that size. For larger applications however
+it makes a lot of sense to use `SQLAlchemy`_ that handles database
+connections in a more intelligent way, allows you to target different
+relational databases at once and more. You might also want to consider
+one of the popular NoSQL databases if your data is more suited for those.
+
+Here a screenshot from the final application:
+
+.. image:: ../_static/flaskr.png
+ :align: center
+ :class: screenshot
+ :alt: screenshot of the final application
+
+Continue with :ref:`tutorial-folders`.
+
+.. _SQLAlchemy: http://www.sqlalchemy.org/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/schema.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,25 @@
+.. _tutorial-schema:
+
+Step 1: Database Schema
+=======================
+
+First we want to create the database schema. For this application only a
+single table is needed and we only want to support SQLite so that is quite
+easy. Just put the following contents into a file named `schema.sql` in
+the just created `flaskr` folder:
+
+.. sourcecode:: sql
+
+ drop table if exists entries;
+ create table entries (
+ id integer primary key autoincrement,
+ title string not null,
+ text string not null
+ );
+
+This schema consists of a single table called `entries` and each row in
+this table has an `id`, a `title` and a `text`. The `id` is an
+automatically incrementing integer and a primary key, the other two are
+strings that must not be null.
+
+Continue with :ref:`tutorial-setup`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/setup.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,83 @@
+.. _tutorial-setup:
+
+Step 2: Application Setup Code
+==============================
+
+Now that we have the schema in place we can create the application module.
+Let's call it `flaskr.py` inside the `flaskr` folder. For starters we
+will add the imports we will need as well as the config section. For
+small applications it's a possibility to drop the configuration directly
+into the module which we will be doing here. However a cleaner solution
+would be to create a separate `.ini` or `.py` file and load that or import
+the values from there.
+
+::
+
+ # all the imports
+ import sqlite3
+ from flask import Flask, request, session, g, redirect, url_for, \
+ abort, render_template, flash
+
+ # configuration
+ DATABASE = '/tmp/flaskr.db'
+ DEBUG = True
+ SECRET_KEY = 'development key'
+ USERNAME = 'admin'
+ PASSWORD = 'default'
+
+Next we can create our actual application and initialize it with the
+config from the same file::
+
+ # create our little application :)
+ app = Flask(__name__)
+ app.config.from_object(__name__)
+
+:meth:`~flask.Config.from_object` will look at the given object (if it's a
+string it will import it) and then look for all uppercase variables
+defined there. In our case, the configuration we just wrote a few lines
+of code above. You can also move that into a separate file.
+
+It is also a good idea to be able to load a configuration from a
+configurable file. This is what :meth:`~flask.Config.from_envvar` can
+do::
+
+ app.config.from_envvar('FLASKR_SETTINGS', silent=True)
+
+That way someone can set an environment variable called
+:envvar:`FLASKR_SETTINGS` to specify a config file to be loaded which will
+then override the default values. The silent switch just tells Flask to
+not complain if no such environment key is set.
+
+The `secret_key` is needed to keep the client-side sessions secure.
+Choose that key wisely and as hard to guess and complex as possible. The
+debug flag enables or disables the interactive debugger. Never leave
+debug mode activated in a production system because it will allow users to
+execute code on the server!
+
+We also add a method to easily connect to the database specified. That
+can be used to open a connection on request and also from the interactive
+Python shell or a script. This will come in handy later.
+
+::
+
+ def connect_db():
+ return sqlite3.connect(app.config['DATABASE'])
+
+Finally we just add a line to the bottom of the file that fires up the
+server if we want to run that file as a standalone application::
+
+ if __name__ == '__main__':
+ app.run()
+
+With that out of the way you should be able to start up the application
+without problems. When you head over to the server you will get an 404
+page not found error because we don't have any views yet. But we will
+focus on that a little later. First we should get the database working.
+
+.. admonition:: Externally Visible Server
+
+ Want your server to be publicly available? Check out the
+ :ref:`externally visible server <public-server>` section for more
+ information.
+
+Continue with :ref:`tutorial-dbinit`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/templates.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,111 @@
+.. _tutorial-templates:
+
+Step 6: The Templates
+=====================
+
+Now we should start working on the templates. If we request the URLs now
+we would only get an exception that Flask cannot find the templates. The
+templates are using `Jinja2`_ syntax and have autoescaping enabled by
+default. This means that unless you mark a value in the code with
+:class:`~flask.Markup` or with the ``|safe`` filter in the template,
+Jinja2 will ensure that special characters such as ``<`` or ``>`` are
+escaped with their XML equivalents.
+
+We are also using template inheritance which makes it possible to reuse
+the layout of the website in all pages.
+
+Put the following templates into the `templates` folder:
+
+.. _Jinja2: http://jinja.pocoo.org/2/documentation/templates
+
+layout.html
+-----------
+
+This template contains the HTML skeleton, the header and a link to log in
+(or log out if the user was already logged in). It also displays the
+flashed messages if there are any. The ``{% block body %}`` block can be
+replaced by a block of the same name (``body``) in a child template.
+
+The :class:`~flask.session` dict is available in the template as well and
+you can use that to check if the user is logged in or not. Note that in
+Jinja you can access missing attributes and items of objects / dicts which
+makes the following code work, even if there is no ``'logged_in'`` key in
+the session:
+
+.. sourcecode:: html+jinja
+
+ <!doctype html>
+ <title>Flaskr</title>
+ <link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
+ <div class=page>
+ <h1>Flaskr</h1>
+ <div class=metanav>
+ {% if not session.logged_in %}
+ <a href="{{ url_for('login') }}">log in</a>
+ {% else %}
+ <a href="{{ url_for('logout') }}">log out</a>
+ {% endif %}
+ </div>
+ {% for message in get_flashed_messages() %}
+ <div class=flash>{{ message }}</div>
+ {% endfor %}
+ {% block body %}{% endblock %}
+ </div>
+
+show_entries.html
+-----------------
+
+This template extends the `layout.html` template from above to display the
+messages. Note that the `for` loop iterates over the messages we passed
+in with the :func:`~flask.render_template` function. We also tell the
+form to submit to your `add_entry` function and use `POST` as `HTTP`
+method:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block body %}
+ {% if session.logged_in %}
+ <form action="{{ url_for('add_entry') }}" method=post class=add-entry>
+ <dl>
+ <dt>Title:
+ <dd><input type=text size=30 name=title>
+ <dt>Text:
+ <dd><textarea name=text rows=5 cols=40></textarea>
+ <dd><input type=submit value=Share>
+ </dl>
+ </form>
+ {% endif %}
+ <ul class=entries>
+ {% for entry in entries %}
+ <li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
+ {% else %}
+ <li><em>Unbelievable. No entries here so far</em>
+ {% endfor %}
+ </ul>
+ {% endblock %}
+
+login.html
+----------
+
+Finally the login template which basically just displays a form to allow
+the user to login:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block body %}
+ <h2>Login</h2>
+ {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
+ <form action="{{ url_for('login') }}" method=post>
+ <dl>
+ <dt>Username:
+ <dd><input type=text name=username>
+ <dt>Password:
+ <dd><input type=password name=password>
+ <dd><input type=submit value=Login>
+ </dl>
+ </form>
+ {% endblock %}
+
+Continue with :ref:`tutorial-css`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/testing.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,10 @@
+.. _tutorial-testing:
+
+Bonus: Testing the Application
+==============================
+
+Now that you have finished the application and everything works as
+expected, it's probably not a bad idea to add automated tests to simplify
+modifications in the future. The application above is used as a basic
+example of how to perform unittesting in the :ref:`testing` section of the
+documentation. Go there to see how easy it is to test Flask applications.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/tutorial/views.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,98 @@
+.. _tutorial-views:
+
+Step 5: The View Functions
+==========================
+
+Now that the database connections are working we can start writing the
+view functions. We will need four of them:
+
+Show Entries
+------------
+
+This view shows all the entries stored in the database. It listens on the
+root of the application and will select title and text from the database.
+The one with the highest id (the newest entry) will be on top. The rows
+returned from the cursor are tuples with the columns ordered like specified
+in the select statement. This is good enough for small applications like
+here, but you might want to convert them into a dict. If you are
+interested in how to do that, check out the :ref:`easy-querying` example.
+
+The view function will pass the entries as dicts to the
+`show_entries.html` template and return the rendered one::
+
+ @app.route('/')
+ def show_entries():
+ cur = g.db.execute('select title, text from entries order by id desc')
+ entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
+ return render_template('show_entries.html', entries=entries)
+
+Add New Entry
+-------------
+
+This view lets the user add new entries if he's logged in. This only
+responds to `POST` requests, the actual form is shown on the
+`show_entries` page. If everything worked out well we will
+:func:`~flask.flash` an information message to the next request and
+redirect back to the `show_entries` page::
+
+ @app.route('/add', methods=['POST'])
+ def add_entry():
+ if not session.get('logged_in'):
+ abort(401)
+ g.db.execute('insert into entries (title, text) values (?, ?)',
+ [request.form['title'], request.form['text']])
+ g.db.commit()
+ flash('New entry was successfully posted')
+ return redirect(url_for('show_entries'))
+
+Note that we check that the user is logged in here (the `logged_in` key is
+present in the session and `True`).
+
+.. admonition:: Security Note
+
+ Be sure to use question marks when building SQL statements, as done in the
+ example above. Otherwise, your app will be vulnerable to SQL injection when
+ you use string formatting to build SQL statements.
+ See :ref:`sqlite3` for more.
+
+Login and Logout
+----------------
+
+These functions are used to sign the user in and out. Login checks the
+username and password against the ones from the configuration and sets the
+`logged_in` key in the session. If the user logged in successfully, that
+key is set to `True`, and the user is redirected back to the `show_entries`
+page. In addition, a message is flashed that informs the user that he or
+she was logged in successfully. If an error occurred, the template is
+notified about that, and the user is asked again::
+
+ @app.route('/login', methods=['GET', 'POST'])
+ def login():
+ error = None
+ if request.method == 'POST':
+ if request.form['username'] != app.config['USERNAME']:
+ error = 'Invalid username'
+ elif request.form['password'] != app.config['PASSWORD']:
+ error = 'Invalid password'
+ else:
+ session['logged_in'] = True
+ flash('You were logged in')
+ return redirect(url_for('show_entries'))
+ return render_template('login.html', error=error)
+
+The logout function, on the other hand, removes that key from the session
+again. We use a neat trick here: if you use the :meth:`~dict.pop` method
+of the dict and pass a second parameter to it (the default), the method
+will delete the key from the dictionary if present or do nothing when that
+key is not in there. This is helpful because now we don't have to check
+if the user was logged in.
+
+::
+
+ @app.route('/logout')
+ def logout():
+ session.pop('logged_in', None)
+ flash('You were logged out')
+ return redirect(url_for('show_entries'))
+
+Continue with :ref:`tutorial-templates`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/unicode.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,107 @@
+Unicode in Flask
+================
+
+Flask like Jinja2 and Werkzeug is totally Unicode based when it comes to
+text. Not only these libraries, also the majority of web related Python
+libraries that deal with text. If you don't know Unicode so far, you
+should probably read `The Absolute Minimum Every Software Developer
+Absolutely, Positively Must Know About Unicode and Character Sets
+<http://www.joelonsoftware.com/articles/Unicode.html>`_. This part of the
+documentation just tries to cover the very basics so that you have a
+pleasant experience with Unicode related things.
+
+Automatic Conversion
+--------------------
+
+Flask has a few assumptions about your application (which you can change
+of course) that give you basic and painless Unicode support:
+
+- the encoding for text on your website is UTF-8
+- internally you will always use Unicode exclusively for text except
+ for literal strings with only ASCII character points.
+- encoding and decoding happens whenever you are talking over a protocol
+ that requires bytes to be transmitted.
+
+So what does this mean to you?
+
+HTTP is based on bytes. Not only the protocol, also the system used to
+address documents on servers (so called URIs or URLs). However HTML which
+is usually transmitted on top of HTTP supports a large variety of
+character sets and which ones are used, are transmitted in an HTTP header.
+To not make this too complex Flask just assumes that if you are sending
+Unicode out you want it to be UTF-8 encoded. Flask will do the encoding
+and setting of the appropriate headers for you.
+
+The same is true if you are talking to databases with the help of
+SQLAlchemy or a similar ORM system. Some databases have a protocol that
+already transmits Unicode and if they do not, SQLAlchemy or your other ORM
+should take care of that.
+
+The Golden Rule
+---------------
+
+So the rule of thumb: if you are not dealing with binary data, work with
+Unicode. What does working with Unicode in Python 2.x mean?
+
+- as long as you are using ASCII charpoints only (basically numbers,
+ some special characters of latin letters without umlauts or anything
+ fancy) you can use regular string literals (``'Hello World'``).
+- if you need anything else than ASCII in a string you have to mark
+ this string as Unicode string by prefixing it with a lowercase `u`.
+ (like ``u'Hänsel und Gretel'``)
+- if you are using non-Unicode characters in your Python files you have
+ to tell Python which encoding your file uses. Again, I recommend
+ UTF-8 for this purpose. To tell the interpreter your encoding you can
+ put the ``# -*- coding: utf-8 -*-`` into the first or second line of
+ your Python source file.
+- Jinja is configured to decode the template files from UTF-8. So make
+ sure to tell your editor to save the file as UTF-8 there as well.
+
+Encoding and Decoding Yourself
+------------------------------
+
+If you are talking with a filesystem or something that is not really based
+on Unicode you will have to ensure that you decode properly when working
+with Unicode interface. So for example if you want to load a file on the
+filesystem and embed it into a Jinja2 template you will have to decode it
+from the encoding of that file. Here the old problem that text files do
+not specify their encoding comes into play. So do yourself a favour and
+limit yourself to UTF-8 for text files as well.
+
+Anyways. To load such a file with Unicode you can use the built-in
+:meth:`str.decode` method::
+
+ def read_file(filename, charset='utf-8'):
+ with open(filename, 'r') as f:
+ return f.read().decode(charset)
+
+To go from Unicode into a specific charset such as UTF-8 you can use the
+:meth:`unicode.encode` method::
+
+ def write_file(filename, contents, charset='utf-8'):
+ with open(filename, 'w') as f:
+ f.write(contents.encode(charset))
+
+Configuring Editors
+-------------------
+
+Most editors save as UTF-8 by default nowadays but in case your editor is
+not configured to do this you have to change it. Here some common ways to
+set your editor to store as UTF-8:
+
+- Vim: put ``set enc=utf-8`` to your ``.vimrc`` file.
+
+- Emacs: either use an encoding cookie or put this into your ``.emacs``
+ file::
+
+ (prefer-coding-system 'utf-8)
+ (setq default-buffer-file-coding-system 'utf-8)
+
+- Notepad++:
+
+ 1. Go to *Settings -> Preferences ...*
+ 2. Select the "New Document/Default Directory" tab
+ 3. Select "UTF-8 without BOM" as encoding
+
+ It is also recommended to use the Unix newline format, you can select
+ it in the same panel but this is not a requirement.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/docs/upgrading.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,137 @@
+Upgrading to Newer Releases
+===========================
+
+Flask itself is changing like any software is changing over time. Most of
+the changes are the nice kind, the kind where you don't have to change
+anything in your code to profit from a new release.
+
+However every once in a while there are changes that do require some
+changes in your code or there are changes that make it possible for you to
+improve your own code quality by taking advantage of new features in
+Flask.
+
+This section of the documentation enumerates all the changes in Flask from
+release to release and how you can change your code to have a painless
+updating experience.
+
+If you want to use the `easy_install` command to upgrade your Flask
+installation, make sure to pass it the ``-U`` parameter::
+
+ $ easy_install -U Flask
+
+Version 0.7
+-----------
+
+Due to a bug in earlier implementations the request local proxies now
+raise a :exc:`RuntimeError` instead of an :exc:`AttributeError` when they
+are unbound. If you caught these exceptions with :exc:`AttributeError`
+before, you should catch them with :exc:`RuntimeError` now.
+
+Additionally the :func:`~flask.send_file` function is now issuing
+deprecation warnings if you depend on functionality that will be removed
+in Flask 1.0. Previously it was possible to use etags and mimetypes
+when file objects were passed. This was unreliable and caused issues
+for a few setups. If you get a deprecation warning, make sure to
+update your application to work with either filenames there or disable
+etag attaching and attach them yourself.
+
+Old code::
+
+ return send_file(my_file_object)
+ return send_file(my_file_object)
+
+New code::
+
+ return send_file(my_file_object, add_etags=False)
+
+Version 0.6
+-----------
+
+Flask 0.6 comes with a backwards incompatible change which affects the
+order of after-request handlers. Previously they were called in the order
+of the registration, now they are called in reverse order. This change
+was made so that Flask behaves more like people expected it to work and
+how other systems handle request pre- and postprocessing. If you
+depend on the order of execution of post-request functions, be sure to
+change the order.
+
+Another change that breaks backwards compatibility is that context
+processors will no longer override values passed directly to the template
+rendering function. If for example `request` is as variable passed
+directly to the template, the default context processor will not override
+it with the current request object. This makes it easier to extend
+context processors later to inject additional variables without breaking
+existing template not expecting them.
+
+Version 0.5
+-----------
+
+Flask 0.5 is the first release that comes as a Python package instead of a
+single module. There were a couple of internal refactoring so if you
+depend on undocumented internal details you probably have to adapt the
+imports.
+
+The following changes may be relevant to your application:
+
+- autoescaping no longer happens for all templates. Instead it is
+ configured to only happen on files ending with ``.html``, ``.htm``,
+ ``.xml`` and ``.xhtml``. If you have templates with different
+ extensions you should override the
+ :meth:`~flask.Flask.select_jinja_autoescape` method.
+- Flask no longer supports zipped applications in this release. This
+ functionality might come back in future releases if there is demand
+ for this feature. Removing support for this makes the Flask internal
+ code easier to understand and fixes a couple of small issues that make
+ debugging harder than necessary.
+- The `create_jinja_loader` function is gone. If you want to customize
+ the Jinja loader now, use the
+ :meth:`~flask.Flask.create_jinja_environment` method instead.
+
+Version 0.4
+-----------
+
+For application developers there are no changes that require changes in
+your code. In case you are developing on a Flask extension however, and
+that extension has a unittest-mode you might want to link the activation
+of that mode to the new ``TESTING`` flag.
+
+Version 0.3
+-----------
+
+Flask 0.3 introduces configuration support and logging as well as
+categories for flashing messages. All these are features that are 100%
+backwards compatible but you might want to take advantage of them.
+
+Configuration Support
+`````````````````````
+
+The configuration support makes it easier to write any kind of application
+that requires some sort of configuration. (Which most likely is the case
+for any application out there).
+
+If you previously had code like this::
+
+ app.debug = DEBUG
+ app.secret_key = SECRET_KEY
+
+You no longer have to do that, instead you can just load a configuration
+into the config object. How this works is outlined in :ref:`config`.
+
+Logging Integration
+```````````````````
+
+Flask now configures a logger for you with some basic and useful defaults.
+If you run your application in production and want to profit from
+automatic error logging, you might be interested in attaching a proper log
+handler. Also you can start logging warnings and errors into the logger
+when appropriately. For more information on that, read
+:ref:`application-errors`.
+
+Categories for Flash Messages
+`````````````````````````````
+
+Flash messages can now have categories attached. This makes it possible
+to render errors, warnings or regular messages differently for example.
+This is an opt-in feature because it requires some rethinking in the code.
+
+Read all about that in the :ref:`message-flashing-pattern` pattern.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/README Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,28 @@
+
+ / Flaskr /
+
+ a minimal blog application
+
+
+ ~ What is Flaskr?
+
+ A sqlite powered thumble blog application
+
+ ~ How do I use it?
+
+ 1. edit the configuration in the flaskr.py file or
+ export an FLASKR_SETTINGS environment variable
+ pointing to a configuration file.
+
+ 2. fire up a python shell and run this:
+
+ >>> from flaskr import init_db; init_db()
+
+ 3. now you can run the flaskr.py file with your
+ python interpreter and the application will
+ greet you on http://localhost:5000/
+
+ ~ Is it tested?
+
+ You betcha. Run the `flaskr_tests.py` file to see
+ the tests pass.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/flaskr.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+"""
+ Flaskr
+ ~~~~~~
+
+ A microblog example application written as Flask tutorial with
+ Flask and sqlite3.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from __future__ import with_statement
+from sqlite3 import dbapi2 as sqlite3
+from contextlib import closing
+from flask import Flask, request, session, g, redirect, url_for, abort, \
+ render_template, flash
+
+# configuration
+DATABASE = '/tmp/flaskr.db'
+DEBUG = True
+SECRET_KEY = 'development key'
+USERNAME = 'admin'
+PASSWORD = 'default'
+
+# create our little application :)
+app = Flask(__name__)
+app.config.from_object(__name__)
+app.config.from_envvar('FLASKR_SETTINGS', silent=True)
+
+
+def connect_db():
+ """Returns a new connection to the database."""
+ return sqlite3.connect(app.config['DATABASE'])
+
+
+def init_db():
+ """Creates the database tables."""
+ with closing(connect_db()) as db:
+ with app.open_resource('schema.sql') as f:
+ db.cursor().executescript(f.read())
+ db.commit()
+
+
+@app.before_request
+def before_request():
+ """Make sure we are connected to the database each request."""
+ g.db = connect_db()
+
+
+@app.after_request
+def after_request(response):
+ """Closes the database again at the end of the request."""
+ g.db.close()
+ return response
+
+
+@app.route('/')
+def show_entries():
+ cur = g.db.execute('select title, text from entries order by id desc')
+ entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
+ return render_template('show_entries.html', entries=entries)
+
+
+@app.route('/add', methods=['POST'])
+def add_entry():
+ if not session.get('logged_in'):
+ abort(401)
+ g.db.execute('insert into entries (title, text) values (?, ?)',
+ [request.form['title'], request.form['text']])
+ g.db.commit()
+ flash('New entry was successfully posted')
+ return redirect(url_for('show_entries'))
+
+
+@app.route('/login', methods=['GET', 'POST'])
+def login():
+ error = None
+ if request.method == 'POST':
+ if request.form['username'] != app.config['USERNAME']:
+ error = 'Invalid username'
+ elif request.form['password'] != app.config['PASSWORD']:
+ error = 'Invalid password'
+ else:
+ session['logged_in'] = True
+ flash('You were logged in')
+ return redirect(url_for('show_entries'))
+ return render_template('login.html', error=error)
+
+
+@app.route('/logout')
+def logout():
+ session.pop('logged_in', None)
+ flash('You were logged out')
+ return redirect(url_for('show_entries'))
+
+
+if __name__ == '__main__':
+ app.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/flaskr_tests.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+"""
+ Flaskr Tests
+ ~~~~~~~~~~~~
+
+ Tests the Flaskr application.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import flaskr
+import unittest
+import tempfile
+
+
+class FlaskrTestCase(unittest.TestCase):
+
+ def setUp(self):
+ """Before each test, set up a blank database"""
+ self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
+ self.app = flaskr.app.test_client()
+ flaskr.init_db()
+
+ def tearDown(self):
+ """Get rid of the database again after each test."""
+ os.close(self.db_fd)
+ os.unlink(flaskr.app.config['DATABASE'])
+
+ def login(self, username, password):
+ return self.app.post('/login', data=dict(
+ username=username,
+ password=password
+ ), follow_redirects=True)
+
+ def logout(self):
+ return self.app.get('/logout', follow_redirects=True)
+
+ # testing functions
+
+ def test_empty_db(self):
+ """Start with a blank database."""
+ rv = self.app.get('/')
+ assert 'No entries here so far' in rv.data
+
+ def test_login_logout(self):
+ """Make sure login and logout works"""
+ rv = self.login(flaskr.app.config['USERNAME'],
+ flaskr.app.config['PASSWORD'])
+ assert 'You were logged in' in rv.data
+ rv = self.logout()
+ assert 'You were logged out' in rv.data
+ rv = self.login(flaskr.app.config['USERNAME'] + 'x',
+ flaskr.app.config['PASSWORD'])
+ assert 'Invalid username' in rv.data
+ rv = self.login(flaskr.app.config['USERNAME'],
+ flaskr.app.config['PASSWORD'] + 'x')
+ assert 'Invalid password' in rv.data
+
+ def test_messages(self):
+ """Test that messages work"""
+ self.login(flaskr.app.config['USERNAME'],
+ flaskr.app.config['PASSWORD'])
+ rv = self.app.post('/add', data=dict(
+ title='<Hello>',
+ text='<strong>HTML</strong> allowed here'
+ ), follow_redirects=True)
+ assert 'No entries here so far' not in rv.data
+ assert '<Hello>' in rv.data
+ assert '<strong>HTML</strong> allowed here' in rv.data
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/schema.sql Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,6 @@
+drop table if exists entries;
+create table entries (
+ id integer primary key autoincrement,
+ title string not null,
+ text string not null
+);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/static/style.css Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,18 @@
+body { font-family: sans-serif; background: #eee; }
+a, h1, h2 { color: #377BA8; }
+h1, h2 { font-family: 'Georgia', serif; margin: 0; }
+h1 { border-bottom: 2px solid #eee; }
+h2 { font-size: 1.2em; }
+
+.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
+ padding: 0.8em; background: white; }
+.entries { list-style: none; margin: 0; padding: 0; }
+.entries li { margin: 0.8em 1.2em; }
+.entries li h2 { margin-left: -1em; }
+.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
+.add-entry dl { font-weight: bold; }
+.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
+ margin-bottom: 1em; background: #fafafa; }
+.flash { background: #CEE5F5; padding: 0.5em;
+ border: 1px solid #AACBE2; }
+.error { background: #F0D6D6; padding: 0.5em; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/templates/layout.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Flaskr</title>
+<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
+<div class=page>
+ <h1>Flaskr</h1>
+ <div class=metanav>
+ {% if not session.logged_in %}
+ <a href="{{ url_for('login') }}">log in</a>
+ {% else %}
+ <a href="{{ url_for('logout') }}">log out</a>
+ {% endif %}
+ </div>
+ {% for message in get_flashed_messages() %}
+ <div class=flash>{{ message }}</div>
+ {% endfor %}
+ {% block body %}{% endblock %}
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/templates/login.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,14 @@
+{% extends "layout.html" %}
+{% block body %}
+ <h2>Login</h2>
+ {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
+ <form action="{{ url_for('login') }}" method=post>
+ <dl>
+ <dt>Username:
+ <dd><input type=text name=username>
+ <dt>Password:
+ <dd><input type=password name=password>
+ <dd><input type=submit value=Login>
+ </dl>
+ </form>
+{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/flaskr/templates/show_entries.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,21 @@
+{% extends "layout.html" %}
+{% block body %}
+ {% if session.logged_in %}
+ <form action="{{ url_for('add_entry') }}" method=post class=add-entry>
+ <dl>
+ <dt>Title:
+ <dd><input type=text size=30 name=title>
+ <dt>Text:
+ <dd><textarea name=text rows=5 cols=40></textarea>
+ <dd><input type=submit value=Share>
+ </dl>
+ </form>
+ {% endif %}
+ <ul class=entries>
+ {% for entry in entries %}
+ <li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
+ {% else %}
+ <li><em>Unbelievable. No entries here so far</em>
+ {% endfor %}
+ </ul>
+{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/jqueryexample/jqueryexample.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+"""
+ jQuery Example
+ ~~~~~~~~~~~~~~
+
+ A simple application that shows how Flask and jQuery get along.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from flask import Flask, jsonify, render_template, request
+app = Flask(__name__)
+
+
+@app.route('/_add_numbers')
+def add_numbers():
+ """Add two numbers server side, ridiculous but well..."""
+ a = request.args.get('a', 0, type=int)
+ b = request.args.get('b', 0, type=int)
+ return jsonify(result=a + b)
+
+
+@app.route('/')
+def index():
+ return render_template('index.html')
+
+
+if __name__ == '__main__':
+ app.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/jqueryexample/templates/index.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,21 @@
+{% extends "layout.html" %}
+{% block body %}
+<script type=text/javascript>
+ $(function() {
+ $('a#calculate').bind('click', function() {
+ $.getJSON($SCRIPT_ROOT + '/_add_numbers', {
+ a: $('input[name="a"]').val(),
+ b: $('input[name="b"]').val()
+ }, function(data) {
+ $("#result").text(data.result);
+ });
+ return false;
+ });
+ });
+</script>
+<h1>jQuery Example</h1>
+<p><input type=text size=5 name=a> +
+ <input type=text size=5 name=b> =
+ <span id=result>?</span>
+<p><a href=# id=calculate>calculate server side</a>
+{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/jqueryexample/templates/layout.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>jQuery Example</title>
+<script type=text/javascript
+ src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
+<script type=text/javascript>
+ var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
+</script>
+{% block body %}{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/README Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,28 @@
+
+ / MiniTwit /
+
+ because writing todo lists is not fun
+
+
+ ~ What is MiniTwit?
+
+ A SQLite and Flask powered twitter clone
+
+ ~ How do I use it?
+
+ 1. edit the configuration in the minitwit.py file or
+ export an MINITWIT_SETTINGS environment variable
+ pointing to a configuration file.
+
+ 2. fire up a python shell and run this:
+
+ >>> from minitwit import init_db; init_db()
+
+ 3. now you can run the minitwit.py file with your
+ python interpreter and the application will
+ greet you on http://localhost:5000/
+
+ ~ Is it tested?
+
+ You betcha. Run the `minitwit_tests.py` file to
+ see the tests pass.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/minitwit.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+"""
+ MiniTwit
+ ~~~~~~~~
+
+ A microblogging application written with Flask and sqlite3.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from __future__ import with_statement
+import time
+from sqlite3 import dbapi2 as sqlite3
+from hashlib import md5
+from datetime import datetime
+from contextlib import closing
+from flask import Flask, request, session, url_for, redirect, \
+ render_template, abort, g, flash
+from werkzeug import check_password_hash, generate_password_hash
+
+
+# configuration
+DATABASE = '/tmp/minitwit.db'
+PER_PAGE = 30
+DEBUG = True
+SECRET_KEY = 'development key'
+
+# create our little application :)
+app = Flask(__name__)
+app.config.from_object(__name__)
+app.config.from_envvar('MINITWIT_SETTINGS', silent=True)
+
+
+def connect_db():
+ """Returns a new connection to the database."""
+ return sqlite3.connect(app.config['DATABASE'])
+
+
+def init_db():
+ """Creates the database tables."""
+ with closing(connect_db()) as db:
+ with app.open_resource('schema.sql') as f:
+ db.cursor().executescript(f.read())
+ db.commit()
+
+
+def query_db(query, args=(), one=False):
+ """Queries the database and returns a list of dictionaries."""
+ cur = g.db.execute(query, args)
+ rv = [dict((cur.description[idx][0], value)
+ for idx, value in enumerate(row)) for row in cur.fetchall()]
+ return (rv[0] if rv else None) if one else rv
+
+
+def get_user_id(username):
+ """Convenience method to look up the id for a username."""
+ rv = g.db.execute('select user_id from user where username = ?',
+ [username]).fetchone()
+ return rv[0] if rv else None
+
+
+def format_datetime(timestamp):
+ """Format a timestamp for display."""
+ return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d @ %H:%M')
+
+
+def gravatar_url(email, size=80):
+ """Return the gravatar image for the given email address."""
+ return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \
+ (md5(email.strip().lower().encode('utf-8')).hexdigest(), size)
+
+
+@app.before_request
+def before_request():
+ """Make sure we are connected to the database each request and look
+ up the current user so that we know he's there.
+ """
+ g.db = connect_db()
+ g.user = None
+ if 'user_id' in session:
+ g.user = query_db('select * from user where user_id = ?',
+ [session['user_id']], one=True)
+
+
+@app.after_request
+def after_request(response):
+ """Closes the database again at the end of the request."""
+ g.db.close()
+ return response
+
+
+@app.route('/')
+def timeline():
+ """Shows a users timeline or if no user is logged in it will
+ redirect to the public timeline. This timeline shows the user's
+ messages as well as all the messages of followed users.
+ """
+ if not g.user:
+ return redirect(url_for('public_timeline'))
+ return render_template('timeline.html', messages=query_db('''
+ select message.*, user.* from message, user
+ where message.author_id = user.user_id and (
+ user.user_id = ? or
+ user.user_id in (select whom_id from follower
+ where who_id = ?))
+ order by message.pub_date desc limit ?''',
+ [session['user_id'], session['user_id'], PER_PAGE]))
+
+
+@app.route('/public')
+def public_timeline():
+ """Displays the latest messages of all users."""
+ return render_template('timeline.html', messages=query_db('''
+ select message.*, user.* from message, user
+ where message.author_id = user.user_id
+ order by message.pub_date desc limit ?''', [PER_PAGE]))
+
+
+@app.route('/<username>')
+def user_timeline(username):
+ """Display's a users tweets."""
+ profile_user = query_db('select * from user where username = ?',
+ [username], one=True)
+ if profile_user is None:
+ abort(404)
+ followed = False
+ if g.user:
+ followed = query_db('''select 1 from follower where
+ follower.who_id = ? and follower.whom_id = ?''',
+ [session['user_id'], profile_user['user_id']],
+ one=True) is not None
+ return render_template('timeline.html', messages=query_db('''
+ select message.*, user.* from message, user where
+ user.user_id = message.author_id and user.user_id = ?
+ order by message.pub_date desc limit ?''',
+ [profile_user['user_id'], PER_PAGE]), followed=followed,
+ profile_user=profile_user)
+
+
+@app.route('/<username>/follow')
+def follow_user(username):
+ """Adds the current user as follower of the given user."""
+ if not g.user:
+ abort(401)
+ whom_id = get_user_id(username)
+ if whom_id is None:
+ abort(404)
+ g.db.execute('insert into follower (who_id, whom_id) values (?, ?)',
+ [session['user_id'], whom_id])
+ g.db.commit()
+ flash('You are now following "%s"' % username)
+ return redirect(url_for('user_timeline', username=username))
+
+
+@app.route('/<username>/unfollow')
+def unfollow_user(username):
+ """Removes the current user as follower of the given user."""
+ if not g.user:
+ abort(401)
+ whom_id = get_user_id(username)
+ if whom_id is None:
+ abort(404)
+ g.db.execute('delete from follower where who_id=? and whom_id=?',
+ [session['user_id'], whom_id])
+ g.db.commit()
+ flash('You are no longer following "%s"' % username)
+ return redirect(url_for('user_timeline', username=username))
+
+
+@app.route('/add_message', methods=['POST'])
+def add_message():
+ """Registers a new message for the user."""
+ if 'user_id' not in session:
+ abort(401)
+ if request.form['text']:
+ g.db.execute('''insert into message (author_id, text, pub_date)
+ values (?, ?, ?)''', (session['user_id'], request.form['text'],
+ int(time.time())))
+ g.db.commit()
+ flash('Your message was recorded')
+ return redirect(url_for('timeline'))
+
+
+@app.route('/login', methods=['GET', 'POST'])
+def login():
+ """Logs the user in."""
+ if g.user:
+ return redirect(url_for('timeline'))
+ error = None
+ if request.method == 'POST':
+ user = query_db('''select * from user where
+ username = ?''', [request.form['username']], one=True)
+ if user is None:
+ error = 'Invalid username'
+ elif not check_password_hash(user['pw_hash'],
+ request.form['password']):
+ error = 'Invalid password'
+ else:
+ flash('You were logged in')
+ session['user_id'] = user['user_id']
+ return redirect(url_for('timeline'))
+ return render_template('login.html', error=error)
+
+
+@app.route('/register', methods=['GET', 'POST'])
+def register():
+ """Registers the user."""
+ if g.user:
+ return redirect(url_for('timeline'))
+ error = None
+ if request.method == 'POST':
+ if not request.form['username']:
+ error = 'You have to enter a username'
+ elif not request.form['email'] or \
+ '@' not in request.form['email']:
+ error = 'You have to enter a valid email address'
+ elif not request.form['password']:
+ error = 'You have to enter a password'
+ elif request.form['password'] != request.form['password2']:
+ error = 'The two passwords do not match'
+ elif get_user_id(request.form['username']) is not None:
+ error = 'The username is already taken'
+ else:
+ g.db.execute('''insert into user (
+ username, email, pw_hash) values (?, ?, ?)''',
+ [request.form['username'], request.form['email'],
+ generate_password_hash(request.form['password'])])
+ g.db.commit()
+ flash('You were successfully registered and can login now')
+ return redirect(url_for('login'))
+ return render_template('register.html', error=error)
+
+
+@app.route('/logout')
+def logout():
+ """Logs the user out."""
+ flash('You were logged out')
+ session.pop('user_id', None)
+ return redirect(url_for('public_timeline'))
+
+
+# add some filters to jinja
+app.jinja_env.filters['datetimeformat'] = format_datetime
+app.jinja_env.filters['gravatar'] = gravatar_url
+
+
+if __name__ == '__main__':
+ app.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/minitwit_tests.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+"""
+ MiniTwit Tests
+ ~~~~~~~~~~~~~~
+
+ Tests the MiniTwit application.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import minitwit
+import unittest
+import tempfile
+
+
+class MiniTwitTestCase(unittest.TestCase):
+
+ def setUp(self):
+ """Before each test, set up a blank database"""
+ self.db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp()
+ self.app = minitwit.app.test_client()
+ minitwit.init_db()
+
+ def tearDown(self):
+ """Get rid of the database again after each test."""
+ os.close(self.db_fd)
+ os.unlink(minitwit.app.config['DATABASE'])
+
+ # helper functions
+
+ def register(self, username, password, password2=None, email=None):
+ """Helper function to register a user"""
+ if password2 is None:
+ password2 = password
+ if email is None:
+ email = username + '@example.com'
+ return self.app.post('/register', data={
+ 'username': username,
+ 'password': password,
+ 'password2': password2,
+ 'email': email,
+ }, follow_redirects=True)
+
+ def login(self, username, password):
+ """Helper function to login"""
+ return self.app.post('/login', data={
+ 'username': username,
+ 'password': password
+ }, follow_redirects=True)
+
+ def register_and_login(self, username, password):
+ """Registers and logs in in one go"""
+ self.register(username, password)
+ return self.login(username, password)
+
+ def logout(self):
+ """Helper function to logout"""
+ return self.app.get('/logout', follow_redirects=True)
+
+ def add_message(self, text):
+ """Records a message"""
+ rv = self.app.post('/add_message', data={'text': text},
+ follow_redirects=True)
+ if text:
+ assert 'Your message was recorded' in rv.data
+ return rv
+
+ # testing functions
+
+ def test_register(self):
+ """Make sure registering works"""
+ rv = self.register('user1', 'default')
+ assert 'You were successfully registered ' \
+ 'and can login now' in rv.data
+ rv = self.register('user1', 'default')
+ assert 'The username is already taken' in rv.data
+ rv = self.register('', 'default')
+ assert 'You have to enter a username' in rv.data
+ rv = self.register('meh', '')
+ assert 'You have to enter a password' in rv.data
+ rv = self.register('meh', 'x', 'y')
+ assert 'The two passwords do not match' in rv.data
+ rv = self.register('meh', 'foo', email='broken')
+ assert 'You have to enter a valid email address' in rv.data
+
+ def test_login_logout(self):
+ """Make sure logging in and logging out works"""
+ rv = self.register_and_login('user1', 'default')
+ assert 'You were logged in' in rv.data
+ rv = self.logout()
+ assert 'You were logged out' in rv.data
+ rv = self.login('user1', 'wrongpassword')
+ assert 'Invalid password' in rv.data
+ rv = self.login('user2', 'wrongpassword')
+ assert 'Invalid username' in rv.data
+
+ def test_message_recording(self):
+ """Check if adding messages works"""
+ self.register_and_login('foo', 'default')
+ self.add_message('test message 1')
+ self.add_message('<test message 2>')
+ rv = self.app.get('/')
+ assert 'test message 1' in rv.data
+ assert '<test message 2>' in rv.data
+
+ def test_timelines(self):
+ """Make sure that timelines work"""
+ self.register_and_login('foo', 'default')
+ self.add_message('the message by foo')
+ self.logout()
+ self.register_and_login('bar', 'default')
+ self.add_message('the message by bar')
+ rv = self.app.get('/public')
+ assert 'the message by foo' in rv.data
+ assert 'the message by bar' in rv.data
+
+ # bar's timeline should just show bar's message
+ rv = self.app.get('/')
+ assert 'the message by foo' not in rv.data
+ assert 'the message by bar' in rv.data
+
+ # now let's follow foo
+ rv = self.app.get('/foo/follow', follow_redirects=True)
+ assert 'You are now following "foo"' in rv.data
+
+ # we should now see foo's message
+ rv = self.app.get('/')
+ assert 'the message by foo' in rv.data
+ assert 'the message by bar' in rv.data
+
+ # but on the user's page we only want the user's message
+ rv = self.app.get('/bar')
+ assert 'the message by foo' not in rv.data
+ assert 'the message by bar' in rv.data
+ rv = self.app.get('/foo')
+ assert 'the message by foo' in rv.data
+ assert 'the message by bar' not in rv.data
+
+ # now unfollow and check if that worked
+ rv = self.app.get('/foo/unfollow', follow_redirects=True)
+ assert 'You are no longer following "foo"' in rv.data
+ rv = self.app.get('/')
+ assert 'the message by foo' not in rv.data
+ assert 'the message by bar' in rv.data
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/schema.sql Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,21 @@
+drop table if exists user;
+create table user (
+ user_id integer primary key autoincrement,
+ username string not null,
+ email string not null,
+ pw_hash string not null
+);
+
+drop table if exists follower;
+create table follower (
+ who_id integer,
+ whom_id integer
+);
+
+drop table if exists message;
+create table message (
+ message_id integer primary key autoincrement,
+ author_id integer not null,
+ text string not null,
+ pub_date integer
+);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/static/style.css Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,178 @@
+body {
+ background: #CAECE9;
+ font-family: 'Trebuchet MS', sans-serif;
+ font-size: 14px;
+}
+
+a {
+ color: #26776F;
+}
+
+a:hover {
+ color: #333;
+}
+
+input[type="text"],
+input[type="password"] {
+ background: white;
+ border: 1px solid #BFE6E2;
+ padding: 2px;
+ font-family: 'Trebuchet MS', sans-serif;
+ font-size: 14px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ color: #105751;
+}
+
+input[type="submit"] {
+ background: #105751;
+ border: 1px solid #073B36;
+ padding: 1px 3px;
+ font-family: 'Trebuchet MS', sans-serif;
+ font-size: 14px;
+ font-weight: bold;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ color: white;
+}
+
+div.page {
+ background: white;
+ border: 1px solid #6ECCC4;
+ width: 700px;
+ margin: 30px auto;
+}
+
+div.page h1 {
+ background: #6ECCC4;
+ margin: 0;
+ padding: 10px 14px;
+ color: white;
+ letter-spacing: 1px;
+ text-shadow: 0 0 3px #24776F;
+ font-weight: normal;
+}
+
+div.page div.navigation {
+ background: #DEE9E8;
+ padding: 4px 10px;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #eee;
+ color: #888;
+ font-size: 12px;
+ letter-spacing: 0.5px;
+}
+
+div.page div.navigation a {
+ color: #444;
+ font-weight: bold;
+}
+
+div.page h2 {
+ margin: 0 0 15px 0;
+ color: #105751;
+ text-shadow: 0 1px 2px #ccc;
+}
+
+div.page div.body {
+ padding: 10px;
+}
+
+div.page div.footer {
+ background: #eee;
+ color: #888;
+ padding: 5px 10px;
+ font-size: 12px;
+}
+
+div.page div.followstatus {
+ border: 1px solid #ccc;
+ background: #E3EBEA;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ padding: 3px;
+ font-size: 13px;
+}
+
+div.page ul.messages {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+div.page ul.messages li {
+ margin: 10px 0;
+ padding: 5px;
+ background: #F0FAF9;
+ border: 1px solid #DBF3F1;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ min-height: 48px;
+}
+
+div.page ul.messages p {
+ margin: 0;
+}
+
+div.page ul.messages li img {
+ float: left;
+ padding: 0 10px 0 0;
+}
+
+div.page ul.messages li small {
+ font-size: 0.9em;
+ color: #888;
+}
+
+div.page div.twitbox {
+ margin: 10px 0;
+ padding: 5px;
+ background: #F0FAF9;
+ border: 1px solid #94E2DA;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+div.page div.twitbox h3 {
+ margin: 0;
+ font-size: 1em;
+ color: #2C7E76;
+}
+
+div.page div.twitbox p {
+ margin: 0;
+}
+
+div.page div.twitbox input[type="text"] {
+ width: 585px;
+}
+
+div.page div.twitbox input[type="submit"] {
+ width: 70px;
+ margin-left: 5px;
+}
+
+ul.flashes {
+ list-style: none;
+ margin: 10px 10px 0 10px;
+ padding: 0;
+}
+
+ul.flashes li {
+ background: #B9F3ED;
+ border: 1px solid #81CEC6;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ padding: 4px;
+ font-size: 13px;
+}
+
+div.error {
+ margin: 10px 0;
+ background: #FAE4E4;
+ border: 1px solid #DD6F6F;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ padding: 4px;
+ font-size: 13px;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/templates/layout.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>{% block title %}Welcome{% endblock %} | MiniTwit</title>
+<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
+<div class=page>
+ <h1>MiniTwit</h1>
+ <div class=navigation>
+ {% if g.user %}
+ <a href="{{ url_for('timeline') }}">my timeline</a> |
+ <a href="{{ url_for('public_timeline') }}">public timeline</a> |
+ <a href="{{ url_for('logout') }}">sign out [{{ g.user.username }}]</a>
+ {% else %}
+ <a href="{{ url_for('public_timeline') }}">public timeline</a> |
+ <a href="{{ url_for('register') }}">sign up</a> |
+ <a href="{{ url_for('login') }}">sign in</a>
+ {% endif %}
+ </div>
+ {% with flashes = get_flashed_messages() %}
+ {% if flashes %}
+ <ul class=flashes>
+ {% for message in flashes %}
+ <li>{{ message }}
+ {% endfor %}
+ </ul>
+ {% endif %}
+ {% endwith %}
+ <div class=body>
+ {% block body %}{% endblock %}
+ </div>
+ <div class=footer>
+ MiniTwit — A Flask Application
+ </div>
+</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/templates/login.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,16 @@
+{% extends "layout.html" %}
+{% block title %}Sign In{% endblock %}
+{% block body %}
+ <h2>Sign In</h2>
+ {% if error %}<div class=error><strong>Error:</strong> {{ error }}</div>{% endif %}
+ <form action="" method=post>
+ <dl>
+ <dt>Username:
+ <dd><input type=text name=username size=30 value="{{ request.form.username }}">
+ <dt>Password:
+ <dd><input type=password name=password size=30>
+ </dl>
+ <div class=actions><input type=submit value="Sign In"></div>
+ </form>
+{% endblock %}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/templates/register.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,19 @@
+{% extends "layout.html" %}
+{% block title %}Sign Up{% endblock %}
+{% block body %}
+ <h2>Sign Up</h2>
+ {% if error %}<div class=error><strong>Error:</strong> {{ error }}</div>{% endif %}
+ <form action="" method=post>
+ <dl>
+ <dt>Username:
+ <dd><input type=text name=username size=30 value="{{ request.form.username }}">
+ <dt>E-Mail:
+ <dd><input type=text name=email size=30 value="{{ request.form.email }}">
+ <dt>Password:
+ <dd><input type=password name=password size=30>
+ <dt>Password <small>(repeat)</small>:
+ <dd><input type=password name=password2 size=30>
+ </dl>
+ <div class=actions><input type=submit value="Sign Up"></div>
+ </form>
+{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/examples/minitwit/templates/timeline.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,49 @@
+{% extends "layout.html" %}
+{% block title %}
+ {% if request.endpoint == 'public_timeline' %}
+ Public Timeline
+ {% elif request.endpoint == 'user_timeline' %}
+ {{ profile_user.username }}'s Timeline
+ {% else %}
+ My Timeline
+ {% endif %}
+{% endblock %}
+{% block body %}
+ <h2>{{ self.title() }}</h2>
+ {% if g.user %}
+ {% if request.endpoint == 'user_timeline' %}
+ <div class=followstatus>
+ {% if g.user.user_id == profile_user.user_id %}
+ This is you!
+ {% elif followed %}
+ You are currently following this user.
+ <a class=unfollow href="{{ url_for('unfollow_user', username=profile_user.username)
+ }}">Unfollow user</a>.
+ {% else %}
+ You are not yet following this user.
+ <a class=follow href="{{ url_for('follow_user', username=profile_user.username)
+ }}">Follow user</a>.
+ {% endif %}
+ </div>
+ {% elif request.endpoint == 'timeline' %}
+ <div class=twitbox>
+ <h3>What's on your mind {{ g.user.username }}?</h3>
+ <form action="{{ url_for('add_message') }}" method=post>
+ <p><input type=text name=text size=60><!--
+ --><input type=submit value="Share">
+ </form>
+ </div>
+ {% endif %}
+ {% endif %}
+ <ul class=messages>
+ {% for message in messages %}
+ <li><img src="{{ message.email|gravatar(size=48) }}"><p>
+ <strong><a href="{{ url_for('user_timeline', username=message.username)
+ }}">{{ message.username }}</a></strong>
+ {{ message.text }}
+ <small>— {{ message.pub_date|datetimeformat }}</small>
+ {% else %}
+ <li><em>There's no message so far.</em>
+ {% endfor %}
+ </ul>
+{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/extreview/approved.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,146 @@
+Approved Extensions
+===================
+
+This document contains a list of all extensions that were approved and the
+date of approval as well as notes. This should make it possible to better
+track the extension approval process.
+
+
+Flask-Babel
+-----------
+
+:First Approval: 2010-07-23
+:Last Review: 2010-07-23
+:Approved Version: 0.6
+:Approved License: BSD
+
+Notes: Developed by the Flask development head
+
+How to improve: add a better long description to the next release
+
+
+Flask-SQLAlchemy
+----------------
+
+:First Approval: 2010-07-25
+:Last Review: 2010-07-25
+:Approved Version: 0.9.1
+:Approved License: BSD
+
+Notes: Developed by the Flask development head
+
+How to improve: add a better long description to the next release
+
+
+Flask-Creole
+------------
+
+:First Approval: 2010-07-25
+:Last Review: 2010-07-25
+:Approved Version: 0.4.4
+:Approved License: BSD
+
+Notes: Flask-Markdown and this should share API, consider that when
+approving Flask-Markdown
+
+
+Flask-Genshi
+------------
+
+:First Approval: 2010-07-26
+:Last Review: 2010-07-26
+:Approved Version: 0.3.1
+:Approved License: BSD
+
+Notes: This is the first template engine extension. When others come
+around it would be a good idea to decide on a common interface.
+
+
+Flask-Script
+------------
+
+:First Approval: 2010-07-26
+:Last Review: 2010-07-26
+:Approved Version: 0.3
+:Approved License: BSD
+
+Notes: Flask-Actions has some overlap. Consider that when approving
+Flask-Actions or similar packages.
+
+
+Flask-CouchDB
+-------------
+
+:First Approval: 2010-07-26
+:Last Review: 2010-07-26
+:Approved Version: 0.2.1
+:Approved License: MIT
+
+There is also Flask-CouchDBKit. Both are fine because they are doing
+different things, but the latter is not yet approved.
+
+
+Flask-Testing
+-------------
+
+:First Approval: 2010-07-27
+:Last Review: 2010-07-27
+:Approved Version: 0.2.3
+:Approved License: BSD
+
+All fine.
+
+
+Flask-WTF
+---------
+
+:First Approval: 2010-07-27
+:Last Review: 2010-07-27
+:Approved Version: 0.2.3
+:Approved License: BSD
+
+All fine.
+
+
+Flask-Themes
+------------
+
+:First Approval: 2010-07-27
+:Last Review: 2010-07-27
+:Approved Version: 0.1.2
+:Approved License: MIT
+
+All fine.
+
+
+Flask-Uploads
+-------------
+
+:First Approval: 2010-07-27
+:Last Review: 2010-07-27
+:Approved Version: 0.1.2
+:Approved License: MIT
+
+All fine.
+
+
+Flask-Mail
+----------
+
+:First Approval: 2010-07-29
+:Last Review: 2010-07-29
+:Approved Version: 0.3.4
+:Approved License: BSD
+
+All fine.
+
+
+Flask-XML-RPC
+-------------
+
+:First Approval: 2010-07-30
+:Last Review: 2010-07-30
+:Approved Version: 0.1.2
+:Approved License: MIT
+
+All fine.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/extreview/listed.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,64 @@
+Listed Extensions
+=================
+
+This list contains extensions that passed listing. This means the
+extension is on the list of extensions on the website. It does not
+contain extensions that are approved.
+
+
+Flask-CouchDBKit
+----------------
+
+:Last-Review: 2010-07-25
+:Reviewed Version: 0.2
+
+Would be fine for approval, but the test suite is not part of the sdist
+package (missing entry in MANIFEST.in) and the test suite does not respond
+to either "make test" or "python setup.py test".
+
+
+flask-csrf
+----------
+
+:Last-Review: 2010-07-25
+:Reviewed Version: 0.2
+
+Will not be approved because this is functionality that should be handled
+in the form handling systems which is for Flask-WTF already the case.
+Also, this implementation only supports one open tab with forms.
+
+Name is not following Flask extension naming rules.
+
+Considered for unlisting.
+
+
+flask-lesscss
+-------------
+
+:Last-Review: 2010-07-25
+:Reviewed Version: 0.9.1
+
+Broken package description, nonconforming package name, does not follow
+standard API rules (init_lesscss instead of lesscss).
+
+Considered for unlisting, improved version should release as
+"Flask-LessCSS" with a conforming API and fixed packages indices, as well
+as a testsuite.
+
+
+Flask-OAuth
+-----------
+
+:Last-Review: 2010-07-25
+:Reviewed Version: 0.9
+
+Short long description, missing tests.
+
+
+Flask-OpenID
+------------
+
+:Last-Review: 2010-07-25
+:Reviewed Version: 1.0.1
+
+Short long description, missing tests.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/extreview/unlisted.rst Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,55 @@
+Unlisted Extensions
+===================
+
+This is a list of extensions that is currently rejected from listing and
+with that also not approved. If an extension ends up here it should
+improved to be listed.
+
+
+Flask-Actions
+-------------
+
+:Last Review: 2010-07-25
+:Reviewed Version: 0.2
+
+Rejected because of missing description in PyPI, formatting issues with
+the documentation (missing headlines, scrollbars etc.) and a general clash
+of functionality with the Flask-Script package. Latter should not be a
+problem, but the documentation should improve. For listing, the extension
+developer should probably discuss the extension on the mailinglist with
+others.
+
+Futhermore it also has an egg registered with an invalid filename.
+
+
+Flask-Jinja2Extender
+--------------------
+
+:Last Review: 2010-07-25
+:Reviewed Version: 0.1
+
+Usecase not obvious, hacky implementation, does not solve a problem that
+could not be solved with Flask itself. I suppose it is to aid other
+extensions, but that should be discussed on the mailinglist.
+
+
+Flask-Markdown
+--------------
+
+:Last Review: 2010-07-25
+:Reviewed Version: 0.2
+
+Would be great for enlisting but it should follow the API of Flask-Creole.
+Besides that, the docstrings are not valid rst (run through rst2html to
+see the issue) and it is missing tests. Otherwise fine :)
+
+
+flask-urls
+----------
+
+:Last Review: 2010-07-25
+:Reviewed Version: 0.9.2
+
+Broken PyPI index and non-conforming extension name. Due to the small
+featureset this was also delisted from the list. It was there previously
+before the approval process was introduced.
--- a/bundled/flask/flask.py Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1512 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- flask
- ~~~~~
-
- A microframework based on Werkzeug. It's extensively documented
- and follows best practice patterns.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-from __future__ import with_statement
-import os
-import sys
-import mimetypes
-from datetime import datetime, timedelta
-
-from itertools import chain
-from threading import Lock
-from jinja2 import Environment, PackageLoader, FileSystemLoader
-from werkzeug import Request as RequestBase, Response as ResponseBase, \
- LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
- ImmutableDict, cached_property, wrap_file, Headers, \
- import_string
-from werkzeug.routing import Map, Rule
-from werkzeug.exceptions import HTTPException, InternalServerError
-from werkzeug.contrib.securecookie import SecureCookie
-
-# try to load the best simplejson implementation available. If JSON
-# is not installed, we add a failing class.
-json_available = True
-try:
- import simplejson as json
-except ImportError:
- try:
- import json
- except ImportError:
- json_available = False
-
-# utilities we import from Werkzeug and Jinja2 that are unused
-# in the module but are exported as public interface.
-from werkzeug import abort, redirect
-from jinja2 import Markup, escape
-
-# use pkg_resource if that works, otherwise fall back to cwd. The
-# current working directory is generally not reliable with the notable
-# exception of google appengine.
-try:
- import pkg_resources
- pkg_resources.resource_stream
-except (ImportError, AttributeError):
- pkg_resources = None
-
-# a lock used for logger initialization
-_logger_lock = Lock()
-
-
-class Request(RequestBase):
- """The request object used by default in flask. Remembers the
- matched endpoint and view arguments.
-
- It is what ends up as :class:`~flask.request`. If you want to replace
- the request object used you can subclass this and set
- :attr:`~flask.Flask.request_class` to your subclass.
- """
-
- endpoint = view_args = routing_exception = None
-
- @property
- def module(self):
- """The name of the current module"""
- if self.endpoint and '.' in self.endpoint:
- return self.endpoint.rsplit('.', 1)[0]
-
- @cached_property
- def json(self):
- """If the mimetype is `application/json` this will contain the
- parsed JSON data.
- """
- if __debug__:
- _assert_have_json()
- if self.mimetype == 'application/json':
- return json.loads(self.data)
-
-
-class Response(ResponseBase):
- """The response object that is used by default in flask. Works like the
- response object from Werkzeug but is set to have a HTML mimetype by
- default. Quite often you don't have to create this object yourself because
- :meth:`~flask.Flask.make_response` will take care of that for you.
-
- If you want to replace the response object used you can subclass this and
- set :attr:`~flask.Flask.response_class` to your subclass.
- """
- default_mimetype = 'text/html'
-
-
-class _RequestGlobals(object):
- pass
-
-
-class Session(SecureCookie):
- """Expands the session with support for switching between permanent
- and non-permanent sessions.
- """
-
- def _get_permanent(self):
- return self.get('_permanent', False)
-
- def _set_permanent(self, value):
- self['_permanent'] = bool(value)
-
- permanent = property(_get_permanent, _set_permanent)
- del _get_permanent, _set_permanent
-
-
-class _NullSession(Session):
- """Class used to generate nicer error messages if sessions are not
- available. Will still allow read-only access to the empty session
- but fail on setting.
- """
-
- def _fail(self, *args, **kwargs):
- raise RuntimeError('the session is unavailable because no secret '
- 'key was set. Set the secret_key on the '
- 'application to something unique and secret')
- __setitem__ = __delitem__ = clear = pop = popitem = \
- update = setdefault = _fail
- del _fail
-
-
-class _RequestContext(object):
- """The request context contains all request relevant information. It is
- created at the beginning of the request and pushed to the
- `_request_ctx_stack` and removed at the end of it. It will create the
- URL adapter and request object for the WSGI environment provided.
- """
-
- def __init__(self, app, environ):
- self.app = app
- self.url_adapter = app.url_map.bind_to_environ(environ)
- self.request = app.request_class(environ)
- self.session = app.open_session(self.request)
- if self.session is None:
- self.session = _NullSession()
- self.g = _RequestGlobals()
- self.flashes = None
-
- try:
- self.request.endpoint, self.request.view_args = \
- self.url_adapter.match()
- except HTTPException, e:
- self.request.routing_exception = e
-
- def push(self):
- """Binds the request context."""
- _request_ctx_stack.push(self)
-
- def pop(self):
- """Pops the request context."""
- _request_ctx_stack.pop()
-
- def __enter__(self):
- self.push()
- return self
-
- def __exit__(self, exc_type, exc_value, tb):
- # do not pop the request stack if we are in debug mode and an
- # exception happened. This will allow the debugger to still
- # access the request object in the interactive shell. Furthermore
- # the context can be force kept alive for the test client.
- if not self.request.environ.get('flask._preserve_context') and \
- (tb is None or not self.app.debug):
- self.pop()
-
-
-def url_for(endpoint, **values):
- """Generates a URL to the given endpoint with the method provided.
- The endpoint is relative to the active module if modules are in use.
-
- Here some examples:
-
- ==================== ======================= =============================
- Active Module Target Endpoint Target Function
- ==================== ======================= =============================
- `None` ``'index'`` `index` of the application
- `None` ``'.index'`` `index` of the application
- ``'admin'`` ``'index'`` `index` of the `admin` module
- any ``'.index'`` `index` of the application
- any ``'admin.index'`` `index` of the `admin` module
- ==================== ======================= =============================
-
- Variable arguments that are unknown to the target endpoint are appended
- to the generated URL as query arguments.
-
- For more information, head over to the :ref:`Quickstart <url-building>`.
-
- :param endpoint: the endpoint of the URL (name of the function)
- :param values: the variable arguments of the URL rule
- :param _external: if set to `True`, an absolute URL is generated.
- """
- ctx = _request_ctx_stack.top
- if '.' not in endpoint:
- mod = ctx.request.module
- if mod is not None:
- endpoint = mod + '.' + endpoint
- elif endpoint.startswith('.'):
- endpoint = endpoint[1:]
- external = values.pop('_external', False)
- return ctx.url_adapter.build(endpoint, values, force_external=external)
-
-
-def get_template_attribute(template_name, attribute):
- """Loads a macro (or variable) a template exports. This can be used to
- invoke a macro from within Python code. If you for example have a
- template named `_cider.html` with the following contents:
-
- .. sourcecode:: html+jinja
-
- {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
-
- You can access this from Python code like this::
-
- hello = get_template_attribute('_cider.html', 'hello')
- return hello('World')
-
- .. versionadded:: 0.2
-
- :param template_name: the name of the template
- :param attribute: the name of the variable of macro to acccess
- """
- return getattr(current_app.jinja_env.get_template(template_name).module,
- attribute)
-
-
-def flash(message, category='message'):
- """Flashes a message to the next request. In order to remove the
- flashed message from the session and to display it to the user,
- the template has to call :func:`get_flashed_messages`.
-
- .. versionchanged: 0.3
- `category` parameter added.
-
- :param message: the message to be flashed.
- :param category: the category for the message. The following values
- are recommended: ``'message'`` for any kind of message,
- ``'error'`` for errors, ``'info'`` for information
- messages and ``'warning'`` for warnings. However any
- kind of string can be used as category.
- """
- session.setdefault('_flashes', []).append((category, message))
-
-
-def get_flashed_messages(with_categories=False):
- """Pulls all flashed messages from the session and returns them.
- Further calls in the same request to the function will return
- the same messages. By default just the messages are returned,
- but when `with_categories` is set to `True`, the return value will
- be a list of tuples in the form ``(category, message)`` instead.
-
- Example usage:
-
- .. sourcecode:: html+jinja
-
- {% for category, msg in get_flashed_messages(with_categories=true) %}
- <p class=flash-{{ category }}>{{ msg }}
- {% endfor %}
-
- .. versionchanged:: 0.3
- `with_categories` parameter added.
-
- :param with_categories: set to `True` to also receive categories.
- """
- flashes = _request_ctx_stack.top.flashes
- if flashes is None:
- _request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
- if not with_categories:
- return [x[1] for x in flashes]
- return flashes
-
-
-def jsonify(*args, **kwargs):
- """Creates a :class:`~flask.Response` with the JSON representation of
- the given arguments with an `application/json` mimetype. The arguments
- to this function are the same as to the :class:`dict` constructor.
-
- Example usage::
-
- @app.route('/_get_current_user')
- def get_current_user():
- return jsonify(username=g.user.username,
- email=g.user.email,
- id=g.user.id)
-
- This will send a JSON response like this to the browser::
-
- {
- "username": "admin",
- "email": "admin@localhost",
- "id": 42
- }
-
- This requires Python 2.6 or an installed version of simplejson. For
- security reasons only objects are supported toplevel. For more
- information about this, have a look at :ref:`json-security`.
-
- .. versionadded:: 0.2
- """
- if __debug__:
- _assert_have_json()
- return current_app.response_class(json.dumps(dict(*args, **kwargs),
- indent=None if request.is_xhr else 2), mimetype='application/json')
-
-
-def send_file(filename_or_fp, mimetype=None, as_attachment=False,
- attachment_filename=None):
- """Sends the contents of a file to the client. This will use the
- most efficient method available and configured. By default it will
- try to use the WSGI server's file_wrapper support. Alternatively
- you can set the application's :attr:`~Flask.use_x_sendfile` attribute
- to ``True`` to directly emit an `X-Sendfile` header. This however
- requires support of the underlying webserver for `X-Sendfile`.
-
- By default it will try to guess the mimetype for you, but you can
- also explicitly provide one. For extra security you probably want
- to sent certain files as attachment (HTML for instance).
-
- Please never pass filenames to this function from user sources without
- checking them first. Something like this is usually sufficient to
- avoid security problems::
-
- if '..' in filename or filename.startswith('/'):
- abort(404)
-
- .. versionadded:: 0.2
-
- :param filename_or_fp: the filename of the file to send. This is
- relative to the :attr:`~Flask.root_path` if a
- relative path is specified.
- Alternatively a file object might be provided
- in which case `X-Sendfile` might not work and
- fall back to the traditional method.
- :param mimetype: the mimetype of the file if provided, otherwise
- auto detection happens.
- :param as_attachment: set to `True` if you want to send this file with
- a ``Content-Disposition: attachment`` header.
- :param attachment_filename: the filename for the attachment if it
- differs from the file's filename.
- """
- if isinstance(filename_or_fp, basestring):
- filename = filename_or_fp
- file = None
- else:
- file = filename_or_fp
- filename = getattr(file, 'name', None)
- if filename is not None:
- filename = os.path.join(current_app.root_path, filename)
- if mimetype is None and (filename or attachment_filename):
- mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
- if mimetype is None:
- mimetype = 'application/octet-stream'
-
- headers = Headers()
- if as_attachment:
- if attachment_filename is None:
- if filename is None:
- raise TypeError('filename unavailable, required for '
- 'sending as attachment')
- attachment_filename = os.path.basename(filename)
- headers.add('Content-Disposition', 'attachment',
- filename=attachment_filename)
-
- if current_app.use_x_sendfile and filename:
- if file is not None:
- file.close()
- headers['X-Sendfile'] = filename
- data = None
- else:
- if file is None:
- file = open(filename, 'rb')
- data = wrap_file(request.environ, file)
-
- return Response(data, mimetype=mimetype, headers=headers,
- direct_passthrough=True)
-
-
-def render_template(template_name, **context):
- """Renders a template from the template folder with the given
- context.
-
- :param template_name: the name of the template to be rendered
- :param context: the variables that should be available in the
- context of the template.
- """
- current_app.update_template_context(context)
- return current_app.jinja_env.get_template(template_name).render(context)
-
-
-def render_template_string(source, **context):
- """Renders a template from the given template source string
- with the given context.
-
- :param template_name: the sourcecode of the template to be
- rendered
- :param context: the variables that should be available in the
- context of the template.
- """
- current_app.update_template_context(context)
- return current_app.jinja_env.from_string(source).render(context)
-
-
-def _default_template_ctx_processor():
- """Default template context processor. Injects `request`,
- `session` and `g`.
- """
- reqctx = _request_ctx_stack.top
- return dict(
- request=reqctx.request,
- session=reqctx.session,
- g=reqctx.g
- )
-
-
-def _assert_have_json():
- """Helper function that fails if JSON is unavailable."""
- if not json_available:
- raise RuntimeError('simplejson not installed')
-
-
-def _get_package_path(name):
- """Returns the path to a package or cwd if that cannot be found."""
- try:
- return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
- except (KeyError, AttributeError):
- return os.getcwd()
-
-
-# figure out if simplejson escapes slashes. This behaviour was changed
-# from one version to another without reason.
-if not json_available or '\\/' not in json.dumps('/'):
-
- def _tojson_filter(*args, **kwargs):
- if __debug__:
- _assert_have_json()
- return json.dumps(*args, **kwargs).replace('/', '\\/')
-else:
- _tojson_filter = json.dumps
-
-
-class _PackageBoundObject(object):
-
- def __init__(self, import_name):
- #: The name of the package or module. Do not change this once
- #: it was set by the constructor.
- self.import_name = import_name
-
- #: Where is the app root located?
- self.root_path = _get_package_path(self.import_name)
-
- def open_resource(self, resource):
- """Opens a resource from the application's resource folder. To see
- how this works, consider the following folder structure::
-
- /myapplication.py
- /schemal.sql
- /static
- /style.css
- /templates
- /layout.html
- /index.html
-
- If you want to open the `schema.sql` file you would do the
- following::
-
- with app.open_resource('schema.sql') as f:
- contents = f.read()
- do_something_with(contents)
-
- :param resource: the name of the resource. To access resources within
- subfolders use forward slashes as separator.
- """
- if pkg_resources is None:
- return open(os.path.join(self.root_path, resource), 'rb')
- return pkg_resources.resource_stream(self.import_name, resource)
-
-
-class _ModuleSetupState(object):
-
- def __init__(self, app, url_prefix=None):
- self.app = app
- self.url_prefix = url_prefix
-
-
-class Module(_PackageBoundObject):
- """Container object that enables pluggable applications. A module can
- be used to organize larger applications. They represent blueprints that,
- in combination with a :class:`Flask` object are used to create a large
- application.
-
- A module is like an application bound to an `import_name`. Multiple
- modules can share the same import names, but in that case a `name` has
- to be provided to keep them apart. If different import names are used,
- the rightmost part of the import name is used as name.
-
- Here an example structure for a larger appliation::
-
- /myapplication
- /__init__.py
- /views
- /__init__.py
- /admin.py
- /frontend.py
-
- The `myapplication/__init__.py` can look like this::
-
- from flask import Flask
- from myapplication.views.admin import admin
- from myapplication.views.frontend import frontend
-
- app = Flask(__name__)
- app.register_module(admin, url_prefix='/admin')
- app.register_module(frontend)
-
- And here an example view module (`myapplication/views/admin.py`)::
-
- from flask import Module
-
- admin = Module(__name__)
-
- @admin.route('/')
- def index():
- pass
-
- @admin.route('/login')
- def login():
- pass
-
- For a gentle introduction into modules, checkout the
- :ref:`working-with-modules` section.
- """
-
- def __init__(self, import_name, name=None, url_prefix=None):
- if name is None:
- assert '.' in import_name, 'name required if package name ' \
- 'does not point to a submodule'
- name = import_name.rsplit('.', 1)[1]
- _PackageBoundObject.__init__(self, import_name)
- self.name = name
- self.url_prefix = url_prefix
- self._register_events = []
-
- def route(self, rule, **options):
- """Like :meth:`Flask.route` but for a module. The endpoint for the
- :func:`url_for` function is prefixed with the name of the module.
- """
- def decorator(f):
- self.add_url_rule(rule, f.__name__, f, **options)
- return f
- return decorator
-
- def add_url_rule(self, rule, endpoint, view_func=None, **options):
- """Like :meth:`Flask.add_url_rule` but for a module. The endpoint for
- the :func:`url_for` function is prefixed with the name of the module.
- """
- def register_rule(state):
- the_rule = rule
- if state.url_prefix:
- the_rule = state.url_prefix + rule
- state.app.add_url_rule(the_rule, '%s.%s' % (self.name, endpoint),
- view_func, **options)
- self._record(register_rule)
-
- def before_request(self, f):
- """Like :meth:`Flask.before_request` but for a module. This function
- is only executed before each request that is handled by a function of
- that module.
- """
- self._record(lambda s: s.app.before_request_funcs
- .setdefault(self.name, []).append(f))
- return f
-
- def before_app_request(self, f):
- """Like :meth:`Flask.before_request`. Such a function is executed
- before each request, even if outside of a module.
- """
- self._record(lambda s: s.app.before_request_funcs
- .setdefault(None, []).append(f))
- return f
-
- def after_request(self, f):
- """Like :meth:`Flask.after_request` but for a module. This function
- is only executed after each request that is handled by a function of
- that module.
- """
- self._record(lambda s: s.app.after_request_funcs
- .setdefault(self.name, []).append(f))
- return f
-
- def after_app_request(self, f):
- """Like :meth:`Flask.after_request` but for a module. Such a function
- is executed after each request, even if outside of the module.
- """
- self._record(lambda s: s.app.after_request_funcs
- .setdefault(None, []).append(f))
- return f
-
- def context_processor(self, f):
- """Like :meth:`Flask.context_processor` but for a module. This
- function is only executed for requests handled by a module.
- """
- self._record(lambda s: s.app.template_context_processors
- .setdefault(self.name, []).append(f))
- return f
-
- def app_context_processor(self, f):
- """Like :meth:`Flask.context_processor` but for a module. Such a
- function is executed each request, even if outside of the module.
- """
- self._record(lambda s: s.app.template_context_processors
- .setdefault(None, []).append(f))
- return f
-
- def app_errorhandler(self, code):
- """Like :meth:`Flask.errorhandler` but for a module. This
- handler is used for all requests, even if outside of the module.
-
- .. versionadded:: 0.4
- """
- def decorator(f):
- self._record(lambda s: s.app.errorhandler(code)(f))
- return f
- return decorator
-
- def _record(self, func):
- self._register_events.append(func)
-
-
-class ConfigAttribute(object):
- """Makes an attribute forward to the config"""
-
- def __init__(self, name):
- self.__name__ = name
-
- def __get__(self, obj, type=None):
- if obj is None:
- return self
- return obj.config[self.__name__]
-
- def __set__(self, obj, value):
- obj.config[self.__name__] = value
-
-
-class Config(dict):
- """Works exactly like a dict but provides ways to fill it from files
- or special dictionaries. There are two common patterns to populate the
- config.
-
- Either you can fill the config from a config file::
-
- app.config.from_pyfile('yourconfig.cfg')
-
- Or alternatively you can define the configuration options in the
- module that calls :meth:`from_object` or provide an import path to
- a module that should be loaded. It is also possible to tell it to
- use the same module and with that provide the configuration values
- just before the call::
-
- DEBUG = True
- SECRET_KEY = 'development key'
- app.config.from_object(__name__)
-
- In both cases (loading from any Python file or loading from modules),
- only uppercase keys are added to the config. This makes it possible to use
- lowercase values in the config file for temporary values that are not added
- to the config or to define the config keys in the same file that implements
- the application.
-
- Probably the most interesting way to load configurations is from an
- environment variable pointing to a file::
-
- app.config.from_envvar('YOURAPPLICATION_SETTINGS')
-
- In this case before launching the application you have to set this
- environment variable to the file you want to use. On Linux and OS X
- use the export statement::
-
- export YOURAPPLICATION_SETTINGS='/path/to/config/file'
-
- On windows use `set` instead.
-
- :param root_path: path to which files are read relative from. When the
- config object is created by the application, this is
- the application's :attr:`~flask.Flask.root_path`.
- :param defaults: an optional dictionary of default values
- """
-
- def __init__(self, root_path, defaults=None):
- dict.__init__(self, defaults or {})
- self.root_path = root_path
-
- def from_envvar(self, variable_name, silent=False):
- """Loads a configuration from an environment variable pointing to
- a configuration file. This basically is just a shortcut with nicer
- error messages for this line of code::
-
- app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
-
- :param variable_name: name of the environment variable
- :param silent: set to `True` if you want silent failing for missing
- files.
- :return: bool. `True` if able to load config, `False` otherwise.
- """
- rv = os.environ.get(variable_name)
- if not rv:
- if silent:
- return False
- raise RuntimeError('The environment variable %r is not set '
- 'and as such configuration could not be '
- 'loaded. Set this variable and make it '
- 'point to a configuration file' %
- variable_name)
- self.from_pyfile(rv)
- return True
-
- def from_pyfile(self, filename):
- """Updates the values in the config from a Python file. This function
- behaves as if the file was imported as module with the
- :meth:`from_object` function.
-
- :param filename: the filename of the config. This can either be an
- absolute filename or a filename relative to the
- root path.
- """
- filename = os.path.join(self.root_path, filename)
- d = type(sys)('config')
- d.__file__ = filename
- execfile(filename, d.__dict__)
- self.from_object(d)
-
- def from_object(self, obj):
- """Updates the values from the given object. An object can be of one
- of the following two types:
-
- - a string: in this case the object with that name will be imported
- - an actual object reference: that object is used directly
-
- Objects are usually either modules or classes.
-
- Just the uppercase variables in that object are stored in the config
- after lowercasing. Example usage::
-
- app.config.from_object('yourapplication.default_config')
- from yourapplication import default_config
- app.config.from_object(default_config)
-
- You should not use this function to load the actual configuration but
- rather configuration defaults. The actual config should be loaded
- with :meth:`from_pyfile` and ideally from a location not within the
- package because the package might be installed system wide.
-
- :param obj: an import name or object
- """
- if isinstance(obj, basestring):
- obj = import_string(obj)
- for key in dir(obj):
- if key.isupper():
- self[key] = getattr(obj, key)
-
- def __repr__(self):
- return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
-
-
-class Flask(_PackageBoundObject):
- """The flask object implements a WSGI application and acts as the central
- object. It is passed the name of the module or package of the
- application. Once it is created it will act as a central registry for
- the view functions, the URL rules, template configuration and much more.
-
- The name of the package is used to resolve resources from inside the
- package or the folder the module is contained in depending on if the
- package parameter resolves to an actual python package (a folder with
- an `__init__.py` file inside) or a standard module (just a `.py` file).
-
- For more information about resource loading, see :func:`open_resource`.
-
- Usually you create a :class:`Flask` instance in your main module or
- in the `__init__.py` file of your package like this::
-
- from flask import Flask
- app = Flask(__name__)
- """
-
- #: The class that is used for request objects. See :class:`~flask.Request`
- #: for more information.
- request_class = Request
-
- #: The class that is used for response objects. See
- #: :class:`~flask.Response` for more information.
- response_class = Response
-
- #: Path for the static files. If you don't want to use static files
- #: you can set this value to `None` in which case no URL rule is added
- #: and the development server will no longer serve any static files.
- static_path = '/static'
-
- #: The debug flag. Set this to `True` to enable debugging of the
- #: application. In debug mode the debugger will kick in when an unhandled
- #: exception ocurrs and the integrated server will automatically reload
- #: the application if changes in the code are detected.
- #:
- #: This attribute can also be configured from the config with the `DEBUG`
- #: configuration key. Defaults to `False`.
- debug = ConfigAttribute('DEBUG')
-
- #: The testing flask. Set this to `True` to enable the test mode of
- #: Flask extensions (and in the future probably also Flask itself).
- #: For example this might activate unittest helpers that have an
- #: additional runtime cost which should not be enabled by default.
- #:
- #: This attribute can also be configured from the config with the
- #: `TESTING` configuration key. Defaults to `False`.
- testing = ConfigAttribute('TESTING')
-
- #: If a secret key is set, cryptographic components can use this to
- #: sign cookies and other things. Set this to a complex random value
- #: when you want to use the secure cookie for instance.
- #:
- #: This attribute can also be configured from the config with the
- #: `SECRET_KEY` configuration key. Defaults to `None`.
- secret_key = ConfigAttribute('SECRET_KEY')
-
- #: The secure cookie uses this for the name of the session cookie.
- #:
- #: This attribute can also be configured from the config with the
- #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'``
- session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
-
- #: A :class:`~datetime.timedelta` which is used to set the expiration
- #: date of a permanent session. The default is 31 days which makes a
- #: permanent session survive for roughly one month.
- #:
- #: This attribute can also be configured from the config with the
- #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to
- #: ``timedelta(days=31)``
- permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME')
-
- #: Enable this if you want to use the X-Sendfile feature. Keep in
- #: mind that the server has to support this. This only affects files
- #: sent with the :func:`send_file` method.
- #:
- #: .. versionadded:: 0.2
- #:
- #: This attribute can also be configured from the config with the
- #: `USE_X_SENDFILE` configuration key. Defaults to `False`.
- use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
-
- #: The name of the logger to use. By default the logger name is the
- #: package name passed to the constructor.
- #:
- #: .. versionadded:: 0.4
- logger_name = ConfigAttribute('LOGGER_NAME')
-
- #: The logging format used for the debug logger. This is only used when
- #: the application is in debug mode, otherwise the attached logging
- #: handler does the formatting.
- #:
- #: .. versionadded:: 0.3
- debug_log_format = (
- '-' * 80 + '\n' +
- '%(levelname)s in %(module)s, %(pathname)s:%(lineno)d]:\n' +
- '%(message)s\n' +
- '-' * 80
- )
-
- #: Options that are passed directly to the Jinja2 environment.
- jinja_options = ImmutableDict(
- autoescape=True,
- extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
- )
-
- #: Default configuration parameters.
- default_config = ImmutableDict({
- 'DEBUG': False,
- 'TESTING': False,
- 'SECRET_KEY': None,
- 'SESSION_COOKIE_NAME': 'session',
- 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
- 'USE_X_SENDFILE': False,
- 'LOGGER_NAME': None
- })
-
- def __init__(self, import_name):
- _PackageBoundObject.__init__(self, import_name)
-
- #: The configuration dictionary as :class:`Config`. This behaves
- #: exactly like a regular dictionary but supports additional methods
- #: to load a config from files.
- self.config = Config(self.root_path, self.default_config)
-
- #: Prepare the deferred setup of the logger.
- self._logger = None
- self.logger_name = self.import_name
-
- #: A dictionary of all view functions registered. The keys will
- #: be function names which are also used to generate URLs and
- #: the values are the function objects themselves.
- #: to register a view function, use the :meth:`route` decorator.
- self.view_functions = {}
-
- #: A dictionary of all registered error handlers. The key is
- #: be the error code as integer, the value the function that
- #: should handle that error.
- #: To register a error handler, use the :meth:`errorhandler`
- #: decorator.
- self.error_handlers = {}
-
- #: A dictionary with lists of functions that should be called at the
- #: beginning of the request. The key of the dictionary is the name of
- #: the module this function is active for, `None` for all requests.
- #: This can for example be used to open database connections or
- #: getting hold of the currently logged in user. To register a
- #: function here, use the :meth:`before_request` decorator.
- self.before_request_funcs = {}
-
- #: A dictionary with lists of functions that should be called after
- #: each request. The key of the dictionary is the name of the module
- #: this function is active for, `None` for all requests. This can for
- #: example be used to open database connections or getting hold of the
- #: currently logged in user. To register a function here, use the
- #: :meth:`before_request` decorator.
- self.after_request_funcs = {}
-
- #: A dictionary with list of functions that are called without argument
- #: to populate the template context. They key of the dictionary is the
- #: name of the module this function is active for, `None` for all
- #: requests. Each returns a dictionary that the template context is
- #: updated with. To register a function here, use the
- #: :meth:`context_processor` decorator.
- self.template_context_processors = {
- None: [_default_template_ctx_processor]
- }
-
- #: The :class:`~werkzeug.routing.Map` for this instance. You can use
- #: this to change the routing converters after the class was created
- #: but before any routes are connected. Example::
- #:
- #: from werkzeug import BaseConverter
- #:
- #: class ListConverter(BaseConverter):
- #: def to_python(self, value):
- #: return value.split(',')
- #: def to_url(self, values):
- #: return ','.join(BaseConverter.to_url(value)
- #: for value in values)
- #:
- #: app = Flask(__name__)
- #: app.url_map.converters['list'] = ListConverter
- self.url_map = Map()
-
- if self.static_path is not None:
- self.add_url_rule(self.static_path + '/<filename>',
- build_only=True, endpoint='static')
- if pkg_resources is not None:
- target = (self.import_name, 'static')
- else:
- target = os.path.join(self.root_path, 'static')
- self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
- self.static_path: target
- })
-
- #: The Jinja2 environment. It is created from the
- #: :attr:`jinja_options` and the loader that is returned
- #: by the :meth:`create_jinja_loader` function.
- self.jinja_env = Environment(loader=self.create_jinja_loader(),
- **self.jinja_options)
- self.jinja_env.globals.update(
- url_for=url_for,
- get_flashed_messages=get_flashed_messages
- )
- self.jinja_env.filters['tojson'] = _tojson_filter
-
- @property
- def logger(self):
- """A :class:`logging.Logger` object for this application. The
- default configuration is to log to stderr if the application is
- in debug mode. This logger can be used to (surprise) log messages.
- Here some examples::
-
- app.logger.debug('A value for debugging')
- app.logger.warning('A warning ocurred (%d apples)', 42)
- app.logger.error('An error occoured')
-
- .. versionadded:: 0.3
- """
- if self._logger and self._logger.name == self.logger_name:
- return self._logger
- with _logger_lock:
- if self._logger and self._logger.name == self.logger_name:
- return self._logger
- from logging import getLogger, StreamHandler, Formatter, DEBUG
- class DebugHandler(StreamHandler):
- def emit(x, record):
- if self.debug:
- StreamHandler.emit(x, record)
- handler = DebugHandler()
- handler.setLevel(DEBUG)
- handler.setFormatter(Formatter(self.debug_log_format))
- logger = getLogger(self.logger_name)
- logger.addHandler(handler)
- self._logger = logger
- return logger
-
- def create_jinja_loader(self):
- """Creates the Jinja loader. By default just a package loader for
- the configured package is returned that looks up templates in the
- `templates` folder. To add other loaders it's possible to
- override this method.
- """
- if pkg_resources is None:
- return FileSystemLoader(os.path.join(self.root_path, 'templates'))
- return PackageLoader(self.import_name)
-
- def update_template_context(self, context):
- """Update the template context with some commonly used variables.
- This injects request, session and g into the template context.
-
- :param context: the context as a dictionary that is updated in place
- to add extra variables.
- """
- funcs = self.template_context_processors[None]
- mod = _request_ctx_stack.top.request.module
- if mod is not None and mod in self.template_context_processors:
- funcs = chain(funcs, self.template_context_processors[mod])
- for func in funcs:
- context.update(func())
-
- def run(self, host='127.0.0.1', port=5000, **options):
- """Runs the application on a local development server. If the
- :attr:`debug` flag is set the server will automatically reload
- for code changes and show a debugger in case an exception happened.
-
- :param host: the hostname to listen on. set this to ``'0.0.0.0'``
- to have the server available externally as well.
- :param port: the port of the webserver
- :param options: the options to be forwarded to the underlying
- Werkzeug server. See :func:`werkzeug.run_simple`
- for more information.
- """
- from werkzeug import run_simple
- if 'debug' in options:
- self.debug = options.pop('debug')
- options.setdefault('use_reloader', self.debug)
- options.setdefault('use_debugger', self.debug)
- return run_simple(host, port, self, **options)
-
- def test_client(self):
- """Creates a test client for this application. For information
- about unit testing head over to :ref:`testing`.
-
- The test client can be used in a `with` block to defer the closing down
- of the context until the end of the `with` block. This is useful if
- you want to access the context locals for testing::
-
- with app.test_client() as c:
- rv = c.get('/?vodka=42')
- assert request.args['vodka'] == '42'
-
- .. versionchanged:: 0.4
- added support for `with` block usage for the client.
- """
- from werkzeug import Client
- class FlaskClient(Client):
- preserve_context = context_preserved = False
- def open(self, *args, **kwargs):
- if self.context_preserved:
- _request_ctx_stack.pop()
- self.context_preserved = False
- kwargs.setdefault('environ_overrides', {}) \
- ['flask._preserve_context'] = self.preserve_context
- old = _request_ctx_stack.top
- try:
- return Client.open(self, *args, **kwargs)
- finally:
- self.context_preserved = _request_ctx_stack.top is not old
- def __enter__(self):
- self.preserve_context = True
- return self
- def __exit__(self, exc_type, exc_value, tb):
- self.preserve_context = False
- if self.context_preserved:
- _request_ctx_stack.pop()
- return FlaskClient(self, self.response_class, use_cookies=True)
-
- def open_session(self, request):
- """Creates or opens a new session. Default implementation stores all
- session data in a signed cookie. This requires that the
- :attr:`secret_key` is set.
-
- :param request: an instance of :attr:`request_class`.
- """
- key = self.secret_key
- if key is not None:
- return Session.load_cookie(request, self.session_cookie_name,
- secret_key=key)
-
- def save_session(self, session, response):
- """Saves the session if it needs updates. For the default
- implementation, check :meth:`open_session`.
-
- :param session: the session to be saved (a
- :class:`~werkzeug.contrib.securecookie.SecureCookie`
- object)
- :param response: an instance of :attr:`response_class`
- """
- expires = None
- if session.permanent:
- expires = datetime.utcnow() + self.permanent_session_lifetime
- session.save_cookie(response, self.session_cookie_name,
- expires=expires, httponly=True)
-
- def register_module(self, module, **options):
- """Registers a module with this application. The keyword argument
- of this function are the same as the ones for the constructor of the
- :class:`Module` class and will override the values of the module if
- provided.
- """
- options.setdefault('url_prefix', module.url_prefix)
- state = _ModuleSetupState(self, **options)
- for func in module._register_events:
- func(state)
-
- def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
- """Connects a URL rule. Works exactly like the :meth:`route`
- decorator. If a view_func is provided it will be registered with the
- endpoint.
-
- Basically this example::
-
- @app.route('/')
- def index():
- pass
-
- Is equivalent to the following::
-
- def index():
- pass
- app.add_url_rule('/', 'index', index)
-
- If the view_func is not provided you will need to connect the endpoint
- to a view function like so::
-
- app.view_functions['index'] = index
-
- .. versionchanged:: 0.2
- `view_func` parameter added.
-
- :param rule: the URL rule as string
- :param endpoint: the endpoint for the registered URL rule. Flask
- itself assumes the name of the view function as
- endpoint
- :param view_func: the function to call when serving a request to the
- provided endpoint
- :param options: the options to be forwarded to the underlying
- :class:`~werkzeug.routing.Rule` object
- """
- if endpoint is None:
- assert view_func is not None, 'expected view func if endpoint ' \
- 'is not provided.'
- endpoint = view_func.__name__
- options['endpoint'] = endpoint
- options.setdefault('methods', ('GET',))
- self.url_map.add(Rule(rule, **options))
- if view_func is not None:
- self.view_functions[endpoint] = view_func
-
- def route(self, rule, **options):
- """A decorator that is used to register a view function for a
- given URL rule. Example::
-
- @app.route('/')
- def index():
- return 'Hello World'
-
- Variables parts in the route can be specified with angular
- brackets (``/user/<username>``). By default a variable part
- in the URL accepts any string without a slash however a different
- converter can be specified as well by using ``<converter:name>``.
-
- Variable parts are passed to the view function as keyword
- arguments.
-
- The following converters are possible:
-
- =========== ===========================================
- `int` accepts integers
- `float` like `int` but for floating point values
- `path` like the default but also accepts slashes
- =========== ===========================================
-
- Here some examples::
-
- @app.route('/')
- def index():
- pass
-
- @app.route('/<username>')
- def show_user(username):
- pass
-
- @app.route('/post/<int:post_id>')
- def show_post(post_id):
- pass
-
- An important detail to keep in mind is how Flask deals with trailing
- slashes. The idea is to keep each URL unique so the following rules
- apply:
-
- 1. If a rule ends with a slash and is requested without a slash
- by the user, the user is automatically redirected to the same
- page with a trailing slash attached.
- 2. If a rule does not end with a trailing slash and the user request
- the page with a trailing slash, a 404 not found is raised.
-
- This is consistent with how web servers deal with static files. This
- also makes it possible to use relative link targets safely.
-
- The :meth:`route` decorator accepts a couple of other arguments
- as well:
-
- :param rule: the URL rule as string
- :param methods: a list of methods this rule should be limited
- to (``GET``, ``POST`` etc.). By default a rule
- just listens for ``GET`` (and implicitly ``HEAD``).
- :param subdomain: specifies the rule for the subdoain in case
- subdomain matching is in use.
- :param strict_slashes: can be used to disable the strict slashes
- setting for this rule. See above.
- :param options: other options to be forwarded to the underlying
- :class:`~werkzeug.routing.Rule` object.
- """
- def decorator(f):
- self.add_url_rule(rule, None, f, **options)
- return f
- return decorator
-
- def errorhandler(self, code):
- """A decorator that is used to register a function give a given
- error code. Example::
-
- @app.errorhandler(404)
- def page_not_found(error):
- return 'This page does not exist', 404
-
- You can also register a function as error handler without using
- the :meth:`errorhandler` decorator. The following example is
- equivalent to the one above::
-
- def page_not_found(error):
- return 'This page does not exist', 404
- app.error_handlers[404] = page_not_found
-
- :param code: the code as integer for the handler
- """
- def decorator(f):
- self.error_handlers[code] = f
- return f
- return decorator
-
- def template_filter(self, name=None):
- """A decorator that is used to register custom template filter.
- You can specify a name for the filter, otherwise the function
- name will be used. Example::
-
- @app.template_filter()
- def reverse(s):
- return s[::-1]
-
- :param name: the optional name of the filter, otherwise the
- function name will be used.
- """
- def decorator(f):
- self.jinja_env.filters[name or f.__name__] = f
- return f
- return decorator
-
- def before_request(self, f):
- """Registers a function to run before each request."""
- self.before_request_funcs.setdefault(None, []).append(f)
- return f
-
- def after_request(self, f):
- """Register a function to be run after each request."""
- self.after_request_funcs.setdefault(None, []).append(f)
- return f
-
- def context_processor(self, f):
- """Registers a template context processor function."""
- self.template_context_processors[None].append(f)
- return f
-
- def handle_http_exception(self, e):
- """Handles an HTTP exception. By default this will invoke the
- registered error handlers and fall back to returning the
- exception as response.
-
- .. versionadded: 0.3
- """
- handler = self.error_handlers.get(e.code)
- if handler is None:
- return e
- return handler(e)
-
- def handle_exception(self, e):
- """Default exception handling that kicks in when an exception
- occours that is not catched. In debug mode the exception will
- be re-raised immediately, otherwise it is logged and the handler
- for a 500 internal server error is used. If no such handler
- exists, a default 500 internal server error message is displayed.
-
- .. versionadded: 0.3
- """
- handler = self.error_handlers.get(500)
- if self.debug:
- raise
- self.logger.exception('Exception on %s [%s]' % (
- request.path,
- request.method
- ))
- if handler is None:
- return InternalServerError()
- return handler(e)
-
- def dispatch_request(self):
- """Does the request dispatching. Matches the URL and returns the
- return value of the view or error handler. This does not have to
- be a response object. In order to convert the return value to a
- proper response object, call :func:`make_response`.
- """
- req = _request_ctx_stack.top.request
- try:
- if req.routing_exception is not None:
- raise req.routing_exception
- return self.view_functions[req.endpoint](**req.view_args)
- except HTTPException, e:
- return self.handle_http_exception(e)
-
- def make_response(self, rv):
- """Converts the return value from a view function to a real
- response object that is an instance of :attr:`response_class`.
-
- The following types are allowed for `rv`:
-
- .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
-
- ======================= ===========================================
- :attr:`response_class` the object is returned unchanged
- :class:`str` a response object is created with the
- string as body
- :class:`unicode` a response object is created with the
- string encoded to utf-8 as body
- :class:`tuple` the response object is created with the
- contents of the tuple as arguments
- a WSGI function the function is called as WSGI application
- and buffered as response object
- ======================= ===========================================
-
- :param rv: the return value from the view function
- """
- if rv is None:
- raise ValueError('View function did not return a response')
- if isinstance(rv, self.response_class):
- return rv
- if isinstance(rv, basestring):
- return self.response_class(rv)
- if isinstance(rv, tuple):
- return self.response_class(*rv)
- return self.response_class.force_type(rv, request.environ)
-
- def preprocess_request(self):
- """Called before the actual request dispatching and will
- call every as :meth:`before_request` decorated function.
- If any of these function returns a value it's handled as
- if it was the return value from the view and further
- request handling is stopped.
- """
- funcs = self.before_request_funcs.get(None, ())
- mod = request.module
- if mod and mod in self.before_request_funcs:
- funcs = chain(funcs, self.before_request_funcs[mod])
- for func in funcs:
- rv = func()
- if rv is not None:
- return rv
-
- def process_response(self, response):
- """Can be overridden in order to modify the response object
- before it's sent to the WSGI server. By default this will
- call all the :meth:`after_request` decorated functions.
-
- :param response: a :attr:`response_class` object.
- :return: a new response object or the same, has to be an
- instance of :attr:`response_class`.
- """
- ctx = _request_ctx_stack.top
- mod = ctx.request.module
- if not isinstance(ctx.session, _NullSession):
- self.save_session(ctx.session, response)
- funcs = ()
- if mod and mod in self.after_request_funcs:
- funcs = chain(funcs, self.after_request_funcs[mod])
- if None in self.after_request_funcs:
- funcs = chain(funcs, self.after_request_funcs[None])
- for handler in funcs:
- response = handler(response)
- return response
-
- def wsgi_app(self, environ, start_response):
- """The actual WSGI application. This is not implemented in
- `__call__` so that middlewares can be applied without losing a
- reference to the class. So instead of doing this::
-
- app = MyMiddleware(app)
-
- It's a better idea to do this instead::
-
- app.wsgi_app = MyMiddleware(app.wsgi_app)
-
- Then you still have the original application object around and
- can continue to call methods on it.
-
- .. versionchanged:: 0.4
- The :meth:`after_request` functions are now called even if an
- error handler took over request processing. This ensures that
- even if an exception happens database have the chance to
- properly close the connection.
-
- :param environ: a WSGI environment
- :param start_response: a callable accepting a status code,
- a list of headers and an optional
- exception context to start the response
- """
- with self.request_context(environ):
- try:
- rv = self.preprocess_request()
- if rv is None:
- rv = self.dispatch_request()
- response = self.make_response(rv)
- except Exception, e:
- response = self.make_response(self.handle_exception(e))
- try:
- response = self.process_response(response)
- except Exception, e:
- response = self.make_response(self.handle_exception(e))
- return response(environ, start_response)
-
- def request_context(self, environ):
- """Creates a request context from the given environment and binds
- it to the current context. This must be used in combination with
- the `with` statement because the request is only bound to the
- current context for the duration of the `with` block.
-
- Example usage::
-
- with app.request_context(environ):
- do_something_with(request)
-
- The object returned can also be used without the `with` statement
- which is useful for working in the shell. The example above is
- doing exactly the same as this code::
-
- ctx = app.request_context(environ)
- ctx.push()
- try:
- do_something_with(request)
- finally:
- ctx.pop()
-
- The big advantage of this approach is that you can use it without
- the try/finally statement in a shell for interactive testing:
-
- >>> ctx = app.test_request_context()
- >>> ctx.bind()
- >>> request.path
- u'/'
- >>> ctx.unbind()
-
- .. versionchanged:: 0.3
- Added support for non-with statement usage and `with` statement
- is now passed the ctx object.
-
- :param environ: a WSGI environment
- """
- return _RequestContext(self, environ)
-
- def test_request_context(self, *args, **kwargs):
- """Creates a WSGI environment from the given values (see
- :func:`werkzeug.create_environ` for more information, this
- function accepts the same arguments).
- """
- return self.request_context(create_environ(*args, **kwargs))
-
- def __call__(self, environ, start_response):
- """Shortcut for :attr:`wsgi_app`."""
- return self.wsgi_app(environ, start_response)
-
-
-# context locals
-_request_ctx_stack = LocalStack()
-current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
-request = LocalProxy(lambda: _request_ctx_stack.top.request)
-session = LocalProxy(lambda: _request_ctx_stack.top.session)
-g = LocalProxy(lambda: _request_ctx_stack.top.g)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/__init__.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+"""
+ flask
+ ~~~~~
+
+ A microframework based on Werkzeug. It's extensively documented
+ and follows best practice patterns.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+# utilities we import from Werkzeug and Jinja2 that are unused
+# in the module but are exported as public interface.
+from werkzeug import abort, redirect
+from jinja2 import Markup, escape
+
+from .app import Flask, Request, Response
+from .config import Config
+from .helpers import url_for, jsonify, json_available, flash, \
+ send_file, send_from_directory, get_flashed_messages, \
+ get_template_attribute, make_response
+from .globals import current_app, g, request, session, _request_ctx_stack
+from .module import Module
+from .templating import render_template, render_template_string
+from .session import Session
+
+# the signals
+from .signals import signals_available, template_rendered, request_started, \
+ request_finished, got_request_exception
+
+# only import json if it's available
+if json_available:
+ from .helpers import json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/app.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,965 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.app
+ ~~~~~~~~~
+
+ This module implements the central WSGI application object.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+from threading import Lock
+from datetime import timedelta, datetime
+from itertools import chain
+
+from jinja2 import Environment
+
+from werkzeug import ImmutableDict
+from werkzeug.routing import Map, Rule
+from werkzeug.exceptions import HTTPException, InternalServerError, \
+ MethodNotAllowed
+
+from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
+ _tojson_filter, _endpoint_from_view_func
+from .wrappers import Request, Response
+from .config import ConfigAttribute, Config
+from .ctx import _RequestContext
+from .globals import _request_ctx_stack, request
+from .session import Session, _NullSession
+from .module import _ModuleSetupState
+from .templating import _DispatchingJinjaLoader, \
+ _default_template_ctx_processor
+from .signals import request_started, request_finished, got_request_exception
+
+# a lock used for logger initialization
+_logger_lock = Lock()
+
+
+class Flask(_PackageBoundObject):
+ """The flask object implements a WSGI application and acts as the central
+ object. It is passed the name of the module or package of the
+ application. Once it is created it will act as a central registry for
+ the view functions, the URL rules, template configuration and much more.
+
+ The name of the package is used to resolve resources from inside the
+ package or the folder the module is contained in depending on if the
+ package parameter resolves to an actual python package (a folder with
+ an `__init__.py` file inside) or a standard module (just a `.py` file).
+
+ For more information about resource loading, see :func:`open_resource`.
+
+ Usually you create a :class:`Flask` instance in your main module or
+ in the `__init__.py` file of your package like this::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ .. admonition:: About the First Parameter
+
+ The idea of the first parameter is to give Flask an idea what
+ belongs to your application. This name is used to find resources
+ on the file system, can be used by extensions to improve debugging
+ information and a lot more.
+
+ So it's important what you provide there. If you are using a single
+ module, `__name__` is always the correct value. If you however are
+ using a package, it's usually recommended to hardcode the name of
+ your package there.
+
+ For example if your application is defined in `yourapplication/app.py`
+ you should create it with one of the two versions below::
+
+ app = Flask('yourapplication')
+ app = Flask(__name__.split('.')[0])
+
+ Why is that? The application will work even with `__name__`, thanks
+ to how resources are looked up. However it will make debugging more
+ painful. Certain extensions can make assumptions based on the
+ import name of your application. For example the Flask-SQLAlchemy
+ extension will look for the code in your application that triggered
+ an SQL query in debug mode. If the import name is not properly set
+ up, that debugging information is lost. (For example it would only
+ pick up SQL queries in `yourapplicaiton.app` and not
+ `yourapplication.views.frontend`)
+
+ .. versionadded:: 0.5
+ The `static_path` parameter was added.
+
+ :param import_name: the name of the application package
+ :param static_path: can be used to specify a different path for the
+ static files on the web. Defaults to ``/static``.
+ This does not affect the folder the files are served
+ *from*.
+ """
+
+ #: The class that is used for request objects. See :class:`~flask.Request`
+ #: for more information.
+ request_class = Request
+
+ #: The class that is used for response objects. See
+ #: :class:`~flask.Response` for more information.
+ response_class = Response
+
+ #: Path for the static files. If you don't want to use static files
+ #: you can set this value to `None` in which case no URL rule is added
+ #: and the development server will no longer serve any static files.
+ #:
+ #: This is the default used for application and modules unless a
+ #: different value is passed to the constructor.
+ static_path = '/static'
+
+ #: The debug flag. Set this to `True` to enable debugging of the
+ #: application. In debug mode the debugger will kick in when an unhandled
+ #: exception ocurrs and the integrated server will automatically reload
+ #: the application if changes in the code are detected.
+ #:
+ #: This attribute can also be configured from the config with the `DEBUG`
+ #: configuration key. Defaults to `False`.
+ debug = ConfigAttribute('DEBUG')
+
+ #: The testing flask. Set this to `True` to enable the test mode of
+ #: Flask extensions (and in the future probably also Flask itself).
+ #: For example this might activate unittest helpers that have an
+ #: additional runtime cost which should not be enabled by default.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `TESTING` configuration key. Defaults to `False`.
+ testing = ConfigAttribute('TESTING')
+
+ #: If a secret key is set, cryptographic components can use this to
+ #: sign cookies and other things. Set this to a complex random value
+ #: when you want to use the secure cookie for instance.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SECRET_KEY` configuration key. Defaults to `None`.
+ secret_key = ConfigAttribute('SECRET_KEY')
+
+ #: The secure cookie uses this for the name of the session cookie.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'``
+ session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
+
+ #: A :class:`~datetime.timedelta` which is used to set the expiration
+ #: date of a permanent session. The default is 31 days which makes a
+ #: permanent session survive for roughly one month.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to
+ #: ``timedelta(days=31)``
+ permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME')
+
+ #: Enable this if you want to use the X-Sendfile feature. Keep in
+ #: mind that the server has to support this. This only affects files
+ #: sent with the :func:`send_file` method.
+ #:
+ #: .. versionadded:: 0.2
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `USE_X_SENDFILE` configuration key. Defaults to `False`.
+ use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
+
+ #: The name of the logger to use. By default the logger name is the
+ #: package name passed to the constructor.
+ #:
+ #: .. versionadded:: 0.4
+ logger_name = ConfigAttribute('LOGGER_NAME')
+
+ #: The logging format used for the debug logger. This is only used when
+ #: the application is in debug mode, otherwise the attached logging
+ #: handler does the formatting.
+ #:
+ #: .. versionadded:: 0.3
+ debug_log_format = (
+ '-' * 80 + '\n' +
+ '%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' +
+ '%(message)s\n' +
+ '-' * 80
+ )
+
+ #: Options that are passed directly to the Jinja2 environment.
+ jinja_options = ImmutableDict(
+ extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
+ )
+
+ #: Default configuration parameters.
+ default_config = ImmutableDict({
+ 'DEBUG': False,
+ 'TESTING': False,
+ 'PROPAGATE_EXCEPTIONS': None,
+ 'SECRET_KEY': None,
+ 'SESSION_COOKIE_NAME': 'session',
+ 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
+ 'USE_X_SENDFILE': False,
+ 'LOGGER_NAME': None,
+ 'SERVER_NAME': None,
+ 'MAX_CONTENT_LENGTH': None
+ })
+
+ #: the test client that is used with when `test_client` is used.
+ #:
+ #: .. versionadded:: 0.7
+ test_client_class = None
+
+ def __init__(self, import_name, static_path=None):
+ _PackageBoundObject.__init__(self, import_name)
+ if static_path is not None:
+ self.static_path = static_path
+
+ #: The configuration dictionary as :class:`Config`. This behaves
+ #: exactly like a regular dictionary but supports additional methods
+ #: to load a config from files.
+ self.config = Config(self.root_path, self.default_config)
+
+ #: Prepare the deferred setup of the logger.
+ self._logger = None
+ self.logger_name = self.import_name
+
+ #: A dictionary of all view functions registered. The keys will
+ #: be function names which are also used to generate URLs and
+ #: the values are the function objects themselves.
+ #: To register a view function, use the :meth:`route` decorator.
+ self.view_functions = {}
+
+ #: A dictionary of all registered error handlers. The key is
+ #: be the error code as integer, the value the function that
+ #: should handle that error.
+ #: To register a error handler, use the :meth:`errorhandler`
+ #: decorator.
+ self.error_handlers = {}
+
+ #: A dictionary with lists of functions that should be called at the
+ #: beginning of the request. The key of the dictionary is the name of
+ #: the module this function is active for, `None` for all requests.
+ #: This can for example be used to open database connections or
+ #: getting hold of the currently logged in user. To register a
+ #: function here, use the :meth:`before_request` decorator.
+ self.before_request_funcs = {}
+
+ #: A dictionary with lists of functions that should be called after
+ #: each request. The key of the dictionary is the name of the module
+ #: this function is active for, `None` for all requests. This can for
+ #: example be used to open database connections or getting hold of the
+ #: currently logged in user. To register a function here, use the
+ #: :meth:`after_request` decorator.
+ self.after_request_funcs = {}
+
+ #: A dictionary with list of functions that are called without argument
+ #: to populate the template context. The key of the dictionary is the
+ #: name of the module this function is active for, `None` for all
+ #: requests. Each returns a dictionary that the template context is
+ #: updated with. To register a function here, use the
+ #: :meth:`context_processor` decorator.
+ self.template_context_processors = {
+ None: [_default_template_ctx_processor]
+ }
+
+ #: all the loaded modules in a dictionary by name.
+ #:
+ #: .. versionadded:: 0.5
+ self.modules = {}
+
+ #: a place where extensions can store application specific state. For
+ #: example this is where an extension could store database engines and
+ #: similar things. For backwards compatibility extensions should register
+ #: themselves like this::
+ #:
+ #: if not hasattr(app, 'extensions'):
+ #: app.extensions = {}
+ #: app.extensions['extensionname'] = SomeObject()
+ #:
+ #: The key must match the name of the `flaskext` module. For example in
+ #: case of a "Flask-Foo" extension in `flaskext.foo`, the key would be
+ #: ``'foo'``.
+ #:
+ #: .. versionadded:: 0.7
+ self.extensions = {}
+
+ #: The :class:`~werkzeug.routing.Map` for this instance. You can use
+ #: this to change the routing converters after the class was created
+ #: but before any routes are connected. Example::
+ #:
+ #: from werkzeug.routing import BaseConverter
+ #:
+ #: class ListConverter(BaseConverter):
+ #: def to_python(self, value):
+ #: return value.split(',')
+ #: def to_url(self, values):
+ #: return ','.join(BaseConverter.to_url(value)
+ #: for value in values)
+ #:
+ #: app = Flask(__name__)
+ #: app.url_map.converters['list'] = ListConverter
+ self.url_map = Map()
+
+ # register the static folder for the application. Do that even
+ # if the folder does not exist. First of all it might be created
+ # while the server is running (usually happens during development)
+ # but also because google appengine stores static files somewhere
+ # else when mapped with the .yml file.
+ self.add_url_rule(self.static_path + '/<path:filename>',
+ endpoint='static',
+ view_func=self.send_static_file)
+
+ #: The Jinja2 environment. It is created from the
+ #: :attr:`jinja_options`.
+ self.jinja_env = self.create_jinja_environment()
+ self.init_jinja_globals()
+
+ @property
+ def propagate_exceptions(self):
+ """Returns the value of the `PROPAGATE_EXCEPTIONS` configuration
+ value in case it's set, otherwise a sensible default is returned.
+
+ .. versionadded:: 0.7
+ """
+ rv = self.config['PROPAGATE_EXCEPTIONS']
+ if rv is not None:
+ return rv
+ return self.testing or self.debug
+
+ @property
+ def logger(self):
+ """A :class:`logging.Logger` object for this application. The
+ default configuration is to log to stderr if the application is
+ in debug mode. This logger can be used to (surprise) log messages.
+ Here some examples::
+
+ app.logger.debug('A value for debugging')
+ app.logger.warning('A warning ocurred (%d apples)', 42)
+ app.logger.error('An error occoured')
+
+ .. versionadded:: 0.3
+ """
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ with _logger_lock:
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ from flask.logging import create_logger
+ self._logger = rv = create_logger(self)
+ return rv
+
+ def create_jinja_environment(self):
+ """Creates the Jinja2 environment based on :attr:`jinja_options`
+ and :meth:`select_jinja_autoescape`.
+
+ .. versionadded:: 0.5
+ """
+ options = dict(self.jinja_options)
+ if 'autoescape' not in options:
+ options['autoescape'] = self.select_jinja_autoescape
+ return Environment(loader=_DispatchingJinjaLoader(self), **options)
+
+ def init_jinja_globals(self):
+ """Called directly after the environment was created to inject
+ some defaults (like `url_for`, `get_flashed_messages` and the
+ `tojson` filter.
+
+ .. versionadded:: 0.5
+ """
+ self.jinja_env.globals.update(
+ url_for=url_for,
+ get_flashed_messages=get_flashed_messages
+ )
+ self.jinja_env.filters['tojson'] = _tojson_filter
+
+ def select_jinja_autoescape(self, filename):
+ """Returns `True` if autoescaping should be active for the given
+ template name.
+
+ .. versionadded:: 0.5
+ """
+ if filename is None:
+ return False
+ return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))
+
+ def update_template_context(self, context):
+ """Update the template context with some commonly used variables.
+ This injects request, session, config and g into the template
+ context as well as everything template context processors want
+ to inject. Note that the as of Flask 0.6, the original values
+ in the context will not be overriden if a context processor
+ decides to return a value with the same key.
+
+ :param context: the context as a dictionary that is updated in place
+ to add extra variables.
+ """
+ funcs = self.template_context_processors[None]
+ mod = _request_ctx_stack.top.request.module
+ if mod is not None and mod in self.template_context_processors:
+ funcs = chain(funcs, self.template_context_processors[mod])
+ orig_ctx = context.copy()
+ for func in funcs:
+ context.update(func())
+ # make sure the original values win. This makes it possible to
+ # easier add new variables in context processors without breaking
+ # existing views.
+ context.update(orig_ctx)
+
+ def run(self, host='127.0.0.1', port=5000, **options):
+ """Runs the application on a local development server. If the
+ :attr:`debug` flag is set the server will automatically reload
+ for code changes and show a debugger in case an exception happened.
+
+ If you want to run the application in debug mode, but disable the
+ code execution on the interactive debugger, you can pass
+ ``use_evalex=False`` as parameter. This will keep the debugger's
+ traceback screen active, but disable code execution.
+
+ .. admonition:: Keep in Mind
+
+ Flask will suppress any server error with a generic error page
+ unless it is in debug mode. As such to enable just the
+ interactive debugger without the code reloading, you have to
+ invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
+ Setting ``use_debugger`` to `True` without being in debug mode
+ won't catch any exceptions because there won't be any to
+ catch.
+
+ :param host: the hostname to listen on. set this to ``'0.0.0.0'``
+ to have the server available externally as well.
+ :param port: the port of the webserver
+ :param options: the options to be forwarded to the underlying
+ Werkzeug server. See :func:`werkzeug.run_simple`
+ for more information.
+ """
+ from werkzeug import run_simple
+ if 'debug' in options:
+ self.debug = options.pop('debug')
+ options.setdefault('use_reloader', self.debug)
+ options.setdefault('use_debugger', self.debug)
+ return run_simple(host, port, self, **options)
+
+ def test_client(self, use_cookies=True):
+ """Creates a test client for this application. For information
+ about unit testing head over to :ref:`testing`.
+
+ The test client can be used in a `with` block to defer the closing down
+ of the context until the end of the `with` block. This is useful if
+ you want to access the context locals for testing::
+
+ with app.test_client() as c:
+ rv = c.get('/?vodka=42')
+ assert request.args['vodka'] == '42'
+
+ .. versionchanged:: 0.4
+ added support for `with` block usage for the client.
+
+ .. versionadded:: 0.7
+ The `use_cookies` parameter was added as well as the ability
+ to override the client to be used by setting the
+ :attr:`test_client_class` attribute.
+ """
+ cls = self.test_client_class
+ if cls is None:
+ from flask.testing import FlaskClient as cls
+ return cls(self, self.response_class, use_cookies=use_cookies)
+
+ def open_session(self, request):
+ """Creates or opens a new session. Default implementation stores all
+ session data in a signed cookie. This requires that the
+ :attr:`secret_key` is set.
+
+ :param request: an instance of :attr:`request_class`.
+ """
+ key = self.secret_key
+ if key is not None:
+ return Session.load_cookie(request, self.session_cookie_name,
+ secret_key=key)
+
+ def save_session(self, session, response):
+ """Saves the session if it needs updates. For the default
+ implementation, check :meth:`open_session`.
+
+ :param session: the session to be saved (a
+ :class:`~werkzeug.contrib.securecookie.SecureCookie`
+ object)
+ :param response: an instance of :attr:`response_class`
+ """
+ expires = domain = None
+ if session.permanent:
+ expires = datetime.utcnow() + self.permanent_session_lifetime
+ if self.config['SERVER_NAME'] is not None:
+ domain = '.' + self.config['SERVER_NAME']
+ session.save_cookie(response, self.session_cookie_name,
+ expires=expires, httponly=True, domain=domain)
+
+ def register_module(self, module, **options):
+ """Registers a module with this application. The keyword argument
+ of this function are the same as the ones for the constructor of the
+ :class:`Module` class and will override the values of the module if
+ provided.
+ """
+ options.setdefault('url_prefix', module.url_prefix)
+ options.setdefault('subdomain', module.subdomain)
+ self.view_functions.update(module.view_functions)
+ state = _ModuleSetupState(self, **options)
+ for func in module._register_events:
+ func(state)
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Connects a URL rule. Works exactly like the :meth:`route`
+ decorator. If a view_func is provided it will be registered with the
+ endpoint.
+
+ Basically this example::
+
+ @app.route('/')
+ def index():
+ pass
+
+ Is equivalent to the following::
+
+ def index():
+ pass
+ app.add_url_rule('/', 'index', index)
+
+ If the view_func is not provided you will need to connect the endpoint
+ to a view function like so::
+
+ app.view_functions['index'] = index
+
+ .. versionchanged:: 0.2
+ `view_func` parameter added.
+
+ .. versionchanged:: 0.6
+ `OPTIONS` is added automatically as method.
+
+ :param rule: the URL rule as string
+ :param endpoint: the endpoint for the registered URL rule. Flask
+ itself assumes the name of the view function as
+ endpoint
+ :param view_func: the function to call when serving a request to the
+ provided endpoint
+ :param options: the options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object. A change
+ to Werkzeug is handling of method options. methods
+ is a list of methods this rule should be limited
+ to (`GET`, `POST` etc.). By default a rule
+ just listens for `GET` (and implicitly `HEAD`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ """
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ options['endpoint'] = endpoint
+ methods = options.pop('methods', ('GET',))
+ provide_automatic_options = False
+ if 'OPTIONS' not in methods:
+ methods = tuple(methods) + ('OPTIONS',)
+ provide_automatic_options = True
+ rule = Rule(rule, methods=methods, **options)
+ rule.provide_automatic_options = provide_automatic_options
+ self.url_map.add(rule)
+ if view_func is not None:
+ self.view_functions[endpoint] = view_func
+
+ def route(self, rule, **options):
+ """A decorator that is used to register a view function for a
+ given URL rule. Example::
+
+ @app.route('/')
+ def index():
+ return 'Hello World'
+
+ Variables parts in the route can be specified with angular
+ brackets (``/user/<username>``). By default a variable part
+ in the URL accepts any string without a slash however a different
+ converter can be specified as well by using ``<converter:name>``.
+
+ Variable parts are passed to the view function as keyword
+ arguments.
+
+ The following converters are possible:
+
+ =========== ===========================================
+ `int` accepts integers
+ `float` like `int` but for floating point values
+ `path` like the default but also accepts slashes
+ =========== ===========================================
+
+ Here some examples::
+
+ @app.route('/')
+ def index():
+ pass
+
+ @app.route('/<username>')
+ def show_user(username):
+ pass
+
+ @app.route('/post/<int:post_id>')
+ def show_post(post_id):
+ pass
+
+ An important detail to keep in mind is how Flask deals with trailing
+ slashes. The idea is to keep each URL unique so the following rules
+ apply:
+
+ 1. If a rule ends with a slash and is requested without a slash
+ by the user, the user is automatically redirected to the same
+ page with a trailing slash attached.
+ 2. If a rule does not end with a trailing slash and the user request
+ the page with a trailing slash, a 404 not found is raised.
+
+ This is consistent with how web servers deal with static files. This
+ also makes it possible to use relative link targets safely.
+
+ The :meth:`route` decorator accepts a couple of other arguments
+ as well:
+
+ :param rule: the URL rule as string
+ :param methods: a list of methods this rule should be limited
+ to (`GET`, `POST` etc.). By default a rule
+ just listens for `GET` (and implicitly `HEAD`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ :param subdomain: specifies the rule for the subdomain in case
+ subdomain matching is in use.
+ :param strict_slashes: can be used to disable the strict slashes
+ setting for this rule. See above.
+ :param options: other options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object.
+ """
+ def decorator(f):
+ self.add_url_rule(rule, None, f, **options)
+ return f
+ return decorator
+
+
+ def endpoint(self, endpoint):
+ """A decorator to register a function as an endpoint.
+ Example::
+
+ @app.endpoint('example.endpoint')
+ def example():
+ return "example"
+
+ :param endpoint: the name of the endpoint
+ """
+ def decorator(f):
+ self.view_functions[endpoint] = f
+ return f
+ return decorator
+
+ def errorhandler(self, code):
+ """A decorator that is used to register a function give a given
+ error code. Example::
+
+ @app.errorhandler(404)
+ def page_not_found(error):
+ return 'This page does not exist', 404
+
+ You can also register a function as error handler without using
+ the :meth:`errorhandler` decorator. The following example is
+ equivalent to the one above::
+
+ def page_not_found(error):
+ return 'This page does not exist', 404
+ app.error_handlers[404] = page_not_found
+
+ :param code: the code as integer for the handler
+ """
+ def decorator(f):
+ self.error_handlers[code] = f
+ return f
+ return decorator
+
+ def template_filter(self, name=None):
+ """A decorator that is used to register custom template filter.
+ You can specify a name for the filter, otherwise the function
+ name will be used. Example::
+
+ @app.template_filter()
+ def reverse(s):
+ return s[::-1]
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.jinja_env.filters[name or f.__name__] = f
+ return f
+ return decorator
+
+ def before_request(self, f):
+ """Registers a function to run before each request."""
+ self.before_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ def after_request(self, f):
+ """Register a function to be run after each request."""
+ self.after_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ def context_processor(self, f):
+ """Registers a template context processor function."""
+ self.template_context_processors[None].append(f)
+ return f
+
+ def handle_http_exception(self, e):
+ """Handles an HTTP exception. By default this will invoke the
+ registered error handlers and fall back to returning the
+ exception as response.
+
+ .. versionadded: 0.3
+ """
+ handler = self.error_handlers.get(e.code)
+ if handler is None:
+ return e
+ return handler(e)
+
+ def handle_exception(self, e):
+ """Default exception handling that kicks in when an exception
+ occours that is not catched. In debug mode the exception will
+ be re-raised immediately, otherwise it is logged and the handler
+ for a 500 internal server error is used. If no such handler
+ exists, a default 500 internal server error message is displayed.
+
+ .. versionadded: 0.3
+ """
+ got_request_exception.send(self, exception=e)
+ handler = self.error_handlers.get(500)
+ if self.propagate_exceptions:
+ raise
+ self.logger.exception('Exception on %s [%s]' % (
+ request.path,
+ request.method
+ ))
+ if handler is None:
+ return InternalServerError()
+ return handler(e)
+
+ def dispatch_request(self):
+ """Does the request dispatching. Matches the URL and returns the
+ return value of the view or error handler. This does not have to
+ be a response object. In order to convert the return value to a
+ proper response object, call :func:`make_response`.
+ """
+ req = _request_ctx_stack.top.request
+ try:
+ if req.routing_exception is not None:
+ raise req.routing_exception
+ rule = req.url_rule
+ # if we provide automatic options for this URL and the
+ # request came with the OPTIONS method, reply automatically
+ if getattr(rule, 'provide_automatic_options', False) \
+ and req.method == 'OPTIONS':
+ return self.make_default_options_response()
+ # otherwise dispatch to the handler for that endpoint
+ return self.view_functions[rule.endpoint](**req.view_args)
+ except HTTPException, e:
+ return self.handle_http_exception(e)
+
+ def make_default_options_response(self):
+ """This method is called to create the default `OPTIONS` response.
+ This can be changed through subclassing to change the default
+ behaviour of `OPTIONS` responses.
+
+ .. versionadded:: 0.7
+ """
+ # This would be nicer in Werkzeug 0.7, which however currently
+ # is not released. Werkzeug 0.7 provides a method called
+ # allowed_methods() that returns all methods that are valid for
+ # a given path.
+ methods = []
+ try:
+ _request_ctx_stack.top.url_adapter.match(method='--')
+ except MethodNotAllowed, e:
+ methods = e.valid_methods
+ except HTTPException, e:
+ pass
+ rv = self.response_class()
+ rv.allow.update(methods)
+ return rv
+
+ def make_response(self, rv):
+ """Converts the return value from a view function to a real
+ response object that is an instance of :attr:`response_class`.
+
+ The following types are allowed for `rv`:
+
+ .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
+
+ ======================= ===========================================
+ :attr:`response_class` the object is returned unchanged
+ :class:`str` a response object is created with the
+ string as body
+ :class:`unicode` a response object is created with the
+ string encoded to utf-8 as body
+ :class:`tuple` the response object is created with the
+ contents of the tuple as arguments
+ a WSGI function the function is called as WSGI application
+ and buffered as response object
+ ======================= ===========================================
+
+ :param rv: the return value from the view function
+ """
+ if rv is None:
+ raise ValueError('View function did not return a response')
+ if isinstance(rv, self.response_class):
+ return rv
+ if isinstance(rv, basestring):
+ return self.response_class(rv)
+ if isinstance(rv, tuple):
+ return self.response_class(*rv)
+ return self.response_class.force_type(rv, request.environ)
+
+ def create_url_adapter(self, request):
+ """Creates a URL adapter for the given request. The URL adapter
+ is created at a point where the request context is not yet set up
+ so the request is passed explicitly.
+
+ .. versionadded:: 0.6
+ """
+ return self.url_map.bind_to_environ(request.environ,
+ server_name=self.config['SERVER_NAME'])
+
+ def preprocess_request(self):
+ """Called before the actual request dispatching and will
+ call every as :meth:`before_request` decorated function.
+ If any of these function returns a value it's handled as
+ if it was the return value from the view and further
+ request handling is stopped.
+ """
+ funcs = self.before_request_funcs.get(None, ())
+ mod = request.module
+ if mod and mod in self.before_request_funcs:
+ funcs = chain(funcs, self.before_request_funcs[mod])
+ for func in funcs:
+ rv = func()
+ if rv is not None:
+ return rv
+
+ def process_response(self, response):
+ """Can be overridden in order to modify the response object
+ before it's sent to the WSGI server. By default this will
+ call all the :meth:`after_request` decorated functions.
+
+ .. versionchanged:: 0.5
+ As of Flask 0.5 the functions registered for after request
+ execution are called in reverse order of registration.
+
+ :param response: a :attr:`response_class` object.
+ :return: a new response object or the same, has to be an
+ instance of :attr:`response_class`.
+ """
+ ctx = _request_ctx_stack.top
+ mod = ctx.request.module
+ if not isinstance(ctx.session, _NullSession):
+ self.save_session(ctx.session, response)
+ funcs = ()
+ if mod and mod in self.after_request_funcs:
+ funcs = reversed(self.after_request_funcs[mod])
+ if None in self.after_request_funcs:
+ funcs = chain(funcs, reversed(self.after_request_funcs[None]))
+ for handler in funcs:
+ response = handler(response)
+ return response
+
+ def request_context(self, environ):
+ """Creates a request context from the given environment and binds
+ it to the current context. This must be used in combination with
+ the `with` statement because the request is only bound to the
+ current context for the duration of the `with` block.
+
+ Example usage::
+
+ with app.request_context(environ):
+ do_something_with(request)
+
+ The object returned can also be used without the `with` statement
+ which is useful for working in the shell. The example above is
+ doing exactly the same as this code::
+
+ ctx = app.request_context(environ)
+ ctx.push()
+ try:
+ do_something_with(request)
+ finally:
+ ctx.pop()
+
+ The big advantage of this approach is that you can use it without
+ the try/finally statement in a shell for interactive testing:
+
+ >>> ctx = app.test_request_context()
+ >>> ctx.bind()
+ >>> request.path
+ u'/'
+ >>> ctx.unbind()
+
+ .. versionchanged:: 0.3
+ Added support for non-with statement usage and `with` statement
+ is now passed the ctx object.
+
+ :param environ: a WSGI environment
+ """
+ return _RequestContext(self, environ)
+
+ def test_request_context(self, *args, **kwargs):
+ """Creates a WSGI environment from the given values (see
+ :func:`werkzeug.create_environ` for more information, this
+ function accepts the same arguments).
+ """
+ from werkzeug import create_environ
+ environ_overrides = kwargs.setdefault('environ_overrides', {})
+ if self.config.get('SERVER_NAME'):
+ server_name = self.config.get('SERVER_NAME')
+ if ':' not in server_name:
+ http_host, http_port = server_name, '80'
+ else:
+ http_host, http_port = server_name.split(':', 1)
+
+ environ_overrides.setdefault('SERVER_NAME', server_name)
+ environ_overrides.setdefault('HTTP_HOST', server_name)
+ environ_overrides.setdefault('SERVER_PORT', http_port)
+ return self.request_context(create_environ(*args, **kwargs))
+
+ def wsgi_app(self, environ, start_response):
+ """The actual WSGI application. This is not implemented in
+ `__call__` so that middlewares can be applied without losing a
+ reference to the class. So instead of doing this::
+
+ app = MyMiddleware(app)
+
+ It's a better idea to do this instead::
+
+ app.wsgi_app = MyMiddleware(app.wsgi_app)
+
+ Then you still have the original application object around and
+ can continue to call methods on it.
+
+ .. versionchanged:: 0.4
+ The :meth:`after_request` functions are now called even if an
+ error handler took over request processing. This ensures that
+ even if an exception happens database have the chance to
+ properly close the connection.
+
+ :param environ: a WSGI environment
+ :param start_response: a callable accepting a status code,
+ a list of headers and an optional
+ exception context to start the response
+ """
+ with self.request_context(environ):
+ try:
+ request_started.send(self)
+ rv = self.preprocess_request()
+ if rv is None:
+ rv = self.dispatch_request()
+ response = self.make_response(rv)
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ try:
+ response = self.process_response(response)
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ request_finished.send(self, response=response)
+ return response(environ, start_response)
+
+ def __call__(self, environ, start_response):
+ """Shortcut for :attr:`wsgi_app`."""
+ return self.wsgi_app(environ, start_response)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/config.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,157 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.config
+ ~~~~~~~~~~~~
+
+ Implements the configuration related objects.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import imp
+import os
+import sys
+
+from werkzeug import import_string
+
+
+class ConfigAttribute(object):
+ """Makes an attribute forward to the config"""
+
+ def __init__(self, name):
+ self.__name__ = name
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return obj.config[self.__name__]
+
+ def __set__(self, obj, value):
+ obj.config[self.__name__] = value
+
+
+class Config(dict):
+ """Works exactly like a dict but provides ways to fill it from files
+ or special dictionaries. There are two common patterns to populate the
+ config.
+
+ Either you can fill the config from a config file::
+
+ app.config.from_pyfile('yourconfig.cfg')
+
+ Or alternatively you can define the configuration options in the
+ module that calls :meth:`from_object` or provide an import path to
+ a module that should be loaded. It is also possible to tell it to
+ use the same module and with that provide the configuration values
+ just before the call::
+
+ DEBUG = True
+ SECRET_KEY = 'development key'
+ app.config.from_object(__name__)
+
+ In both cases (loading from any Python file or loading from modules),
+ only uppercase keys are added to the config. This makes it possible to use
+ lowercase values in the config file for temporary values that are not added
+ to the config or to define the config keys in the same file that implements
+ the application.
+
+ Probably the most interesting way to load configurations is from an
+ environment variable pointing to a file::
+
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+ In this case before launching the application you have to set this
+ environment variable to the file you want to use. On Linux and OS X
+ use the export statement::
+
+ export YOURAPPLICATION_SETTINGS='/path/to/config/file'
+
+ On windows use `set` instead.
+
+ :param root_path: path to which files are read relative from. When the
+ config object is created by the application, this is
+ the application's :attr:`~flask.Flask.root_path`.
+ :param defaults: an optional dictionary of default values
+ """
+
+ def __init__(self, root_path, defaults=None):
+ dict.__init__(self, defaults or {})
+ self.root_path = root_path
+
+ def from_envvar(self, variable_name, silent=False):
+ """Loads a configuration from an environment variable pointing to
+ a configuration file. This basically is just a shortcut with nicer
+ error messages for this line of code::
+
+ app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
+
+ :param variable_name: name of the environment variable
+ :param silent: set to `True` if you want silent failing for missing
+ files.
+ :return: bool. `True` if able to load config, `False` otherwise.
+ """
+ rv = os.environ.get(variable_name)
+ if not rv:
+ if silent:
+ return False
+ raise RuntimeError('The environment variable %r is not set '
+ 'and as such configuration could not be '
+ 'loaded. Set this variable and make it '
+ 'point to a configuration file' %
+ variable_name)
+ self.from_pyfile(rv)
+ return True
+
+ def from_pyfile(self, filename):
+ """Updates the values in the config from a Python file. This function
+ behaves as if the file was imported as module with the
+ :meth:`from_object` function.
+
+ :param filename: the filename of the config. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ """
+ filename = os.path.join(self.root_path, filename)
+ d = imp.new_module('config')
+ d.__file__ = filename
+ try:
+ execfile(filename, d.__dict__)
+ except IOError, e:
+ e.strerror = 'Unable to load configuration file (%s)' % e.strerror
+ raise
+ self.from_object(d)
+
+ def from_object(self, obj):
+ """Updates the values from the given object. An object can be of one
+ of the following two types:
+
+ - a string: in this case the object with that name will be imported
+ - an actual object reference: that object is used directly
+
+ Objects are usually either modules or classes.
+
+ Just the uppercase variables in that object are stored in the config
+ after lowercasing. Example usage::
+
+ app.config.from_object('yourapplication.default_config')
+ from yourapplication import default_config
+ app.config.from_object(default_config)
+
+ You should not use this function to load the actual configuration but
+ rather configuration defaults. The actual config should be loaded
+ with :meth:`from_pyfile` and ideally from a location not within the
+ package because the package might be installed system wide.
+
+ :param obj: an import name or object
+ """
+ if isinstance(obj, basestring):
+ obj = import_string(obj)
+ for key in dir(obj):
+ if key.isupper():
+ self[key] = getattr(obj, key)
+
+ def __repr__(self):
+ return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/ctx.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ctx
+ ~~~~~~~~~
+
+ Implements the objects required to keep the context.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.exceptions import HTTPException
+
+from .globals import _request_ctx_stack
+from .session import _NullSession
+
+
+class _RequestGlobals(object):
+ pass
+
+
+class _RequestContext(object):
+ """The request context contains all request relevant information. It is
+ created at the beginning of the request and pushed to the
+ `_request_ctx_stack` and removed at the end of it. It will create the
+ URL adapter and request object for the WSGI environment provided.
+ """
+
+ def __init__(self, app, environ):
+ self.app = app
+ self.request = app.request_class(environ)
+ self.url_adapter = app.create_url_adapter(self.request)
+ self.session = app.open_session(self.request)
+ if self.session is None:
+ self.session = _NullSession()
+ self.g = _RequestGlobals()
+ self.flashes = None
+
+ try:
+ url_rule, self.request.view_args = \
+ self.url_adapter.match(return_rule=True)
+ self.request.url_rule = url_rule
+ except HTTPException, e:
+ self.request.routing_exception = e
+
+ def push(self):
+ """Binds the request context."""
+ _request_ctx_stack.push(self)
+
+ def pop(self):
+ """Pops the request context."""
+ _request_ctx_stack.pop()
+
+ def __enter__(self):
+ self.push()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ # do not pop the request stack if we are in debug mode and an
+ # exception happened. This will allow the debugger to still
+ # access the request object in the interactive shell. Furthermore
+ # the context can be force kept alive for the test client.
+ # See flask.testing for how this works.
+ if not self.request.environ.get('flask._preserve_context') and \
+ (tb is None or not self.app.debug):
+ self.pop()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/globals.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.globals
+ ~~~~~~~~~~~~~
+
+ Defines all the global objects that are proxies to the current
+ active context.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from functools import partial
+from werkzeug import LocalStack, LocalProxy
+
+def _lookup_object(name):
+ top = _request_ctx_stack.top
+ if top is None:
+ raise RuntimeError('working outside of request context')
+ return getattr(top, name)
+
+# context locals
+_request_ctx_stack = LocalStack()
+current_app = LocalProxy(partial(_lookup_object, 'app'))
+request = LocalProxy(partial(_lookup_object, 'request'))
+session = LocalProxy(partial(_lookup_object, 'session'))
+g = LocalProxy(partial(_lookup_object, 'g'))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/helpers.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,500 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.helpers
+ ~~~~~~~~~~~~~
+
+ Implements various helpers.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import sys
+import posixpath
+import mimetypes
+from time import time
+from zlib import adler32
+
+# try to load the best simplejson implementation available. If JSON
+# is not installed, we add a failing class.
+json_available = True
+json = None
+try:
+ import simplejson as json
+except ImportError:
+ try:
+ import json
+ except ImportError:
+ try:
+ # Google Appengine offers simplejson via django
+ from django.utils import simplejson as json
+ except ImportError:
+ json_available = False
+
+
+from werkzeug import Headers, wrap_file, cached_property
+from werkzeug.exceptions import NotFound
+
+from jinja2 import FileSystemLoader
+
+from .globals import session, _request_ctx_stack, current_app, request
+
+
+def _assert_have_json():
+ """Helper function that fails if JSON is unavailable."""
+ if not json_available:
+ raise RuntimeError('simplejson not installed')
+
+# figure out if simplejson escapes slashes. This behaviour was changed
+# from one version to another without reason.
+if not json_available or '\\/' not in json.dumps('/'):
+
+ def _tojson_filter(*args, **kwargs):
+ if __debug__:
+ _assert_have_json()
+ return json.dumps(*args, **kwargs).replace('/', '\\/')
+else:
+ _tojson_filter = json.dumps
+
+
+# what separators does this operating system provide that are not a slash?
+# this is used by the send_from_directory function to ensure that nobody is
+# able to access files from outside the filesystem.
+_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
+ if sep not in (None, '/'))
+
+
+def _endpoint_from_view_func(view_func):
+ """Internal helper that returns the default endpoint for a given
+ function. This always is the function name.
+ """
+ assert view_func is not None, 'expected view func if endpoint ' \
+ 'is not provided.'
+ return view_func.__name__
+
+
+def jsonify(*args, **kwargs):
+ """Creates a :class:`~flask.Response` with the JSON representation of
+ the given arguments with an `application/json` mimetype. The arguments
+ to this function are the same as to the :class:`dict` constructor.
+
+ Example usage::
+
+ @app.route('/_get_current_user')
+ def get_current_user():
+ return jsonify(username=g.user.username,
+ email=g.user.email,
+ id=g.user.id)
+
+ This will send a JSON response like this to the browser::
+
+ {
+ "username": "admin",
+ "email": "admin@localhost",
+ "id": 42
+ }
+
+ This requires Python 2.6 or an installed version of simplejson. For
+ security reasons only objects are supported toplevel. For more
+ information about this, have a look at :ref:`json-security`.
+
+ .. versionadded:: 0.2
+ """
+ if __debug__:
+ _assert_have_json()
+ return current_app.response_class(json.dumps(dict(*args, **kwargs),
+ indent=None if request.is_xhr else 2), mimetype='application/json')
+
+
+def make_response(*args):
+ """Sometimes it is necessary to set additional headers in a view. Because
+ views do not have to return response objects but can return a value that
+ is converted into a response object by Flask itself, it becomes tricky to
+ add headers to it. This function can be called instead of using a return
+ and you will get a response object which you can use to attach headers.
+
+ If view looked like this and you want to add a new header::
+
+ def index():
+ return render_template('index.html', foo=42)
+
+ You can now do something like this::
+
+ def index():
+ response = make_response(render_template('index.html', foo=42))
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+ return response
+
+ This function accepts the very same arguments you can return from a
+ view function. This for example creates a response with a 404 error
+ code::
+
+ response = make_response(render_template('not_found.html'), 404)
+
+ Internally this function does the following things:
+
+ - if no arguments are passed, it creates a new response argument
+ - if one argument is passed, :meth:`flask.Flask.make_response`
+ is invoked with it.
+ - if more than one argument is passed, the arguments are passed
+ to the :meth:`flask.Flask.make_response` function as tuple.
+
+ .. versionadded:: 0.6
+ """
+ if not args:
+ return current_app.response_class()
+ if len(args) == 1:
+ args = args[0]
+ return current_app.make_response(args)
+
+
+def url_for(endpoint, **values):
+ """Generates a URL to the given endpoint with the method provided.
+ The endpoint is relative to the active module if modules are in use.
+
+ Here are some examples:
+
+ ==================== ======================= =============================
+ Active Module Target Endpoint Target Function
+ ==================== ======================= =============================
+ `None` ``'index'`` `index` of the application
+ `None` ``'.index'`` `index` of the application
+ ``'admin'`` ``'index'`` `index` of the `admin` module
+ any ``'.index'`` `index` of the application
+ any ``'admin.index'`` `index` of the `admin` module
+ ==================== ======================= =============================
+
+ Variable arguments that are unknown to the target endpoint are appended
+ to the generated URL as query arguments.
+
+ For more information, head over to the :ref:`Quickstart <url-building>`.
+
+ :param endpoint: the endpoint of the URL (name of the function)
+ :param values: the variable arguments of the URL rule
+ :param _external: if set to `True`, an absolute URL is generated.
+ """
+ ctx = _request_ctx_stack.top
+ if '.' not in endpoint:
+ mod = ctx.request.module
+ if mod is not None:
+ endpoint = mod + '.' + endpoint
+ elif endpoint.startswith('.'):
+ endpoint = endpoint[1:]
+ external = values.pop('_external', False)
+ return ctx.url_adapter.build(endpoint, values, force_external=external)
+
+
+def get_template_attribute(template_name, attribute):
+ """Loads a macro (or variable) a template exports. This can be used to
+ invoke a macro from within Python code. If you for example have a
+ template named `_cider.html` with the following contents:
+
+ .. sourcecode:: html+jinja
+
+ {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
+
+ You can access this from Python code like this::
+
+ hello = get_template_attribute('_cider.html', 'hello')
+ return hello('World')
+
+ .. versionadded:: 0.2
+
+ :param template_name: the name of the template
+ :param attribute: the name of the variable of macro to acccess
+ """
+ return getattr(current_app.jinja_env.get_template(template_name).module,
+ attribute)
+
+
+def flash(message, category='message'):
+ """Flashes a message to the next request. In order to remove the
+ flashed message from the session and to display it to the user,
+ the template has to call :func:`get_flashed_messages`.
+
+ .. versionchanged: 0.3
+ `category` parameter added.
+
+ :param message: the message to be flashed.
+ :param category: the category for the message. The following values
+ are recommended: ``'message'`` for any kind of message,
+ ``'error'`` for errors, ``'info'`` for information
+ messages and ``'warning'`` for warnings. However any
+ kind of string can be used as category.
+ """
+ session.setdefault('_flashes', []).append((category, message))
+
+
+def get_flashed_messages(with_categories=False):
+ """Pulls all flashed messages from the session and returns them.
+ Further calls in the same request to the function will return
+ the same messages. By default just the messages are returned,
+ but when `with_categories` is set to `True`, the return value will
+ be a list of tuples in the form ``(category, message)`` instead.
+
+ Example usage:
+
+ .. sourcecode:: html+jinja
+
+ {% for category, msg in get_flashed_messages(with_categories=true) %}
+ <p class=flash-{{ category }}>{{ msg }}
+ {% endfor %}
+
+ .. versionchanged:: 0.3
+ `with_categories` parameter added.
+
+ :param with_categories: set to `True` to also receive categories.
+ """
+ flashes = _request_ctx_stack.top.flashes
+ if flashes is None:
+ _request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
+ if not with_categories:
+ return [x[1] for x in flashes]
+ return flashes
+
+
+def send_file(filename_or_fp, mimetype=None, as_attachment=False,
+ attachment_filename=None, add_etags=True,
+ cache_timeout=60 * 60 * 12, conditional=False):
+ """Sends the contents of a file to the client. This will use the
+ most efficient method available and configured. By default it will
+ try to use the WSGI server's file_wrapper support. Alternatively
+ you can set the application's :attr:`~Flask.use_x_sendfile` attribute
+ to ``True`` to directly emit an `X-Sendfile` header. This however
+ requires support of the underlying webserver for `X-Sendfile`.
+
+ By default it will try to guess the mimetype for you, but you can
+ also explicitly provide one. For extra security you probably want
+ to send certain files as attachment (HTML for instance). The mimetype
+ guessing requires a `filename` or an `attachment_filename` to be
+ provided.
+
+ Please never pass filenames to this function from user sources without
+ checking them first. Something like this is usually sufficient to
+ avoid security problems::
+
+ if '..' in filename or filename.startswith('/'):
+ abort(404)
+
+ .. versionadded:: 0.2
+
+ .. versionadded:: 0.5
+ The `add_etags`, `cache_timeout` and `conditional` parameters were
+ added. The default behaviour is now to attach etags.
+
+ .. versionchanged:: 0.7
+ mimetype guessing and etag support for file objects was
+ deprecated because it was unreliable. Pass a filename if you are
+ able to, otherwise attach an etag yourself. This functionality
+ will be removed in Flask 1.0
+
+ :param filename_or_fp: the filename of the file to send. This is
+ relative to the :attr:`~Flask.root_path` if a
+ relative path is specified.
+ Alternatively a file object might be provided
+ in which case `X-Sendfile` might not work and
+ fall back to the traditional method. Make sure
+ that the file pointer is positioned at the start
+ of data to send before calling :func:`send_file`.
+ :param mimetype: the mimetype of the file if provided, otherwise
+ auto detection happens.
+ :param as_attachment: set to `True` if you want to send this file with
+ a ``Content-Disposition: attachment`` header.
+ :param attachment_filename: the filename for the attachment if it
+ differs from the file's filename.
+ :param add_etags: set to `False` to disable attaching of etags.
+ :param conditional: set to `True` to enable conditional responses.
+ :param cache_timeout: the timeout in seconds for the headers.
+ """
+ mtime = None
+ if isinstance(filename_or_fp, basestring):
+ filename = filename_or_fp
+ file = None
+ else:
+ from warnings import warn
+ file = filename_or_fp
+ filename = getattr(file, 'name', None)
+
+ # XXX: this behaviour is now deprecated because it was unreliable.
+ # removed in Flask 1.0
+ if not attachment_filename and not mimetype \
+ and isinstance(filename, basestring):
+ warn(DeprecationWarning('The filename support for file objects '
+ 'passed to send_file is not deprecated. Pass an '
+ 'attach_filename if you want mimetypes to be guessed.'),
+ stacklevel=2)
+ if add_etags:
+ warn(DeprecationWarning('In future flask releases etags will no '
+ 'longer be generated for file objects passed to the send_file '
+ 'function because this behaviour was unreliable. Pass '
+ 'filenames instead if possible, otherwise attach an etag '
+ 'yourself based on another value'), stacklevel=2)
+
+ if filename is not None:
+ if not os.path.isabs(filename):
+ filename = os.path.join(current_app.root_path, filename)
+ if mimetype is None and (filename or attachment_filename):
+ mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
+ if mimetype is None:
+ mimetype = 'application/octet-stream'
+
+ headers = Headers()
+ if as_attachment:
+ if attachment_filename is None:
+ if filename is None:
+ raise TypeError('filename unavailable, required for '
+ 'sending as attachment')
+ attachment_filename = os.path.basename(filename)
+ headers.add('Content-Disposition', 'attachment',
+ filename=attachment_filename)
+
+ if current_app.use_x_sendfile and filename:
+ if file is not None:
+ file.close()
+ headers['X-Sendfile'] = filename
+ data = None
+ else:
+ if file is None:
+ file = open(filename, 'rb')
+ mtime = os.path.getmtime(filename)
+ data = wrap_file(request.environ, file)
+
+ rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
+ direct_passthrough=True)
+
+ # if we know the file modification date, we can store it as the
+ # current time to better support conditional requests. Werkzeug
+ # as of 0.6.1 will override this value however in the conditional
+ # response with the current time. This will be fixed in Werkzeug
+ # with a new release, however many WSGI servers will still emit
+ # a separate date header.
+ if mtime is not None:
+ rv.date = int(mtime)
+
+ rv.cache_control.public = True
+ if cache_timeout:
+ rv.cache_control.max_age = cache_timeout
+ rv.expires = int(time() + cache_timeout)
+
+ if add_etags and filename is not None:
+ rv.set_etag('flask-%s-%s-%s' % (
+ os.path.getmtime(filename),
+ os.path.getsize(filename),
+ adler32(filename) & 0xffffffff
+ ))
+ if conditional:
+ rv = rv.make_conditional(request)
+ # make sure we don't send x-sendfile for servers that
+ # ignore the 304 status code for x-sendfile.
+ if rv.status_code == 304:
+ rv.headers.pop('x-sendfile', None)
+ return rv
+
+
+def send_from_directory(directory, filename, **options):
+ """Send a file from a given directory with :func:`send_file`. This
+ is a secure way to quickly expose static files from an upload folder
+ or something similar.
+
+ Example usage::
+
+ @app.route('/uploads/<path:filename>')
+ def download_file(filename):
+ return send_from_directory(app.config['UPLOAD_FOLDER'],
+ filename, as_attachment=True)
+
+ .. admonition:: Sending files and Performance
+
+ It is strongly recommended to activate either `X-Sendfile` support in
+ your webserver or (if no authentication happens) to tell the webserver
+ to serve files for the given path on its own without calling into the
+ web application for improved performance.
+
+ .. versionadded:: 0.5
+
+ :param directory: the directory where all the files are stored.
+ :param filename: the filename relative to that directory to
+ download.
+ :param options: optional keyword arguments that are directly
+ forwarded to :func:`send_file`.
+ """
+ filename = posixpath.normpath(filename)
+ for sep in _os_alt_seps:
+ if sep in filename:
+ raise NotFound()
+ if os.path.isabs(filename) or filename.startswith('../'):
+ raise NotFound()
+ filename = os.path.join(directory, filename)
+ if not os.path.isfile(filename):
+ raise NotFound()
+ return send_file(filename, conditional=True, **options)
+
+
+def _get_package_path(name):
+ """Returns the path to a package or cwd if that cannot be found."""
+ try:
+ return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
+ except (KeyError, AttributeError):
+ return os.getcwd()
+
+
+class _PackageBoundObject(object):
+
+ def __init__(self, import_name):
+ #: The name of the package or module. Do not change this once
+ #: it was set by the constructor.
+ self.import_name = import_name
+
+ #: Where is the app root located?
+ self.root_path = _get_package_path(self.import_name)
+
+ @property
+ def has_static_folder(self):
+ """This is `True` if the package bound object's container has a
+ folder named ``'static'``.
+
+ .. versionadded:: 0.5
+ """
+ return os.path.isdir(os.path.join(self.root_path, 'static'))
+
+ @cached_property
+ def jinja_loader(self):
+ """The Jinja loader for this package bound object.
+
+ .. versionadded:: 0.5
+ """
+ return FileSystemLoader(os.path.join(self.root_path, 'templates'))
+
+ def send_static_file(self, filename):
+ """Function used internally to send static files from the static
+ folder to the browser.
+
+ .. versionadded:: 0.5
+ """
+ return send_from_directory(os.path.join(self.root_path, 'static'),
+ filename)
+
+ def open_resource(self, resource):
+ """Opens a resource from the application's resource folder. To see
+ how this works, consider the following folder structure::
+
+ /myapplication.py
+ /schema.sql
+ /static
+ /style.css
+ /templates
+ /layout.html
+ /index.html
+
+ If you want to open the `schema.sql` file you would do the
+ following::
+
+ with app.open_resource('schema.sql') as f:
+ contents = f.read()
+ do_something_with(contents)
+
+ :param resource: the name of the resource. To access resources within
+ subfolders use forward slashes as separator.
+ """
+ return open(os.path.join(self.root_path, resource), 'rb')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/logging.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.logging
+ ~~~~~~~~~~~~~
+
+ Implements the logging support for Flask.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import absolute_import
+
+from logging import getLogger, StreamHandler, Formatter, Logger, DEBUG
+
+
+def create_logger(app):
+ """Creates a logger for the given application. This logger works
+ similar to a regular Python logger but changes the effective logging
+ level based on the application's debug flag. Furthermore this
+ function also removes all attached handlers in case there was a
+ logger with the log name before.
+ """
+
+ class DebugLogger(Logger):
+ def getEffectiveLevel(x):
+ return DEBUG if app.debug else Logger.getEffectiveLevel(x)
+
+ class DebugHandler(StreamHandler):
+ def emit(x, record):
+ StreamHandler.emit(x, record) if app.debug else None
+
+ handler = DebugHandler()
+ handler.setLevel(DEBUG)
+ handler.setFormatter(Formatter(app.debug_log_format))
+ logger = getLogger(app.logger_name)
+ # just in case that was not a new logger, get rid of all the handlers
+ # already attached to it.
+ del logger.handlers[:]
+ logger.__class__ = DebugLogger
+ logger.addHandler(handler)
+ return logger
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/module.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.module
+ ~~~~~~~~~~~~
+
+ Implements a class that represents module blueprints.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from .helpers import _PackageBoundObject, _endpoint_from_view_func
+
+
+def _register_module(module, static_path):
+ """Internal helper function that returns a function for recording
+ that registers the `send_static_file` function for the module on
+ the application if necessary. It also registers the module on
+ the application.
+ """
+ def _register(state):
+ state.app.modules[module.name] = module
+ # do not register the rule if the static folder of the
+ # module is the same as the one from the application.
+ if state.app.root_path == module.root_path:
+ return
+ path = static_path
+ if path is None:
+ path = state.app.static_path
+ if state.url_prefix:
+ path = state.url_prefix + path
+ state.app.add_url_rule(path + '/<path:filename>',
+ endpoint='%s.static' % module.name,
+ view_func=module.send_static_file,
+ subdomain=state.subdomain)
+ return _register
+
+
+class _ModuleSetupState(object):
+
+ def __init__(self, app, url_prefix=None, subdomain=None):
+ self.app = app
+ self.url_prefix = url_prefix
+ self.subdomain = subdomain
+
+
+class Module(_PackageBoundObject):
+ """Container object that enables pluggable applications. A module can
+ be used to organize larger applications. They represent blueprints that,
+ in combination with a :class:`Flask` object are used to create a large
+ application.
+
+ A module is like an application bound to an `import_name`. Multiple
+ modules can share the same import names, but in that case a `name` has
+ to be provided to keep them apart. If different import names are used,
+ the rightmost part of the import name is used as name.
+
+ Here's an example structure for a larger application::
+
+ /myapplication
+ /__init__.py
+ /views
+ /__init__.py
+ /admin.py
+ /frontend.py
+
+ The `myapplication/__init__.py` can look like this::
+
+ from flask import Flask
+ from myapplication.views.admin import admin
+ from myapplication.views.frontend import frontend
+
+ app = Flask(__name__)
+ app.register_module(admin, url_prefix='/admin')
+ app.register_module(frontend)
+
+ And here's an example view module (`myapplication/views/admin.py`)::
+
+ from flask import Module
+
+ admin = Module(__name__)
+
+ @admin.route('/')
+ def index():
+ pass
+
+ @admin.route('/login')
+ def login():
+ pass
+
+ For a gentle introduction into modules, checkout the
+ :ref:`working-with-modules` section.
+
+ .. versionadded:: 0.5
+ The `static_path` parameter was added and it's now possible for
+ modules to refer to their own templates and static files. See
+ :ref:`modules-and-resources` for more information.
+
+ .. versionadded:: 0.6
+ The `subdomain` parameter was added.
+
+ :param import_name: the name of the Python package or module
+ implementing this :class:`Module`.
+ :param name: the internal short name for the module. Unless specified
+ the rightmost part of the import name
+ :param url_prefix: an optional string that is used to prefix all the
+ URL rules of this module. This can also be specified
+ when registering the module with the application.
+ :param subdomain: used to set the subdomain setting for URL rules that
+ do not have a subdomain setting set.
+ :param static_path: can be used to specify a different path for the
+ static files on the web. Defaults to ``/static``.
+ This does not affect the folder the files are served
+ *from*.
+ """
+
+ def __init__(self, import_name, name=None, url_prefix=None,
+ static_path=None, subdomain=None):
+ if name is None:
+ assert '.' in import_name, 'name required if package name ' \
+ 'does not point to a submodule'
+ name = import_name.rsplit('.', 1)[1]
+ _PackageBoundObject.__init__(self, import_name)
+ self.name = name
+ self.url_prefix = url_prefix
+ self.subdomain = subdomain
+ self.view_functions = {}
+ self._register_events = [_register_module(self, static_path)]
+
+ def route(self, rule, **options):
+ """Like :meth:`Flask.route` but for a module. The endpoint for the
+ :func:`url_for` function is prefixed with the name of the module.
+ """
+ def decorator(f):
+ self.add_url_rule(rule, f.__name__, f, **options)
+ return f
+ return decorator
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Like :meth:`Flask.add_url_rule` but for a module. The endpoint for
+ the :func:`url_for` function is prefixed with the name of the module.
+
+ .. versionchanged:: 0.6
+ The `endpoint` argument is now optional and will default to the
+ function name to consistent with the function of the same name
+ on the application object.
+ """
+ def register_rule(state):
+ the_rule = rule
+ if state.url_prefix:
+ the_rule = state.url_prefix + rule
+ options.setdefault('subdomain', state.subdomain)
+ the_endpoint = endpoint
+ if the_endpoint is None:
+ the_endpoint = _endpoint_from_view_func(view_func)
+ state.app.add_url_rule(the_rule, '%s.%s' % (self.name,
+ the_endpoint),
+ view_func, **options)
+ self._record(register_rule)
+
+ def endpoint(self, endpoint):
+ """Like :meth:`Flask.endpoint` but for a module."""
+ def decorator(f):
+ self.view_functions[endpoint] = f
+ return f
+ return decorator
+
+ def before_request(self, f):
+ """Like :meth:`Flask.before_request` but for a module. This function
+ is only executed before each request that is handled by a function of
+ that module.
+ """
+ self._record(lambda s: s.app.before_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def before_app_request(self, f):
+ """Like :meth:`Flask.before_request`. Such a function is executed
+ before each request, even if outside of a module.
+ """
+ self._record(lambda s: s.app.before_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def after_request(self, f):
+ """Like :meth:`Flask.after_request` but for a module. This function
+ is only executed after each request that is handled by a function of
+ that module.
+ """
+ self._record(lambda s: s.app.after_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def after_app_request(self, f):
+ """Like :meth:`Flask.after_request` but for a module. Such a function
+ is executed after each request, even if outside of the module.
+ """
+ self._record(lambda s: s.app.after_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a module. This
+ function is only executed for requests handled by a module.
+ """
+ self._record(lambda s: s.app.template_context_processors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a module. Such a
+ function is executed each request, even if outside of the module.
+ """
+ self._record(lambda s: s.app.template_context_processors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_errorhandler(self, code):
+ """Like :meth:`Flask.errorhandler` but for a module. This
+ handler is used for all requests, even if outside of the module.
+
+ .. versionadded:: 0.4
+ """
+ def decorator(f):
+ self._record(lambda s: s.app.errorhandler(code)(f))
+ return f
+ return decorator
+
+ def _record(self, func):
+ self._register_events.append(func)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/session.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.session
+ ~~~~~~~~~~~~~
+
+ Implements cookie based sessions based on Werkzeug's secure cookie
+ system.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.contrib.securecookie import SecureCookie
+
+
+class Session(SecureCookie):
+ """Expands the session with support for switching between permanent
+ and non-permanent sessions.
+ """
+
+ def _get_permanent(self):
+ return self.get('_permanent', False)
+
+ def _set_permanent(self, value):
+ self['_permanent'] = bool(value)
+
+ permanent = property(_get_permanent, _set_permanent)
+ del _get_permanent, _set_permanent
+
+
+class _NullSession(Session):
+ """Class used to generate nicer error messages if sessions are not
+ available. Will still allow read-only access to the empty session
+ but fail on setting.
+ """
+
+ def _fail(self, *args, **kwargs):
+ raise RuntimeError('the session is unavailable because no secret '
+ 'key was set. Set the secret_key on the '
+ 'application to something unique and secret.')
+ __setitem__ = __delitem__ = clear = pop = popitem = \
+ update = setdefault = _fail
+ del _fail
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/signals.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.signals
+ ~~~~~~~~~~~~~
+
+ Implements signals based on blinker if available, otherwise
+ falls silently back to a noop
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+signals_available = False
+try:
+ from blinker import Namespace
+ signals_available = True
+except ImportError:
+ class Namespace(object):
+ def signal(self, name, doc=None):
+ return _FakeSignal(name, doc)
+
+ class _FakeSignal(object):
+ """If blinker is unavailable, create a fake class with the same
+ interface that allows sending of signals but will fail with an
+ error on anything else. Instead of doing anything on send, it
+ will just ignore the arguments and do nothing instead.
+ """
+
+ def __init__(self, name, doc=None):
+ self.name = name
+ self.__doc__ = doc
+ def _fail(self, *args, **kwargs):
+ raise RuntimeError('signalling support is unavailable '
+ 'because the blinker library is '
+ 'not installed.')
+ send = lambda *a, **kw: None
+ connect = disconnect = has_receivers_for = receivers_for = \
+ temporarily_connected_to = _fail
+ del _fail
+
+# the namespace for code signals. If you are not flask code, do
+# not put signals in here. Create your own namespace instead.
+_signals = Namespace()
+
+
+# core signals. For usage examples grep the sourcecode or consult
+# the API documentation in docs/api.rst as well as docs/signals.rst
+template_rendered = _signals.signal('template-rendered')
+request_started = _signals.signal('request-started')
+request_finished = _signals.signal('request-finished')
+got_request_exception = _signals.signal('got-request-exception')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/templating.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.templating
+ ~~~~~~~~~~~~~~~~
+
+ Implements the bridge to Jinja2.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import posixpath
+from jinja2 import BaseLoader, TemplateNotFound
+
+from .globals import _request_ctx_stack
+from .signals import template_rendered
+
+
+def _default_template_ctx_processor():
+ """Default template context processor. Injects `request`,
+ `session` and `g`.
+ """
+ reqctx = _request_ctx_stack.top
+ return dict(
+ config=reqctx.app.config,
+ request=reqctx.request,
+ session=reqctx.session,
+ g=reqctx.g
+ )
+
+
+class _DispatchingJinjaLoader(BaseLoader):
+ """A loader that looks for templates in the application and all
+ the module folders.
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def get_source(self, environment, template):
+ template = posixpath.normpath(template)
+ if template.startswith('../'):
+ raise TemplateNotFound(template)
+ loader = None
+ try:
+ module, name = template.split('/', 1)
+ loader = self.app.modules[module].jinja_loader
+ except (ValueError, KeyError):
+ pass
+ # if there was a module and it has a loader, try this first
+ if loader is not None:
+ try:
+ return loader.get_source(environment, name)
+ except TemplateNotFound:
+ pass
+ # fall back to application loader if module failed
+ return self.app.jinja_loader.get_source(environment, template)
+
+ def list_templates(self):
+ result = self.app.jinja_loader.list_templates()
+ for name, module in self.app.modules.iteritems():
+ if module.jinja_loader is not None:
+ for template in module.jinja_loader.list_templates():
+ result.append('%s/%s' % (name, template))
+ return result
+
+
+def _render(template, context, app):
+ """Renders the template and fires the signal"""
+ rv = template.render(context)
+ template_rendered.send(app, template=template, context=context)
+ return rv
+
+
+def render_template(template_name, **context):
+ """Renders a template from the template folder with the given
+ context.
+
+ :param template_name: the name of the template to be rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _request_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.get_template(template_name),
+ context, ctx.app)
+
+
+def render_template_string(source, **context):
+ """Renders a template from the given template source string
+ with the given context.
+
+ :param template_name: the sourcecode of the template to be
+ rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _request_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.from_string(source),
+ context, ctx.app)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/testing.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testing
+ ~~~~~~~~~~~~~
+
+ Implements test support helpers. This module is lazily imported
+ and usually not used in production environments.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug import Client, EnvironBuilder
+from flask import _request_ctx_stack
+
+
+class FlaskClient(Client):
+ """Works like a regular Werkzeug test client but has some
+ knowledge about how Flask works to defer the cleanup of the
+ request context stack to the end of a with body when used
+ in a with statement.
+ """
+
+ preserve_context = context_preserved = False
+
+ def open(self, *args, **kwargs):
+ if self.context_preserved:
+ _request_ctx_stack.pop()
+ self.context_preserved = False
+ kwargs.setdefault('environ_overrides', {}) \
+ ['flask._preserve_context'] = self.preserve_context
+
+ as_tuple = kwargs.pop('as_tuple', False)
+ buffered = kwargs.pop('buffered', False)
+ follow_redirects = kwargs.pop('follow_redirects', False)
+
+ builder = EnvironBuilder(*args, **kwargs)
+
+ if self.application.config.get('SERVER_NAME'):
+ server_name = self.application.config.get('SERVER_NAME')
+ if ':' not in server_name:
+ http_host, http_port = server_name, None
+ else:
+ http_host, http_port = server_name.split(':', 1)
+ if builder.base_url == 'http://localhost/':
+ # Default Generated Base URL
+ if http_port != None:
+ builder.host = http_host + ':' + http_port
+ else:
+ builder.host = http_host
+ old = _request_ctx_stack.top
+ try:
+ return Client.open(self, builder,
+ as_tuple=as_tuple,
+ buffered=buffered,
+ follow_redirects=follow_redirects)
+ finally:
+ self.context_preserved = _request_ctx_stack.top is not old
+
+ def __enter__(self):
+ self.preserve_context = True
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ self.preserve_context = False
+ if self.context_preserved:
+ _request_ctx_stack.pop()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/flask/wrappers.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.wrappers
+ ~~~~~~~~~~~~~~
+
+ Implements the WSGI wrappers (request and response).
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug import Request as RequestBase, Response as ResponseBase, \
+ cached_property
+
+from .helpers import json, _assert_have_json
+from .globals import _request_ctx_stack
+
+
+class Request(RequestBase):
+ """The request object used by default in Flask. Remembers the
+ matched endpoint and view arguments.
+
+ It is what ends up as :class:`~flask.request`. If you want to replace
+ the request object used you can subclass this and set
+ :attr:`~flask.Flask.request_class` to your subclass.
+ """
+
+ #: the internal URL rule that matched the request. This can be
+ #: useful to inspect which methods are allowed for the URL from
+ #: a before/after handler (``request.url_rule.methods``) etc.
+ #:
+ #: .. versionadded:: 0.6
+ url_rule = None
+
+ #: a dict of view arguments that matched the request. If an exception
+ #: happened when matching, this will be `None`.
+ view_args = None
+
+ #: if matching the URL failed, this is the exception that will be
+ #: raised / was raised as part of the request handling. This is
+ #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
+ #: something similar.
+ routing_exception = None
+
+ @property
+ def max_content_length(self):
+ """Read-only view of the `MAX_CONTENT_LENGTH` config key."""
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.app.config['MAX_CONTENT_LENGTH']
+
+ @property
+ def endpoint(self):
+ """The endpoint that matched the request. This in combination with
+ :attr:`view_args` can be used to reconstruct the same or a
+ modified URL. If an exception happened when matching, this will
+ be `None`.
+ """
+ if self.url_rule is not None:
+ return self.url_rule.endpoint
+
+ @property
+ def module(self):
+ """The name of the current module"""
+ if self.url_rule and '.' in self.url_rule.endpoint:
+ return self.url_rule.endpoint.rsplit('.', 1)[0]
+
+ @cached_property
+ def json(self):
+ """If the mimetype is `application/json` this will contain the
+ parsed JSON data.
+ """
+ if __debug__:
+ _assert_have_json()
+ if self.mimetype == 'application/json':
+ return json.loads(self.data)
+
+
+class Response(ResponseBase):
+ """The response object that is used by default in Flask. Works like the
+ response object from Werkzeug but is set to have an HTML mimetype by
+ default. Quite often you don't have to create this object yourself because
+ :meth:`~flask.Flask.make_response` will take care of that for you.
+
+ If you want to replace the response object used you can subclass this and
+ set :attr:`~flask.Flask.response_class` to your subclass.
+ """
+ default_mimetype = 'text/html'
--- a/bundled/flask/setup.py Fri Aug 20 16:29:35 2010 +0200
+++ b/bundled/flask/setup.py Wed Jun 29 19:47:16 2011 -0400
@@ -38,8 +38,44 @@
<http://github.com/mitsuhiko/flask/zipball/master#egg=Flask-dev>`_
"""
-from setuptools import setup
+from setuptools import Command, setup
+
+class run_audit(Command):
+ """Audits source code using PyFlakes for following issues:
+ - Names which are used but not defined or used before they are defined.
+ - Names which are redefined without having been used.
+ """
+ description = "Audit source code with PyFlakes"
+ user_options = []
+
+ def initialize_options(self):
+ all = None
+
+ def finalize_options(self):
+ pass
+ def run(self):
+ import os, sys
+ try:
+ import pyflakes.scripts.pyflakes as flakes
+ except ImportError:
+ print "Audit requires PyFlakes installed in your system."""
+ sys.exit(-1)
+
+ dirs = ['flask', 'tests']
+ # Add example directories
+ for dir in ['flaskr', 'jqueryexample', 'minitwit']:
+ dirs.append(os.path.join('examples', dir))
+ # TODO: Add test subdirectories
+ warns = 0
+ for dir in dirs:
+ for filename in os.listdir(dir):
+ if filename.endswith('.py') and filename != '__init__.py':
+ warns += flakes.checkPath(os.path.join(dir, filename))
+ if warns > 0:
+ print ("Audit finished with total %d warnings." % warns)
+ else:
+ print ("No problems found in sourcecode.")
def run_tests():
import os, sys
@@ -50,7 +86,7 @@
setup(
name='Flask',
- version='0.4',
+ version='0.7',
url='http://github.com/mitsuhiko/flask/',
license='BSD',
author='Armin Ronacher',
@@ -58,7 +94,7 @@
description='A microframework based on Werkzeug, Jinja2 '
'and good intentions',
long_description=__doc__,
- py_modules=['flask'],
+ packages=['flask'],
zip_safe=False,
platforms='any',
install_requires=[
@@ -75,5 +111,6 @@
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
],
+ cmdclass={'audit': run_audit},
test_suite='__main__.run_tests'
)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/flask_tests.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1487 @@
+# -*- coding: utf-8 -*-
+"""
+ Flask Tests
+ ~~~~~~~~~~~
+
+ Tests Flask itself. The majority of Flask is already tested
+ as part of Werkzeug.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from __future__ import with_statement
+import os
+import re
+import sys
+import flask
+import unittest
+from threading import Thread
+from logging import StreamHandler
+from contextlib import contextmanager
+from datetime import datetime
+from werkzeug import parse_date, parse_options_header
+from werkzeug.exceptions import NotFound
+from jinja2 import TemplateNotFound
+from cStringIO import StringIO
+
+example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
+sys.path.append(os.path.join(example_path, 'flaskr'))
+sys.path.append(os.path.join(example_path, 'minitwit'))
+
+
+def has_encoding(name):
+ try:
+ import codecs
+ codecs.lookup(name)
+ return True
+ except LookupError:
+ return False
+
+
+# config keys used for the ConfigTestCase
+TEST_KEY = 'foo'
+SECRET_KEY = 'devkey'
+
+
+@contextmanager
+def catch_warnings():
+ """Catch warnings in a with block in a list"""
+ import warnings
+ filters = warnings.filters
+ warnings.filters = filters[:]
+ old_showwarning = warnings.showwarning
+ log = []
+ def showwarning(message, category, filename, lineno, file=None, line=None):
+ log.append(locals())
+ try:
+ warnings.showwarning = showwarning
+ yield log
+ finally:
+ warnings.filters = filters
+ warnings.showwarning = old_showwarning
+
+
+@contextmanager
+def catch_stderr():
+ """Catch stderr in a StringIO"""
+ old_stderr = sys.stderr
+ sys.stderr = rv = StringIO()
+ try:
+ yield rv
+ finally:
+ sys.stderr = old_stderr
+
+
+class ContextTestCase(unittest.TestCase):
+
+ def test_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return 'Hello %s!' % flask.request.args['name']
+ @app.route('/meh')
+ def meh():
+ return flask.request.url
+
+ with app.test_request_context('/?name=World'):
+ assert index() == 'Hello World!'
+ with app.test_request_context('/meh'):
+ assert meh() == 'http://localhost/meh'
+ assert flask._request_ctx_stack.top is None
+
+ def test_manual_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return 'Hello %s!' % flask.request.args['name']
+
+ ctx = app.test_request_context('/?name=World')
+ ctx.push()
+ assert index() == 'Hello World!'
+ ctx.pop()
+ try:
+ index()
+ except RuntimeError:
+ pass
+ else:
+ assert 0, 'expected runtime error'
+
+ def test_test_client_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ flask.g.value = 42
+ return 'Hello World!'
+
+ @app.route('/other')
+ def other():
+ 1/0
+
+ with app.test_client() as c:
+ resp = c.get('/')
+ assert flask.g.value == 42
+ assert resp.data == 'Hello World!'
+ assert resp.status_code == 200
+
+ resp = c.get('/other')
+ assert not hasattr(flask.g, 'value')
+ assert 'Internal Server Error' in resp.data
+ assert resp.status_code == 500
+ flask.g.value = 23
+
+ try:
+ flask.g.value
+ except (AttributeError, RuntimeError):
+ pass
+ else:
+ raise AssertionError('some kind of exception expected')
+
+
+class BasicFunctionalityTestCase(unittest.TestCase):
+
+ def test_options_work(self):
+ app = flask.Flask(__name__)
+ @app.route('/', methods=['GET', 'POST'])
+ def index():
+ return 'Hello World'
+ rv = app.test_client().open('/', method='OPTIONS')
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
+ assert rv.data == ''
+
+ def test_options_on_multiple_rules(self):
+ app = flask.Flask(__name__)
+ @app.route('/', methods=['GET', 'POST'])
+ def index():
+ return 'Hello World'
+ @app.route('/', methods=['PUT'])
+ def index_put():
+ return 'Aha!'
+ rv = app.test_client().open('/', method='OPTIONS')
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
+
+ def test_request_dispatching(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.request.method
+ @app.route('/more', methods=['GET', 'POST'])
+ def more():
+ return flask.request.method
+
+ c = app.test_client()
+ assert c.get('/').data == 'GET'
+ rv = c.post('/')
+ assert rv.status_code == 405
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS']
+ rv = c.head('/')
+ assert rv.status_code == 200
+ assert not rv.data # head truncates
+ assert c.post('/more').data == 'POST'
+ assert c.get('/more').data == 'GET'
+ rv = c.delete('/more')
+ assert rv.status_code == 405
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
+
+ def test_url_mapping(self):
+ app = flask.Flask(__name__)
+ def index():
+ return flask.request.method
+ def more():
+ return flask.request.method
+
+ app.add_url_rule('/', 'index', index)
+ app.add_url_rule('/more', 'more', more, methods=['GET', 'POST'])
+
+ c = app.test_client()
+ assert c.get('/').data == 'GET'
+ rv = c.post('/')
+ assert rv.status_code == 405
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS']
+ rv = c.head('/')
+ assert rv.status_code == 200
+ assert not rv.data # head truncates
+ assert c.post('/more').data == 'POST'
+ assert c.get('/more').data == 'GET'
+ rv = c.delete('/more')
+ assert rv.status_code == 405
+ assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST']
+
+ def test_werkzeug_routing(self):
+ from werkzeug.routing import Submount, Rule
+ app = flask.Flask(__name__)
+ app.url_map.add(Submount('/foo', [
+ Rule('/bar', endpoint='bar'),
+ Rule('/', endpoint='index')
+ ]))
+ def bar():
+ return 'bar'
+ def index():
+ return 'index'
+ app.view_functions['bar'] = bar
+ app.view_functions['index'] = index
+
+ c = app.test_client()
+ assert c.get('/foo/').data == 'index'
+ assert c.get('/foo/bar').data == 'bar'
+
+ def test_endpoint_decorator(self):
+ from werkzeug.routing import Submount, Rule
+ app = flask.Flask(__name__)
+ app.url_map.add(Submount('/foo', [
+ Rule('/bar', endpoint='bar'),
+ Rule('/', endpoint='index')
+ ]))
+
+ @app.endpoint('bar')
+ def bar():
+ return 'bar'
+
+ @app.endpoint('index')
+ def index():
+ return 'index'
+
+ c = app.test_client()
+ assert c.get('/foo/').data == 'index'
+ assert c.get('/foo/bar').data == 'bar'
+
+ def test_session(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+ @app.route('/set', methods=['POST'])
+ def set():
+ flask.session['value'] = flask.request.form['value']
+ return 'value set'
+ @app.route('/get')
+ def get():
+ return flask.session['value']
+
+ c = app.test_client()
+ assert c.post('/set', data={'value': '42'}).data == 'value set'
+ assert c.get('/get').data == '42'
+
+ def test_session_using_server_name(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SECRET_KEY='foo',
+ SERVER_NAME='example.com'
+ )
+ @app.route('/')
+ def index():
+ flask.session['testing'] = 42
+ return 'Hello World'
+ rv = app.test_client().get('/', 'http://example.com/')
+ assert 'domain=.example.com' in rv.headers['set-cookie'].lower()
+ assert 'httponly' in rv.headers['set-cookie'].lower()
+
+ def test_missing_session(self):
+ app = flask.Flask(__name__)
+ def expect_exception(f, *args, **kwargs):
+ try:
+ f(*args, **kwargs)
+ except RuntimeError, e:
+ assert e.args and 'session is unavailable' in e.args[0]
+ else:
+ assert False, 'expected exception'
+ with app.test_request_context():
+ assert flask.session.get('missing_key') is None
+ expect_exception(flask.session.__setitem__, 'foo', 42)
+ expect_exception(flask.session.pop, 'foo')
+
+ def test_session_expiration(self):
+ permanent = True
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+ @app.route('/')
+ def index():
+ flask.session['test'] = 42
+ flask.session.permanent = permanent
+ return ''
+
+ @app.route('/test')
+ def test():
+ return unicode(flask.session.permanent)
+
+ client = app.test_client()
+ rv = client.get('/')
+ assert 'set-cookie' in rv.headers
+ match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
+ expires = parse_date(match.group())
+ expected = datetime.utcnow() + app.permanent_session_lifetime
+ assert expires.year == expected.year
+ assert expires.month == expected.month
+ assert expires.day == expected.day
+
+ rv = client.get('/test')
+ assert rv.data == 'True'
+
+ permanent = False
+ rv = app.test_client().get('/')
+ assert 'set-cookie' in rv.headers
+ match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
+ assert match is None
+
+ def test_flashes(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+
+ with app.test_request_context():
+ assert not flask.session.modified
+ flask.flash('Zap')
+ flask.session.modified = False
+ flask.flash('Zip')
+ assert flask.session.modified
+ assert list(flask.get_flashed_messages()) == ['Zap', 'Zip']
+
+ def test_extended_flashing(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+
+ @app.route('/')
+ def index():
+ flask.flash(u'Hello World')
+ flask.flash(u'Hello World', 'error')
+ flask.flash(flask.Markup(u'<em>Testing</em>'), 'warning')
+ return ''
+
+ @app.route('/test')
+ def test():
+ messages = flask.get_flashed_messages(with_categories=True)
+ assert len(messages) == 3
+ assert messages[0] == ('message', u'Hello World')
+ assert messages[1] == ('error', u'Hello World')
+ assert messages[2] == ('warning', flask.Markup(u'<em>Testing</em>'))
+ return ''
+ messages = flask.get_flashed_messages()
+ assert len(messages) == 3
+ assert messages[0] == u'Hello World'
+ assert messages[1] == u'Hello World'
+ assert messages[2] == flask.Markup(u'<em>Testing</em>')
+
+ c = app.test_client()
+ c.get('/')
+ c.get('/test')
+
+ def test_request_processing(self):
+ app = flask.Flask(__name__)
+ evts = []
+ @app.before_request
+ def before_request():
+ evts.append('before')
+ @app.after_request
+ def after_request(response):
+ response.data += '|after'
+ evts.append('after')
+ return response
+ @app.route('/')
+ def index():
+ assert 'before' in evts
+ assert 'after' not in evts
+ return 'request'
+ assert 'after' not in evts
+ rv = app.test_client().get('/').data
+ assert 'after' in evts
+ assert rv == 'request|after'
+
+ def test_after_request_errors(self):
+ app = flask.Flask(__name__)
+ called = []
+ @app.after_request
+ def after_request(response):
+ called.append(True)
+ return response
+ @app.route('/')
+ def fails():
+ 1/0
+ rv = app.test_client().get('/')
+ assert rv.status_code == 500
+ assert 'Internal Server Error' in rv.data
+ assert len(called) == 1
+
+ def test_after_request_handler_error(self):
+ called = []
+ app = flask.Flask(__name__)
+ @app.after_request
+ def after_request(response):
+ called.append(True)
+ 1/0
+ return response
+ @app.route('/')
+ def fails():
+ 1/0
+ rv = app.test_client().get('/')
+ assert rv.status_code == 500
+ assert 'Internal Server Error' in rv.data
+ assert len(called) == 1
+
+ def test_before_after_request_order(self):
+ called = []
+ app = flask.Flask(__name__)
+ @app.before_request
+ def before1():
+ called.append(1)
+ @app.before_request
+ def before2():
+ called.append(2)
+ @app.after_request
+ def after1(response):
+ called.append(4)
+ return response
+ @app.after_request
+ def after2(response):
+ called.append(3)
+ return response
+ @app.route('/')
+ def index():
+ return '42'
+ rv = app.test_client().get('/')
+ assert rv.data == '42'
+ assert called == [1, 2, 3, 4]
+
+ def test_error_handling(self):
+ app = flask.Flask(__name__)
+ @app.errorhandler(404)
+ def not_found(e):
+ return 'not found', 404
+ @app.errorhandler(500)
+ def internal_server_error(e):
+ return 'internal server error', 500
+ @app.route('/')
+ def index():
+ flask.abort(404)
+ @app.route('/error')
+ def error():
+ 1 // 0
+ c = app.test_client()
+ rv = c.get('/')
+ assert rv.status_code == 404
+ assert rv.data == 'not found'
+ rv = c.get('/error')
+ assert rv.status_code == 500
+ assert 'internal server error' == rv.data
+
+ def test_response_creation(self):
+ app = flask.Flask(__name__)
+ @app.route('/unicode')
+ def from_unicode():
+ return u'Hällo Wörld'
+ @app.route('/string')
+ def from_string():
+ return u'Hällo Wörld'.encode('utf-8')
+ @app.route('/args')
+ def from_tuple():
+ return 'Meh', 400, {'X-Foo': 'Testing'}, 'text/plain'
+ c = app.test_client()
+ assert c.get('/unicode').data == u'Hällo Wörld'.encode('utf-8')
+ assert c.get('/string').data == u'Hällo Wörld'.encode('utf-8')
+ rv = c.get('/args')
+ assert rv.data == 'Meh'
+ assert rv.headers['X-Foo'] == 'Testing'
+ assert rv.status_code == 400
+ assert rv.mimetype == 'text/plain'
+
+ def test_make_response(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ rv = flask.make_response()
+ assert rv.status_code == 200
+ assert rv.data == ''
+ assert rv.mimetype == 'text/html'
+
+ rv = flask.make_response('Awesome')
+ assert rv.status_code == 200
+ assert rv.data == 'Awesome'
+ assert rv.mimetype == 'text/html'
+
+ rv = flask.make_response('W00t', 404)
+ assert rv.status_code == 404
+ assert rv.data == 'W00t'
+ assert rv.mimetype == 'text/html'
+
+ def test_url_generation(self):
+ app = flask.Flask(__name__)
+ @app.route('/hello/<name>', methods=['POST'])
+ def hello():
+ pass
+ with app.test_request_context():
+ assert flask.url_for('hello', name='test x') == '/hello/test%20x'
+ assert flask.url_for('hello', name='test x', _external=True) \
+ == 'http://localhost/hello/test%20x'
+
+ def test_custom_converters(self):
+ from werkzeug.routing import BaseConverter
+ class ListConverter(BaseConverter):
+ def to_python(self, value):
+ return value.split(',')
+ def to_url(self, value):
+ base_to_url = super(ListConverter, self).to_url
+ return ','.join(base_to_url(x) for x in value)
+ app = flask.Flask(__name__)
+ app.url_map.converters['list'] = ListConverter
+ @app.route('/<list:args>')
+ def index(args):
+ return '|'.join(args)
+ c = app.test_client()
+ assert c.get('/1,2,3').data == '1|2|3'
+
+ def test_static_files(self):
+ app = flask.Flask(__name__)
+ rv = app.test_client().get('/static/index.html')
+ assert rv.status_code == 200
+ assert rv.data.strip() == '<h1>Hello World!</h1>'
+ with app.test_request_context():
+ assert flask.url_for('static', filename='index.html') \
+ == '/static/index.html'
+
+ def test_none_response(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def test():
+ return None
+ try:
+ app.test_client().get('/')
+ except ValueError, e:
+ assert str(e) == 'View function did not return a response'
+ pass
+ else:
+ assert "Expected ValueError"
+
+ def test_request_locals(self):
+ self.assertEqual(repr(flask.g), '<LocalProxy unbound>')
+ self.assertFalse(flask.g)
+
+ def test_proper_test_request_context(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SERVER_NAME='localhost.localdomain:5000'
+ )
+ @app.route('/')
+ def index():
+ return None
+
+ @app.route('/', subdomain='foo')
+ def sub():
+ return None
+
+ with app.test_request_context('/'):
+ assert flask.url_for('index', _external=True) == 'http://localhost.localdomain:5000/'
+
+ with app.test_request_context('/'):
+ assert flask.url_for('sub', _external=True) == 'http://foo.localhost.localdomain:5000/'
+
+ try:
+ with app.test_request_context('/', environ_overrides={'HTTP_HOST': 'localhost'}):
+ pass
+ except Exception, e:
+ assert isinstance(e, ValueError)
+ assert str(e) == "the server name provided " + \
+ "('localhost.localdomain:5000') does not match the " + \
+ "server name from the WSGI environment ('localhost')", str(e)
+
+ try:
+ app.config.update(SERVER_NAME='localhost')
+ with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost'}):
+ pass
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ try:
+ app.config.update(SERVER_NAME='localhost:80')
+ with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost:80'}):
+ pass
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ def test_test_app_proper_environ(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SERVER_NAME='localhost.localdomain:5000'
+ )
+ @app.route('/')
+ def index():
+ return 'Foo'
+
+ @app.route('/', subdomain='foo')
+ def subdomain():
+ return 'Foo SubDomain'
+
+ try:
+ rv = app.test_client().get('/')
+ assert rv.data == 'Foo'
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ try:
+ rv = app.test_client().get('/', 'http://localhost.localdomain:5000')
+ assert rv.data == 'Foo'
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ try:
+ rv = app.test_client().get('/', 'https://localhost.localdomain:5000')
+ assert rv.data == 'Foo'
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ try:
+ app.config.update(SERVER_NAME='localhost.localdomain')
+ rv = app.test_client().get('/', 'https://localhost.localdomain')
+ assert rv.data == 'Foo'
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ try:
+ app.config.update(SERVER_NAME='localhost.localdomain:443')
+ rv = app.test_client().get('/', 'https://localhost.localdomain')
+ assert rv.data == 'Foo'
+ except ValueError, e:
+ assert str(e) == "the server name provided " + \
+ "('localhost.localdomain:443') does not match the " + \
+ "server name from the WSGI environment ('localhost.localdomain')", str(e)
+
+ try:
+ app.config.update(SERVER_NAME='localhost.localdomain')
+ app.test_client().get('/', 'http://foo.localhost')
+ except ValueError, e:
+ assert str(e) == "the server name provided " + \
+ "('localhost.localdomain') does not match the " + \
+ "server name from the WSGI environment ('foo.localhost')", str(e)
+
+ try:
+ rv = app.test_client().get('/', 'http://foo.localhost.localdomain')
+ assert rv.data == 'Foo SubDomain'
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ def test_exception_propagation(self):
+ def apprunner(configkey):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ 1/0
+ c = app.test_client()
+ if config_key is not None:
+ app.config[config_key] = True
+ try:
+ resp = c.get('/')
+ except Exception:
+ pass
+ else:
+ self.fail('expected exception')
+ else:
+ assert c.get('/').status_code == 500
+
+ # we have to run this test in an isolated thread because if the
+ # debug flag is set to true and an exception happens the context is
+ # not torn down. This causes other tests that run after this fail
+ # when they expect no exception on the stack.
+ for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None:
+ t = Thread(target=apprunner, args=(config_key,))
+ t.start()
+ t.join()
+
+
+class JSONTestCase(unittest.TestCase):
+
+ def test_jsonify(self):
+ d = dict(a=23, b=42, c=[1, 2, 3])
+ app = flask.Flask(__name__)
+ @app.route('/kw')
+ def return_kwargs():
+ return flask.jsonify(**d)
+ @app.route('/dict')
+ def return_dict():
+ return flask.jsonify(d)
+ c = app.test_client()
+ for url in '/kw', '/dict':
+ rv = c.get(url)
+ assert rv.mimetype == 'application/json'
+ assert flask.json.loads(rv.data) == d
+
+ def test_json_attr(self):
+ app = flask.Flask(__name__)
+ @app.route('/add', methods=['POST'])
+ def add():
+ return unicode(flask.request.json['a'] + flask.request.json['b'])
+ c = app.test_client()
+ rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}),
+ content_type='application/json')
+ assert rv.data == '3'
+
+ def test_template_escaping(self):
+ app = flask.Flask(__name__)
+ render = flask.render_template_string
+ with app.test_request_context():
+ rv = render('{{ "</script>"|tojson|safe }}')
+ assert rv == '"<\\/script>"'
+ rv = render('{{ "<\0/script>"|tojson|safe }}')
+ assert rv == '"<\\u0000\\/script>"'
+
+ def test_modified_url_encoding(self):
+ class ModifiedRequest(flask.Request):
+ url_charset = 'euc-kr'
+ app = flask.Flask(__name__)
+ app.request_class = ModifiedRequest
+ app.url_map.charset = 'euc-kr'
+
+ @app.route('/')
+ def index():
+ return flask.request.args['foo']
+
+ rv = app.test_client().get(u'/?foo=정상처리'.encode('euc-kr'))
+ assert rv.status_code == 200
+ assert rv.data == u'정상처리'.encode('utf-8')
+
+ if not has_encoding('euc-kr'):
+ test_modified_url_encoding = None
+
+
+class TemplatingTestCase(unittest.TestCase):
+
+ def test_context_processing(self):
+ app = flask.Flask(__name__)
+ @app.context_processor
+ def context_processor():
+ return {'injected_value': 42}
+ @app.route('/')
+ def index():
+ return flask.render_template('context_template.html', value=23)
+ rv = app.test_client().get('/')
+ assert rv.data == '<p>23|42'
+
+ def test_original_win(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template_string('{{ config }}', config=42)
+ rv = app.test_client().get('/')
+ assert rv.data == '42'
+
+ def test_standard_context(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'development key'
+ @app.route('/')
+ def index():
+ flask.g.foo = 23
+ flask.session['test'] = 'aha'
+ return flask.render_template_string('''
+ {{ request.args.foo }}
+ {{ g.foo }}
+ {{ config.DEBUG }}
+ {{ session.test }}
+ ''')
+ rv = app.test_client().get('/?foo=42')
+ assert rv.data.split() == ['42', '23', 'False', 'aha']
+
+ def test_escaping(self):
+ text = '<p>Hello World!'
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template('escaping_template.html', text=text,
+ html=flask.Markup(text))
+ lines = app.test_client().get('/').data.splitlines()
+ assert lines == [
+ '<p>Hello World!',
+ '<p>Hello World!',
+ '<p>Hello World!',
+ '<p>Hello World!',
+ '<p>Hello World!',
+ '<p>Hello World!'
+ ]
+
+ def test_no_escaping(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ assert flask.render_template_string('{{ foo }}',
+ foo='<test>') == '<test>'
+ assert flask.render_template('mail.txt', foo='<test>') \
+ == '<test> Mail'
+
+ def test_macros(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ macro = flask.get_template_attribute('_macro.html', 'hello')
+ assert macro('World') == 'Hello World!'
+
+ def test_template_filter(self):
+ app = flask.Flask(__name__)
+ @app.template_filter()
+ def my_reverse(s):
+ return s[::-1]
+ assert 'my_reverse' in app.jinja_env.filters.keys()
+ assert app.jinja_env.filters['my_reverse'] == my_reverse
+ assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba'
+
+ def test_template_filter_with_name(self):
+ app = flask.Flask(__name__)
+ @app.template_filter('strrev')
+ def my_reverse(s):
+ return s[::-1]
+ assert 'strrev' in app.jinja_env.filters.keys()
+ assert app.jinja_env.filters['strrev'] == my_reverse
+ assert app.jinja_env.filters['strrev']('abcd') == 'dcba'
+
+ def test_template_filter_with_template(self):
+ app = flask.Flask(__name__)
+ @app.template_filter()
+ def super_reverse(s):
+ return s[::-1]
+ @app.route('/')
+ def index():
+ return flask.render_template('template_filter.html', value='abcd')
+ rv = app.test_client().get('/')
+ assert rv.data == 'dcba'
+
+ def test_template_filter_with_name_and_template(self):
+ app = flask.Flask(__name__)
+ @app.template_filter('super_reverse')
+ def my_reverse(s):
+ return s[::-1]
+ @app.route('/')
+ def index():
+ return flask.render_template('template_filter.html', value='abcd')
+ rv = app.test_client().get('/')
+ assert rv.data == 'dcba'
+
+
+class ModuleTestCase(unittest.TestCase):
+
+ def test_basic_module(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin', url_prefix='/admin')
+ @admin.route('/')
+ def admin_index():
+ return 'admin index'
+ @admin.route('/login')
+ def admin_login():
+ return 'admin login'
+ @admin.route('/logout')
+ def admin_logout():
+ return 'admin logout'
+ @app.route('/')
+ def index():
+ return 'the index'
+ app.register_module(admin)
+ c = app.test_client()
+ assert c.get('/').data == 'the index'
+ assert c.get('/admin/').data == 'admin index'
+ assert c.get('/admin/login').data == 'admin login'
+ assert c.get('/admin/logout').data == 'admin logout'
+
+ def test_default_endpoint_name(self):
+ app = flask.Flask(__name__)
+ mod = flask.Module(__name__, 'frontend')
+ def index():
+ return 'Awesome'
+ mod.add_url_rule('/', view_func=index)
+ app.register_module(mod)
+ rv = app.test_client().get('/')
+ assert rv.data == 'Awesome'
+ with app.test_request_context():
+ assert flask.url_for('frontend.index') == '/'
+
+ def test_request_processing(self):
+ catched = []
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin', url_prefix='/admin')
+ @admin.before_request
+ def before_admin_request():
+ catched.append('before-admin')
+ @admin.after_request
+ def after_admin_request(response):
+ catched.append('after-admin')
+ return response
+ @admin.route('/')
+ def admin_index():
+ return 'the admin'
+ @app.before_request
+ def before_request():
+ catched.append('before-app')
+ @app.after_request
+ def after_request(response):
+ catched.append('after-app')
+ return response
+ @app.route('/')
+ def index():
+ return 'the index'
+ app.register_module(admin)
+ c = app.test_client()
+
+ assert c.get('/').data == 'the index'
+ assert catched == ['before-app', 'after-app']
+ del catched[:]
+
+ assert c.get('/admin/').data == 'the admin'
+ assert catched == ['before-app', 'before-admin',
+ 'after-admin', 'after-app']
+
+ def test_context_processors(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin', url_prefix='/admin')
+ @app.context_processor
+ def inject_all_regualr():
+ return {'a': 1}
+ @admin.context_processor
+ def inject_admin():
+ return {'b': 2}
+ @admin.app_context_processor
+ def inject_all_module():
+ return {'c': 3}
+ @app.route('/')
+ def index():
+ return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
+ @admin.route('/')
+ def admin_index():
+ return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
+ app.register_module(admin)
+ c = app.test_client()
+ assert c.get('/').data == '13'
+ assert c.get('/admin/').data == '123'
+
+ def test_late_binding(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin')
+ @admin.route('/')
+ def index():
+ return '42'
+ app.register_module(admin, url_prefix='/admin')
+ assert app.test_client().get('/admin/').data == '42'
+
+ def test_error_handling(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin')
+ @admin.app_errorhandler(404)
+ def not_found(e):
+ return 'not found', 404
+ @admin.app_errorhandler(500)
+ def internal_server_error(e):
+ return 'internal server error', 500
+ @admin.route('/')
+ def index():
+ flask.abort(404)
+ @admin.route('/error')
+ def error():
+ 1 // 0
+ app.register_module(admin)
+ c = app.test_client()
+ rv = c.get('/')
+ assert rv.status_code == 404
+ assert rv.data == 'not found'
+ rv = c.get('/error')
+ assert rv.status_code == 500
+ assert 'internal server error' == rv.data
+
+ def test_templates_and_static(self):
+ from moduleapp import app
+ c = app.test_client()
+
+ rv = c.get('/')
+ assert rv.data == 'Hello from the Frontend'
+ rv = c.get('/admin/')
+ assert rv.data == 'Hello from the Admin'
+ rv = c.get('/admin/index2')
+ assert rv.data == 'Hello from the Admin'
+ rv = c.get('/admin/static/test.txt')
+ assert rv.data.strip() == 'Admin File'
+ rv = c.get('/admin/static/css/test.css')
+ assert rv.data.strip() == '/* nested file */'
+
+ with app.test_request_context():
+ assert flask.url_for('admin.static', filename='test.txt') \
+ == '/admin/static/test.txt'
+
+ with app.test_request_context():
+ try:
+ flask.render_template('missing.html')
+ except TemplateNotFound, e:
+ assert e.name == 'missing.html'
+ else:
+ assert 0, 'expected exception'
+
+ with flask.Flask(__name__).test_request_context():
+ assert flask.render_template('nested/nested.txt') == 'I\'m nested'
+
+ def test_safe_access(self):
+ from moduleapp import app
+
+ with app.test_request_context():
+ f = app.view_functions['admin.static']
+
+ try:
+ f('/etc/passwd')
+ except NotFound:
+ pass
+ else:
+ assert 0, 'expected exception'
+ try:
+ f('../__init__.py')
+ except NotFound:
+ pass
+ else:
+ assert 0, 'expected exception'
+
+ # testcase for a security issue that may exist on windows systems
+ import os
+ import ntpath
+ old_path = os.path
+ os.path = ntpath
+ try:
+ try:
+ f('..\\__init__.py')
+ except NotFound:
+ pass
+ else:
+ assert 0, 'expected exception'
+ finally:
+ os.path = old_path
+
+ def test_endpoint_decorator(self):
+ from werkzeug.routing import Submount, Rule
+ from flask import Module
+
+ app = flask.Flask(__name__)
+ app.url_map.add(Submount('/foo', [
+ Rule('/bar', endpoint='bar'),
+ Rule('/', endpoint='index')
+ ]))
+ module = Module(__name__, __name__)
+
+ @module.endpoint('bar')
+ def bar():
+ return 'bar'
+
+ @module.endpoint('index')
+ def index():
+ return 'index'
+
+ app.register_module(module)
+
+ c = app.test_client()
+ assert c.get('/foo/').data == 'index'
+ assert c.get('/foo/bar').data == 'bar'
+
+
+class SendfileTestCase(unittest.TestCase):
+
+ def test_send_file_regular(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ rv = flask.send_file('static/index.html')
+ assert rv.direct_passthrough
+ assert rv.mimetype == 'text/html'
+ with app.open_resource('static/index.html') as f:
+ assert rv.data == f.read()
+
+ def test_send_file_xsendfile(self):
+ app = flask.Flask(__name__)
+ app.use_x_sendfile = True
+ with app.test_request_context():
+ rv = flask.send_file('static/index.html')
+ assert rv.direct_passthrough
+ assert 'x-sendfile' in rv.headers
+ assert rv.headers['x-sendfile'] == \
+ os.path.join(app.root_path, 'static/index.html')
+ assert rv.mimetype == 'text/html'
+
+ def test_send_file_object(self):
+ app = flask.Flask(__name__)
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f)
+ with app.open_resource('static/index.html') as f:
+ assert rv.data == f.read()
+ assert rv.mimetype == 'text/html'
+ # mimetypes + etag
+ assert len(captured) == 2
+
+ app.use_x_sendfile = True
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f)
+ assert rv.mimetype == 'text/html'
+ assert 'x-sendfile' in rv.headers
+ assert rv.headers['x-sendfile'] == \
+ os.path.join(app.root_path, 'static/index.html')
+ # mimetypes + etag
+ assert len(captured) == 2
+
+ app.use_x_sendfile = False
+ with app.test_request_context():
+ with catch_warnings() as captured:
+ f = StringIO('Test')
+ rv = flask.send_file(f)
+ assert rv.data == 'Test'
+ assert rv.mimetype == 'application/octet-stream'
+ # etags
+ assert len(captured) == 1
+ with catch_warnings() as captured:
+ f = StringIO('Test')
+ rv = flask.send_file(f, mimetype='text/plain')
+ assert rv.data == 'Test'
+ assert rv.mimetype == 'text/plain'
+ # etags
+ assert len(captured) == 1
+
+ app.use_x_sendfile = True
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = StringIO('Test')
+ rv = flask.send_file(f)
+ assert 'x-sendfile' not in rv.headers
+ # etags
+ assert len(captured) == 1
+
+ def test_attachment(self):
+ app = flask.Flask(__name__)
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f, as_attachment=True)
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ assert value == 'attachment'
+ # mimetypes + etag
+ assert len(captured) == 2
+
+ with app.test_request_context():
+ assert options['filename'] == 'index.html'
+ rv = flask.send_file('static/index.html', as_attachment=True)
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ assert value == 'attachment'
+ assert options['filename'] == 'index.html'
+
+ with app.test_request_context():
+ rv = flask.send_file(StringIO('Test'), as_attachment=True,
+ attachment_filename='index.txt',
+ add_etags=False)
+ assert rv.mimetype == 'text/plain'
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ assert value == 'attachment'
+ assert options['filename'] == 'index.txt'
+
+
+class LoggingTestCase(unittest.TestCase):
+
+ def test_logger_cache(self):
+ app = flask.Flask(__name__)
+ logger1 = app.logger
+ assert app.logger is logger1
+ assert logger1.name == __name__
+ app.logger_name = __name__ + '/test_logger_cache'
+ assert app.logger is not logger1
+
+ def test_debug_log(self):
+ app = flask.Flask(__name__)
+ app.debug = True
+
+ @app.route('/')
+ def index():
+ app.logger.warning('the standard library is dead')
+ app.logger.debug('this is a debug statement')
+ return ''
+
+ @app.route('/exc')
+ def exc():
+ 1/0
+ c = app.test_client()
+
+ with catch_stderr() as err:
+ c.get('/')
+ out = err.getvalue()
+ assert 'WARNING in flask_tests [' in out
+ assert 'flask_tests.py' in out
+ assert 'the standard library is dead' in out
+ assert 'this is a debug statement' in out
+
+ with catch_stderr() as err:
+ try:
+ c.get('/exc')
+ except ZeroDivisionError:
+ pass
+ else:
+ assert False, 'debug log ate the exception'
+
+ def test_exception_logging(self):
+ out = StringIO()
+ app = flask.Flask(__name__)
+ app.logger_name = 'flask_tests/test_exception_logging'
+ app.logger.addHandler(StreamHandler(out))
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ rv = app.test_client().get('/')
+ assert rv.status_code == 500
+ assert 'Internal Server Error' in rv.data
+
+ err = out.getvalue()
+ assert 'Exception on / [GET]' in err
+ assert 'Traceback (most recent call last):' in err
+ assert '1/0' in err
+ assert 'ZeroDivisionError:' in err
+
+ def test_processor_exceptions(self):
+ app = flask.Flask(__name__)
+ @app.before_request
+ def before_request():
+ if trigger == 'before':
+ 1/0
+ @app.after_request
+ def after_request(response):
+ if trigger == 'after':
+ 1/0
+ return response
+ @app.route('/')
+ def index():
+ return 'Foo'
+ @app.errorhandler(500)
+ def internal_server_error(e):
+ return 'Hello Server Error', 500
+ for trigger in 'before', 'after':
+ rv = app.test_client().get('/')
+ assert rv.status_code == 500
+ assert rv.data == 'Hello Server Error'
+
+
+class ConfigTestCase(unittest.TestCase):
+
+ def common_object_test(self, app):
+ assert app.secret_key == 'devkey'
+ assert app.config['TEST_KEY'] == 'foo'
+ assert 'ConfigTestCase' not in app.config
+
+ def test_config_from_file(self):
+ app = flask.Flask(__name__)
+ app.config.from_pyfile('flask_tests.py')
+ self.common_object_test(app)
+
+ def test_config_from_object(self):
+ app = flask.Flask(__name__)
+ app.config.from_object(__name__)
+ self.common_object_test(app)
+
+ def test_config_from_class(self):
+ class Base(object):
+ TEST_KEY = 'foo'
+ class Test(Base):
+ SECRET_KEY = 'devkey'
+ app = flask.Flask(__name__)
+ app.config.from_object(Test)
+ self.common_object_test(app)
+
+ def test_config_from_envvar(self):
+ import os
+ env = os.environ
+ try:
+ os.environ = {}
+ app = flask.Flask(__name__)
+ try:
+ app.config.from_envvar('FOO_SETTINGS')
+ except RuntimeError, e:
+ assert "'FOO_SETTINGS' is not set" in str(e)
+ else:
+ assert 0, 'expected exception'
+ not app.config.from_envvar('FOO_SETTINGS', silent=True)
+
+ os.environ = {'FOO_SETTINGS': 'flask_tests.py'}
+ assert app.config.from_envvar('FOO_SETTINGS')
+ self.common_object_test(app)
+ finally:
+ os.environ = env
+
+ def test_config_missing(self):
+ app = flask.Flask(__name__)
+ try:
+ app.config.from_pyfile('missing.cfg')
+ except IOError, e:
+ msg = str(e)
+ assert msg.startswith('[Errno 2] Unable to load configuration '
+ 'file (No such file or directory):')
+ assert msg.endswith("missing.cfg'")
+ else:
+ assert 0, 'expected config'
+
+
+class SubdomainTestCase(unittest.TestCase):
+
+ def test_basic_support(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'localhost'
+ @app.route('/')
+ def normal_index():
+ return 'normal index'
+ @app.route('/', subdomain='test')
+ def test_index():
+ return 'test index'
+
+ c = app.test_client()
+ rv = c.get('/', 'http://localhost/')
+ assert rv.data == 'normal index'
+
+ rv = c.get('/', 'http://test.localhost/')
+ assert rv.data == 'test index'
+
+ def test_module_static_path_subdomain(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'example.com'
+ from subdomaintestmodule import mod
+ app.register_module(mod)
+ c = app.test_client()
+ rv = c.get('/static/hello.txt', 'http://foo.example.com/')
+ assert rv.data.strip() == 'Hello Subdomain'
+
+ def test_subdomain_matching(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'localhost'
+ @app.route('/', subdomain='<user>')
+ def index(user):
+ return 'index for %s' % user
+
+ c = app.test_client()
+ rv = c.get('/', 'http://mitsuhiko.localhost/')
+ assert rv.data == 'index for mitsuhiko'
+
+ def test_module_subdomain_support(self):
+ app = flask.Flask(__name__)
+ mod = flask.Module(__name__, 'test', subdomain='testing')
+ app.config['SERVER_NAME'] = 'localhost'
+
+ @mod.route('/test')
+ def test():
+ return 'Test'
+
+ @mod.route('/outside', subdomain='xtesting')
+ def bar():
+ return 'Outside'
+
+ app.register_module(mod)
+
+ c = app.test_client()
+ rv = c.get('/test', 'http://testing.localhost/')
+ assert rv.data == 'Test'
+ rv = c.get('/outside', 'http://xtesting.localhost/')
+ assert rv.data == 'Outside'
+
+
+class TestSignals(unittest.TestCase):
+
+ def test_template_rendered(self):
+ app = flask.Flask(__name__)
+
+ @app.route('/')
+ def index():
+ return flask.render_template('simple_template.html', whiskey=42)
+
+ recorded = []
+ def record(sender, template, context):
+ recorded.append((template, context))
+
+ flask.template_rendered.connect(record, app)
+ try:
+ app.test_client().get('/')
+ assert len(recorded) == 1
+ template, context = recorded[0]
+ assert template.name == 'simple_template.html'
+ assert context['whiskey'] == 42
+ finally:
+ flask.template_rendered.disconnect(record, app)
+
+ def test_request_signals(self):
+ app = flask.Flask(__name__)
+ calls = []
+
+ def before_request_signal(sender):
+ calls.append('before-signal')
+
+ def after_request_signal(sender, response):
+ assert response.data == 'stuff'
+ calls.append('after-signal')
+
+ @app.before_request
+ def before_request_handler():
+ calls.append('before-handler')
+
+ @app.after_request
+ def after_request_handler(response):
+ calls.append('after-handler')
+ response.data = 'stuff'
+ return response
+
+ @app.route('/')
+ def index():
+ calls.append('handler')
+ return 'ignored anyway'
+
+ flask.request_started.connect(before_request_signal, app)
+ flask.request_finished.connect(after_request_signal, app)
+
+ try:
+ rv = app.test_client().get('/')
+ assert rv.data == 'stuff'
+
+ assert calls == ['before-signal', 'before-handler',
+ 'handler', 'after-handler',
+ 'after-signal']
+ finally:
+ flask.request_started.disconnect(before_request_signal, app)
+ flask.request_finished.disconnect(after_request_signal, app)
+
+ def test_request_exception_signal(self):
+ app = flask.Flask(__name__)
+ recorded = []
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ def record(sender, exception):
+ recorded.append(exception)
+
+ flask.got_request_exception.connect(record, app)
+ try:
+ assert app.test_client().get('/').status_code == 500
+ assert len(recorded) == 1
+ assert isinstance(recorded[0], ZeroDivisionError)
+ finally:
+ flask.got_request_exception.disconnect(record, app)
+
+
+def suite():
+ from minitwit_tests import MiniTwitTestCase
+ from flaskr_tests import FlaskrTestCase
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ContextTestCase))
+ suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase))
+ suite.addTest(unittest.makeSuite(TemplatingTestCase))
+ suite.addTest(unittest.makeSuite(ModuleTestCase))
+ suite.addTest(unittest.makeSuite(SendfileTestCase))
+ suite.addTest(unittest.makeSuite(LoggingTestCase))
+ suite.addTest(unittest.makeSuite(ConfigTestCase))
+ suite.addTest(unittest.makeSuite(SubdomainTestCase))
+ if flask.json_available:
+ suite.addTest(unittest.makeSuite(JSONTestCase))
+ if flask.signals_available:
+ suite.addTest(unittest.makeSuite(TestSignals))
+ suite.addTest(unittest.makeSuite(MiniTwitTestCase))
+ suite.addTest(unittest.makeSuite(FlaskrTestCase))
+ return suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/flaskext_test.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,311 @@
+# -*- coding: utf-8 -*-
+"""
+ Flask Extension Tests
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the Flask extensions.
+
+ :copyright: (c) 2010 by Ali Afshar.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import sys
+import shutil
+import urllib2
+import tempfile
+import subprocess
+import argparse
+
+from flask import json
+
+from setuptools.package_index import PackageIndex
+from setuptools.archive_util import unpack_archive
+
+flask_svc_url = 'http://flask.pocoo.org/extensions/'
+
+
+# OS X has awful paths when using mkstemp or gettempdir(). I don't
+# care about security or clashes here, so pick something that is
+# actually rememberable.
+if sys.platform == 'darwin':
+ _tempdir = '/private/tmp'
+else:
+ _tempdir = tempfile.gettempdir()
+tdir = _tempdir + '/flaskext-test'
+flaskdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+
+
+# virtualenv hack *cough*
+os.environ['PYTHONDONTWRITEBYTECODE'] = ''
+
+
+RESULT_TEMPATE = u'''\
+<!doctype html>
+<title>Flask-Extension Test Results</title>
+<style type=text/css>
+ body { font-family: 'Georgia', serif; font-size: 17px; color: #000; }
+ a { color: #004B6B; }
+ a:hover { color: #6D4100; }
+ h1, h2, h3 { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; }
+ h1 { font-size: 30px; margin: 15px 0 5px 0; }
+ h2 { font-size: 24px; margin: 15px 0 5px 0; }
+ h3 { font-size: 19px; margin: 15px 0 5px 0; }
+ textarea, code,
+ pre { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono',
+ 'Bitstream Vera Sans Mono', monospace!important; font-size: 15px;
+ background: #eee; }
+ pre { padding: 7px 15px; line-height: 1.3; }
+ p { line-height: 1.4; }
+ table { border: 1px solid black; border-collapse: collapse;
+ margin: 15px 0; }
+ td, th { border: 1px solid black; padding: 4px 10px;
+ text-align: left; }
+ th { background: #eee; font-weight: normal; }
+ tr.success { background: #D3F5CC; }
+ tr.failed { background: #F5D2CB; }
+</style>
+<h1>Flask-Extension Test Results</h1>
+<p>
+ This page contains the detailed test results for the test run of
+ all {{ 'approved' if approved }} Flask extensions.
+<h2>Summary</h2>
+<table class=results>
+ <thead>
+ <tr>
+ <th>Extension
+ <th>Version
+ <th>Author
+ <th>License
+ <th>Outcome
+ {%- for iptr, _ in results[0].logs|dictsort %}
+ <th>{{ iptr }}
+ {%- endfor %}
+ </tr>
+ </thead>
+ <tbody>
+ {%- for result in results %}
+ {% set outcome = 'success' if result.success else 'failed' %}
+ <tr class={{ outcome }}>
+ <th>{{ result.name }}
+ <td>{{ result.version }}
+ <td>{{ result.author }}
+ <td>{{ result.license }}
+ <td>{{ outcome }}
+ {%- for iptr, _ in result.logs|dictsort %}
+ <td><a href="#{{ result.name }}-{{ iptr }}">see log</a>
+ {%- endfor %}
+ </tr>
+ {%- endfor %}
+ </tbody>
+</table>
+<h2>Test Logs</h2>
+<p>Detailed test logs for all tests on all platforms:
+{%- for result in results %}
+ {%- for iptr, log in result.logs|dictsort %}
+ <h3 id="{{ result.name }}-{{ iptr }}">
+ {{ result.name }} - {{ result.version }} [{{ iptr }}]</h3>
+ <pre>{{ log }}</pre>
+ {%- endfor %}
+{%- endfor %}
+'''
+
+
+def log(msg, *args):
+ print '[EXTTEST]', msg % args
+
+
+class TestResult(object):
+
+ def __init__(self, name, folder, statuscode, interpreters):
+ intrptr = os.path.join(folder, '.tox/%s/bin/python'
+ % interpreters[0])
+ self.statuscode = statuscode
+ self.folder = folder
+ self.success = statuscode == 0
+
+ def fetch(field):
+ try:
+ c = subprocess.Popen([intrptr, 'setup.py',
+ '--' + field], cwd=folder,
+ stdout=subprocess.PIPE)
+ return c.communicate()[0].strip()
+ except OSError:
+ return '?'
+ self.name = name
+ self.license = fetch('license')
+ self.author = fetch('author')
+ self.version = fetch('version')
+
+ self.logs = {}
+ for interpreter in interpreters:
+ logfile = os.path.join(folder, '.tox/%s/log/test.log'
+ % interpreter)
+ if os.path.isfile(logfile):
+ self.logs[interpreter] = open(logfile).read()
+ else:
+ self.logs[interpreter] = ''
+
+
+def create_tdir():
+ try:
+ shutil.rmtree(tdir)
+ except Exception:
+ pass
+ os.mkdir(tdir)
+
+
+def package_flask():
+ distfolder = tdir + '/.flask-dist'
+ c = subprocess.Popen(['python', 'setup.py', 'sdist', '--formats=gztar',
+ '--dist', distfolder], cwd=flaskdir)
+ c.wait()
+ return os.path.join(distfolder, os.listdir(distfolder)[0])
+
+
+def get_test_command(checkout_dir):
+ if os.path.isfile(checkout_dir + '/Makefile'):
+ return 'make test'
+ return 'python setup.py test'
+
+
+def fetch_extensions_list():
+ req = urllib2.Request(flask_svc_url, headers={'accept':'application/json'})
+ d = urllib2.urlopen(req).read()
+ data = json.loads(d)
+ for ext in data['extensions']:
+ yield ext
+
+
+def checkout_extension(name):
+ log('Downloading extension %s to temporary folder', name)
+ root = os.path.join(tdir, name)
+ os.mkdir(root)
+ checkout_path = PackageIndex().download(name, root)
+
+ unpack_archive(checkout_path, root)
+ path = None
+ for fn in os.listdir(root):
+ path = os.path.join(root, fn)
+ if os.path.isdir(path):
+ break
+ log('Downloaded to %s', path)
+ return path
+
+
+tox_template = """[tox]
+envlist=%(env)s
+
+[testenv]
+deps=
+ %(deps)s
+ distribute
+ py
+commands=bash flaskext-runtest.sh {envlogdir}/test.log
+downloadcache=%(cache)s
+"""
+
+
+def create_tox_ini(checkout_path, interpreters, flask_dep):
+ tox_path = os.path.join(checkout_path, 'tox-flask-test.ini')
+ if not os.path.exists(tox_path):
+ with open(tox_path, 'w') as f:
+ f.write(tox_template % {
+ 'env': ','.join(interpreters),
+ 'cache': tdir,
+ 'deps': flask_dep
+ })
+ return tox_path
+
+
+def iter_extensions(only_approved=True):
+ for ext in fetch_extensions_list():
+ if ext['approved'] or not only_approved:
+ yield ext['name']
+
+
+def test_extension(name, interpreters, flask_dep):
+ checkout_path = checkout_extension(name)
+ log('Running tests with tox in %s', checkout_path)
+
+ # figure out the test command and write a wrapper script. We
+ # can't write that directly into the tox ini because tox does
+ # not invoke the command from the shell so we have no chance
+ # to pipe the output into a logfile. The /dev/null hack is
+ # to trick py.test (if used) into not guessing widths from the
+ # invoking terminal.
+ test_command = get_test_command(checkout_path)
+ log('Test command: %s', test_command)
+ f = open(checkout_path + '/flaskext-runtest.sh', 'w')
+ f.write(test_command + ' &> "$1" < /dev/null\n')
+ f.close()
+
+ # if there is a tox.ini, remove it, it will cause troubles
+ # for us. Remove it if present, we are running tox ourselves
+ # afterall.
+
+ create_tox_ini(checkout_path, interpreters, flask_dep)
+ rv = subprocess.call(['tox', '-c', 'tox-flask-test.ini'], cwd=checkout_path)
+ return TestResult(name, checkout_path, rv, interpreters)
+
+
+def run_tests(extensions, interpreters):
+ results = {}
+ create_tdir()
+ log('Packaging Flask')
+ flask_dep = package_flask()
+ log('Running extension tests')
+ log('Temporary Environment: %s', tdir)
+ for name in extensions:
+ log('Testing %s', name)
+ result = test_extension(name, interpreters, flask_dep)
+ if result.success:
+ log('Extension test succeeded')
+ else:
+ log('Extension test failed')
+ results[name] = result
+ return results
+
+
+def render_results(results, approved):
+ from jinja2 import Template
+ items = results.values()
+ items.sort(key=lambda x: x.name.lower())
+ rv = Template(RESULT_TEMPATE, autoescape=True).render(results=items,
+ approved=approved)
+ fd, filename = tempfile.mkstemp(suffix='.html')
+ os.fdopen(fd, 'w').write(rv.encode('utf-8') + '\n')
+ return filename
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Runs Flask extension tests')
+ parser.add_argument('--all', dest='all', action='store_true',
+ help='run against all extensions, not just approved')
+ parser.add_argument('--browse', dest='browse', action='store_true',
+ help='show browser with the result summary')
+ parser.add_argument('--env', dest='env', default='py25,py26,py27',
+ help='the tox environments to run against')
+ parser.add_argument('--extension=', dest='extension', default=None,
+ help='tests a single extension')
+ args = parser.parse_args()
+
+ if args.extension is not None:
+ only_approved = False
+ extensions = [args.extension]
+ else:
+ only_approved = not args.all
+ extensions = iter_extensions(only_approved)
+
+ results = run_tests(extensions, [x.strip() for x in args.env.split(',')])
+ filename = render_results(results, only_approved)
+ if args.browse:
+ import webbrowser
+ webbrowser.open('file:///' + filename.lstrip('/'))
+ print 'Results written to', filename
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/moduleapp/__init__.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,7 @@
+from flask import Flask
+
+app = Flask(__name__)
+from moduleapp.apps.admin import admin
+from moduleapp.apps.frontend import frontend
+app.register_module(admin)
+app.register_module(frontend)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/moduleapp/apps/admin/__init__.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,14 @@
+from flask import Module, render_template
+
+
+admin = Module(__name__, url_prefix='/admin')
+
+
+@admin.route('/')
+def index():
+ return render_template('admin/index.html')
+
+
+@admin.route('/index2')
+def index2():
+ return render_template('./admin/index.html')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/moduleapp/apps/admin/static/css/test.css Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+/* nested file */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/moduleapp/apps/admin/static/test.txt Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+Admin File
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/moduleapp/apps/admin/templates/index.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+Hello from the Admin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/moduleapp/apps/frontend/__init__.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,9 @@
+from flask import Module, render_template
+
+
+frontend = Module(__name__)
+
+
+@frontend.route('/')
+def index():
+ return render_template('frontend/index.html')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/moduleapp/apps/frontend/templates/index.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+Hello from the Frontend
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/static/index.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+<h1>Hello World!</h1>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/subdomaintestmodule/__init__.py Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,4 @@
+from flask import Module
+
+
+mod = Module(__name__, 'foo', subdomain='foo')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/subdomaintestmodule/static/hello.txt Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+Hello Subdomain
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/templates/_macro.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/templates/context_template.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+<p>{{ value }}|{{ injected_value }}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/templates/escaping_template.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,6 @@
+{{ text }}
+{{ html }}
+{% autoescape false %}{{ text }}
+{{ html }}{% endautoescape %}
+{% autoescape true %}{{ text }}
+{{ html }}{% endautoescape %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/templates/mail.txt Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+{{ foo}} Mail
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/templates/nested/nested.txt Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+I'm nested
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/templates/simple_template.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+<h1>{{ whiskey }}</h1>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tests/templates/template_filter.html Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,1 @@
+{{ value|super_reverse }}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bundled/flask/tox.ini Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,5 @@
+[tox]
+envlist=py25,py26,py27
+
+[testenv]
+commands=make test
--- a/kick Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-kicker -qs -l 0.1 -e reload_safari \
-review/**.py \
-review/**/*.html \
-review/**/*.less \
-review/**/*.js
--- a/review/static/aal.css Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- aardvark.legs by Anatoli Papirovski - http://fecklessmind.com/
- Licensed under the MIT license. http://www.opensource.org/licenses/mit-license.php
-*/
-
-/*
- Reset first. Modified version of Eric Meyer and Paul Chaplin reset
- from http://meyerweb.com/eric/tools/css/reset/
-*/
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, font, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-header, nav, section, article, aside, footer
-{border: 0; margin: 0; outline: 0; padding: 0; background: transparent; vertical-align: baseline;}
-
-blockquote, q {quotes: none;}
-blockquote:before,blockquote:after,q:before,q:after {content: ''; content: none;}
-
-header, nav, section, article, aside, footer {display: block;}
-
-/* Basic styles */
-body {background: #fff; color: #111; font: 0.75em/1.5em "Helvetica Neue", Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif;}
-html>body {font-size: 12px;}
-
-img {display: inline-block; vertical-align: bottom;}
-
-h1,h2,h3,h4,h5,h6,strong,b,dt,th {font-weight: 700;}
-address,cite,em,i,caption,dfn,var {font-style: italic;}
-
-h1 {margin: 0 0 0.75em; font-size: 2em;}
-h2 {margin: 0 0 1em; font-size: 1.5em;}
-h3 {margin: 0 0 1.286em; font-size: 1.167em;}
-h4 {margin: 0 0 1.5em; font-size: 1em;}
-h5 {margin: 0 0 1.8em; font-size: .834em;}
-h6 {margin: 0 0 2em; font-size: .75em;}
-
-p,ul,ol,dl,blockquote,pre {margin: 0 0 1.5em;}
-
-li ul,li ol {margin: 0;}
-ul {list-style: outside disc;}
-ol {list-style: outside decimal;}
-li {margin: 0 0 0 2em;}
-dd {padding-left: 1.5em;}
-blockquote {padding: 0 1.5em;}
-
-a {text-decoration: underline;}
-a:hover {text-decoration: none;}
-abbr,acronym {border-bottom: 1px dotted; cursor: help;}
-del {text-decoration: line-through;}
-ins {text-decoration: overline;}
-sub {font-size: .834em; line-height: 1em; vertical-align: sub;}
-sup {font-size: .834em; line-height: 1em; vertical-align: super;}
-
-tt,code,kbd,samp,pre {font-size: 1em; font-family: Consolas, Monaco, "Courier New", Courier, monospace;}
-
-/* Table styles */
-table {border-collapse: collapse; border-spacing: 0; margin: 0 0 1.5em;}
-caption {text-align: left;}
-th, td {padding: .25em .5em;}
-tbody td, tbody th {border: 1px solid #000;}
-tfoot {font-style: italic;}
-
-/* Form styles */
-fieldset {clear: both;}
-legend {padding: 0 0 1.286em; font-size: 1.167em; font-weight: 700;}
-fieldset fieldset legend {padding: 0 0 1.5em; font-size: 1em;}
-* html legend {margin-left: -7px;}
-*+html legend {margin-left: -7px;}
-
-form .field, form .buttons {clear: both; margin: 0 0 1.5em;}
-form .field label {display: block;}
-form ul.fields li {list-style-type: none; margin: 0;}
-form ul.inline li, form ul.inline label {display: inline;}
-form ul.inline li {padding: 0 .75em 0 0;}
-
-input.radio, input.checkbox {vertical-align: top;}
-label, button, input.submit, input.image {cursor: pointer;}
-* html input.radio, * html input.checkbox {vertical-align: middle;}
-*+html input.radio, *+html input.checkbox {vertical-align: middle;}
-
-textarea {overflow: auto;}
-input.text, input.password, textarea, select {margin: 0; font: 1em/1.3 Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif; vertical-align: baseline;}
-input.text, input.password, textarea {border: 1px solid #444; border-bottom-color: #666; border-right-color: #666; padding: 2px;}
-
-* html button {margin: 0 .34em 0 0;}
-*+html button {margin: 0 .34em 0 0;}
-
-form.horizontal .field {padding-left: 150px;}
-form.horizontal .field label {display: inline; float: left; width: 140px; margin-left: -150px;}
-
-/* Useful classes */
-img.left {display: inline; float: left; margin: 0 1.5em .75em 0;}
-img.right {display: inline; float: right; margin: 0 0 .75em .75em;}
Binary file review/static/bg.png has changed
--- a/review/static/colorbox/colorbox.css Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- ColorBox Core Style
- The following rules are the styles that are consistant between themes.
- Avoid changing this area to maintain compatability with future versions of ColorBox.
-*/
-#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;}
-#cboxOverlay{position:fixed; width:100%; height:100%;}
-#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
-#cboxContent{position:relative; overflow:hidden;}
-#cboxLoadedContent{overflow:auto;}
-#cboxLoadedContent iframe{display:block; width:100%; height:100%; border:0;}
-#cboxTitle{margin:0;}
-#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%;}
-#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
-
-/*
- Example user style
- The following rules are ordered and tabbed in a way that represents the
- order/nesting of the generated HTML, so that the structure easier to understand.
-*/
-#cboxOverlay{background:url(images/overlay.png) 0 0 repeat;}
-#colorbox{}
- #cboxTopLeft{width:21px; height:21px; background:url(images/controls.png) -100px 0 no-repeat;}
- #cboxTopRight{width:21px; height:21px; background:url(images/controls.png) -129px 0 no-repeat;}
- #cboxBottomLeft{width:21px; height:21px; background:url(images/controls.png) -100px -29px no-repeat;}
- #cboxBottomRight{width:21px; height:21px; background:url(images/controls.png) -129px -29px no-repeat;}
- #cboxMiddleLeft{width:21px; background:url(images/controls.png) left top repeat-y;}
- #cboxMiddleRight{width:21px; background:url(images/controls.png) right top repeat-y;}
- #cboxTopCenter{height:21px; background:url(images/border.png) 0 0 repeat-x;}
- #cboxBottomCenter{height:21px; background:url(images/border.png) 0 -29px repeat-x;}
- #cboxContent{background:#fff;}
- #cboxLoadedContent{margin-bottom:28px;}
- #cboxTitle{position:absolute; bottom:4px; left:0; text-align:center; width:100%; color:#949494;}
- #cboxCurrent{position:absolute; bottom:4px; left:58px; color:#949494;}
- #cboxSlideshow{position:absolute; bottom:4px; right:30px; color:#0092ef;}
- #cboxPrevious{position:absolute; bottom:0; left:0px; background:url(images/controls.png) -75px 0px no-repeat; width:25px; height:25px; text-indent:-9999px;}
- #cboxPrevious.hover{background-position:-75px -25px;}
- #cboxNext{position:absolute; bottom:0; left:27px; background:url(images/controls.png) -50px 0px no-repeat; width:25px; height:25px; text-indent:-9999px;}
- #cboxNext.hover{background-position:-50px -25px;}
- #cboxLoadingOverlay{background:url(images/loading_background.png) center center no-repeat;}
- #cboxLoadingGraphic{background:url(images/loading.gif) center center no-repeat;}
- #cboxClose{position:absolute; bottom:0; right:0; background:url(images/controls.png) -25px 0px no-repeat; width:25px; height:25px; text-indent:-9999px;}
- #cboxClose.hover{background-position:-25px -25px;}
-
-/*
- The following fixes png-transparency for IE6.
- It is also necessary for png-transparency in IE7 & IE8 to avoid 'black halos' with the fade transition
-
- Since this method does not support CSS background-positioning, it is incompatible with CSS sprites.
- Colorbox preloads navigation hover classes to account for this.
-
- !! Important Note: AlphaImageLoader src paths are relative to the HTML document,
- while regular CSS background images are relative to the CSS document.
-*/
-.cboxIE #cboxTopLeft{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderTopLeft.png, sizingMethod='scale');}
-.cboxIE #cboxTopCenter{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderTopCenter.png, sizingMethod='scale');}
-.cboxIE #cboxTopRight{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderTopRight.png, sizingMethod='scale');}
-.cboxIE #cboxBottomLeft{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderBottomLeft.png, sizingMethod='scale');}
-.cboxIE #cboxBottomCenter{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderBottomCenter.png, sizingMethod='scale');}
-.cboxIE #cboxBottomRight{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderBottomRight.png, sizingMethod='scale');}
-.cboxIE #cboxMiddleLeft{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderMiddleLeft.png, sizingMethod='scale');}
-.cboxIE #cboxMiddleRight{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderMiddleRight.png, sizingMethod='scale');}
Binary file review/static/colorbox/images/border.png has changed
Binary file review/static/colorbox/images/controls.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderBottomCenter.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderBottomLeft.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderBottomRight.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderMiddleLeft.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderMiddleRight.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderTopCenter.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderTopLeft.png has changed
Binary file review/static/colorbox/images/internet_explorer/borderTopRight.png has changed
Binary file review/static/colorbox/images/loading.gif has changed
Binary file review/static/colorbox/images/loading_background.png has changed
Binary file review/static/colorbox/images/overlay.png has changed
--- a/review/static/colorbox/jquery.colorbox.js Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,772 +0,0 @@
-// ColorBox v1.3.7 - a full featured, light-weight, customizable lightbox based on jQuery 1.3
-// c) 2009 Jack Moore - www.colorpowered.com - jack@colorpowered.com
-// Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
-
-/*jslint browser: true */
-
-(function ($) {
- // Shortcuts (to increase compression)
- var colorbox = 'colorbox',
- hover = 'hover',
- TRUE = true,
- FALSE = false,
- cboxPublic,
- isIE = $.browser.msie && !$.support.opacity, // feature detection alone gave a false positive on at least one phone browser
- isIE6 = isIE && $.browser.version < 7,
-
- // Event Strings (to increase compression)
- cbox_open = 'cbox_open',
- cbox_load = 'cbox_load',
- cbox_complete = 'cbox_complete',
- cbox_cleanup = 'cbox_cleanup',
- cbox_closed = 'cbox_closed',
-
- // Cached jQuery Object Variables
- $overlay,
- $cbox,
- $wrap,
- $content,
- $topBorder,
- $leftBorder,
- $rightBorder,
- $bottomBorder,
- $related,
- $window,
- $loaded,
- $loadingBay,
- $loadingOverlay,
- $title,
- $current,
- $slideshow,
- $next,
- $prev,
- $close,
-
- // Variables for cached values or use across multiple functions
- interfaceHeight,
- interfaceWidth,
- loadedHeight,
- loadedWidth,
- element,
- bookmark,
- index,
- settings,
- open,
- active,
-
- // ColorBox Default Settings.
- // See http://colorpowered.com/colorbox for details.
- defaults = {
- transition: "elastic",
- speed: 350,
- width: FALSE,
- height: FALSE,
- innerWidth: FALSE,
- innerHeight: FALSE,
- initialWidth: "400",
- initialHeight: "400",
- maxWidth: FALSE,
- maxHeight: FALSE,
- scalePhotos: TRUE,
- scrolling: TRUE,
- inline: FALSE,
- html: FALSE,
- iframe: FALSE,
- photo: FALSE,
- href: FALSE,
- title: FALSE,
- rel: FALSE,
- opacity: 0.9,
- preloading: TRUE,
- current: "{current} of {total}",
- previous: "previous",
- next: "next",
- close: "close",
- open: FALSE,
- overlayClose: TRUE,
- loop: TRUE,
-
- slideshow: FALSE,
- slideshowAuto: TRUE,
- slideshowSpeed: 2500,
- slideshowStart: "start slideshow",
- slideshowStop: "stop slideshow",
-
- onOpen: FALSE,
- onLoad: FALSE,
- onComplete: FALSE,
- onCleanup: FALSE,
- onClosed: FALSE,
-
- escKey: TRUE,
- arrowKey: TRUE
- };
-
- // ****************
- // HELPER FUNCTIONS
- // ****************
-
- // Convert % values to pixels
- function setSize(size, dimension) {
- dimension = dimension === 'x' ? $window.width() : $window.height();//document.documentElement.clientWidth : document.documentElement.clientHeight;
- return (typeof size === 'string') ? Math.round((size.match(/%/) ? (dimension / 100) * parseInt(size, 10) : parseInt(size, 10))) : size;
- }
-
- // Checks an href to see if it is a photo.
- // There is a force photo option (photo: true) for hrefs that cannot be matched by this regex.
- function isImage(url) {
- url = $.isFunction(url) ? url.call(element) : url;
- return settings.photo || url.match(/\.(gif|png|jpg|jpeg|bmp)(?:\?([^#]*))?(?:#(\.*))?$/i);
- }
-
- // Assigns functions results to their respective settings. This allows functions to be used to set ColorBox options.
- function process() {
- for (var i in settings) {
- if ($.isFunction(settings[i]) && i.substring(0, 2) !== 'on') { // checks to make sure the function isn't one of the callbacks, they will be handled at the appropriate time.
- settings[i] = settings[i].call(element);
- }
- }
- settings.rel = settings.rel || element.rel || 'nofollow';
- settings.href = settings.href || $(element).attr('href');
- settings.title = settings.title || element.title;
- }
-
- function launch(elem) {
-
- element = elem;
-
- settings = $.extend({}, $(element).data(colorbox));
-
- process(); // Convert functions to their returned values.
-
- if (settings.rel !== 'nofollow') {
- $related = $('.cboxElement').filter(function () {
- var relRelated = $(this).data(colorbox).rel || this.rel;
- return (relRelated === settings.rel);
- });
- index = $related.index(element);
-
- // Check direct calls to ColorBox.
- if (index < 0) {
- $related = $related.add(element);
- index = $related.length - 1;
- }
- } else {
- $related = $(element);
- index = 0;
- }
-
- if (!open) {
- open = TRUE;
-
- active = TRUE; // Prevents the page-change action from queuing up if the visitor holds down the left or right keys.
-
- bookmark = element;
-
- try {
- bookmark.blur(); // Remove the focus from the calling element.
- }catch (e) {}
-
- $.event.trigger(cbox_open);
- if (settings.onOpen) {
- settings.onOpen.call(element);
- }
-
- $overlay.css({"opacity": parseFloat(settings.opacity), "cursor": settings.overlayClose ? "pointer" : "auto"}).show();
-
- // Opens inital empty ColorBox prior to content being loaded.
- settings.w = setSize(settings.initialWidth, 'x');
- settings.h = setSize(settings.initialHeight, 'y');
- cboxPublic.position(0);
-
- if (isIE6) {
- $window.bind('resize.cboxIE6 scroll.cboxIE6', function () {
- $overlay.css({width: $window.width(), height: $window.height(), top: $window.scrollTop(), left: $window.scrollLeft()});
- }).trigger("scroll.cboxIE6");
- }
- }
-
- $current.add($prev).add($next).add($slideshow).add($title).hide();
-
- $close.html(settings.close).show();
-
- cboxPublic.slideshow();
-
- cboxPublic.load();
- }
-
- // ****************
- // PUBLIC FUNCTIONS
- // Usage format: $.fn.colorbox.close();
- // Usage from within an iframe: parent.$.fn.colorbox.close();
- // ****************
-
- cboxPublic = $.fn.colorbox = $.colorbox = function (options, callback) {
- var $this = this;
-
- if ($this.selector && !$this.length) {
- return $this;
- }
-
- options = options || {};
-
- if (callback) {
- options.onComplete = callback;
- }
-
- if (!$this.length || $this.selector === undefined) { // detects $.colorbox() and $.fn.colorbox()
- $this = $('<a/>');
- options.open = TRUE; // assume an immediate open
- }
-
- $this.each(function () {
- $(this).data(colorbox, $.extend({}, $(this).data(colorbox) || defaults, options)).addClass("cboxElement");
- });
-
- if (options.open) {
- launch($this[0]);
- }
-
- return $this;
- };
-
- // Initialize ColorBox: store common calculations, preload the interface graphics, append the html.
- // This preps colorbox for a speedy open when clicked, and lightens the burdon on the browser by only
- // having to run once, instead of each time colorbox is opened.
- cboxPublic.init = function () {
-
- // jQuery object generator to save a bit of space
- function $div(id) {
- return $('<div id="cbox' + id + '"/>');
- }
-
- // Create & Append jQuery Objects
- $window = $(window);
- $cbox = $('<div id="colorbox"/>');
- $overlay = $div("Overlay").hide();
- $wrap = $div("Wrapper");
- $content = $div("Content").append(
- $loaded = $div("LoadedContent").css({width: 0, height: 0}),
- $loadingOverlay = $div("LoadingOverlay").add($div("LoadingGraphic")),
- $title = $div("Title"),
- $current = $div("Current"),
- $next = $div("Next"),
- $prev = $div("Previous"),
- $slideshow = $div("Slideshow"),
- $close = $div("Close")
- );
- $wrap.append( // The 3x3 Grid that makes up ColorBox
- $('<div/>').append(
- $div("TopLeft"),
- $topBorder = $div("TopCenter"),
- $div("TopRight")
- ),
- $('<div/>').append(
- $leftBorder = $div("MiddleLeft"),
- $content,
- $rightBorder = $div("MiddleRight")
- ),
- $('<div/>').append(
- $div("BottomLeft"),
- $bottomBorder = $div("BottomCenter"),
- $div("BottomRight")
- )
- ).children().children().css({'float': 'left'});
-
- $loadingBay = $("<div id='cboxLoadingBay' style='position:absolute; width:9999px;'/>");
-
- $('body').prepend($overlay, $cbox.append($wrap, $loadingBay));
-
- if (isIE) {
- $cbox.addClass('cboxIE');
- if (isIE6) {
- $overlay.css('position', 'absolute');
- }
- }
-
- $content.children()
- .hover(function () {
- $(this).addClass(hover);
- }, function () {
- $(this).removeClass(hover);
- }).addClass(hover);
-
- // Cache values needed for size calculations
- interfaceHeight = $topBorder.height() + $bottomBorder.height() + $content.outerHeight(TRUE) - $content.height();//Subtraction needed for IE6
- interfaceWidth = $leftBorder.width() + $rightBorder.width() + $content.outerWidth(TRUE) - $content.width();
- loadedHeight = $loaded.outerHeight(TRUE);
- loadedWidth = $loaded.outerWidth(TRUE);
-
- // Setting padding to remove the need to do size conversions during the animation step.
- $cbox.css({"padding-bottom": interfaceHeight, "padding-right": interfaceWidth}).hide();
-
- // Setup button events.
- $next.click(cboxPublic.next);
- $prev.click(cboxPublic.prev);
- $close.click(cboxPublic.close);
-
- // Adding the 'hover' class allowed the browser to load the hover-state
- // background graphics. The class can now can be removed.
- $content.children().removeClass(hover);
-
- $('.cboxElement').live('click', function (e) {
- // checks to see if it was a non-left mouse-click and for clicks modified with ctrl, shift, or alt.
- if ((e.button !== 0 && typeof e.button !== 'undefined') || e.ctrlKey || e.shiftKey || e.altKey) {
- return TRUE;
- } else {
- launch(this);
- return FALSE;
- }
- });
-
- $overlay.click(function () {
- if (settings.overlayClose) {
- cboxPublic.close();
- }
- });
-
- // Set Navigation Key Bindings
- $(document).bind("keydown", function (e) {
- if (open && settings.escKey && e.keyCode === 27) {
- e.preventDefault();
- cboxPublic.close();
- }
- if (open && settings.arrowKey && !active && $related.length > 1) {
- if (e.keyCode === 37 && (index > 0 || settings.loop)) {
- e.preventDefault();
- $prev.click();
- } else if (e.keyCode === 39 && (index < $related.length - 1 || settings.loop)) {
- e.preventDefault();
- $next.click();
- }
- }
- });
- };
-
- cboxPublic.remove = function () {
- $cbox.add($overlay).remove();
- $('.cboxElement').removeData(colorbox).removeClass('cboxElement');
- };
-
- cboxPublic.position = function (speed, loadedCallback) {
- var
- animate_speed,
- // keeps the top and left positions within the browser's viewport.
- posTop = Math.max($window.height() - settings.h - loadedHeight - interfaceHeight, 0) / 2 + $window.scrollTop(),
- posLeft = Math.max($window.width() - settings.w - loadedWidth - interfaceWidth, 0) / 2 + $window.scrollLeft();
-
- // setting the speed to 0 to reduce the delay between same-sized content.
- animate_speed = ($cbox.width() === settings.w + loadedWidth && $cbox.height() === settings.h + loadedHeight) ? 0 : speed;
-
- // this gives the wrapper plenty of breathing room so it's floated contents can move around smoothly,
- // but it has to be shrank down around the size of div#colorbox when it's done. If not,
- // it can invoke an obscure IE bug when using iframes.
- $wrap[0].style.width = $wrap[0].style.height = "9999px";
-
- function modalDimensions(that) {
- // loading overlay height has to be explicitly set for IE6.
- $topBorder[0].style.width = $bottomBorder[0].style.width = $content[0].style.width = that.style.width;
- $loadingOverlay[0].style.height = $loadingOverlay[1].style.height = $content[0].style.height = $leftBorder[0].style.height = $rightBorder[0].style.height = that.style.height;
- }
-
- $cbox.dequeue().animate({width: settings.w + loadedWidth, height: settings.h + loadedHeight, top: posTop, left: posLeft}, {
- duration: animate_speed,
- complete: function () {
- modalDimensions(this);
-
- active = FALSE;
-
- // shrink the wrapper down to exactly the size of colorbox to avoid a bug in IE's iframe implementation.
- $wrap[0].style.width = (settings.w + loadedWidth + interfaceWidth) + "px";
- $wrap[0].style.height = (settings.h + loadedHeight + interfaceHeight) + "px";
-
- if (loadedCallback) {
- loadedCallback();
- }
- },
- step: function () {
- modalDimensions(this);
- }
- });
- };
-
- cboxPublic.resize = function (options) {
- if (open) {
- options = options || {};
-
- if (options.width) {
- settings.w = setSize(options.width, 'x') - loadedWidth - interfaceWidth;
- }
- if (options.innerWidth) {
- settings.w = setSize(options.innerWidth, 'x');
- }
- $loaded.css({width: settings.w});
-
- if (options.height) {
- settings.h = setSize(options.height, 'y') - loadedHeight - interfaceHeight;
- }
- if (options.innerHeight) {
- settings.h = setSize(options.innerHeight, 'y');
- }
- if (!options.innerHeight && !options.height) {
- var $child = $loaded.wrapInner("<div style='overflow:auto'></div>").children(); // temporary wrapper to get an accurate estimate of just how high the total content should be.
- settings.h = $child.height();
- $child.replaceWith($child.children()); // ditch the temporary wrapper div used in height calculation
- }
- $loaded.css({height: settings.h});
-
- cboxPublic.position(settings.transition === "none" ? 0 : settings.speed);
- }
- };
-
- cboxPublic.prep = function (object) {
- if (!open) {
- return;
- }
-
- var photo,
- speed = settings.transition === "none" ? 0 : settings.speed;
-
- $window.unbind('resize.cbox');
-
- $loaded.remove();
- $loaded = $('<div id="cboxLoadedContent"/>').html(object);
-
- function getWidth() {
- settings.w = settings.w || $loaded.width();
- settings.w = settings.mw && settings.mw < settings.w ? settings.mw : settings.w;
- return settings.w;
- }
- function getHeight() {
- settings.h = settings.h || $loaded.height();
- settings.h = settings.mh && settings.mh < settings.h ? settings.mh : settings.h;
- return settings.h;
- }
-
- $loaded.hide()
- .appendTo($loadingBay)// content has to be appended to the DOM for accurate size calculations.
- .css({width: getWidth(), overflow: settings.scrolling ? 'auto' : 'hidden'})
- .css({height: getHeight()})// sets the height independently from the width in case the new width influences the value of height.
- .prependTo($content);
-
- $('#cboxPhoto').css({cssFloat: 'none'});// floating the IMG removes the bottom line-height and fixed a problem where IE miscalculates the width of the parent element as 100% of the document width.
-
- // Hides SELECT elements in IE6 because they would otherwise sit on top of the overlay.
- if (isIE6) {
- $('select:not(#colorbox select)').filter(function () {
- return this.style.visibility !== 'hidden';
- }).css({'visibility': 'hidden'}).one(cbox_cleanup, function () {
- this.style.visibility = 'inherit';
- });
- }
-
- function setPosition(s) {
- var prev, prevSrc, next, nextSrc, total = $related.length, loop = settings.loop;
- cboxPublic.position(s, function () {
- function defilter() {
- if (isIE) {
- //IE adds a filter when ColorBox fades in and out that can cause problems if the loaded content contains transparent pngs.
- $cbox[0].style.removeAttribute("filter");
- }
- }
-
- if (!open) {
- return;
- }
-
- if (isIE) {
- //This fadeIn helps the bicubic resampling to kick-in.
- if (photo) {
- $loaded.fadeIn(100);
- }
- }
-
- //Waited until the iframe is added to the DOM & it is visible before setting the src.
- //This increases compatability with pages using DOM dependent JavaScript.
- if (settings.iframe) {
- $("<iframe frameborder=0" + (settings.scrolling ? "" : " scrolling='no'") + (isIE ? " allowtransparency='true'" : '') + "/>")
- .appendTo($loaded)
- .attr({src: settings.href, id: 'cboxIframe', name: new Date().getTime()});
- }
-
- $loaded.show();
-
- $title.show().html(settings.title);
-
- if (total > 1) { // handle grouping
- $current.html(settings.current.replace(/\{current\}/, index + 1).replace(/\{total\}/, total)).show();
-
- $next[(loop || index < total - 1) ? "show" : "hide"]().html(settings.next);
- $prev[(loop || index > 0) ? "show" : "hide"]().html(settings.previous);
-
- prev = index > 0 ? $related[index - 1] : $related[total - 1];
- next = index < total - 1 ? $related[index + 1] : $related[0];
-
- if (settings.slideshow) {
- $slideshow.show();
- if (index === total - 1 && !loop && $cbox.is('.cboxSlideshow_on')) {
- $slideshow.click();
- }
- }
-
- // Preloads images within a rel group
- if (settings.preloading) {
- nextSrc = $(next).data(colorbox).href || next.href;
- prevSrc = $(prev).data(colorbox).href || prev.href;
-
- if (isImage(nextSrc)) {
- $('<img/>')[0].src = nextSrc;
- }
-
- if (isImage(prevSrc)) {
- $('<img/>')[0].src = prevSrc;
- }
- }
- }
-
- $loadingOverlay.hide();
-
- if (settings.transition === 'fade') {
- $cbox.fadeTo(speed, 1, function () {
- defilter();
- });
- } else {
- defilter();
- }
-
- $window.bind('resize.cbox', function () {
- cboxPublic.position(0);
- });
-
- $.event.trigger(cbox_complete);
- if (settings.onComplete) {
- settings.onComplete.call(element);
- }
- });
- }
-
- if (settings.transition === 'fade') {
- $cbox.fadeTo(speed, 0, function () {
- setPosition(0);
- });
- } else {
- setPosition(speed);
- }
- };
-
- cboxPublic.load = function () {
- var href, img, setResize, prep = cboxPublic.prep;
-
- active = TRUE;
-
- element = $related[index];
-
- settings = $.extend({}, $(element).data(colorbox));
-
- //convert functions to static values
- process();
-
- $.event.trigger(cbox_load);
- if (settings.onLoad) {
- settings.onLoad.call(element);
- }
-
- // Evaluate the height based on the optional height and width settings.
- settings.h = settings.height ?
- setSize(settings.height, 'y') - loadedHeight - interfaceHeight :
- settings.innerHeight ?
- setSize(settings.innerHeight, 'y') :
- FALSE;
- settings.w = settings.width ?
- setSize(settings.width, 'x') - loadedWidth - interfaceWidth :
- settings.innerWidth ?
- setSize(settings.innerWidth, 'x') :
- FALSE;
-
- // Sets the minimum dimensions for use in image scaling
- settings.mw = settings.w;
- settings.mh = settings.h;
-
- // Re-evaluate the minimum width and height based on maxWidth and maxHeight values.
- // If the width or height exceed the maxWidth or maxHeight, use the maximum values instead.
- if (settings.maxWidth) {
- settings.mw = setSize(settings.maxWidth, 'x') - loadedWidth - interfaceWidth;
- settings.mw = settings.w && settings.w < settings.mw ? settings.w : settings.mw;
- }
- if (settings.maxHeight) {
- settings.mh = setSize(settings.maxHeight, 'y') - loadedHeight - interfaceHeight;
- settings.mh = settings.h && settings.h < settings.mh ? settings.h : settings.mh;
- }
-
- href = settings.href;
-
- $loadingOverlay.show();
-
- if (settings.inline) {
- // Inserts an empty placeholder where inline content is being pulled from.
- // An event is bound to put inline content back when ColorBox closes or loads new content.
- $('<div id="cboxInlineTemp"/>').hide().insertBefore($(href)[0]).bind(cbox_load + ' ' + cbox_cleanup, function () {
- $(this).replaceWith($loaded.children());
- });
- prep($(href));
- } else if (settings.iframe) {
- // IFrame element won't be added to the DOM until it is ready to be displayed,
- // to avoid problems with DOM-ready JS that might be trying to run in that iframe.
- prep(" ");
- } else if (settings.html) {
- prep(settings.html);
- } else if (isImage(href)) {
- img = new Image();
- img.onload = function () {
- var percent;
-
- img.onload = null;
- img.id = 'cboxPhoto';
- $(img).css({margin: 'auto', border: 'none', display: 'block', cssFloat: 'left'});
-
- if (settings.scalePhotos) {
- setResize = function () {
- img.height -= img.height * percent;
- img.width -= img.width * percent;
- };
- if (settings.mw && img.width > settings.mw) {
- percent = (img.width - settings.mw) / img.width;
- setResize();
- }
- if (settings.mh && img.height > settings.mh) {
- percent = (img.height - settings.mh) / img.height;
- setResize();
- }
- }
-
- if (settings.h) {
- img.style.marginTop = Math.max(settings.h - img.height, 0) / 2 + 'px';
- }
-
- prep(img);
-
- if ($related.length > 1) {
- $(img).css({cursor: 'pointer'}).click(cboxPublic.next);
- }
-
- if (isIE) {
- img.style.msInterpolationMode = 'bicubic';
- }
- };
- img.src = href;
- } else {
- $('<div>Request unsuccessful.</div>').appendTo($loadingBay).load(href, function (data, textStatus) {
- prep(this);
- });
- }
- };
-
- // Navigates to the next page/image in a set.
- cboxPublic.next = function () {
- if (!active) {
- index = index < $related.length - 1 ? index + 1 : 0;
- cboxPublic.load();
- }
- };
-
- cboxPublic.prev = function () {
- if (!active) {
- index = index > 0 ? index - 1 : $related.length - 1;
- cboxPublic.load();
- }
- };
-
- cboxPublic.slideshow = function () {
- var stop, timeOut, className = 'cboxSlideshow_';
-
- $slideshow.bind(cbox_closed, function () {
- $slideshow.unbind();
- clearTimeout(timeOut);
- $cbox.removeClass(className + "off " + className + "on");
- });
-
- function start() {
- $slideshow
- .text(settings.slideshowStop)
- .bind(cbox_complete, function () {
- timeOut = setTimeout(cboxPublic.next, settings.slideshowSpeed);
- })
- .bind(cbox_load, function () {
- clearTimeout(timeOut);
- }).one("click", function () {
- stop();
- });
- $cbox.removeClass(className + "off").addClass(className + "on");
- }
-
- stop = function () {
- clearTimeout(timeOut);
- $slideshow
- .text(settings.slideshowStart)
- .unbind(cbox_complete + ' ' + cbox_load)
- .one("click", function () {
- start();
- timeOut = setTimeout(cboxPublic.next, settings.slideshowSpeed);
- });
- $cbox.removeClass(className + "on").addClass(className + "off");
- };
-
- if (settings.slideshow && $related.length > 1) {
- if (settings.slideshowAuto) {
- start();
- } else {
- stop();
- }
- }
- };
-
- // Note: to use this within an iframe use the following format: parent.$.fn.colorbox.close();
- cboxPublic.close = function () {
- if (open) {
- open = FALSE;
-
- $.event.trigger(cbox_cleanup);
-
- if (settings.onCleanup) {
- settings.onCleanup.call(element);
- }
-
- $window.unbind('.cbox .cboxIE6');
-
- $cbox.add($overlay)
- .stop()
- .fadeTo('fast', 0, function () {
- $('#colorbox iframe').attr('src', 'about:blank'); // change the location of the iframe to avoid a problem in IE with flash objects not clearing.
-
- $loaded.remove();
-
- $cbox.add($overlay).css({'opacity': 1, cursor: 'auto'}).hide();
-
- try {
- bookmark.focus();
- } catch (e) {
- // do nothing
- }
-
- setTimeout(function () {
- $.event.trigger(cbox_closed);
- if (settings.onClosed) {
- settings.onClosed.call(element);
- }
- }, 1);
- });
- }
- };
-
- // A method for fetching the current element ColorBox is referencing.
- // returns a jQuery object.
- cboxPublic.element = function () {
- return $(element);
- };
-
- cboxPublic.settings = defaults;
-
- // Initializes ColorBox when the DOM has loaded
- $(cboxPublic.init);
-
-}(jQuery));
--- a/review/static/comments.js Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-_.templateSettings = {
- start : '{{',
- end : '}}',
- interpolate : /\{\{(.+?)\}\}/g
-};
-
-var comment_form = _.template(
- '<tr class="comment-form">' +
- '<td colspan="3">' +
- '<form id="id_comment-line-form_{{ currNum }}" method="POST" action="">' +
- '<span class="lastlinenumber disabled">{{ currNum }}</span>' +
- '<div class="field">' +
- '<label class="infield" for="id_comment-line-form_{{ currNum }}_body">Comment</label>' +
- '<textarea id="id_comment-line-form_{{ currNum }}_body" class="body"' +
- 'name="new-comment-body">{{ body }}</textarea>' +
- '</div>' +
-
- '<div class="field cuddly">' +
- '<input type="checkbox" class="checkbox markdown-select" name="comment-markdown" id="id_comment-line-form_{{ currNum }}_markdown" {{ markdown_checked }} />' +
- '<label for="id_comment-line-form_{{ currNum }}_markdown">Use Markdown to format this comment.</label>' +
- '</div>' +
-
- '<a class="submit button"><span>Post Comment</span></a>' +
- '<a class="cancel-line button"><span>Cancel</span></a>' +
-
- '<input type="hidden" name="filename-u" value="{{ filename_u }}" />' +
- '<input type="hidden" name="filename-b64" value="{{ filename_b64 }}" />' +
- '<input type="hidden" name="current" value="{{ identifier }}" class="current" />' +
- '<input class="lines" type="hidden" name="lines" value="{{ currNum }}" />' +
- '</form>' +
- '</td>' +
- '</tr>'
-);
-
-function RenderLineCommentForm(line, currNum, body, markdown, identifier) {
- var filename_u = line.closest(".file").find(".filename .filename-u").text();
- var filename_b64 = line.closest(".file").find(".filename .filename-b64").text();
- markdown_checked = markdown ? 'checked="checked"' : '';
- return comment_form({ filename_u: filename_u, filename_b64: filename_b64,
- currNum: currNum, body: body, identifier: identifier,
- markdown_checked: markdown_checked });
-}
-
-$(function() {
- $(".activate a").click(function(event) {
- $(event.target).closest(".activate").hide();
- $(event.target).closest("div").children("form").fadeIn("fast");
- return false;
- });
- $(".activate-edit a").click(function(event) {
- $(event.target).closest(".activate-edit").hide();
- $(event.target).closest(".comment").find(".message").hide();
- $(event.target).closest("div").children("form").fadeIn("fast");
- return false;
- });
- $("a.cancel").click(function(event) {
- $(event.target).closest(".togglebox").children(".activate").show();
- $(event.target).parents("form").hide();
- return false;
- });
- $("a.cancel-edit").click(function(event) {
- $(event.target).closest(".toggleinline").children(".activate-edit").show();
- $(event.target).closest(".comment").find(".message").show();
- $(event.target).parents("form").hide();
- return false;
- });
- $("a.cancel-line").live('click', function(event) {
- $(event.target).closest(".diff").find(".chosen").removeClass("chosen");
- $(event.target).closest("tr.comment-form").hide();
- $(event.target).closest(".diff").find("tr.comment").fadeIn('fast');
- $(event.target).closest("tr.comment-form").remove();
- return false;
- });
- $("a.edit-line").click(function(event) {
- var diff = $(this).closest(".diff");
- var comment = $(this).closest("tr.comment");
-
- diff.find(".chosen").removeClass("chosen");
- diff.find(".comment-form").remove();
- comment.hide();
-
- _.each($(event.target).closest(".comment").find(".commentlines").html().split(","),
- function(i) { diff.find(".line-" + i).addClass("chosen") });
-
- var lines_chosen = diff.find(".chosen");
- var last_line = lines_chosen.last();
- var last_line_number = parseInt(lines_chosen.find(".linenumber").last().html());
- var body = comment.find(".raw").html();
- var markdown = comment.find(".message").hasClass("markdown");
- var identifier = comment.find(".identifier").html();
-
- var comment_form = RenderLineCommentForm(last_line, last_line_number, body, markdown, identifier);
- last_line.after(comment_form);
- diff.find("label").inFieldLabels();
-
- return false;
- });
-
- $("tr.comment").hover(function(event) {
- var diff = $(event.target).closest(".diff");
-
- _.each($(event.target).find(".commentlines").html().split(","),
- function (i) { diff.find(".line-" + i).addClass("viewing"); });
- }, function(event) {
- $(".viewing").removeClass("viewing");
- });
-
- var lastSelected = null;
- $(".commentable").click(function(event) {
- var currNum = parseInt($(this).find(".linenumber").html());
- var diff = $(this).closest(".diff");
-
- if ($(this).hasClass("chosen")) {
- $(this).removeClass("chosen");
-
- var newLines = _.reduce(diff.find(".chosen .linenumber"), "",
- function(memo, lne) { return memo + $(lne).html() + ','; });
- diff.find(".comment-form form .lines").val(newLines);
-
- lastSelected = null;
- } else {
- if (event.shiftKey && lastSelected) {
- if (lastSelected && jQuery.contains(diff.get(0), lastSelected.get(0))) {
- var lastNum = parseInt(lastSelected.find(".linenumber").html());
- _.each(_.range(currNum, lastNum, lastNum > currNum ? 1 : -1),
- function(i) { diff.find(".line-" + i).addClass("chosen"); });
- }
- } else {
- $(this).addClass("chosen");
- }
- }
-
- var lines_chosen = diff.find(".chosen");
- var last_line_number = parseInt(lines_chosen.find(".linenumber").last().html());
- var existing_forms = diff.find(".comment-form");
-
- if (existing_forms.length) {
- if (_.isNaN(last_line_number)) {
- existing_forms.remove();
- diff.find("tr.comment").fadeIn('fast');
- } else {
- var existing_form = existing_forms.last();
- var existing_form_line_number = parseInt(existing_form.find(".lastlinenumber").html());
- var existing_body = existing_form.find(".body").val();
- var existing_markdown = existing_form.find('.markdown-select').is(':checked');
- var existing_current = existing_form.find('.current').val();
-
- if (existing_form_line_number != last_line_number) {
- existing_forms.remove();
-
- var comment_form = RenderLineCommentForm($(this), last_line_number, existing_body,
- existing_markdown, existing_current);
- lines_chosen.last().after(comment_form);
- diff.find("label").inFieldLabels();
- }
-
- var newLines = _.reduce(diff.find(".chosen .linenumber"), "",
- function(memo, lne) { return memo + $(lne).html() + ','; });
- diff.find(".comment-form form .lines").val(newLines);
- }
- } else {
- var comment_form = RenderLineCommentForm($(this), last_line_number, '', true, '');
- $(this).after(comment_form);
- diff.find("label").inFieldLabels();
- }
-
- return false;
- });
-
- $("label.infield").inFieldLabels();
-});
--- a/review/static/extra.css Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-a.button span, a.button {
- background: -webkit-gradient(
- linear,
- left bottom,
- left top,
- color-stop(0.00, rgba(0, 0, 0, 0.15)),
- color-stop(1, rgba(0, 0, 0, 0.0))
- );
- background: -moz-linear-gradient(
- center bottom,
- rgba(0, 0, 0, 0.15) 0%,
- rgba(0, 0, 0, 0.0) 100%
- );
-}
-a.button:active span {
- background: -webkit-gradient(
- linear,
- left bottom,
- left top,
- color-stop(0.00, rgba(0, 0, 0, 0.1)),
- color-stop(1, rgba(0, 0, 0, 0.0))
- );
- background: -moz-linear-gradient(
- center bottom,
- rgba(0, 0, 0, 0.1) 0%,
- rgba(0, 0, 0, 0.0) 100%
- );
-}
-
-
-#index .content table tr:nth-child(even) td.node {
- background: -webkit-gradient(
- linear,
- left 50%,
- right 50%,
- color-stop(0.0, rgba(250, 250, 250, 1)),
- color-stop(0.35, rgba(250, 250, 250, 0.0))
- );
- background: -moz-linear-gradient(
- center left,
- rgba(250, 250, 250, 1) 0%,
- rgba(250, 250, 250, 0) 35%
- );
-}
-
-
-#index .content table tr:nth-child(even) td.stats {
- background: -webkit-gradient(
- linear,
- right 50%,
- left 50%,
- color-stop(0.0, rgba(250, 250, 250, 1)),
- color-stop(0.9, rgba(250, 250, 250, 0.0))
- );
- background: -moz-linear-gradient(
- center right,
- rgba(250, 250, 250, 1) 0%,
- rgba(250, 250, 250, 0) 35%
- );
-}
-
-#changeset .content .togglebox form {
- background: -webkit-gradient(
- linear,
- left bottom,
- left top,
- color-stop(0.0, rgb(234, 233, 223)),
- color-stop(0.3, rgb(250, 249, 239)),
- color-stop(0.95, rgb(250, 249, 239)),
- color-stop(1, rgb(255, 254, 243))
- );
-}
-
Binary file review/static/images/bg.png has changed
--- a/review/static/jquery-1.4.2.min.js Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.4.2
- * http://jquery.com/
- *
- * Copyright 2010, John Resig
- * Dual licensed under the MIT or GPL Version 2 licenses.
- * http://jquery.org/license
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- * Copyright 2010, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- *
- * Date: Sat Feb 13 22:33:48 2010 -0500
- */
-(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
-e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
-j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
-"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
-true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
-Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
-(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
-a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
-"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
-function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
-c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
-L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
-"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
-a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
-d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
-a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
-!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
-true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
-var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
-parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
-false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
-s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
-applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
-else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
-a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
-w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
-cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
-i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
-" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
-this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
-e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
-c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
-a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
-function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
-k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
-C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
-null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
-e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
-f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
-if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
-fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
-d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
-"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
-a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
-isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
-{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
-if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
-e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
-"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
-d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
-!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
-toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
-u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
-function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
-if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
-e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
-t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
-g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
-for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
-1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
-CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
-relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
-l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
-h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
-CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
-g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
-text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
-setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
-h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
-m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
-"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
-h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
-!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
-h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
-q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
-if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
-(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
-function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
-gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
-c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
-{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
-"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
-d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
-a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
-1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
-a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
-c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
-wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
-prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
-this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
-return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
-""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
-this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
-u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
-1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
-return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
-""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
-c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
-c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
-function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
-Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
-"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
-a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
-a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
-"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
-serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
-function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
-global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
-e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
-"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
-false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
-false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
-c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
-d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
-g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
-1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
-"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
-if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
-this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
-"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
-animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
-j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
-this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
-"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
-c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
-this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
-this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
-e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
-c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
-function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
-this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
-k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
-f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
-a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
-c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
-d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
-f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
-"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
-e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
\ No newline at end of file
--- a/review/static/jquery.infieldlabel.min.js Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-/*
- * In-Field Label jQuery Plugin
- * http://fuelyourcoding.com/scripts/infield.html
- *
- * Copyright (c) 2009 Doug Neiner
- * Dual licensed under the MIT and GPL licenses.
- * Uses the same license as jQuery, see:
- * http://docs.jquery.com/License
- *
- * @version 0.1
- */
-(function($){$.InFieldLabels=function(b,c,d){var f=this;f.$label=$(b);f.label=b;f.$field=$(c);f.field=c;f.$label.data("InFieldLabels",f);f.showing=true;f.init=function(){f.options=$.extend({},$.InFieldLabels.defaultOptions,d);if(f.$field.val()!=""){f.$label.hide();f.showing=false};f.$field.focus(function(){f.fadeOnFocus()}).blur(function(){f.checkForEmpty(true)}).bind('keydown.infieldlabel',function(e){f.hideOnChange(e)}).change(function(e){f.checkForEmpty()}).bind('onPropertyChange',function(){f.checkForEmpty()})};f.fadeOnFocus=function(){if(f.showing){f.setOpacity(f.options.fadeOpacity)}};f.setOpacity=function(a){f.$label.stop().animate({opacity:a},f.options.fadeDuration);f.showing=(a>0.0)};f.checkForEmpty=function(a){if(f.$field.val()==""){f.prepForShow();f.setOpacity(a?1.0:f.options.fadeOpacity)}else{f.setOpacity(0.0)}};f.prepForShow=function(e){if(!f.showing){f.$label.css({opacity:0.0}).show();f.$field.bind('keydown.infieldlabel',function(e){f.hideOnChange(e)})}};f.hideOnChange=function(e){if((e.keyCode==16)||(e.keyCode==9))return;if(f.showing){f.$label.hide();f.showing=false};f.$field.unbind('keydown.infieldlabel')};f.init()};$.InFieldLabels.defaultOptions={fadeOpacity:0.5,fadeDuration:300};$.fn.inFieldLabels=function(c){return this.each(function(){var a=$(this).attr('for');if(!a)return;var b=$("input#"+a+"[type='text'],"+"input#"+a+"[type='password'],"+"textarea#"+a);if(b.length==0)return;(new $.InFieldLabels(this,b[0],c))})}})(jQuery);
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/colorbox/colorbox.css Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,62 @@
+/*
+ ColorBox Core Style
+ The following rules are the styles that are consistant between themes.
+ Avoid changing this area to maintain compatability with future versions of ColorBox.
+*/
+#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;}
+#cboxOverlay{position:fixed; width:100%; height:100%;}
+#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
+#cboxContent{position:relative; overflow:hidden;}
+#cboxLoadedContent{overflow:auto;}
+#cboxLoadedContent iframe{display:block; width:100%; height:100%; border:0;}
+#cboxTitle{margin:0;}
+#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%;}
+#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
+
+/*
+ Example user style
+ The following rules are ordered and tabbed in a way that represents the
+ order/nesting of the generated HTML, so that the structure easier to understand.
+*/
+#cboxOverlay{background:url(images/overlay.png) 0 0 repeat;}
+#colorbox{}
+ #cboxTopLeft{width:21px; height:21px; background:url(images/controls.png) -100px 0 no-repeat;}
+ #cboxTopRight{width:21px; height:21px; background:url(images/controls.png) -129px 0 no-repeat;}
+ #cboxBottomLeft{width:21px; height:21px; background:url(images/controls.png) -100px -29px no-repeat;}
+ #cboxBottomRight{width:21px; height:21px; background:url(images/controls.png) -129px -29px no-repeat;}
+ #cboxMiddleLeft{width:21px; background:url(images/controls.png) left top repeat-y;}
+ #cboxMiddleRight{width:21px; background:url(images/controls.png) right top repeat-y;}
+ #cboxTopCenter{height:21px; background:url(images/border.png) 0 0 repeat-x;}
+ #cboxBottomCenter{height:21px; background:url(images/border.png) 0 -29px repeat-x;}
+ #cboxContent{background:#fff;}
+ #cboxLoadedContent{margin-bottom:28px;}
+ #cboxTitle{position:absolute; bottom:4px; left:0; text-align:center; width:100%; color:#949494;}
+ #cboxCurrent{position:absolute; bottom:4px; left:58px; color:#949494;}
+ #cboxSlideshow{position:absolute; bottom:4px; right:30px; color:#0092ef;}
+ #cboxPrevious{position:absolute; bottom:0; left:0px; background:url(images/controls.png) -75px 0px no-repeat; width:25px; height:25px; text-indent:-9999px;}
+ #cboxPrevious.hover{background-position:-75px -25px;}
+ #cboxNext{position:absolute; bottom:0; left:27px; background:url(images/controls.png) -50px 0px no-repeat; width:25px; height:25px; text-indent:-9999px;}
+ #cboxNext.hover{background-position:-50px -25px;}
+ #cboxLoadingOverlay{background:url(images/loading_background.png) center center no-repeat;}
+ #cboxLoadingGraphic{background:url(images/loading.gif) center center no-repeat;}
+ #cboxClose{position:absolute; bottom:0; right:0; background:url(images/controls.png) -25px 0px no-repeat; width:25px; height:25px; text-indent:-9999px;}
+ #cboxClose.hover{background-position:-25px -25px;}
+
+/*
+ The following fixes png-transparency for IE6.
+ It is also necessary for png-transparency in IE7 & IE8 to avoid 'black halos' with the fade transition
+
+ Since this method does not support CSS background-positioning, it is incompatible with CSS sprites.
+ Colorbox preloads navigation hover classes to account for this.
+
+ !! Important Note: AlphaImageLoader src paths are relative to the HTML document,
+ while regular CSS background images are relative to the CSS document.
+*/
+.cboxIE #cboxTopLeft{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderTopLeft.png, sizingMethod='scale');}
+.cboxIE #cboxTopCenter{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderTopCenter.png, sizingMethod='scale');}
+.cboxIE #cboxTopRight{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderTopRight.png, sizingMethod='scale');}
+.cboxIE #cboxBottomLeft{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderBottomLeft.png, sizingMethod='scale');}
+.cboxIE #cboxBottomCenter{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderBottomCenter.png, sizingMethod='scale');}
+.cboxIE #cboxBottomRight{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderBottomRight.png, sizingMethod='scale');}
+.cboxIE #cboxMiddleLeft{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderMiddleLeft.png, sizingMethod='scale');}
+.cboxIE #cboxMiddleRight{background:transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src=images/internet_explorer/borderMiddleRight.png, sizingMethod='scale');}
Binary file review/static/scripts/colorbox/images/border.png has changed
Binary file review/static/scripts/colorbox/images/controls.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderBottomCenter.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderBottomLeft.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderBottomRight.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderMiddleLeft.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderMiddleRight.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderTopCenter.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderTopLeft.png has changed
Binary file review/static/scripts/colorbox/images/internet_explorer/borderTopRight.png has changed
Binary file review/static/scripts/colorbox/images/loading.gif has changed
Binary file review/static/scripts/colorbox/images/loading_background.png has changed
Binary file review/static/scripts/colorbox/images/overlay.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/colorbox/jquery.colorbox.js Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,772 @@
+// ColorBox v1.3.7 - a full featured, light-weight, customizable lightbox based on jQuery 1.3
+// c) 2009 Jack Moore - www.colorpowered.com - jack@colorpowered.com
+// Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+/*jslint browser: true */
+
+(function ($) {
+ // Shortcuts (to increase compression)
+ var colorbox = 'colorbox',
+ hover = 'hover',
+ TRUE = true,
+ FALSE = false,
+ cboxPublic,
+ isIE = $.browser.msie && !$.support.opacity, // feature detection alone gave a false positive on at least one phone browser
+ isIE6 = isIE && $.browser.version < 7,
+
+ // Event Strings (to increase compression)
+ cbox_open = 'cbox_open',
+ cbox_load = 'cbox_load',
+ cbox_complete = 'cbox_complete',
+ cbox_cleanup = 'cbox_cleanup',
+ cbox_closed = 'cbox_closed',
+
+ // Cached jQuery Object Variables
+ $overlay,
+ $cbox,
+ $wrap,
+ $content,
+ $topBorder,
+ $leftBorder,
+ $rightBorder,
+ $bottomBorder,
+ $related,
+ $window,
+ $loaded,
+ $loadingBay,
+ $loadingOverlay,
+ $title,
+ $current,
+ $slideshow,
+ $next,
+ $prev,
+ $close,
+
+ // Variables for cached values or use across multiple functions
+ interfaceHeight,
+ interfaceWidth,
+ loadedHeight,
+ loadedWidth,
+ element,
+ bookmark,
+ index,
+ settings,
+ open,
+ active,
+
+ // ColorBox Default Settings.
+ // See http://colorpowered.com/colorbox for details.
+ defaults = {
+ transition: "elastic",
+ speed: 350,
+ width: FALSE,
+ height: FALSE,
+ innerWidth: FALSE,
+ innerHeight: FALSE,
+ initialWidth: "400",
+ initialHeight: "400",
+ maxWidth: FALSE,
+ maxHeight: FALSE,
+ scalePhotos: TRUE,
+ scrolling: TRUE,
+ inline: FALSE,
+ html: FALSE,
+ iframe: FALSE,
+ photo: FALSE,
+ href: FALSE,
+ title: FALSE,
+ rel: FALSE,
+ opacity: 0.9,
+ preloading: TRUE,
+ current: "{current} of {total}",
+ previous: "previous",
+ next: "next",
+ close: "close",
+ open: FALSE,
+ overlayClose: TRUE,
+ loop: TRUE,
+
+ slideshow: FALSE,
+ slideshowAuto: TRUE,
+ slideshowSpeed: 2500,
+ slideshowStart: "start slideshow",
+ slideshowStop: "stop slideshow",
+
+ onOpen: FALSE,
+ onLoad: FALSE,
+ onComplete: FALSE,
+ onCleanup: FALSE,
+ onClosed: FALSE,
+
+ escKey: TRUE,
+ arrowKey: TRUE
+ };
+
+ // ****************
+ // HELPER FUNCTIONS
+ // ****************
+
+ // Convert % values to pixels
+ function setSize(size, dimension) {
+ dimension = dimension === 'x' ? $window.width() : $window.height();//document.documentElement.clientWidth : document.documentElement.clientHeight;
+ return (typeof size === 'string') ? Math.round((size.match(/%/) ? (dimension / 100) * parseInt(size, 10) : parseInt(size, 10))) : size;
+ }
+
+ // Checks an href to see if it is a photo.
+ // There is a force photo option (photo: true) for hrefs that cannot be matched by this regex.
+ function isImage(url) {
+ url = $.isFunction(url) ? url.call(element) : url;
+ return settings.photo || url.match(/\.(gif|png|jpg|jpeg|bmp)(?:\?([^#]*))?(?:#(\.*))?$/i);
+ }
+
+ // Assigns functions results to their respective settings. This allows functions to be used to set ColorBox options.
+ function process() {
+ for (var i in settings) {
+ if ($.isFunction(settings[i]) && i.substring(0, 2) !== 'on') { // checks to make sure the function isn't one of the callbacks, they will be handled at the appropriate time.
+ settings[i] = settings[i].call(element);
+ }
+ }
+ settings.rel = settings.rel || element.rel || 'nofollow';
+ settings.href = settings.href || $(element).attr('href');
+ settings.title = settings.title || element.title;
+ }
+
+ function launch(elem) {
+
+ element = elem;
+
+ settings = $.extend({}, $(element).data(colorbox));
+
+ process(); // Convert functions to their returned values.
+
+ if (settings.rel !== 'nofollow') {
+ $related = $('.cboxElement').filter(function () {
+ var relRelated = $(this).data(colorbox).rel || this.rel;
+ return (relRelated === settings.rel);
+ });
+ index = $related.index(element);
+
+ // Check direct calls to ColorBox.
+ if (index < 0) {
+ $related = $related.add(element);
+ index = $related.length - 1;
+ }
+ } else {
+ $related = $(element);
+ index = 0;
+ }
+
+ if (!open) {
+ open = TRUE;
+
+ active = TRUE; // Prevents the page-change action from queuing up if the visitor holds down the left or right keys.
+
+ bookmark = element;
+
+ try {
+ bookmark.blur(); // Remove the focus from the calling element.
+ }catch (e) {}
+
+ $.event.trigger(cbox_open);
+ if (settings.onOpen) {
+ settings.onOpen.call(element);
+ }
+
+ $overlay.css({"opacity": parseFloat(settings.opacity), "cursor": settings.overlayClose ? "pointer" : "auto"}).show();
+
+ // Opens inital empty ColorBox prior to content being loaded.
+ settings.w = setSize(settings.initialWidth, 'x');
+ settings.h = setSize(settings.initialHeight, 'y');
+ cboxPublic.position(0);
+
+ if (isIE6) {
+ $window.bind('resize.cboxIE6 scroll.cboxIE6', function () {
+ $overlay.css({width: $window.width(), height: $window.height(), top: $window.scrollTop(), left: $window.scrollLeft()});
+ }).trigger("scroll.cboxIE6");
+ }
+ }
+
+ $current.add($prev).add($next).add($slideshow).add($title).hide();
+
+ $close.html(settings.close).show();
+
+ cboxPublic.slideshow();
+
+ cboxPublic.load();
+ }
+
+ // ****************
+ // PUBLIC FUNCTIONS
+ // Usage format: $.fn.colorbox.close();
+ // Usage from within an iframe: parent.$.fn.colorbox.close();
+ // ****************
+
+ cboxPublic = $.fn.colorbox = $.colorbox = function (options, callback) {
+ var $this = this;
+
+ if ($this.selector && !$this.length) {
+ return $this;
+ }
+
+ options = options || {};
+
+ if (callback) {
+ options.onComplete = callback;
+ }
+
+ if (!$this.length || $this.selector === undefined) { // detects $.colorbox() and $.fn.colorbox()
+ $this = $('<a/>');
+ options.open = TRUE; // assume an immediate open
+ }
+
+ $this.each(function () {
+ $(this).data(colorbox, $.extend({}, $(this).data(colorbox) || defaults, options)).addClass("cboxElement");
+ });
+
+ if (options.open) {
+ launch($this[0]);
+ }
+
+ return $this;
+ };
+
+ // Initialize ColorBox: store common calculations, preload the interface graphics, append the html.
+ // This preps colorbox for a speedy open when clicked, and lightens the burdon on the browser by only
+ // having to run once, instead of each time colorbox is opened.
+ cboxPublic.init = function () {
+
+ // jQuery object generator to save a bit of space
+ function $div(id) {
+ return $('<div id="cbox' + id + '"/>');
+ }
+
+ // Create & Append jQuery Objects
+ $window = $(window);
+ $cbox = $('<div id="colorbox"/>');
+ $overlay = $div("Overlay").hide();
+ $wrap = $div("Wrapper");
+ $content = $div("Content").append(
+ $loaded = $div("LoadedContent").css({width: 0, height: 0}),
+ $loadingOverlay = $div("LoadingOverlay").add($div("LoadingGraphic")),
+ $title = $div("Title"),
+ $current = $div("Current"),
+ $next = $div("Next"),
+ $prev = $div("Previous"),
+ $slideshow = $div("Slideshow"),
+ $close = $div("Close")
+ );
+ $wrap.append( // The 3x3 Grid that makes up ColorBox
+ $('<div/>').append(
+ $div("TopLeft"),
+ $topBorder = $div("TopCenter"),
+ $div("TopRight")
+ ),
+ $('<div/>').append(
+ $leftBorder = $div("MiddleLeft"),
+ $content,
+ $rightBorder = $div("MiddleRight")
+ ),
+ $('<div/>').append(
+ $div("BottomLeft"),
+ $bottomBorder = $div("BottomCenter"),
+ $div("BottomRight")
+ )
+ ).children().children().css({'float': 'left'});
+
+ $loadingBay = $("<div id='cboxLoadingBay' style='position:absolute; width:9999px;'/>");
+
+ $('body').prepend($overlay, $cbox.append($wrap, $loadingBay));
+
+ if (isIE) {
+ $cbox.addClass('cboxIE');
+ if (isIE6) {
+ $overlay.css('position', 'absolute');
+ }
+ }
+
+ $content.children()
+ .hover(function () {
+ $(this).addClass(hover);
+ }, function () {
+ $(this).removeClass(hover);
+ }).addClass(hover);
+
+ // Cache values needed for size calculations
+ interfaceHeight = $topBorder.height() + $bottomBorder.height() + $content.outerHeight(TRUE) - $content.height();//Subtraction needed for IE6
+ interfaceWidth = $leftBorder.width() + $rightBorder.width() + $content.outerWidth(TRUE) - $content.width();
+ loadedHeight = $loaded.outerHeight(TRUE);
+ loadedWidth = $loaded.outerWidth(TRUE);
+
+ // Setting padding to remove the need to do size conversions during the animation step.
+ $cbox.css({"padding-bottom": interfaceHeight, "padding-right": interfaceWidth}).hide();
+
+ // Setup button events.
+ $next.click(cboxPublic.next);
+ $prev.click(cboxPublic.prev);
+ $close.click(cboxPublic.close);
+
+ // Adding the 'hover' class allowed the browser to load the hover-state
+ // background graphics. The class can now can be removed.
+ $content.children().removeClass(hover);
+
+ $('.cboxElement').live('click', function (e) {
+ // checks to see if it was a non-left mouse-click and for clicks modified with ctrl, shift, or alt.
+ if ((e.button !== 0 && typeof e.button !== 'undefined') || e.ctrlKey || e.shiftKey || e.altKey) {
+ return TRUE;
+ } else {
+ launch(this);
+ return FALSE;
+ }
+ });
+
+ $overlay.click(function () {
+ if (settings.overlayClose) {
+ cboxPublic.close();
+ }
+ });
+
+ // Set Navigation Key Bindings
+ $(document).bind("keydown", function (e) {
+ if (open && settings.escKey && e.keyCode === 27) {
+ e.preventDefault();
+ cboxPublic.close();
+ }
+ if (open && settings.arrowKey && !active && $related.length > 1) {
+ if (e.keyCode === 37 && (index > 0 || settings.loop)) {
+ e.preventDefault();
+ $prev.click();
+ } else if (e.keyCode === 39 && (index < $related.length - 1 || settings.loop)) {
+ e.preventDefault();
+ $next.click();
+ }
+ }
+ });
+ };
+
+ cboxPublic.remove = function () {
+ $cbox.add($overlay).remove();
+ $('.cboxElement').removeData(colorbox).removeClass('cboxElement');
+ };
+
+ cboxPublic.position = function (speed, loadedCallback) {
+ var
+ animate_speed,
+ // keeps the top and left positions within the browser's viewport.
+ posTop = Math.max($window.height() - settings.h - loadedHeight - interfaceHeight, 0) / 2 + $window.scrollTop(),
+ posLeft = Math.max($window.width() - settings.w - loadedWidth - interfaceWidth, 0) / 2 + $window.scrollLeft();
+
+ // setting the speed to 0 to reduce the delay between same-sized content.
+ animate_speed = ($cbox.width() === settings.w + loadedWidth && $cbox.height() === settings.h + loadedHeight) ? 0 : speed;
+
+ // this gives the wrapper plenty of breathing room so it's floated contents can move around smoothly,
+ // but it has to be shrank down around the size of div#colorbox when it's done. If not,
+ // it can invoke an obscure IE bug when using iframes.
+ $wrap[0].style.width = $wrap[0].style.height = "9999px";
+
+ function modalDimensions(that) {
+ // loading overlay height has to be explicitly set for IE6.
+ $topBorder[0].style.width = $bottomBorder[0].style.width = $content[0].style.width = that.style.width;
+ $loadingOverlay[0].style.height = $loadingOverlay[1].style.height = $content[0].style.height = $leftBorder[0].style.height = $rightBorder[0].style.height = that.style.height;
+ }
+
+ $cbox.dequeue().animate({width: settings.w + loadedWidth, height: settings.h + loadedHeight, top: posTop, left: posLeft}, {
+ duration: animate_speed,
+ complete: function () {
+ modalDimensions(this);
+
+ active = FALSE;
+
+ // shrink the wrapper down to exactly the size of colorbox to avoid a bug in IE's iframe implementation.
+ $wrap[0].style.width = (settings.w + loadedWidth + interfaceWidth) + "px";
+ $wrap[0].style.height = (settings.h + loadedHeight + interfaceHeight) + "px";
+
+ if (loadedCallback) {
+ loadedCallback();
+ }
+ },
+ step: function () {
+ modalDimensions(this);
+ }
+ });
+ };
+
+ cboxPublic.resize = function (options) {
+ if (open) {
+ options = options || {};
+
+ if (options.width) {
+ settings.w = setSize(options.width, 'x') - loadedWidth - interfaceWidth;
+ }
+ if (options.innerWidth) {
+ settings.w = setSize(options.innerWidth, 'x');
+ }
+ $loaded.css({width: settings.w});
+
+ if (options.height) {
+ settings.h = setSize(options.height, 'y') - loadedHeight - interfaceHeight;
+ }
+ if (options.innerHeight) {
+ settings.h = setSize(options.innerHeight, 'y');
+ }
+ if (!options.innerHeight && !options.height) {
+ var $child = $loaded.wrapInner("<div style='overflow:auto'></div>").children(); // temporary wrapper to get an accurate estimate of just how high the total content should be.
+ settings.h = $child.height();
+ $child.replaceWith($child.children()); // ditch the temporary wrapper div used in height calculation
+ }
+ $loaded.css({height: settings.h});
+
+ cboxPublic.position(settings.transition === "none" ? 0 : settings.speed);
+ }
+ };
+
+ cboxPublic.prep = function (object) {
+ if (!open) {
+ return;
+ }
+
+ var photo,
+ speed = settings.transition === "none" ? 0 : settings.speed;
+
+ $window.unbind('resize.cbox');
+
+ $loaded.remove();
+ $loaded = $('<div id="cboxLoadedContent"/>').html(object);
+
+ function getWidth() {
+ settings.w = settings.w || $loaded.width();
+ settings.w = settings.mw && settings.mw < settings.w ? settings.mw : settings.w;
+ return settings.w;
+ }
+ function getHeight() {
+ settings.h = settings.h || $loaded.height();
+ settings.h = settings.mh && settings.mh < settings.h ? settings.mh : settings.h;
+ return settings.h;
+ }
+
+ $loaded.hide()
+ .appendTo($loadingBay)// content has to be appended to the DOM for accurate size calculations.
+ .css({width: getWidth(), overflow: settings.scrolling ? 'auto' : 'hidden'})
+ .css({height: getHeight()})// sets the height independently from the width in case the new width influences the value of height.
+ .prependTo($content);
+
+ $('#cboxPhoto').css({cssFloat: 'none'});// floating the IMG removes the bottom line-height and fixed a problem where IE miscalculates the width of the parent element as 100% of the document width.
+
+ // Hides SELECT elements in IE6 because they would otherwise sit on top of the overlay.
+ if (isIE6) {
+ $('select:not(#colorbox select)').filter(function () {
+ return this.style.visibility !== 'hidden';
+ }).css({'visibility': 'hidden'}).one(cbox_cleanup, function () {
+ this.style.visibility = 'inherit';
+ });
+ }
+
+ function setPosition(s) {
+ var prev, prevSrc, next, nextSrc, total = $related.length, loop = settings.loop;
+ cboxPublic.position(s, function () {
+ function defilter() {
+ if (isIE) {
+ //IE adds a filter when ColorBox fades in and out that can cause problems if the loaded content contains transparent pngs.
+ $cbox[0].style.removeAttribute("filter");
+ }
+ }
+
+ if (!open) {
+ return;
+ }
+
+ if (isIE) {
+ //This fadeIn helps the bicubic resampling to kick-in.
+ if (photo) {
+ $loaded.fadeIn(100);
+ }
+ }
+
+ //Waited until the iframe is added to the DOM & it is visible before setting the src.
+ //This increases compatability with pages using DOM dependent JavaScript.
+ if (settings.iframe) {
+ $("<iframe frameborder=0" + (settings.scrolling ? "" : " scrolling='no'") + (isIE ? " allowtransparency='true'" : '') + "/>")
+ .appendTo($loaded)
+ .attr({src: settings.href, id: 'cboxIframe', name: new Date().getTime()});
+ }
+
+ $loaded.show();
+
+ $title.show().html(settings.title);
+
+ if (total > 1) { // handle grouping
+ $current.html(settings.current.replace(/\{current\}/, index + 1).replace(/\{total\}/, total)).show();
+
+ $next[(loop || index < total - 1) ? "show" : "hide"]().html(settings.next);
+ $prev[(loop || index > 0) ? "show" : "hide"]().html(settings.previous);
+
+ prev = index > 0 ? $related[index - 1] : $related[total - 1];
+ next = index < total - 1 ? $related[index + 1] : $related[0];
+
+ if (settings.slideshow) {
+ $slideshow.show();
+ if (index === total - 1 && !loop && $cbox.is('.cboxSlideshow_on')) {
+ $slideshow.click();
+ }
+ }
+
+ // Preloads images within a rel group
+ if (settings.preloading) {
+ nextSrc = $(next).data(colorbox).href || next.href;
+ prevSrc = $(prev).data(colorbox).href || prev.href;
+
+ if (isImage(nextSrc)) {
+ $('<img/>')[0].src = nextSrc;
+ }
+
+ if (isImage(prevSrc)) {
+ $('<img/>')[0].src = prevSrc;
+ }
+ }
+ }
+
+ $loadingOverlay.hide();
+
+ if (settings.transition === 'fade') {
+ $cbox.fadeTo(speed, 1, function () {
+ defilter();
+ });
+ } else {
+ defilter();
+ }
+
+ $window.bind('resize.cbox', function () {
+ cboxPublic.position(0);
+ });
+
+ $.event.trigger(cbox_complete);
+ if (settings.onComplete) {
+ settings.onComplete.call(element);
+ }
+ });
+ }
+
+ if (settings.transition === 'fade') {
+ $cbox.fadeTo(speed, 0, function () {
+ setPosition(0);
+ });
+ } else {
+ setPosition(speed);
+ }
+ };
+
+ cboxPublic.load = function () {
+ var href, img, setResize, prep = cboxPublic.prep;
+
+ active = TRUE;
+
+ element = $related[index];
+
+ settings = $.extend({}, $(element).data(colorbox));
+
+ //convert functions to static values
+ process();
+
+ $.event.trigger(cbox_load);
+ if (settings.onLoad) {
+ settings.onLoad.call(element);
+ }
+
+ // Evaluate the height based on the optional height and width settings.
+ settings.h = settings.height ?
+ setSize(settings.height, 'y') - loadedHeight - interfaceHeight :
+ settings.innerHeight ?
+ setSize(settings.innerHeight, 'y') :
+ FALSE;
+ settings.w = settings.width ?
+ setSize(settings.width, 'x') - loadedWidth - interfaceWidth :
+ settings.innerWidth ?
+ setSize(settings.innerWidth, 'x') :
+ FALSE;
+
+ // Sets the minimum dimensions for use in image scaling
+ settings.mw = settings.w;
+ settings.mh = settings.h;
+
+ // Re-evaluate the minimum width and height based on maxWidth and maxHeight values.
+ // If the width or height exceed the maxWidth or maxHeight, use the maximum values instead.
+ if (settings.maxWidth) {
+ settings.mw = setSize(settings.maxWidth, 'x') - loadedWidth - interfaceWidth;
+ settings.mw = settings.w && settings.w < settings.mw ? settings.w : settings.mw;
+ }
+ if (settings.maxHeight) {
+ settings.mh = setSize(settings.maxHeight, 'y') - loadedHeight - interfaceHeight;
+ settings.mh = settings.h && settings.h < settings.mh ? settings.h : settings.mh;
+ }
+
+ href = settings.href;
+
+ $loadingOverlay.show();
+
+ if (settings.inline) {
+ // Inserts an empty placeholder where inline content is being pulled from.
+ // An event is bound to put inline content back when ColorBox closes or loads new content.
+ $('<div id="cboxInlineTemp"/>').hide().insertBefore($(href)[0]).bind(cbox_load + ' ' + cbox_cleanup, function () {
+ $(this).replaceWith($loaded.children());
+ });
+ prep($(href));
+ } else if (settings.iframe) {
+ // IFrame element won't be added to the DOM until it is ready to be displayed,
+ // to avoid problems with DOM-ready JS that might be trying to run in that iframe.
+ prep(" ");
+ } else if (settings.html) {
+ prep(settings.html);
+ } else if (isImage(href)) {
+ img = new Image();
+ img.onload = function () {
+ var percent;
+
+ img.onload = null;
+ img.id = 'cboxPhoto';
+ $(img).css({margin: 'auto', border: 'none', display: 'block', cssFloat: 'left'});
+
+ if (settings.scalePhotos) {
+ setResize = function () {
+ img.height -= img.height * percent;
+ img.width -= img.width * percent;
+ };
+ if (settings.mw && img.width > settings.mw) {
+ percent = (img.width - settings.mw) / img.width;
+ setResize();
+ }
+ if (settings.mh && img.height > settings.mh) {
+ percent = (img.height - settings.mh) / img.height;
+ setResize();
+ }
+ }
+
+ if (settings.h) {
+ img.style.marginTop = Math.max(settings.h - img.height, 0) / 2 + 'px';
+ }
+
+ prep(img);
+
+ if ($related.length > 1) {
+ $(img).css({cursor: 'pointer'}).click(cboxPublic.next);
+ }
+
+ if (isIE) {
+ img.style.msInterpolationMode = 'bicubic';
+ }
+ };
+ img.src = href;
+ } else {
+ $('<div>Request unsuccessful.</div>').appendTo($loadingBay).load(href, function (data, textStatus) {
+ prep(this);
+ });
+ }
+ };
+
+ // Navigates to the next page/image in a set.
+ cboxPublic.next = function () {
+ if (!active) {
+ index = index < $related.length - 1 ? index + 1 : 0;
+ cboxPublic.load();
+ }
+ };
+
+ cboxPublic.prev = function () {
+ if (!active) {
+ index = index > 0 ? index - 1 : $related.length - 1;
+ cboxPublic.load();
+ }
+ };
+
+ cboxPublic.slideshow = function () {
+ var stop, timeOut, className = 'cboxSlideshow_';
+
+ $slideshow.bind(cbox_closed, function () {
+ $slideshow.unbind();
+ clearTimeout(timeOut);
+ $cbox.removeClass(className + "off " + className + "on");
+ });
+
+ function start() {
+ $slideshow
+ .text(settings.slideshowStop)
+ .bind(cbox_complete, function () {
+ timeOut = setTimeout(cboxPublic.next, settings.slideshowSpeed);
+ })
+ .bind(cbox_load, function () {
+ clearTimeout(timeOut);
+ }).one("click", function () {
+ stop();
+ });
+ $cbox.removeClass(className + "off").addClass(className + "on");
+ }
+
+ stop = function () {
+ clearTimeout(timeOut);
+ $slideshow
+ .text(settings.slideshowStart)
+ .unbind(cbox_complete + ' ' + cbox_load)
+ .one("click", function () {
+ start();
+ timeOut = setTimeout(cboxPublic.next, settings.slideshowSpeed);
+ });
+ $cbox.removeClass(className + "on").addClass(className + "off");
+ };
+
+ if (settings.slideshow && $related.length > 1) {
+ if (settings.slideshowAuto) {
+ start();
+ } else {
+ stop();
+ }
+ }
+ };
+
+ // Note: to use this within an iframe use the following format: parent.$.fn.colorbox.close();
+ cboxPublic.close = function () {
+ if (open) {
+ open = FALSE;
+
+ $.event.trigger(cbox_cleanup);
+
+ if (settings.onCleanup) {
+ settings.onCleanup.call(element);
+ }
+
+ $window.unbind('.cbox .cboxIE6');
+
+ $cbox.add($overlay)
+ .stop()
+ .fadeTo('fast', 0, function () {
+ $('#colorbox iframe').attr('src', 'about:blank'); // change the location of the iframe to avoid a problem in IE with flash objects not clearing.
+
+ $loaded.remove();
+
+ $cbox.add($overlay).css({'opacity': 1, cursor: 'auto'}).hide();
+
+ try {
+ bookmark.focus();
+ } catch (e) {
+ // do nothing
+ }
+
+ setTimeout(function () {
+ $.event.trigger(cbox_closed);
+ if (settings.onClosed) {
+ settings.onClosed.call(element);
+ }
+ }, 1);
+ });
+ }
+ };
+
+ // A method for fetching the current element ColorBox is referencing.
+ // returns a jQuery object.
+ cboxPublic.element = function () {
+ return $(element);
+ };
+
+ cboxPublic.settings = defaults;
+
+ // Initializes ColorBox when the DOM has loaded
+ $(cboxPublic.init);
+
+}(jQuery));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/comments.js Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,171 @@
+_.templateSettings = {
+ start : '{{',
+ end : '}}',
+ interpolate : /\{\{(.+?)\}\}/g
+};
+
+var comment_form = _.template(
+ '<tr class="comment-form">' +
+ '<td colspan="3">' +
+ '<form id="id_comment-line-form_{{ currNum }}" method="POST" action="">' +
+ '<span class="lastlinenumber disabled">{{ currNum }}</span>' +
+ '<div class="field">' +
+ '<label class="infield" for="id_comment-line-form_{{ currNum }}_body">Comment</label>' +
+ '<textarea id="id_comment-line-form_{{ currNum }}_body" class="body"' +
+ 'name="new-comment-body">{{ body }}</textarea>' +
+ '</div>' +
+
+ '<div class="field cuddly">' +
+ '<input type="checkbox" class="checkbox markdown-select" name="comment-markdown" id="id_comment-line-form_{{ currNum }}_markdown" {{ markdown_checked }} />' +
+ '<label for="id_comment-line-form_{{ currNum }}_markdown">Use Markdown to format this comment.</label>' +
+ '</div>' +
+
+ '<a class="submit button"><span>Post Comment</span></a>' +
+ '<a class="cancel-line button"><span>Cancel</span></a>' +
+
+ '<input type="hidden" name="filename-u" value="{{ filename_u }}" />' +
+ '<input type="hidden" name="filename-b64" value="{{ filename_b64 }}" />' +
+ '<input type="hidden" name="current" value="{{ identifier }}" class="current" />' +
+ '<input class="lines" type="hidden" name="lines" value="{{ currNum }}" />' +
+ '</form>' +
+ '</td>' +
+ '</tr>'
+);
+
+function RenderLineCommentForm(line, currNum, body, markdown, identifier) {
+ var filename_u = line.closest(".file").find(".filename .filename-u").text();
+ var filename_b64 = line.closest(".file").find(".filename .filename-b64").text();
+ markdown_checked = markdown ? 'checked="checked"' : '';
+ return comment_form({ filename_u: filename_u, filename_b64: filename_b64,
+ currNum: currNum, body: body, identifier: identifier,
+ markdown_checked: markdown_checked });
+}
+
+$(function() {
+ $(".activate a").click(function(event) {
+ $(event.target).closest(".activate").hide();
+ $(event.target).closest("div").children("form").fadeIn("fast");
+ return false;
+ });
+ $(".activate-edit a").click(function(event) {
+ $(event.target).closest(".activate-edit").hide();
+ $(event.target).closest(".comment").find(".message").hide();
+ $(event.target).closest("div").children("form").fadeIn("fast");
+ return false;
+ });
+ $("a.cancel").click(function(event) {
+ $(event.target).closest(".togglebox").children(".activate").show();
+ $(event.target).parents("form").hide();
+ return false;
+ });
+ $("a.cancel-edit").click(function(event) {
+ $(event.target).closest(".toggleinline").children(".activate-edit").show();
+ $(event.target).closest(".comment").find(".message").show();
+ $(event.target).parents("form").hide();
+ return false;
+ });
+ $("a.cancel-line").live('click', function(event) {
+ $(event.target).closest(".diff").find(".chosen").removeClass("chosen");
+ $(event.target).closest("tr.comment-form").hide();
+ $(event.target).closest(".diff").find("tr.comment").fadeIn('fast');
+ $(event.target).closest("tr.comment-form").remove();
+ return false;
+ });
+ $("a.edit-line").click(function(event) {
+ var diff = $(this).closest(".diff");
+ var comment = $(this).closest("tr.comment");
+
+ diff.find(".chosen").removeClass("chosen");
+ diff.find(".comment-form").remove();
+ comment.hide();
+
+ _.each($(event.target).closest(".comment").find(".commentlines").html().split(","),
+ function(i) { diff.find(".line-" + i).addClass("chosen") });
+
+ var lines_chosen = diff.find(".chosen");
+ var last_line = lines_chosen.last();
+ var last_line_number = parseInt(lines_chosen.find(".linenumber").last().html());
+ var body = comment.find(".raw").html();
+ var markdown = comment.find(".message").hasClass("markdown");
+ var identifier = comment.find(".identifier").html();
+
+ var comment_form = RenderLineCommentForm(last_line, last_line_number, body, markdown, identifier);
+ last_line.after(comment_form);
+ diff.find("label").inFieldLabels();
+
+ return false;
+ });
+
+ $("tr.comment").hover(function(event) {
+ var diff = $(event.target).closest(".diff");
+
+ _.each($(event.target).find(".commentlines").html().split(","),
+ function (i) { diff.find(".line-" + i).addClass("viewing"); });
+ }, function(event) {
+ $(".viewing").removeClass("viewing");
+ });
+
+ var lastSelected = null;
+ $(".commentable").click(function(event) {
+ var currNum = parseInt($(this).find(".linenumber").html());
+ var diff = $(this).closest(".diff");
+
+ if ($(this).hasClass("chosen")) {
+ $(this).removeClass("chosen");
+
+ var newLines = _.reduce(diff.find(".chosen .linenumber"), "",
+ function(memo, lne) { return memo + $(lne).html() + ','; });
+ diff.find(".comment-form form .lines").val(newLines);
+
+ lastSelected = null;
+ } else {
+ if (event.shiftKey && lastSelected) {
+ if (lastSelected && jQuery.contains(diff.get(0), lastSelected.get(0))) {
+ var lastNum = parseInt(lastSelected.find(".linenumber").html());
+ _.each(_.range(currNum, lastNum, lastNum > currNum ? 1 : -1),
+ function(i) { diff.find(".line-" + i).addClass("chosen"); });
+ }
+ } else {
+ $(this).addClass("chosen");
+ }
+ }
+
+ var lines_chosen = diff.find(".chosen");
+ var last_line_number = parseInt(lines_chosen.find(".linenumber").last().html());
+ var existing_forms = diff.find(".comment-form");
+
+ if (existing_forms.length) {
+ if (_.isNaN(last_line_number)) {
+ existing_forms.remove();
+ diff.find("tr.comment").fadeIn('fast');
+ } else {
+ var existing_form = existing_forms.last();
+ var existing_form_line_number = parseInt(existing_form.find(".lastlinenumber").html());
+ var existing_body = existing_form.find(".body").val();
+ var existing_markdown = existing_form.find('.markdown-select').is(':checked');
+ var existing_current = existing_form.find('.current').val();
+
+ if (existing_form_line_number != last_line_number) {
+ existing_forms.remove();
+
+ var comment_form = RenderLineCommentForm($(this), last_line_number, existing_body,
+ existing_markdown, existing_current);
+ lines_chosen.last().after(comment_form);
+ diff.find("label").inFieldLabels();
+ }
+
+ var newLines = _.reduce(diff.find(".chosen .linenumber"), "",
+ function(memo, lne) { return memo + $(lne).html() + ','; });
+ diff.find(".comment-form form .lines").val(newLines);
+ }
+ } else {
+ var comment_form = RenderLineCommentForm($(this), last_line_number, '', true, '');
+ $(this).after(comment_form);
+ diff.find("label").inFieldLabels();
+ }
+
+ return false;
+ });
+
+ $("label.infield").inFieldLabels();
+});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/jquery.infieldlabels.js Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,12 @@
+/*
+ * In-Field Label jQuery Plugin
+ * http://fuelyourcoding.com/scripts/infield.html
+ *
+ * Copyright (c) 2009 Doug Neiner
+ * Dual licensed under the MIT and GPL licenses.
+ * Uses the same license as jQuery, see:
+ * http://docs.jquery.com/License
+ *
+ * @version 0.1
+ */
+(function($){$.InFieldLabels=function(b,c,d){var f=this;f.$label=$(b);f.label=b;f.$field=$(c);f.field=c;f.$label.data("InFieldLabels",f);f.showing=true;f.init=function(){f.options=$.extend({},$.InFieldLabels.defaultOptions,d);if(f.$field.val()!=""){f.$label.hide();f.showing=false};f.$field.focus(function(){f.fadeOnFocus()}).blur(function(){f.checkForEmpty(true)}).bind('keydown.infieldlabel',function(e){f.hideOnChange(e)}).change(function(e){f.checkForEmpty()}).bind('onPropertyChange',function(){f.checkForEmpty()})};f.fadeOnFocus=function(){if(f.showing){f.setOpacity(f.options.fadeOpacity)}};f.setOpacity=function(a){f.$label.stop().animate({opacity:a},f.options.fadeDuration);f.showing=(a>0.0)};f.checkForEmpty=function(a){if(f.$field.val()==""){f.prepForShow();f.setOpacity(a?1.0:f.options.fadeOpacity)}else{f.setOpacity(0.0)}};f.prepForShow=function(e){if(!f.showing){f.$label.css({opacity:0.0}).show();f.$field.bind('keydown.infieldlabel',function(e){f.hideOnChange(e)})}};f.hideOnChange=function(e){if((e.keyCode==16)||(e.keyCode==9))return;if(f.showing){f.$label.hide();f.showing=false};f.$field.unbind('keydown.infieldlabel')};f.init()};$.InFieldLabels.defaultOptions={fadeOpacity:0.5,fadeDuration:300};$.fn.inFieldLabels=function(c){return this.each(function(){var a=$(this).attr('for');if(!a)return;var b=$("input#"+a+"[type='text'],"+"input#"+a+"[type='password'],"+"textarea#"+a);if(b.length==0)return;(new $.InFieldLabels(this,b[0],c))})}})(jQuery);
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/jquery.js Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/less.js Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,70 @@
+//
+// LESS - Leaner CSS v1.0.41
+// http://lesscss.org
+//
+// Copyright (c) 2010, Alexis Sellier
+// Licensed under the Apache 2.0 License.
+//
+(function(z){function s(d){return z.less[d.split("/")[1]]}function U(){for(var d=document.getElementsByTagName("style"),b=0;b<d.length;b++)if(d[b].type.match(V))(new o.Parser).parse(d[b].innerHTML||"",function(a,g){d[b].type="text/css";d[b].innerHTML=g.toCSS()})}function W(d,b){for(var a=0;a<o.sheets.length;a++)X(o.sheets[a],d,b,o.sheets.length-(a+1))}function X(d,b,a,g){var e=z.location.href.replace(/[#?].*$/,""),h=d.href.replace(/\?.*$/,""),i=B&&B.getItem(h),k=B&&B.getItem(h+":timestamp"),n={css:i,
+timestamp:k};/^(https?|file):/.test(h)||(h=e.slice(0,e.lastIndexOf("/")+1)+h);Z(d.href,d.type,function(r,p){if(!a&&n&&p&&(new Date(p)).valueOf()===(new Date(n.timestamp)).valueOf()){N(n.css,d);b(null,d,{local:true,remaining:g})}else try{(new o.Parser({optimization:o.optimization,paths:[h.replace(/[\w\.-]+$/,"")],mime:d.type})).parse(r,function(K,O){if(K)return Q(K,h);try{b(O,d,{local:false,lastModified:p,remaining:g});var F=document.getElementById("less-error-message:"+R(h));F&&F.parentNode.removeChild(F)}catch(f){Q(f,
+h)}})}catch(t){Q(t,h)}},function(r,p){throw Error("Couldn't load "+p+" ("+r+")");})}function R(d){return d.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function N(d,b,a){var g,e=b.href?b.href.replace(/\?.*$/,""):"",h="less:"+(b.title||R(e));if((g=document.getElementById(h))===null){g=document.createElement("style");g.type="text/css";g.media=b.media||"screen";g.id=h;document.getElementsByTagName("head")[0].appendChild(g)}if(g.styleSheet)try{g.styleSheet.cssText=
+d}catch(i){throw Error("Couldn't reassign styleSheet.cssText.");}else(function(k){if(g.childNodes.length>0)g.firstChild.nodeValue!==k.nodeValue&&g.replaceChild(k,g.firstChild);else g.appendChild(k)})(document.createTextNode(d));if(a&&B){H("saving "+e+" to cache.");B.setItem(e,d);B.setItem(e+":timestamp",a)}}function Z(d,b,a,g){function e(k,n,r){if(k.status>=200&&k.status<300)n(k.responseText,k.getResponseHeader("Last-Modified"));else typeof r==="function"&&r(k.status,d)}var h=$(),i=P?false:o.async;
+typeof h.overrideMimeType==="function"&&h.overrideMimeType("text/css");h.open("GET",d,i);h.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5");h.send(null);if(P)h.status===0?a(h.responseText):g(h.status,d);else if(i)h.onreadystatechange=function(){h.readyState==4&&e(h,a,g)};else e(h,a,g)}function $(){if(z.XMLHttpRequest)return new XMLHttpRequest;else try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(d){H("browser doesn't support AJAX.");return null}}function H(d){o.env==
+"development"&&typeof console!=="undefined"&&console.log("less: "+d)}function Q(d,b){var a="less-error-message:"+R(b),g=document.createElement("div"),e,h;g.id=a;g.className="less-error-message";h="<h3>"+(d.message||"There is an error in your .less file")+'</h3><p><a href="'+b+'">'+b+"</a> ";if(d.extract)h+="on line "+d.line+", column "+(d.column+1)+":</p>"+'<ul>\n<li><label>[-1]</label><pre class="ctx">{0}</pre></li>\n<li><label>[0]</label><pre>{current}</pre></li>\n<li><label>[1]</label><pre class="ctx">{2}</pre></li>\n</ul>'.replace(/\[(-?\d)\]/g,
+function(i,k){return parseInt(d.line)+parseInt(k)||""}).replace(/\{(\d)\}/g,function(i,k){return d.extract[parseInt(k)]||""}).replace(/\{current\}/,d.extract[1].slice(0,d.column)+'<span class="error">'+d.extract[1].slice(d.column)+"</span>");g.innerHTML=h;N(".less-error-message ul, .less-error-message li {\nlist-style-type: none;\nmargin-right: 15px;\npadding: 4px 0;\nmargin: 0;\n}\n.less-error-message label {\nfont-size: 12px;\nmargin-right: 15px;\npadding: 4px 0;\ncolor: #cc7777;\n}\n.less-error-message pre {\ncolor: #ee4444;\npadding: 4px 0;\nmargin: 0;\ndisplay: inline-block;\n}\n.less-error-message pre.ctx {\ncolor: #dd4444;\n}\n.less-error-message h3 {\nfont-size: 20px;\nfont-weight: bold;\npadding: 15px 0 5px 0;\nmargin: 0;\n}\n.less-error-message a {\ncolor: #10a\n}\n.less-error-message .error {\ncolor: red;\nfont-weight: bold;\npadding-bottom: 2px;\nborder-bottom: 1px dashed red;\n}",
+{title:"error-message"});g.style.cssText="font-family: Arial, sans-serif;border: 1px solid #e00;background-color: #eee;border-radius: 5px;-webkit-border-radius: 5px;-moz-border-radius: 5px;color: #e00;padding: 15px;margin-bottom: 15px";if(o.env=="development")e=setInterval(function(){if(document.body){document.getElementById(a)?document.body.replaceChild(g,document.getElementById(a)):document.body.insertBefore(g,document.body.firstChild);clearInterval(e)}},10)}if(!Array.isArray)Array.isArray=function(d){return Object.prototype.toString.call(d)===
+"[object Array]"||d instanceof Array};if(!Array.prototype.forEach)Array.prototype.forEach=function(d,b){for(var a=this.length>>>0,g=0;g<a;g++)g in this&&d.call(b,this[g],g,this)};if(!Array.prototype.map)Array.prototype.map=function(d,b){for(var a=this.length>>>0,g=Array(a),e=0;e<a;e++)if(e in this)g[e]=d.call(b,this[e],e,this);return g};if(!Array.prototype.filter)Array.prototype.filter=function(d,b){for(var a=[],g=0;g<this.length;g++)d.call(b,this[g])&&a.push(this[g]);return a};if(!Array.prototype.reduce)Array.prototype.reduce=
+function(d){var b=this.length>>>0,a=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var g=arguments[1];else{do{if(a in this){g=this[a++];break}if(++a>=b)throw new TypeError;}while(1)}for(;a<b;a++)if(a in this)g=d.call(null,g,this[a],a,this);return g};if(!Array.prototype.indexOf)Array.prototype.indexOf=function(d,b){var a=this.length,g=b||0;if(!a)return-1;if(g>=a)return-1;if(g<0)g+=a;for(;g<a;g++)if(Object.prototype.hasOwnProperty.call(this,g))if(d===this[g])return g;return-1};
+if(!Object.keys)Object.keys=function(d){var b=[],a;for(a in d)Object.prototype.hasOwnProperty.call(d,a)&&b.push(a);return b};if(!String.prototype.trim)String.prototype.trim=function(){return String(this).replace(/^\s\s*/,"").replace(/\s\s*$/,"")};var o,m;if(typeof z==="undefined"){o=exports;m=s("less/tree")}else{if(typeof z.less==="undefined")z.less={};o=z.less;m=z.less.tree={}}o.Parser=function(d){function b(){if(h>t){p[i]=p[i].slice(h-t);t=h}}function a(f){var j,l,q;if(f instanceof Function)return f.call(K.parsers);
+else if(typeof f==="string"){f=e.charAt(h)===f?f:null;j=1;b()}else{b();if(f=f.exec(p[i]))j=f[0].length;else return null}if(f){mem=h+=j;for(q=h+p[i].length-j;h<q;){l=e.charCodeAt(h);if(!(l===32||l===10||l===9))break;h++}p[i]=p[i].slice(j+(h-mem));t=h;p[i].length===0&&i<p.length-1&&i++;return typeof f==="string"?f:f.length===1?f[0]:f}}function g(f){return typeof f==="string"?e.charAt(h)===f:f.test(p[i])?true:false}var e,h,i,k,n,r,p,t,K,O=function(){},F=this.imports={paths:d&&d.paths||[],queue:[],files:{},
+mime:d&&d.mime,push:function(f,j){var l=this;this.queue.push(f);o.Parser.importer(f,this.paths,function(q){l.queue.splice(l.queue.indexOf(f),1);l.files[f]=q;j(q);l.queue.length===0&&O()},d)}};this.env=d=d||{};this.optimization="optimization"in this.env?this.env.optimization:1;this.env.filename=this.env.filename||null;return K={imports:F,parse:function(f,j){var l,q,I,S=null;h=i=t=r=0;p=[];e=f.replace(/\r\n/g,"\n");p=function(L){for(var D=0,E=/[^"'`\{\}\/]+/g,G=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
+A=0,w,x=L[0],y,u=0,v;u<e.length;u++){E.lastIndex=u;if(w=E.exec(e))if(w.index===u){u+=w[0].length;x.push(w[0])}v=e.charAt(u);G.lastIndex=u;if(!y&&v==="/"){w=e.charAt(u+1);if(w==="/"||w==="*")if(w=G.exec(e))if(w.index===u){u+=w[0].length;x.push(w[0]);v=e.charAt(u)}}if(v==="{"&&!y){A++;x.push(v)}else if(v==="}"&&!y){A--;x.push(v);L[++D]=x=[]}else{if(v==='"'||v==="'"||v==="`")y=y?y===v?false:y:v;x.push(v)}}if(A>0)throw{type:"Syntax",message:"Missing closing `}`",filename:d.filename};return L.map(function(C){return C.join("")})}([[]]);
+l=new m.Ruleset([],a(this.parsers.primary));l.root=true;l.toCSS=function(L){var D,E;return function(G,A){function w(v){return v?(e.slice(0,v).match(/\n/g)||"").length:null}var x=[];G=G||{};if(typeof A==="object"&&!Array.isArray(A)){A=Object.keys(A).map(function(v){var C=A[v];if(!(C instanceof m.Value)){C instanceof m.Expression||(C=new m.Expression([C]));C=new m.Value([C])}return new m.Rule("@"+v,C,false,0)});x=[new m.Ruleset(null,A)]}try{var y=L.call(this,{frames:x}).toCSS([],{compress:G.compress||
+false})}catch(u){E=e.split("\n");D=w(u.index);x=u.index;for(y=-1;x>=0&&e.charAt(x)!=="\n";x--)y++;throw{type:u.type,message:u.message,filename:d.filename,index:u.index,line:typeof D==="number"?D+1:null,callLine:u.call&&w(u.call)+1,callExtract:E[w(u.call)],stack:u.stack,column:y,extract:[E[D-1],E[D],E[D+1]]};}return G.compress?y.replace(/(\s)+/g,"$1"):y}}(l.eval);if(h<e.length-1){h=r;I=e.split("\n");q=(e.slice(0,h).match(/\n/g)||"").length+1;for(var T=h,Y=-1;T>=0&&e.charAt(T)!=="\n";T--)Y++;S={name:"ParseError",
+message:"Syntax Error on line "+q,filename:d.filename,line:q,column:Y,extract:[I[q-2],I[q-1],I[q]]}}if(this.imports.queue.length>0)O=function(){j(S,l)};else j(S,l)},parsers:{primary:function(){for(var f,j=[];(f=a(this.mixin.definition)||a(this.rule)||a(this.ruleset)||a(this.mixin.call)||a(this.comment)||a(this.directive))||a(/^[\s\n]+/);)f&&j.push(f);return j},comment:function(){var f;if(e.charAt(h)==="/")if(e.charAt(h+1)==="/")return new m.Comment(a(/^\/\/.*/),true);else if(f=a(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new m.Comment(f)},
+entities:{quoted:function(){var f;if(!(e.charAt(h)!=='"'&&e.charAt(h)!=="'"))if(f=a(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new m.Quoted(f[0],f[1]||f[2])},keyword:function(){var f;if(f=a(/^[A-Za-z-]+/))return new m.Keyword(f)},call:function(){var f,j;if(f=/^([\w-]+|%)\(/.exec(p[i])){f=f[1].toLowerCase();if(f==="url")return null;else h+=f.length+1;if(f==="alpha")return a(this.alpha);j=a(this.entities.arguments);if(a(")"))if(f)return new m.Call(f,j)}},arguments:function(){for(var f=
+[],j;j=a(this.expression);){f.push(j);if(!a(","))break}return f},literal:function(){return a(this.entities.dimension)||a(this.entities.color)||a(this.entities.quoted)},url:function(){var f;if(!(e.charAt(h)!=="u"||!a(/^url\(/))){f=a(this.entities.quoted)||a(this.entities.variable)||a(this.entities.dataURI)||a(/^[-\w%@$\/.&=:;#+?]+/)||"";if(!a(")"))throw Error("missing closing ) for url()");return new m.URL(f.value||f.data||f instanceof m.Variable?f:new m.Anonymous(f),F.paths)}},dataURI:function(){var f;
+if(a(/^data:/)){f={};f.mime=a(/^[^\/]+\/[^,;)]+/)||"";f.charset=a(/^;\s*charset=[^,;)]+/)||"";f.base64=a(/^;\s*base64/)||"";f.data=a(/^,\s*[^)]+/);if(f.data)return f}},variable:function(){var f,j=h;if(e.charAt(h)==="@"&&(f=a(/^@[\w-]+/)))return new m.Variable(f,j)},color:function(){var f;if(e.charAt(h)==="#"&&(f=a(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new m.Color(f[1])},dimension:function(){var f;f=e.charCodeAt(h);if(!(f>57||f<45||f===47))if(f=a(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new m.Dimension(f[1],
+f[2])},javascript:function(){var f;if(e.charAt(h)==="`")if(f=a(/^`([^`]*)`/))return new m.JavaScript(f[1],h)}},variable:function(){var f;if(e.charAt(h)==="@"&&(f=a(/^(@[\w-]+)\s*:/)))return f[1]},shorthand:function(){var f,j;if(g(/^[@\w.%-]+\/[@\w.-]+/))if((f=a(this.entity))&&a("/")&&(j=a(this.entity)))return new m.Shorthand(f,j)},mixin:{call:function(){var f=[],j,l,q,I=h;j=e.charAt(h);if(!(j!=="."&&j!=="#")){for(;j=a(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/);){f.push(new m.Element(l,
+j));l=a(">")}a("(")&&(q=a(this.entities.arguments))&&a(")");if(f.length>0&&(a(";")||g("}")))return new m.mixin.Call(f,q,I)}},definition:function(){var f,j=[],l,q;if(!(e.charAt(h)!=="."&&e.charAt(h)!=="#"||g(/^[^{]*(;|})/)))if(f=a(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){for(f=f[1];l=a(this.entities.variable)||a(this.entities.literal)||a(this.entities.keyword);){if(l instanceof m.Variable)if(a(":"))if(q=a(this.expression))j.push({name:l.name,value:q});else throw Error("Expected value");
+else j.push({name:l.name});else j.push({value:l});if(!a(","))break}if(!a(")"))throw Error("Expected )");if(l=a(this.block))return new m.mixin.Definition(f,j,l)}}},entity:function(){return a(this.entities.literal)||a(this.entities.variable)||a(this.entities.url)||a(this.entities.call)||a(this.entities.keyword)||a(this.entities.javascript)},end:function(){return a(";")||g("}")},alpha:function(){var f;if(a(/^opacity=/i))if(f=a(/^\d+/)||a(this.entities.variable)){if(!a(")"))throw Error("missing closing ) for alpha()");
+return new m.Alpha(f)}},element:function(){var f;c=a(this.combinator);if(f=a(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||a("*")||a(this.attribute)||a(/^\([^)@]+\)/))return new m.Element(c,f)},combinator:function(){var f=e.charAt(h);if(f===">"||f==="&"||f==="+"||f==="~"){for(h++;e.charAt(h)===" ";)h++;return new m.Combinator(f)}else if(f===":"&&e.charAt(h+1)===":"){for(h+=2;e.charAt(h)===" ";)h++;return new m.Combinator("::")}else return e.charAt(h-1)===" "?new m.Combinator(" "):
+new m.Combinator(null)},selector:function(){for(var f,j=[],l;f=a(this.element);){l=e.charAt(h);j.push(f);if(l==="{"||l==="}"||l===";"||l===",")break}if(j.length>0)return new m.Selector(j)},tag:function(){return a(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||a("*")},attribute:function(){var f="",j,l,q;if(a("[")){if(j=a(/^[a-zA-Z-]+/)||a(this.entities.quoted))f=(q=a(/^[|~*$^]?=/))&&(l=a(this.entities.quoted)||a(/^[\w-]+/))?[j,q,l.toCSS?l.toCSS():l].join(""):j;if(a("]"))if(f)return"["+f+"]"}},block:function(){var f;
+if(a("{")&&(f=a(this.primary))&&a("}"))return f},ruleset:function(){var f=[],j,l;k=p[i];t=n=h;if(j=/^([.#: \w-]+)[\s\n]*\{/.exec(p[i])){h+=j[0].length-1;f=[new m.Selector([new m.Element(null,j[1])])]}else{for(;j=a(this.selector);){f.push(j);if(!a(","))break}j&&a(this.comment)}if(f.length>0&&(l=a(this.block)))return new m.Ruleset(f,l);else{r=h;p[i]=k;t=h=n}},rule:function(){var f,j;f=e.charAt(h);var l;k=p[i];t=n=h;if(!(f==="."||f==="#"||f==="&"))if(f=a(this.variable)||a(this.property)){if(f.charAt(0)!=
+"@"&&(match=/^([^@+\/'"*`(;{}-]*);/.exec(p[i]))){h+=match[0].length-1;j=new m.Anonymous(match[1])}else j=f==="font"?a(this.font):a(this.value);l=a(this.important);if(j&&a(this.end))return new m.Rule(f,j,l,n);else{r=h;p[i]=k;t=h=n}}},"import":function(){var f;if(a(/^@import\s+/)&&(f=a(this.entities.quoted)||a(this.entities.url))&&a(";"))return new m.Import(f,F)},directive:function(){var f,j,l;if(e.charAt(h)==="@")if(j=a(this["import"]))return j;else if(f=a(/^@media|@page/)){l=(a(/^[^{]+/)||"").trim();
+if(j=a(this.block))return new m.Directive(f+" "+l,j)}else if(f=a(/^@[-a-z]+/))if(f==="@font-face"){if(j=a(this.block))return new m.Directive(f,j)}else if((j=a(this.entity))&&a(";"))return new m.Directive(f,j)},font:function(){for(var f=[],j=[],l;l=a(this.shorthand)||a(this.entity);)j.push(l);f.push(new m.Expression(j));if(a(","))for(;l=a(this.expression);){f.push(l);if(!a(","))break}return new m.Value(f)},value:function(){for(var f,j=[];f=a(this.expression);){j.push(f);if(!a(","))break}if(j.length>
+0)return new m.Value(j)},important:function(){if(e.charAt(h)==="!")return a(/^! *important/)},sub:function(){var f;if(a("(")&&(f=a(this.expression))&&a(")"))return f},multiplication:function(){var f,j,l,q;if(f=a(this.operand)){for(;(l=a("/")||a("*"))&&(j=a(this.operand));)q=new m.Operation(l,[q||f,j]);return q||f}},addition:function(){var f,j,l,q;if(f=a(this.multiplication)){for(;(l=a(/^[-+]\s+/)||e.charAt(h-1)!=" "&&(a("+")||a("-")))&&(j=a(this.multiplication));)q=new m.Operation(l,[q||f,j]);return q||
+f}},operand:function(){return a(this.sub)||a(this.entities.dimension)||a(this.entities.color)||a(this.entities.variable)||a(this.entities.call)},expression:function(){for(var f,j=[];f=a(this.addition)||a(this.entity);)j.push(f);if(j.length>0)return new m.Expression(j)},property:function(){var f;if(f=a(/^(\*?-?[-a-z_0-9]+)\s*:/))return f[1]}}}};if(typeof z!=="undefined")o.Parser.importer=function(d,b,a,g){if(d.charAt(0)!=="/"&&b.length>0)d=b[0]+d;X({href:d,title:d,type:g.mime},a,true)};(function(d){function b(e){return d.functions.hsla(e.h,
+e.s,e.l,e.a)}function a(e){if(e instanceof d.Dimension)return parseFloat(e.unit=="%"?e.value/100:e.value);else if(typeof e==="number")return e;else throw{error:"RuntimeError",message:"color functions take numbers as parameters"};}function g(e){return Math.min(1,Math.max(0,e))}d.functions={rgb:function(e,h,i){return this.rgba(e,h,i,1)},rgba:function(e,h,i,k){e=[e,h,i].map(function(n){return a(n)});k=a(k);return new d.Color(e,k)},hsl:function(e,h,i){return this.hsla(e,h,i,1)},hsla:function(e,h,i,k){function n(t){t=
+t<0?t+1:t>1?t-1:t;return t*6<1?p+(r-p)*t*6:t*2<1?r:t*3<2?p+(r-p)*(2/3-t)*6:p}e=a(e)%360/360;h=a(h);i=a(i);k=a(k);var r=i<=0.5?i*(h+1):i+h-i*h,p=i*2-r;return this.rgba(n(e+1/3)*255,n(e)*255,n(e-1/3)*255,k)},hue:function(e){return new d.Dimension(Math.round(e.toHSL().h))},saturation:function(e){return new d.Dimension(Math.round(e.toHSL().s*100),"%")},lightness:function(e){return new d.Dimension(Math.round(e.toHSL().l*100),"%")},alpha:function(e){return new d.Dimension(e.toHSL().a)},saturate:function(e,
+h){var i=e.toHSL();i.s+=h.value/100;i.s=g(i.s);return b(i)},desaturate:function(e,h){var i=e.toHSL();i.s-=h.value/100;i.s=g(i.s);return b(i)},lighten:function(e,h){var i=e.toHSL();i.l+=h.value/100;i.l=g(i.l);return b(i)},darken:function(e,h){var i=e.toHSL();i.l-=h.value/100;i.l=g(i.l);return b(i)},fadein:function(e,h){var i=e.toHSL();i.a+=h.value/100;i.a=g(i.a);return b(i)},fadeout:function(e,h){var i=e.toHSL();i.a-=h.value/100;i.a=g(i.a);return b(i)},spin:function(e,h){var i=e.toHSL(),k=(i.h+h.value)%
+360;i.h=k<0?360+k:k;return b(i)},mix:function(e,h,i){i=i.value/100;var k=i*2-1,n=e.toHSL().a-h.toHSL().a;k=((k*n==-1?k:(k+n)/(1+k*n))+1)/2;n=1-k;return new d.Color([e.rgb[0]*k+h.rgb[0]*n,e.rgb[1]*k+h.rgb[1]*n,e.rgb[2]*k+h.rgb[2]*n],e.alpha*i+h.alpha*(1-i))},greyscale:function(e){return this.desaturate(e,new d.Dimension(100))},e:function(e){return new d.Anonymous(e instanceof d.JavaScript?e.evaluated:e)},"%":function(e){for(var h=Array.prototype.slice.call(arguments,1),i=e.value,k=0;k<h.length;k++)i=
+i.replace(/%s/,h[k].value).replace(/%[da]/,h[k].toCSS());i=i.replace(/%%/g,"%");return new d.Quoted('"'+i+'"',i)}}})(s("less/tree"));(function(d){d.Alpha=function(b){this.value=b};d.Alpha.prototype={toCSS:function(){return"alpha(opacity="+(this.value.toCSS?this.value.toCSS():this.value)+")"},eval:function(){return this}}})(s("less/tree"));(function(d){d.Anonymous=function(b){this.value=b.value||b};d.Anonymous.prototype={toCSS:function(){return this.value},eval:function(){return this}}})(s("less/tree"));
+(function(d){d.Call=function(b,a){this.name=b;this.args=a};d.Call.prototype={eval:function(b){var a=this.args.map(function(g){return g.eval(b)});return this.name in d.functions?d.functions[this.name].apply(d.functions,a):new d.Anonymous(this.name+"("+a.map(function(g){return g.toCSS()}).join(", ")+")")},toCSS:function(b){return this.eval(b).toCSS()}}})(s("less/tree"));(function(d){d.Color=function(b,a){this.rgb=Array.isArray(b)?b:b.length==6?b.match(/.{2}/g).map(function(g){return parseInt(g,16)}):
+b.split("").map(function(g){return parseInt(g+g,16)});this.alpha=typeof a==="number"?a:1};d.Color.prototype={eval:function(){return this},toCSS:function(){return this.alpha<1?"rgba("+this.rgb.map(function(b){return Math.round(b)}).concat(this.alpha).join(", ")+")":"#"+this.rgb.map(function(b){b=Math.round(b);b=(b>255?255:b<0?0:b).toString(16);return b.length===1?"0"+b:b}).join("")},operate:function(b,a){var g=[];a instanceof d.Color||(a=a.toColor());for(var e=0;e<3;e++)g[e]=d.operate(b,this.rgb[e],
+a.rgb[e]);return new d.Color(g)},toHSL:function(){var b=this.rgb[0]/255,a=this.rgb[1]/255,g=this.rgb[2]/255,e=this.alpha,h=Math.max(b,a,g),i=Math.min(b,a,g),k,n=(h+i)/2,r=h-i;if(h===i)k=i=0;else{i=n>0.5?r/(2-h-i):r/(h+i);switch(h){case b:k=(a-g)/r+(a<g?6:0);break;case a:k=(g-b)/r+2;break;case g:k=(b-a)/r+4}k/=6}return{h:k*360,s:i,l:n,a:e}}}})(s("less/tree"));(function(d){d.Comment=function(b,a){this.value=b;this.silent=!!a};d.Comment.prototype={toCSS:function(b){return b.compress?"":this.value},eval:function(){return this}}})(s("less/tree"));
+(function(d){d.Dimension=function(b,a){this.value=parseFloat(b);this.unit=a||null};d.Dimension.prototype={eval:function(){return this},toColor:function(){return new d.Color([this.value,this.value,this.value])},toCSS:function(){return this.value+this.unit},operate:function(b,a){return new d.Dimension(d.operate(b,this.value,a.value),this.unit||a.unit)}}})(s("less/tree"));(function(d){d.Directive=function(b,a){this.name=b;if(Array.isArray(a))this.ruleset=new d.Ruleset([],a);else this.value=a};d.Directive.prototype=
+{toCSS:function(b,a){if(this.ruleset){this.ruleset.root=true;return this.name+(a.compress?"{":" {\n ")+this.ruleset.toCSS(b,a).trim().replace(/\n/g,"\n ")+(a.compress?"}":"\n}\n")}else return this.name+" "+this.value.toCSS()+";\n"},eval:function(b){b.frames.unshift(this);this.ruleset=this.ruleset&&this.ruleset.eval(b);b.frames.shift();return this},variable:function(b){return d.Ruleset.prototype.variable.call(this.ruleset,b)},find:function(){return d.Ruleset.prototype.find.apply(this.ruleset,arguments)},
+rulesets:function(){return d.Ruleset.prototype.rulesets.apply(this.ruleset)}}})(s("less/tree"));(function(d){d.Element=function(b,a){this.combinator=b instanceof d.Combinator?b:new d.Combinator(b);this.value=a.trim()};d.Element.prototype.toCSS=function(b){return this.combinator.toCSS(b||{})+this.value};d.Combinator=function(b){this.value=b===" "?" ":b?b.trim():""};d.Combinator.prototype.toCSS=function(b){return{"":""," ":" ","&":"",":":" :","::":"::","+":b.compress?"+":" + ","~":b.compress?"~":" ~ ",
+">":b.compress?">":" > "}[this.value]}})(s("less/tree"));(function(d){d.Expression=function(b){this.value=b};d.Expression.prototype={eval:function(b){return this.value.length>1?new d.Expression(this.value.map(function(a){return a.eval(b)})):this.value[0].eval(b)},toCSS:function(b){return this.value.map(function(a){return a.toCSS(b)}).join(" ")}}})(s("less/tree"));(function(d){d.Import=function(b,a){var g=this;this._path=b;this.path=b instanceof d.Quoted?/\.(le?|c)ss$/.test(b.value)?b.value:b.value+
+".less":b.value.value||b.value;(this.css=/css$/.test(this.path))||a.push(this.path,function(e){if(!e)throw Error("Error parsing "+g.path);g.root=e})};d.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var a;if(this.css)return this;else{a=new d.Ruleset(null,this.root.rules.slice(0));for(var g=0;g<a.rules.length;g++)a.rules[g]instanceof d.Import&&Array.prototype.splice.apply(a.rules,[g,1].concat(a.rules[g].eval(b)));return a.rules}}}})(s("less/tree"));
+(function(d){d.JavaScript=function(b,a){this.expression=b;this.index=a};d.JavaScript.prototype={toCSS:function(){return JSON.stringify(this.evaluated)},eval:function(b){var a=new Function("return ("+this.expression+")"),g={},e;for(e in b.frames[0].variables())g[e.slice(1)]={value:b.frames[0].variables()[e].value,toJS:function(){return this.value.eval(b).toCSS()}};try{this.evaluated=a.call(g)}catch(h){throw{message:"JavaScript evaluation error: '"+h.name+": "+h.message+"'",index:this.index};}return this}}})(s("less/tree"));
+(function(d){d.Keyword=function(b){this.value=b};d.Keyword.prototype={eval:function(){return this},toCSS:function(){return this.value}}})(s("less/tree"));(function(d){d.mixin={};d.mixin.Call=function(b,a,g){this.selector=new d.Selector(b);this.arguments=a;this.index=g};d.mixin.Call.prototype={eval:function(b){for(var a,g=[],e=false,h=0;h<b.frames.length;h++)if((a=b.frames[h].find(this.selector)).length>0){for(h=0;h<a.length;h++)if(a[h].match(this.arguments,b))try{Array.prototype.push.apply(g,a[h].eval(b,
+this.arguments).rules);e=true}catch(i){throw{message:i.message,index:i.index,stack:i.stack,call:this.index};}if(e)return g;else throw{message:"No matching definition was found for `"+this.selector.toCSS().trim()+"("+this.arguments.map(function(k){return k.toCSS()}).join(", ")+")`",index:this.index};}throw{message:this.selector.toCSS().trim()+" is undefined",index:this.index};}};d.mixin.Definition=function(b,a,g){this.name=b;this.selectors=[new d.Selector([new d.Element(null,b)])];this.params=a;this.arity=
+a.length;this.rules=g;this._lookups={};this.required=a.reduce(function(e,h){return!h.name||h.name&&!h.value?e+1:e},0);this.parent=d.Ruleset.prototype;this.frames=[]};d.mixin.Definition.prototype={toCSS:function(){return""},variable:function(b){return this.parent.variable.call(this,b)},variables:function(){return this.parent.variables.call(this)},find:function(){return this.parent.find.apply(this,arguments)},rulesets:function(){return this.parent.rulesets.apply(this)},eval:function(b,a){for(var g=
+new d.Ruleset(null,[]),e=0,h;e<this.params.length;e++)if(this.params[e].name)if(h=a&&a[e]||this.params[e].value)g.rules.unshift(new d.Rule(this.params[e].name,h.eval(b)));else throw{message:"wrong number of arguments for "+this.name+" ("+a.length+" for "+this.arity+")"};return(new d.Ruleset(null,this.rules.slice(0))).eval({frames:[this,g].concat(this.frames,b.frames)})},match:function(b,a){var g=b&&b.length||0;if(g<this.required)return false;if(this.required>0&&g>this.params.length)return false;g=
+Math.min(g,this.arity);for(var e=0;e<g;e++)if(!this.params[e].name)if(b[e].eval(a).toCSS()!=this.params[e].value.eval(a).toCSS())return false;return true}}})(s("less/tree"));(function(d){d.Operation=function(b,a){this.op=b.trim();this.operands=a};d.Operation.prototype.eval=function(b){var a=this.operands[0].eval(b);b=this.operands[1].eval(b);var g;if(a instanceof d.Dimension&&b instanceof d.Color)if(this.op==="*"||this.op==="+"){g=b;b=a;a=g}else throw{name:"OperationError",message:"Can't substract or divide a color from a number"};
+return a.operate(this.op,b)};d.operate=function(b,a,g){switch(b){case "+":return a+g;case "-":return a-g;case "*":return a*g;case "/":return a/g}}})(s("less/tree"));(function(d){d.Quoted=function(b,a){this.value=a||"";this.quote=b.charAt(0)};d.Quoted.prototype={toCSS:function(){return this.quote+this.value+this.quote},eval:function(){return this}}})(s("less/tree"));(function(d){d.Rule=function(b,a,g,e){this.name=b;this.value=a instanceof d.Value?a:new d.Value([a]);this.important=g?" "+g.trim():"";
+this.index=e;this.variable=b.charAt(0)==="@"?true:false};d.Rule.prototype.toCSS=function(b){return this.variable?"":this.name+(b.compress?":":": ")+this.value.toCSS(b)+this.important+";"};d.Rule.prototype.eval=function(b){return new d.Rule(this.name,this.value.eval(b),this.important,this.index)};d.Shorthand=function(b,a){this.a=b;this.b=a};d.Shorthand.prototype={toCSS:function(b){return this.a.toCSS(b)+"/"+this.b.toCSS(b)},eval:function(){return this}}})(s("less/tree"));(function(d){d.Ruleset=function(b,
+a){this.selectors=b;this.rules=a;this._lookups={}};d.Ruleset.prototype={eval:function(b){var a=new d.Ruleset(this.selectors,this.rules.slice(0));a.root=this.root;b.frames.unshift(a);if(a.root)for(var g=0;g<a.rules.length;g++)a.rules[g]instanceof d.Import&&Array.prototype.splice.apply(a.rules,[g,1].concat(a.rules[g].eval(b)));for(g=0;g<a.rules.length;g++)if(a.rules[g]instanceof d.mixin.Definition)a.rules[g].frames=b.frames.slice(0);for(g=0;g<a.rules.length;g++)a.rules[g]instanceof d.mixin.Call&&Array.prototype.splice.apply(a.rules,
+[g,1].concat(a.rules[g].eval(b)));g=0;for(var e;g<a.rules.length;g++){e=a.rules[g];e instanceof d.mixin.Definition||(a.rules[g]=e.eval?e.eval(b):e)}b.frames.shift();return a},match:function(b){return!b||b.length===0},variables:function(){return this._variables?this._variables:this._variables=this.rules.reduce(function(b,a){if(a instanceof d.Rule&&a.variable===true)b[a.name]=a;return b},{})},variable:function(b){return this.variables()[b]},rulesets:function(){return this._rulesets?this._rulesets:this._rulesets=
+this.rules.filter(function(b){return b instanceof d.Ruleset||b instanceof d.mixin.Definition})},find:function(b,a){a=a||this;var g=[],e=b.toCSS();if(e in this._lookups)return this._lookups[e];this.rulesets().forEach(function(h){if(h!==a)for(var i=0;i<h.selectors.length;i++)if(b.match(h.selectors[i])){b.elements.length>1?Array.prototype.push.apply(g,h.find(new d.Selector(b.elements.slice(1)),a)):g.push(h);break}});return this._lookups[e]=g},toCSS:function(b,a){var g=[],e=[],h=[],i=[],k;if(!this.root)if(b.length===
+0)i=this.selectors.map(function(r){return[r]});else for(k=0;k<this.selectors.length;k++)for(var n=0;n<b.length;n++)i.push(b[n].concat([this.selectors[k]]));for(n=0;n<this.rules.length;n++){k=this.rules[n];if(k.rules||k instanceof d.Directive)h.push(k.toCSS(i,a));else if(k instanceof d.Comment)k.silent||(this.root?h.push(k.toCSS(a)):e.push(k.toCSS(a)));else if(k.toCSS&&!k.variable)e.push(k.toCSS(a));else k.value&&!k.variable&&e.push(k.value.toString())}h=h.join("");if(this.root)g.push(e.join(a.compress?
+"":"\n"));else if(e.length>0){i=i.map(function(r){return r.map(function(p){return p.toCSS(a)}).join("").trim()}).join(a.compress?",":i.length>3?",\n":", ");g.push(i,(a.compress?"{":" {\n ")+e.join(a.compress?"":"\n ")+(a.compress?"}":"\n}\n"))}g.push(h);return g.join("")+(a.compress?"\n":"")}}})(s("less/tree"));(function(d){d.Selector=function(b){this.elements=b;if(this.elements[0].combinator.value==="")this.elements[0].combinator.value=" "};d.Selector.prototype.match=function(b){return this.elements[0].value===
+b.elements[0].value?true:false};d.Selector.prototype.toCSS=function(b){if(this._css)return this._css;return this._css=this.elements.map(function(a){return typeof a==="string"?" "+a.trim():a.toCSS(b)}).join("")}})(s("less/tree"));(function(d){d.URL=function(b,a){if(b.data)this.attrs=b;else{if(!/^(?:https?:\/|file:\/)?\//.test(b.value)&&a.length>0&&typeof z!=="undefined")b.value=a[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value);this.value=b;this.paths=a}};d.URL.prototype={toCSS:function(){return"url("+
+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(b){return this.attrs?this:new d.URL(this.value.eval(b),this.paths)}}})(s("less/tree"));(function(d){d.Value=function(b){this.value=b;this.is="value"};d.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new d.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(b){return this.value.map(function(a){return a.toCSS(b)}).join(b.compress?
+",":", ")}}})(s("less/tree"));(function(d){d.Variable=function(b,a){this.name=b;this.index=a};d.Variable.prototype={eval:function(b){var a,g,e=this.name;if(a=d.find(b.frames,function(h){if(g=h.variable(e))return g.value.eval(b)}))return a;else throw{message:"variable "+this.name+" is undefined",index:this.index};}}})(s("less/tree"));s("less/tree").find=function(d,b){for(var a=0,g;a<d.length;a++)if(g=b.call(d,d[a]))return g;return null};var P=location.protocol==="file:"||location.protocol==="chrome:"||
+location.protocol==="resource:";o.env=o.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||P?"development":"production");o.async=false;o.poll=o.poll||(P?1E3:1500);o.watch=function(){return this.watchMode=true};o.unwatch=function(){return this.watchMode=false};if(o.env==="development"){o.optimization=0;/!watch/.test(location.hash)&&o.watch();o.watchTimer=setInterval(function(){o.watchMode&&W(function(d,b,a){d&&N(d.toCSS(),b,a.lastModified)})},
+o.poll)}else o.optimization=3;var B;try{B=typeof z.localStorage==="undefined"?null:z.localStorage}catch(aa){B=null}var M=document.getElementsByTagName("link"),V=/^text\/(x-)?less$/;o.sheets=[];for(var J=0;J<M.length;J++)if(M[J].rel==="stylesheet/less"||M[J].rel.match(/stylesheet/)&&M[J].type.match(V))o.sheets.push(M[J]);o.refresh=function(d){var b=endTime=new Date;W(function(a,g,e){if(e.local)H("loading "+g.href+" from cache.");else{H("parsed "+g.href+" successfully.");N(a.toCSS(),g,e.lastModified)}H("css for "+
+g.href+" generated in "+(new Date-endTime)+"ms");e.remaining===0&&H("css generated in "+(new Date-b)+"ms");endTime=new Date},d);U()};o.refreshStyles=U;o.refresh(o.env==="development")})(window);
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/ui.js Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,17 @@
+$(function() {
+ $("a.fold").toggle(function(event) {
+ $(event.target).closest(".file").find(".file-review-contents").slideUp("fast", function () {
+ $(event.target).closest("h3").find(".status").html("→");
+ });
+ return false;
+ }, function(event) {
+ $(event.target).closest(".file").find(".file-review-contents").slideDown("fast", function() {
+ $(event.target).closest("h3").find(".status").html("↓");
+ });
+ return false;
+ });
+
+ $(".submit").live('click', function() {
+ $(this).closest("form").submit();
+ });
+});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/scripts/underscore.js Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,17 @@
+(function(){var n=this,A=n._,r=typeof StopIteration!=="undefined"?StopIteration:"__break__",j=Array.prototype,l=Object.prototype,o=j.slice,B=j.unshift,C=l.toString,p=l.hasOwnProperty,s=j.forEach,t=j.map,u=j.reduce,v=j.reduceRight,w=j.filter,x=j.every,y=j.some,m=j.indexOf,z=j.lastIndexOf;l=Array.isArray;var D=Object.keys,b=function(a){return new k(a)};if(typeof exports!=="undefined")exports._=b;n._=b;b.VERSION="1.0.4";var i=b.forEach=function(a,c,d){try{if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=
+0,f=a.length;e<f;e++)c.call(d,a[e],e,a);else for(e in a)p.call(a,e)&&c.call(d,a[e],e,a)}catch(g){if(g!=r)throw g;}return a};b.map=function(a,c,d){if(t&&a.map===t)return a.map(c,d);var e=[];i(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(u&&a.reduce===u)return a.reduce(b.bind(d,e),c);i(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(v&&a.reduceRight===v)return a.reduceRight(b.bind(d,e),c);a=b.clone(b.toArray(a)).reverse();return b.reduce(a,
+c,d,e)};b.detect=function(a,c,d){var e;i(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.filter=function(a,c,d){if(w&&a.filter===w)return a.filter(c,d);var e=[];i(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];i(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.every=function(a,c,d){c=c||b.identity;if(x&&a.every===x)return a.every(c,d);var e=true;i(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.some=
+function(a,c,d){c=c||b.identity;if(y&&a.some===y)return a.some(c,d);var e=false;i(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c)!=-1;var d=false;i(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,
+a);var e={computed:-Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){var g=e.criteria,h=f.criteria;return g<h?-1:g>h?
+1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return o.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?o.call(a,0,c):a[0]};b.rest=function(a,c,d){return o.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};
+b.compact=function(a){return b.filter(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.filter(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.filter(b.uniq(a),function(d){return b.every(c,
+function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(z&&a.lastIndexOf===z)return a.lastIndexOf(c);for(var d=a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=
+1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||{},d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);i(c,function(d){a[d]=b.bind(a[d],a)});return a};b.memoize=function(a,c){var d={};c=c||b.identity;return function(){var e=c.apply(this,arguments);return e in
+d?d[e]:(d[e]=a.apply(this,arguments))}};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=D||function(a){if(b.isArray(a))return b.range(0,
+a.length);var c=[];for(var d in a)p.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){i(b.rest(arguments),function(c){for(var d in c)a[d]=c[d]});return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;
+if(a==c)return true;if(!a&&c||a&&!c)return false;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return false;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!(f in c)||!b.isEqual(a[f],
+c[f]))return false;return true};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(p.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=l||function(a){return!!(a&&a.concat&&a.unshift&&!a.callee)};b.isArguments=function(a){return a&&a.callee};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===
++a||C.call(a)==="[object Number]"};b.isBoolean=function(a){return a===true||a===false};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){n._=A;return this};b.identity=function(a){return a};b.times=function(a,c,d){for(var e=
+0;e<a;e++)c.call(d,e)};b.breakLoop=function(){throw r;};b.mixin=function(a){i(b.functions(a),function(c){E(c,b[c]=a[c])})};var F=0;b.uniqueId=function(a){var c=F++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings,e=new RegExp("'(?=[^"+d.end.substr(0,1)+"]*"+d.end.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")+")","g");d=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g,
+" ").replace(e,"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?d(c):d};b.each=b.forEach;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.select=b.filter;b.all=b.every;b.any=b.some;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var k=function(a){this._wrapped=a},q=function(a,c){return c?b(a).chain():a},E=function(a,c){k.prototype[a]=function(){var d=b.toArray(arguments);
+B.call(d,this._wrapped);return q(c.apply(b,d),this._chain)}};b.mixin(b);i(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=j[a];k.prototype[a]=function(){c.apply(this._wrapped,arguments);return q(this._wrapped,this._chain)}});i(["concat","join","slice"],function(a){var c=j[a];k.prototype[a]=function(){return q(c.apply(this._wrapped,arguments),this._chain)}});k.prototype.chain=function(){this._chain=true;return this};k.prototype.value=function(){return this._wrapped}})();
\ No newline at end of file
--- a/review/static/style.css Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,713 +0,0 @@
-.markdown p:last-child, .markdown ol:last-child, .markdown ul:last-child {
- margin-bottom: 0;
-}
-.plain {
- font-family: Monaco, Consolas, "Courier New", monospace;
- font-size: 12px;
- white-space: pre;
-}
-.group:after {
- clear: both;
- content: ' ';
- display: block;
- font-size: 0;
- line-height: 0;
- visibility: hidden;
- width: 0;
- height: 0;
-}
-* html .group, *:first-child + html .group {
- zoom: 1;
-}
-html {
- overflow-y: scroll;
-}
-html, body {
- background-color: #edecc7;
- background: #edecc7 url('bg.png') top left repeat;
-}
-body {
- color: #292620;
- font-family: "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
-}
-body .hr {
- height: 0;
- width: 100%;
- border-top: 1px solid #fcfcfc;
-}
-body .wrap {
- width: 800px;
- margin: 0 auto;
-}
-body .hr.one {
- border-top: none;
- height: 3px;
- background-color: #87b04d;
- border-bottom: 1px solid #648239;
-}
-body .hr.two {
- border-top: none;
- border-bottom: 1px solid #94b95f;
-}
-body .header {
- background-color: #648239;
- border-bottom: 1px solid #5a7533;
-}
-body .header h1 {
- color: #292620;
- text-shadow: 0px 1px 1px #94b95f;
- float: right;
- line-height: 60px;
- margin: 0 10px 0 0;
-}
-body .header h1 a {
- color: black;
- text-decoration: none;
-}
-body .header h1 a:hover {
- text-decoration: underline;
-}
-body .header .remotes .remote {
- padding-top: 6px;
-}
-body .header .remotes form {
- display: inline;
- margin-bottom: 0;
-}
-body .header .remotes form a {
- margin-right: 3px;
- cursor: pointer;
- font: bold 12px "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
- color: #212000;
- line-height: 1.45;
- display: inline-block;
- text-decoration: none;
- padding: 1px;
- background-color: #edecc7;
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- border-top: 1px solid #bab994;
- border-right: 1px solid #a9a883;
- border-left: 1px solid #a9a883;
- border-bottom: 1px solid #989772;
-}
-body .header .remotes form a:focus {
- box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -moz-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -webkit-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
-}
-body .header .remotes form a span {
- display: inline-block;
- padding: 0 6px;
- text-shadow: 0px 1px 1px #e5e4bf;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-body .header .remotes form a:hover {
- border-top: 1px solid #dddbac;
- border-right: 1px solid #c8c695;
- border-left: 1px solid #c8c695;
- border-bottom: 1px solid #aead83;
- background-color: #edecc7;
-}
-body .header .remotes form a:hover span {
- background-color: #edecc7;
-}
-body .content {
- border-top: 1px solid #f8f7e8;
-}
-body .content .navigation {
- line-height: 1;
- width: 780px;
- margin: 15px auto;
-}
-body .content .wrap {
- border: 1px solid #d8d685;
- -webkit-border-radius: 7px;
- -moz-border-radius: 7px;
- border-radius: 7px;
- box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.05);
- -moz-box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.05);
- -webkit-box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.05);
- padding: 21px 20px;
- width: 760px;
- background-color: #fafafa;
-}
-body .footer {
- text-align: center;
- margin-top: 14px;
-}
-body .footer a {
- color: #292620;
- font-weight: bold;
- text-decoration: none;
-}
-body .footer a:hover {
- color: #bf4c18;
-}
-body a {
- color: #b6410c;
- text-decoration: none;
- font-weight: bold;
-}
-body a:hover {
- text-decoration: underline;
-}
-body a:active {
- color: #bf1900;
-}
-body .disabled {
- display: none;
-}
-form .field {
- position: relative;
-}
-form .field label {
- display: inline;
-}
-form .field label.radio {
- margin-right: 15px;
-}
-form .field label.infield {
- display: block;
- font-family: Monaco, Consolas, "Courier New", monospace;
- position: absolute;
- top: 5px;
- left: 5px;
-}
-form .field textarea {
- font-family: Monaco, Consolas, "Courier New", monospace;
- padding: 5px;
- -webkit-border-radius: 2px;
- -moz-border-radius: 2px;
- border-radius: 2px;
- border: 1px solid #444;
-}
-form .field.cuddly {
- margin-top: -13px;
-}
-#index .content table {
- width: 100%;
-}
-#index .content table tr {
- border: none;
-}
-#index .content table tr:nth-child(even) {
- background-color: #f5f5e1;
-}
-#index .content table tr td {
- border: none;
- line-height: 26px;
- padding: 1px 5px 0;
-}
-#index .content table tr td a.changeset-link {
- display: block;
- width: 100%;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-#index .content table tr td.node {
- padding-left: 0px;
- width: 110px;
-}
-#index .content table tr td.node .rev {
- font-weight: bold;
-}
-#index .content table tr td.node .sep {
- font-weight: normal;
- padding: 0px 2px 0px 1px;
- color: #bebebe;
-}
-#index .content table tr td.node .hash {
- color: #888888;
- font: normal 11px Monaco, Consolas, "Courier New", monospace;
-}
-#index .content table tr td.stats {
- text-align: right;
- padding-right: 0px;
-}
-#index .content table tr td.stats .badge {
- cursor: pointer;
- color: white;
- font-weight: bold;
- display: inline-block;
- line-height: 1;
- padding: 3px 5px 4px;
- margin-left: 4px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-#index .content table tr td.stats .badge.yes {
- background-color: green;
-}
-#index .content table tr td.stats .badge.no {
- background-color: red;
-}
-#index .content table tr td.stats .badge.neutral {
- background-color: #666;
-}
-#index .content table tr td.stats .badge.comments {
- background-color: #ff5700;
-}
-#index .content table tr td.stats .badge:hover {
- text-decoration: none;
-}
-#index .content .pagination a.older {
- float: right;
-}
-#changeset .content a.submit {
- margin-right: 4px;
- cursor: pointer;
- font: bold 12px "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
- color: #1e1e1e;
- line-height: 1.45;
- display: inline-block;
- text-decoration: none;
- padding: 1px;
- background-color: #eaeaea;
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- border-top: 1px solid #b7b7b7;
- border-right: 1px solid #a6a6a6;
- border-left: 1px solid #a6a6a6;
- border-bottom: 1px solid #959595;
-}
-#changeset .content a.submit:focus {
- box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -moz-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -webkit-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
-}
-#changeset .content a.submit span {
- display: inline-block;
- padding: 0 6px;
- text-shadow: 0px 1px 1px #e2e2e2;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-#changeset .content a.submit:hover {
- border-top: 1px solid #d3d3d3;
- border-right: 1px solid #bbbbbb;
- border-left: 1px solid #bbbbbb;
- border-bottom: 1px solid #a4a4a4;
- background-color: #eaeaea;
-}
-#changeset .content a.submit:hover span {
- background-color: #eaeaea;
-}
-#changeset .content a.cancel, #changeset .content a.cancel-line, #changeset .content a.cancel-edit {
- cursor: pointer;
- font: bold 12px "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
- color: #1e1e1e;
- line-height: 1.45;
- display: inline-block;
- text-decoration: none;
- padding: 1px;
- background-color: #eaeaea;
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- border-top: 1px solid #b7b7b7;
- border-right: 1px solid #a6a6a6;
- border-left: 1px solid #a6a6a6;
- border-bottom: 1px solid #959595;
-}
-#changeset .content a.cancel:focus, #changeset .content a.cancel-line:focus, #changeset .content a.cancel-edit:focus {
- box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -moz-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -webkit-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
-}
-#changeset .content a.cancel span, #changeset .content a.cancel-line span, #changeset .content a.cancel-edit span {
- display: inline-block;
- padding: 0 6px;
- text-shadow: 0px 1px 1px #e2e2e2;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-#changeset .content a.cancel:hover, #changeset .content a.cancel-line:hover, #changeset .content a.cancel-edit:hover {
- border-top: 1px solid #d3d3d3;
- border-right: 1px solid #bbbbbb;
- border-left: 1px solid #bbbbbb;
- border-bottom: 1px solid #a4a4a4;
- background-color: #eaeaea;
-}
-#changeset .content a.cancel:hover span, #changeset .content a.cancel-line:hover span, #changeset .content a.cancel-edit:hover span {
- background-color: #eaeaea;
-}
-#changeset .content .navigation .middle {
- display: inline-block;
- width: 49%;
- text-align: center;
-}
-#changeset .content .navigation .right {
- display: inline-block;
- width: 25%;
- text-align: right;
-}
-#changeset .content .navigation .left {
- display: inline-block;
- width: 25%;
-}
-#changeset .content .head {
- position: relative;
-}
-#changeset .content .head h2 {
- width: 660px;
- color: #888888;
-}
-#changeset .content .head h2 .desc {
- color: #292620;
-}
-#changeset .content .head .committer-avatar {
- position: absolute;
- top: -22px;
- right: -21px;
- width: 60px;
- height: 60px;
- border: 1px solid #cdca64;
- border-top: 1px solid #cdca64;
- border-right: 1px solid #cdca64;
- border: 1px solid #999;
- box-shadow: inset 1px -1px 10px rgba(0, 0, 0, 0.25);
- -moz-box-shadow: inset 1px -1px 10px rgba(0, 0, 0, 0.25);
- -webkit-box-shadow: inset 1px -1px 10px rgba(0, 0, 0, 0.25);
- -webkit-border-top-right-radius: 7px;
- -moz-border-radius-topright: 7px;
- border-top-right-radius: 7px;
- -webkit-border-bottom-left-radius: 3px;
- -moz-border-radius-bottomleft: 3px;
- border-bottom-left-radius: 3px;
-}
-#changeset .content .head .patch {
- position: absolute;
- top: -18px;
- right: -80px;
- width: 50px;
-}
-#changeset .content .head .fulldesc {
- width: 660px;
- font-size: 12px;
- line-height: 1.25;
- margin-bottom: 14px;
- overflow-x: auto;
- overflow-y: hidden;
- padding-bottom: 4px;
- white-space: pre;
- font-family: Monaco, Consolas, "Courier New", monospace;
-}
-#changeset .content .review-level-comments {
- margin-top: 30px;
-}
-#changeset .content .edit-review-comment {
- margin-top: 3px;
-}
-#changeset .content .activate a {
- cursor: pointer;
- font: bold 12px "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
- color: #1e1e1e;
- line-height: 1.45;
- display: inline-block;
- text-decoration: none;
- padding: 1px;
- background-color: #eaeaea;
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- border-top: 1px solid #b7b7b7;
- border-right: 1px solid #a6a6a6;
- border-left: 1px solid #a6a6a6;
- border-bottom: 1px solid #959595;
-}
-#changeset .content .activate a:focus {
- box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -moz-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
- -webkit-box-shadow: 0px 0px 4px rgba(100, 100, 100, 0.9);
-}
-#changeset .content .activate a span {
- display: inline-block;
- padding: 0 6px;
- text-shadow: 0px 1px 1px #e2e2e2;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-#changeset .content .activate a:hover {
- border-top: 1px solid #d3d3d3;
- border-right: 1px solid #bbbbbb;
- border-left: 1px solid #bbbbbb;
- border-bottom: 1px solid #a4a4a4;
- background-color: #eaeaea;
-}
-#changeset .content .activate a:hover span {
- background-color: #eaeaea;
-}
-#changeset .content .edit-line {
- display: inline-block;
- margin-top: 3px;
-}
-#changeset .content .togglebox form {
- float: left;
- border: 1px solid #ccc;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
- padding: 15px 10px;
- background-color: #faf9ef;
- box-shadow: 0px 5px 5px rgba(0, 0, 0, 0.02);
- -moz-box-shadow: 0px 5px 5px rgba(0, 0, 0, 0.02);
- -webkit-box-shadow: 0px 5px 5px rgba(0, 0, 0, 0.02);
-}
-#changeset .content .placeholder {
- font-style: italic;
- color: #888888;
-}
-#changeset .content .item-listing {
- background-color: #faf9ef;
- border: 1px solid #ccc;
- margin-bottom: 14px;
-}
-#changeset .content .item-listing .comment, #changeset .content .item-listing .signoff {
- padding: 8px 12px 8px 10px;
- border-top: 1px solid #fff;
- border-bottom: 1px solid #ddd;
- position: relative;
- min-height: 41px;
-}
-#changeset .content .item-listing .comment:first-child, #changeset .content .item-listing .signoff:first-child {
- border-top: none;
-}
-#changeset .content .item-listing .comment:last-child, #changeset .content .item-listing .signoff:last-child {
- border-bottom: none;
-}
-#changeset .content .item-listing .comment img, #changeset .content .item-listing .signoff img {
- float: right;
-}
-#changeset .content .item-listing .comment .message, #changeset .content .item-listing .signoff .message {
- overflow-x: auto;
- width: 690px;
- padding-top: 3px;
-}
-#changeset .content .item-listing .comment .avatar img, #changeset .content .item-listing .signoff .avatar img {
- height: 30px;
- width: 30px;
- margin-top: 5px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-#changeset .content .item-listing .comment .expand, #changeset .content .item-listing .signoff .expand {
- position: absolute;
- top: 17px;
- right: -18px;
- font-size: 14px;
- font-weight: bold;
-}
-#changeset .content .item-listing .comment .expand:hover, #changeset .content .item-listing .signoff .expand:hover {
- text-decoration: none;
-}
-#changeset .content .item-listing .comment .colorboxed, #changeset .content .item-listing .signoff .colorboxed {
- display: none;
-}
-#changeset .content .item-listing .signoff .signoff-opinion {
- float: right;
- font: bold 30px/1 "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
- text-transform: capitalize;
- margin-right: 15px;
- margin-top: 5px;
-}
-#changeset .content .item-listing .signoff .signoff-opinion.yes {
- color: #568019;
-}
-#changeset .content .item-listing .signoff .signoff-opinion.no {
- color: #c00;
-}
-#changeset .content .item-listing .signoff .signoff-opinion.neutral {
- color: #bab994;
-}
-#changeset .content .item-listing .signoff .author .opinion {
- font-weight: bold;
-}
-#changeset .content .item-listing .signoff .message {
- overflow-x: auto;
- width: 620px;
-}
-#changeset .content .add-review-comment {
- margin-bottom: 40px;
-}
-#changeset .content .add-signoff {
- margin-bottom: 40px;
-}
-#changeset .content .add-signoff form .sign-off-as {
- font-weight: bold;
- margin-bottom: 0;
-}
-#changeset .content .file .filename h3 a {
- color: #292620;
-}
-#changeset .content .file .add-file-comment {
- margin-bottom: 24px;
-}
-#changeset .content .diff {
- overflow-x: auto;
- border: 1px solid #ddd;
- margin-bottom: 24px;
-}
-#changeset .content .diff table {
- width: 100%;
- background-color: #f9f9f9;
- margin-bottom: 0;
-}
-#changeset .content .diff table td.code:after {
- color: #ccc;
- content: "↩";
- vertical-align: -1px;
- line-height: 1;
-}
-#changeset .content .diff table tr.rem {
- color: black;
- background-color: #f7cdcd;
-}
-#changeset .content .diff table tr.rem td.code:after {
- color: white;
-}
-#changeset .content .diff table tr.rem td.linenumber {
- color: #292620;
-}
-#changeset .content .diff table tr.add {
- color: black;
- background-color: #c5e5ce;
-}
-#changeset .content .diff table tr.add td.code:after {
- color: white;
-}
-#changeset .content .diff table tr.add td.linenumber {
- color: #292620;
-}
-#changeset .content .diff table tr.viewing {
- background-color: #f2f2d7;
-}
-#changeset .content .diff table tr.commentable {
- cursor: pointer;
-}
-#changeset .content .diff table tr.chosen {
- background-color: #edecc7;
-}
-#changeset .content .diff table tr.comment-form td {
- line-height: 15px;
- padding: 10px 10px 15px 56px;
- border-bottom: 1px solid #ddd;
- border-top: 1px solid #ddd;
- background-color: #f4f4f4;
-}
-#changeset .content .diff table tr.comment-form td textarea {
- width: 500px;
- height: 50px;
- border: 1px solid #888888;
-}
-#changeset .content .diff table td {
- border: none;
- line-height: 1;
- padding: 1px 5px;
-}
-#changeset .content .diff table td.addrem-add, #changeset .content .diff table td.addrem-rem {
- font-weight: bold;
- text-align: center;
- width: 10px;
-}
-#changeset .content .diff table td.linenumber {
- width: 25px;
- text-align: right;
- font-family: Monaco, Consolas, "Courier New", monospace;
- color: #888888;
-}
-#changeset .content .diff table td.skip {
- text-align: right;
- color: #888888;
-}
-#changeset .content .diff table td.code {
- white-space: pre;
-}
-#changeset .content .diff table td.comment {
- line-height: 15px;
- padding: 10px 10px 10px 56px;
- border-bottom: 1px solid #ddd;
- border-top: 1px solid #ddd;
- background-color: #f4f4f4;
-}
-#changeset .content .diff table td.comment .avatar {
- float: right;
- margin-top: 2px;
-}
-#changeset .content .diff table td.comment .avatar img {
- height: 30px;
- width: 30px;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
-#changeset .content .diff table td.comment .author {
- padding-bottom: 3px;
-}
-#changeset .content .diff table td.comment .comment-content {
- position: relative;
-}
-#changeset .content .diff table td.comment .expand {
- position: absolute;
- top: -13px;
- right: -8px;
- font-size: 14px;
- font-weight: bold;
-}
-#changeset .content .diff table td.comment .expand:hover {
- text-decoration: none;
-}
-#changeset .content .diff table td.comment .colorboxed {
- display: none;
-}
-#colorbox #cboxLoadedContent {
- padding: 10px;
-}
-#colorbox #cboxContent {
- position: relative;
-}
-#colorbox .avatar {
- position: absolute;
- top: 0px;
- right: 0px;
-}
-#colorbox .author {
- font-size: 20px;
- width: 400px;
- margin-bottom: 20px;
-}
-#colorbox .message {
- overflow-x: auto;
- max-width: 910px;
- padding: 0 0 5px;
- font-family: Monaco, Consolas, "Courier New", monospace;
- white-space: pre;
-}
-#colorbox .context .context-head {
- color: #888888;
- font-size: 20px;
- margin-top: -16px;
- margin-bottom: 18px;
-}
--- a/review/static/style.less Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,691 +0,0 @@
-@font-normal: "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
-@font-mono: Monaco, Consolas, "Courier New", monospace;
-
-@c-cream: #edecc7;
-@c-green: #648239;
-@c-orange: #bf4c18;
-@c-green-light: #9AF040;
-@c-orange-light: #FF5700;
-
-@c-dark: #292620;
-@c-light: #888888;
-
-@c-metal: #eaeaea;
-@c-soft-cream: lighten(@c-cream, 12%);
-
-@c-diff-add: #c5e5ce;
-@c-diff-rem: #f7cdcd;
-
-@content-width: 800px;
-@content-background: rgb(250, 250, 250);
-
-.border-radius(@radius) {
- -webkit-border-radius: @radius;
- -moz-border-radius: @radius;
- border-radius: @radius;
-}
-.box-shadow(@horiz, @vert, @blur, @color) {
- box-shadow: @horiz @vert @blur @color;
- -moz-box-shadow: @horiz @vert @blur @color;
- -webkit-box-shadow: @horiz @vert @blur @color;
-}
-.box-inner-shadow(@horiz, @vert, @blur, @color) {
- box-shadow: inset @horiz @vert @blur @color;
- -moz-box-shadow: inset @horiz @vert @blur @color;
- -webkit-box-shadow: inset @horiz @vert @blur @color;
-}
-.multi-border(@top, @sides, @bottom) {
- border-top: 1px solid @top;
- border-right: 1px solid @sides;
- border-left: 1px solid @sides;
- border-bottom: 1px solid @bottom;
-}
-.button(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
- cursor: pointer;
- font: bold @fsize @font-normal;
- color: @bgcolor - #ccc;
- line-height: @lheight;
- display: inline-block;
- text-decoration: none;
- padding: 1px;
- background-color: @bgcolor;
- .box-shadow(0px, 1px, 3px, rgba(0,0,0,0.1));
- .border-radius(4px);
- .multi-border((@bgcolor - #333), (@bgcolor - #444), (@bgcolor - #555));
- &:focus {
- .box-shadow(0px, 0px, 4px, rgba(100,100,100,0.9));
- }
-}
-.button-span(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
- display: inline-block;
- padding: 0 @fsize/2;
- text-shadow: 0px 1px 1px (@bgcolor - #080808);
- .border-radius(3px);
-}
-.button-hover(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
- .multi-border(desaturate(darken(@bgcolor, 10%), 10%), desaturate(darken(@bgcolor, 20%), 20%), desaturate(darken(@bgcolor, 30%), 30%));
- background-color: @bgcolor;
-}
-.button-hover-span(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
- background-color: @bgcolor;
-}
-
-.markdown {
- p, ol, ul {
- &:last-child {
- margin-bottom: 0;
- }
- }
-}
-.plain {
- font-family: @font-mono;
- font-size: 12px;
- white-space: pre;
-}
-
-
-.group:after {
- clear:both; content:' '; display:block; font-size:0; line-height:0; visibility:hidden; width:0; height:0;
-}
-* html .group, *:first-child + html .group {
- zoom:1
-}
-
-html {
- overflow-y: scroll;
-}
-html, body {
- background-color: @c-cream;
- background: @c-cream url('bg.png') top left repeat;
-}
-body {
- color: @c-dark;
- font-family: @font-normal;
-
- .hr {
- height: 0;
- width: 100%;
- border-top: 1px solid #fcfcfc;
- }
- .wrap {
- width: @content-width;
- margin: 0 auto;
- }
- .hr.one {
- border-top: none;
- height: 3px;
- background-color: lighten(@c-green, 35%);
- border-bottom: 1px solid @c-green;
- }
- .hr.two {
- border-top: none;
- border-bottom: 1px solid lighten(@c-green, 50%);
- }
- .header {
- background-color: @c-green;
- border-bottom: 1px solid darken(@c-green, 10%);
-
- h1 {
- color: @c-dark;
- text-shadow: 0px 1px 1px lighten(@c-green, 50%);
- float: right;
- line-height: 60px;
- margin: 0 10px 0 0;
-
- a {
- color: black;
- text-decoration: none;
-
- &:hover {
- text-decoration: underline;
- }
- }
- }
- .remotes {
- .remote {
- padding-top: 6px;
- }
- form {
- display: inline;
- margin-bottom: 0;
-
- a {
- margin-right: 3px;
-
- .button(@c-cream, #000, 12px, 1.45);
- span { .button-span(@c-cream, #000, 12px, 1.45); }
- &:hover { .button-hover(@c-cream, #000, 12px, 1.45); }
- &:hover span { .button-hover-span(@c-cream, #000, 12px, 1.45); }
- }
- }
- }
- }
- .content {
- border-top: 1px solid lighten(@c-cream, 10%);
-
- .navigation {
- line-height: 1;
- width: @content-width - 20px;
- margin: 15px auto;
- }
- .wrap {
- border: 1px solid darken(@c-cream, 20%);
- .border-radius(7px);
- .box-shadow(0px, 5px, 10px, rgba(0, 0, 0, 0.05));
- @content-padding-horiz: 20px;
-
- padding: 21px @content-padding-horiz;
- width: @content-width - (@content-padding-horiz * 2);
- background-color: @content-background;
- }
- }
- .footer {
- text-align: center;
- margin-top: 14px;
-
- a {
- color: @c-dark;
- font-weight: bold;
- text-decoration: none;
-
- &:hover {
- color: @c-orange;
- }
- }
- }
- a {
- color: saturate(darken(@c-orange, 10%), 10%);
- text-decoration: none;
- font-weight: bold;
-
- &:hover {
- text-decoration: underline;
- }
- &:active {
- color: @c-orange - #033;
- }
- }
- .disabled {
- display: none;
- }
-}
-
-form {
- .field {
- position: relative;
-
- label {
- display: inline;
-
- &.radio {
- margin-right: 15px;
- }
- &.infield {
- display: block;
- font-family: @font-mono;
- position: absolute;
- top: 5px;
- left: 5px;
- }
- }
- textarea {
- font-family: @font-mono;
- padding: 5px;
- .border-radius(2px);
- border: 1px solid #444;
- }
- &.cuddly {
- margin-top: -13px;
- }
- }
-}
-
-#index .content {
- table {
- width: 100%;
-
- tr {
- border: none;
-
- &:nth-child(even) {
- background-color: lighten(@c-cream, 8%);
- }
-
- td {
- border: none;
- line-height: 26px;
- padding: 1px 5px 0;
-
- a.changeset-link {
- display: block;
- width: 100%;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- &.node {
- padding-left: 0px;
- width: 110px;
-
- .rev {
- font-weight: bold;
- }
- .sep {
- font-weight: normal;
- padding: 0px 2px 0px 1px;
- color: lighten(@c-light, 40%);
- }
- .hash {
- color: @c-light;
- font: normal 11px @font-mono;
- }
- }
- &.stats {
- text-align: right;
- padding-right: 0px;
-
- .badge {
- cursor: pointer;
- color: white;
- font-weight: bold;
- display: inline-block;
- line-height: 1;
- padding: 3px 5px 4px;
- margin-left: 4px;
-
- .border-radius(3px);
-
- &.yes {
- background-color: green;
- }
- &.no {
- background-color: red;
- }
- &.neutral {
- background-color: #666;
- }
- &.comments {
- background-color: @c-orange-light;
- }
- &:hover {
- text-decoration: none;
- }
- }
- }
- }
- }
- }
- .pagination {
- a.older {
- float: right;
- }
- }
-}
-#changeset .content {
- a.submit {
- margin-right: 4px;
- .button(@c-metal, #000, 12px, 1.45);
- span { .button-span(@c-metal, #000, 12px, 1.45); }
- &:hover { .button-hover(@c-metal, #000, 12px, 1.45); }
- &:hover span { .button-hover-span(@c-metal, #000, 12px, 1.45); }
- }
- a.cancel, a.cancel-line, a.cancel-edit {
- .button(@c-metal, #000, 12px, 1.45);
- span { .button-span(@c-metal, #000, 12px, 1.45); }
- &:hover { .button-hover(@c-metal, #000, 12px, 1.45); }
- &:hover span { .button-hover-span(@c-metal, #000, 12px, 1.45); }
- }
- .navigation {
- .middle {
- display: inline-block;
- width: 49%;
- text-align: center;
- }
- .right {
- display: inline-block;
- width: 25%;
- text-align: right;
- }
- .left {
- display: inline-block;
- width: 25%;
- }
- }
- .head {
- position: relative;
-
- h2 {
- width: 660px;
- color: @c-light;
-
- .desc {
- color: @c-dark;
- }
- }
- .committer-avatar {
- position: absolute;
- top: -22px;
- right: -21px;
- width: 60px;
- height: 60px;
- border: 1px solid darken(@c-cream, 30%);
- border-top: 1px solid darken(@c-cream, 30%);
- border-right: 1px solid darken(@c-cream, 30%);
- border: 1px solid #999;
- .box-inner-shadow(1px, -1px, 10px, rgba(0,0,0,0.25));
-
- -webkit-border-top-right-radius: 7px;
- -moz-border-radius-topright: 7px;
- border-top-right-radius: 7px;
- -webkit-border-bottom-left-radius: 3px;
- -moz-border-radius-bottomleft: 3px;
- border-bottom-left-radius: 3px;
- }
- .patch {
- position: absolute;
- top: -18px;
- right: -80px;
- width: 50px;
- }
- .fulldesc {
- width: 660px;
- font-size: 12px;
- line-height: 1.25;
- margin-bottom: 14px;
- overflow-x: auto;
- overflow-y: hidden;
- padding-bottom: 4px;
- white-space: pre;
- font-family: @font-mono;
- }
- }
- .review-level-comments {
- margin-top: 30px;
- }
- .edit-review-comment {
- margin-top: 3px;
- }
- .activate a {
- .button(@c-metal, #000, 12px, 1.45);
- span { .button-span(@c-metal, #000, 12px, 1.45); }
- &:hover { .button-hover(@c-metal, #000, 12px, 1.45); }
- &:hover span { .button-hover-span(@c-metal, #000, 12px, 1.45); }
- }
- .edit-line {
- display: inline-block;
- margin-top: 3px;
- }
- .togglebox {
- form {
- float: left;
- border: 1px solid #ccc;
- .border-radius(3px);
- padding: 15px 10px;
- background-color: @c-soft-cream;
- .box-shadow(0px, 5px, 5px, rgba(0, 0, 0, 0.02);
- }
- }
- .placeholder {
- font-style: italic;
- color: @c-light;
- }
- .item-listing {
- background-color: @c-soft-cream;
- border: 1px solid #ccc;
- margin-bottom: 14px;
-
- .comment, .signoff {
- padding: 8px 12px 8px 10px;
- border-top: 1px solid #fff;
- border-bottom: 1px solid #ddd;
- position: relative;
- min-height: 41px;
-
- &:first-child {
- border-top: none;
- }
- &:last-child {
- border-bottom: none;
- }
- img {
- float: right;
- }
- .message {
- overflow-x: auto;
- width: 690px;
- padding-top: 3px;
- }
- .avatar img {
- height: 30px;
- width: 30px;
- margin-top: 5px;
- .border-radius(3px);
- }
- .expand {
- position: absolute;
- top: 17px;
- right: -18px;
- font-size: 14px;
- font-weight: bold;
-
- &:hover {
- text-decoration: none;
- }
- }
- .colorboxed {
- display: none;
- }
- }
- .signoff {
- .signoff-opinion {
- float: right;
- font: bold 30px/1 @font-normal;
- text-transform: capitalize;
- margin-right: 15px;
- margin-top: 5px;
-
- &.yes { color: saturate(@c-green - #111, 20%); }
- &.no { color: #c00; }
- &.neutral { color: @c-cream - #333; }
- }
- .author {
- .opinion {
- font-weight: bold;
- }
- }
- .message {
- overflow-x: auto;
- width: 620px;
- }
- }
- }
- .add-review-comment {
- margin-bottom: 40px;
- }
-
- .add-signoff {
- margin-bottom: 40px;
-
- form {
- .sign-off-as {
- font-weight: bold;
- margin-bottom: 0;
- }
- }
- }
-
- .file {
- .filename {
- h3 {
- a {
- color: @c-dark;
- }
- }
- }
- .add-file-comment {
- margin-bottom: 24px;
- }
- }
- .diff {
- overflow-x: auto;
- border: 1px solid #ddd;
- margin-bottom: 24px;
-
- table {
- width: 100%;
- background-color: #f9f9f9;
- margin-bottom: 0;
-
- td.code:after {
- color: #ccc;
- content: "↩";
- vertical-align: -1px;
- line-height: 1;
- }
- tr {
- &.rem {
- color: black;
- background-color: @c-diff-rem;
-
- td.code:after {
- color: white;
- }
- td.linenumber {
- color: @c-dark;
- }
- }
- &.add {
- color: black;
- background-color: @c-diff-add;
-
- td.code:after {
- color: white;
- }
- td.linenumber {
- color: @c-dark;
- }
- }
- &.viewing {
- background-color: lighten(@c-cream, 5%);
- }
- &.commentable {
- cursor: pointer;
- }
- &.chosen {
- background-color: @c-cream;
- }
- &.comment-form td {
- line-height: 15px;
- padding: 10px 10px 15px 56px;
- border-bottom: 1px solid #ddd;
- border-top: 1px solid #ddd;
- background-color: #f4f4f4;
-
- textarea {
- width: 500px;
- height: 50px;
- border: 1px solid @c-light;
- }
- }
- }
-
- td {
- border: none;
- line-height: 1;
- padding: 1px 5px;
-
- &.addrem-add, &.addrem-rem {
- font-weight: bold;
- text-align: center;
- width: 10px;
- }
- &.linenumber {
- width: 25px;
- text-align: right;
- font-family: @font-mono;
- color: @c-light;
- }
- &.skip {
- text-align: right;
- color: @c-light;
- }
- &.code {
- white-space: pre;
- }
- &.comment {
- line-height: 15px;
- padding: 10px 10px 10px 56px;
- border-bottom: 1px solid #ddd;
- border-top: 1px solid #ddd;
- background-color: #f4f4f4;
-
- .avatar {
- float: right;
- margin-top: 2px;
-
- img {
- height: 30px;
- width: 30px;
- .border-radius(3px);
- }
- }
- .author {
- padding-bottom: 3px;
- }
- .comment-content {
- position: relative;
- }
- .expand {
- position: absolute;
- top: -13px;
- right: -8px;
- font-size: 14px;
- font-weight: bold;
-
- &:hover {
- text-decoration: none;
- }
- }
- .colorboxed {
- display: none;
- }
- }
- }
- }
- }
-}
-
-#colorbox {
- #cboxLoadedContent {
- padding: 10px;
- }
- #cboxContent {
- position: relative;
- }
- .avatar {
- position: absolute;
- top: 0px;
- right: 0px;
-
- }
- .author {
- font-size: 20px;
- width: 400px;
- margin-bottom: 20px;
- }
- .message {
- overflow-x: auto;
- max-width: 910px;
- padding: 0 0 5px;
- font-family: @font-mono;
- white-space: pre;
- }
- .context {
- .context-head {
- color: @c-light;
- font-size: 20px;
- margin-top: -16px;
- margin-bottom: 18px;
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/styles/aal.css Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,99 @@
+/*
+ aardvark.legs by Anatoli Papirovski - http://fecklessmind.com/
+ Licensed under the MIT license. http://www.opensource.org/licenses/mit-license.php
+*/
+
+/*
+ Reset first. Modified version of Eric Meyer and Paul Chaplin reset
+ from http://meyerweb.com/eric/tools/css/reset/
+*/
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+header, nav, section, article, aside, footer
+{border: 0; margin: 0; outline: 0; padding: 0; background: transparent; vertical-align: baseline;}
+
+blockquote, q {quotes: none;}
+blockquote:before,blockquote:after,q:before,q:after {content: ''; content: none;}
+
+header, nav, section, article, aside, footer {display: block;}
+
+/* Basic styles */
+body {background: #fff; color: #111; font: 0.75em/1.5em "Helvetica Neue", Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif;}
+html>body {font-size: 12px;}
+
+img {display: inline-block; vertical-align: bottom;}
+
+h1,h2,h3,h4,h5,h6,strong,b,dt,th {font-weight: 700;}
+address,cite,em,i,caption,dfn,var {font-style: italic;}
+
+h1 {margin: 0 0 0.75em; font-size: 2em;}
+h2 {margin: 0 0 1em; font-size: 1.5em;}
+h3 {margin: 0 0 1.286em; font-size: 1.167em;}
+h4 {margin: 0 0 1.5em; font-size: 1em;}
+h5 {margin: 0 0 1.8em; font-size: .834em;}
+h6 {margin: 0 0 2em; font-size: .75em;}
+
+p,ul,ol,dl,blockquote,pre {margin: 0 0 1.5em;}
+
+li ul,li ol {margin: 0;}
+ul {list-style: outside disc;}
+ol {list-style: outside decimal;}
+li {margin: 0 0 0 2em;}
+dd {padding-left: 1.5em;}
+blockquote {padding: 0 1.5em;}
+
+a {text-decoration: underline;}
+a:hover {text-decoration: none;}
+abbr,acronym {border-bottom: 1px dotted; cursor: help;}
+del {text-decoration: line-through;}
+ins {text-decoration: overline;}
+sub {font-size: .834em; line-height: 1em; vertical-align: sub;}
+sup {font-size: .834em; line-height: 1em; vertical-align: super;}
+
+tt,code,kbd,samp,pre {font-size: 1em; font-family: Consolas, Monaco, "Courier New", Courier, monospace;}
+
+/* Table styles */
+table {border-collapse: collapse; border-spacing: 0; margin: 0 0 1.5em;}
+caption {text-align: left;}
+th, td {padding: .25em .5em;}
+tbody td, tbody th {border: 1px solid #000;}
+tfoot {font-style: italic;}
+
+/* Form styles */
+fieldset {clear: both;}
+legend {padding: 0 0 1.286em; font-size: 1.167em; font-weight: 700;}
+fieldset fieldset legend {padding: 0 0 1.5em; font-size: 1em;}
+* html legend {margin-left: -7px;}
+*+html legend {margin-left: -7px;}
+
+form .field, form .buttons {clear: both; margin: 0 0 1.5em;}
+form .field label {display: block;}
+form ul.fields li {list-style-type: none; margin: 0;}
+form ul.inline li, form ul.inline label {display: inline;}
+form ul.inline li {padding: 0 .75em 0 0;}
+
+input.radio, input.checkbox {vertical-align: top;}
+label, button, input.submit, input.image {cursor: pointer;}
+* html input.radio, * html input.checkbox {vertical-align: middle;}
+*+html input.radio, *+html input.checkbox {vertical-align: middle;}
+
+textarea {overflow: auto;}
+input.text, input.password, textarea, select {margin: 0; font: 1em/1.3 Helvetica, Arial, "Liberation Sans", "Bitstream Vera Sans", sans-serif; vertical-align: baseline;}
+input.text, input.password, textarea {border: 1px solid #444; border-bottom-color: #666; border-right-color: #666; padding: 2px;}
+
+* html button {margin: 0 .34em 0 0;}
+*+html button {margin: 0 .34em 0 0;}
+
+form.horizontal .field {padding-left: 150px;}
+form.horizontal .field label {display: inline; float: left; width: 140px; margin-left: -150px;}
+
+/* Useful classes */
+img.left {display: inline; float: left; margin: 0 1.5em .75em 0;}
+img.right {display: inline; float: right; margin: 0 0 .75em .75em;}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/styles/extra.css Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,73 @@
+a.button span, a.button {
+ background: -webkit-gradient(
+ linear,
+ left bottom,
+ left top,
+ color-stop(0.00, rgba(0, 0, 0, 0.15)),
+ color-stop(1, rgba(0, 0, 0, 0.0))
+ );
+ background: -moz-linear-gradient(
+ center bottom,
+ rgba(0, 0, 0, 0.15) 0%,
+ rgba(0, 0, 0, 0.0) 100%
+ );
+}
+a.button:active span {
+ background: -webkit-gradient(
+ linear,
+ left bottom,
+ left top,
+ color-stop(0.00, rgba(0, 0, 0, 0.1)),
+ color-stop(1, rgba(0, 0, 0, 0.0))
+ );
+ background: -moz-linear-gradient(
+ center bottom,
+ rgba(0, 0, 0, 0.1) 0%,
+ rgba(0, 0, 0, 0.0) 100%
+ );
+}
+
+
+#index .content table tr:nth-child(even) td.node {
+ background: -webkit-gradient(
+ linear,
+ left 50%,
+ right 50%,
+ color-stop(0.0, rgba(250, 250, 250, 1)),
+ color-stop(0.35, rgba(250, 250, 250, 0.0))
+ );
+ background: -moz-linear-gradient(
+ center left,
+ rgba(250, 250, 250, 1) 0%,
+ rgba(250, 250, 250, 0) 35%
+ );
+}
+
+
+#index .content table tr:nth-child(even) td.stats {
+ background: -webkit-gradient(
+ linear,
+ right 50%,
+ left 50%,
+ color-stop(0.0, rgba(250, 250, 250, 1)),
+ color-stop(0.9, rgba(250, 250, 250, 0.0))
+ );
+ background: -moz-linear-gradient(
+ center right,
+ rgba(250, 250, 250, 1) 0%,
+ rgba(250, 250, 250, 0) 35%
+ );
+}
+
+#changeset .content .togglebox form {
+ background: -webkit-gradient(
+ linear,
+ left bottom,
+ left top,
+ color-stop(0.0, rgb(234, 233, 223)),
+ color-stop(0.3, rgb(250, 249, 239)),
+ color-stop(0.95, rgb(250, 249, 239)),
+ color-stop(1, rgb(255, 254, 243))
+ );
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/review/static/styles/style.less Wed Jun 29 19:47:16 2011 -0400
@@ -0,0 +1,702 @@
+@font-normal: "Helvetica Neue", HelveticaNeue, Arial, Helvetica, sans-serif;
+@font-mono: Monaco, Consolas, "Courier New", monospace;
+
+@c-cream: #edecc7;
+@c-green: #648239;
+@c-orange: #bf4c18;
+@c-green-light: #9AF040;
+@c-orange-light: #FF5700;
+
+@c-dark: #292620;
+@c-light: #888888;
+
+@c-metal: #eaeaea;
+@c-soft-cream: lighten(@c-cream, 12%);
+
+@c-diff-add: #c5e5ce;
+@c-diff-rem: #f7cdcd;
+
+@content-width: 800px;
+@content-background: rgb(250, 250, 250);
+
+.border-radius(@radius) {
+ -webkit-border-radius: @radius;
+ -moz-border-radius: @radius;
+ border-radius: @radius;
+}
+.box-shadow(@horiz, @vert, @blur, @color) {
+ box-shadow: @horiz @vert @blur @color;
+ -moz-box-shadow: @horiz @vert @blur @color;
+ -webkit-box-shadow: @horiz @vert @blur @color;
+}
+.box-inner-shadow(@horiz, @vert, @blur, @color) {
+ box-shadow: inset @horiz @vert @blur @color;
+ -moz-box-shadow: inset @horiz @vert @blur @color;
+ -webkit-box-shadow: inset @horiz @vert @blur @color;
+}
+.multi-border(@top, @sides, @bottom) {
+ border-top: 1px solid @top;
+ border-right: 1px solid @sides;
+ border-left: 1px solid @sides;
+ border-bottom: 1px solid @bottom;
+}
+.button(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
+ cursor: pointer;
+ font: bold @fsize @font-normal;
+ color: @bgcolor - #ccc;
+ line-height: @lheight;
+ display: inline-block;
+ text-decoration: none;
+ padding: 1px;
+ background-color: @bgcolor;
+ .box-shadow(0px, 1px, 3px, rgba(0,0,0,0.1));
+ .border-radius(4px);
+ .multi-border((@bgcolor - #333), (@bgcolor - #444), (@bgcolor - #555));
+ &:focus {
+ .box-shadow(0px, 0px, 4px, rgba(100,100,100,0.9));
+ }
+}
+.button-span(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
+ display: inline-block;
+ padding: 0 @fsize/2;
+ text-shadow: 0px 1px 1px (@bgcolor - #080808);
+ .border-radius(3px);
+}
+.button-hover(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
+ .multi-border(desaturate(darken(@bgcolor, 10%), 10%), desaturate(darken(@bgcolor, 20%), 20%), desaturate(darken(@bgcolor, 30%), 30%));
+ background-color: @bgcolor;
+}
+.button-hover-span(@bgcolor: #ddd, @fcolor: #000000, @fsize: 14px, @lheight: 1.75) {
+ background-color: @bgcolor;
+}
+
+.markdown {
+ p, ol, ul {
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+}
+.plain {
+ font-family: @font-mono;
+ font-size: 12px;
+ white-space: pre;
+}
+
+
+.group:after {
+ clear:both; content:' '; display:block; font-size:0; line-height:0; visibility:hidden; width:0; height:0;
+}
+* html .group, *:first-child + html .group {
+ zoom:1
+}
+
+html {
+ overflow-y: scroll;
+}
+html, body {
+ background-color: @c-cream;
+ background: @c-cream url('/static/images/bg.png') top left repeat;
+}
+body {
+ color: @c-dark;
+ font-family: @font-normal;
+
+ .hr {
+ height: 0;
+ width: 100%;
+ border-top: 1px solid #fcfcfc;
+ }
+ .wrap {
+ width: @content-width;
+ margin: 0 auto;
+ }
+ .hr.one {
+ border-top: none;
+ height: 3px;
+ background-color: lighten(@c-green, 35%);
+ border-bottom: 1px solid @c-green;
+ }
+ .hr.two {
+ border-top: none;
+ border-bottom: 1px solid lighten(@c-green, 50%);
+ }
+ .header {
+ background-color: @c-green;
+ border-bottom: 1px solid darken(@c-green, 10%);
+
+ h1 {
+ color: @c-dark;
+ text-shadow: 0px 1px 1px lighten(@c-green, 50%);
+ float: right;
+ line-height: 60px;
+ margin: 0 10px 0 0;
+
+ a {
+ color: black;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+ .remotes {
+ .remote {
+ padding-top: 6px;
+ }
+ form {
+ display: inline;
+ margin-bottom: 0;
+
+ a {
+ margin-right: 3px;
+
+ .button(@c-cream, #000, 12px, 1.45);
+ span { .button-span(@c-cream, #000, 12px, 1.45); }
+ &:hover { .button-hover(@c-cream, #000, 12px, 1.45); }
+ &:hover span { .button-hover-span(@c-cream, #000, 12px, 1.45); }
+ }
+ }
+ }
+ }
+ .content {
+ border-top: 1px solid lighten(@c-cream, 10%);
+
+ .navigation {
+ line-height: 1;
+ width: @content-width - 20px;
+ margin: 15px auto;
+ }
+ .wrap {
+ border: 1px solid darken(@c-cream, 20%);
+ .border-radius(7px);
+ .box-shadow(0px, 5px, 10px, rgba(0, 0, 0, 0.05));
+ @content-padding-horiz: 20px;
+
+ padding: 21px @content-padding-horiz;
+ width: @content-width - (@content-padding-horiz * 2);
+ background-color: @content-background;
+ }
+ }
+ .footer {
+ text-align: center;
+ margin-top: 14px;
+
+ a {
+ color: @c-dark;
+ font-weight: bold;
+ text-decoration: none;
+
+ &:hover {
+ color: @c-orange;
+ }
+ }
+ }
+ a {
+ color: saturate(darken(@c-orange, 10%), 10%);
+ text-decoration: none;
+ font-weight: bold;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ &:active {
+ color: @c-orange - #033;
+ }
+ }
+ .disabled {
+ display: none;
+ }
+}
+
+form {
+ .field {
+ position: relative;
+
+ label {
+ display: inline;
+
+ &.radio {
+ margin-right: 15px;
+ }
+ &.infield {
+ display: block;
+ font-family: @font-mono;
+ position: absolute;
+ top: 5px;
+ left: 5px;
+ }
+ }
+ textarea {
+ font-family: @font-mono;
+ padding: 5px;
+ .border-radius(2px);
+ border: 1px solid #444;
+ }
+ &.cuddly {
+ margin-top: -13px;
+ }
+ }
+}
+
+#index .content {
+ table {
+ width: 100%;
+
+ tr {
+ border: none;
+
+ &:nth-child(even) {
+ background-color: lighten(@c-cream, 8%);
+ }
+
+ td {
+ border: none;
+ line-height: 26px;
+ padding: 1px 5px 0;
+
+ a.changeset-link {
+ display: block;
+ width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ &.desc {
+ a {
+ display: inline-block;
+ width: 430px;
+ }
+ }
+ &.node {
+ padding-left: 0px;
+ width: 110px;
+
+ .rev {
+ font-weight: bold;
+ }
+ .sep {
+ font-weight: normal;
+ padding: 0px 2px 0px 1px;
+ color: lighten(@c-light, 40%);
+ }
+ .hash {
+ color: @c-light;
+ font: normal 11px @font-mono;
+ }
+ }
+ &.stats {
+ text-align: right;
+ padding-right: 0px;
+
+ .badge {
+ cursor: pointer;
+ color: white;
+ font-weight: bold;
+ display: inline-block;
+ line-height: 1;
+ padding: 3px 5px 4px;
+ margin-left: 4px;
+
+ .border-radius(3px);
+
+ &.yes {
+ background-color: green;
+ }
+ &.no {
+ background-color: red;
+ }
+ &.neutral {
+ background-color: #666;
+ }
+ &.comments {
+ background-color: @c-orange-light;
+ }
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ }
+ }
+ }
+ }
+ .pagination {
+ a.older {
+ float: right;
+ }
+ }
+}
+#changeset .content {
+ a.submit {
+ margin-right: 4px;
+ .button(@c-metal, #000, 12px, 1.45);
+ span { .button-span(@c-metal, #000, 12px, 1.45); }
+ &:hover { .button-hover(@c-metal, #000, 12px, 1.45); }
+ &:hover span { .button-hover-span(@c-metal, #000, 12px, 1.45); }
+ }
+ a.cancel, a.cancel-line, a.cancel-edit {
+ .button(@c-metal, #000, 12px, 1.45);
+ span { .button-span(@c-metal, #000, 12px, 1.45); }
+ &:hover { .button-hover(@c-metal, #000, 12px, 1.45); }
+ &:hover span { .button-hover-span(@c-metal, #000, 12px, 1.45); }
+ }
+ .navigation {
+ .middle {
+ display: inline-block;
+ width: 49%;
+ text-align: center;
+ }
+ .right {
+ display: inline-block;
+ width: 25%;
+ text-align: right;
+ }
+ .left {
+ display: inline-block;
+ width: 25%;
+ }
+ }
+ .head {
+ position: relative;
+
+ h2 {
+ width: 680px;
+ color: @c-light;
+
+ .desc {
+ color: @c-dark;
+ max-width: 500px;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ height: 20px;
+ }
+ }
+ .committer-avatar {
+ position: absolute;
+ top: -22px;
+ right: -21px;
+ width: 60px;
+ height: 60px;
+ border: 1px solid darken(@c-cream, 30%);
+ border-top: 1px solid darken(@c-cream, 30%);
+ border-right: 1px solid darken(@c-cream, 30%);
+ border: 1px solid #999;
+ .box-inner-shadow(1px, -1px, 10px, rgba(0,0,0,0.25));
+
+ -webkit-border-top-right-radius: 7px;
+ -moz-border-radius-topright: 7px;
+ border-top-right-radius: 7px;
+ -webkit-border-bottom-left-radius: 3px;
+ -moz-border-radius-bottomleft: 3px;
+ border-bottom-left-radius: 3px;
+ }
+ .patch {
+ position: absolute;
+ top: -18px;
+ right: -80px;
+ width: 50px;
+ }
+ .fulldesc {
+ width: 660px;
+ font-size: 12px;
+ line-height: 1.25;
+ margin-bottom: 14px;
+ overflow-x: auto;
+ overflow-y: hidden;
+ padding-bottom: 4px;
+ white-space: pre;
+ font-family: @font-mono;
+ }
+ }
+ .review-level-comments {
+ margin-top: 30px;
+ }
+ .edit-review-comment {
+ margin-top: 3px;
+ }
+ .activate a {
+ .button(@c-metal, #000, 12px, 1.45);
+ span { .button-span(@c-metal, #000, 12px, 1.45); }
+ &:hover { .button-hover(@c-metal, #000, 12px, 1.45); }
+ &:hover span { .button-hover-span(@c-metal, #000, 12px, 1.45); }
+ }
+ .edit-line {
+ display: inline-block;
+ margin-top: 3px;
+ }
+ .togglebox {
+ form {
+ float: left;
+ border: 1px solid #ccc;
+ .border-radius(3px);
+ padding: 15px 10px;
+ background-color: @c-soft-cream;
+ .box-shadow(0px, 5px, 5px, rgba(0, 0, 0, 0.02);
+ }
+ }
+ .placeholder {
+ font-style: italic;
+ color: @c-light;
+ }
+ .item-listing {
+ background-color: @c-soft-cream;
+ border: 1px solid #ccc;
+ margin-bottom: 14px;
+
+ .comment, .signoff {
+ padding: 8px 12px 8px 10px;
+ border-top: 1px solid #fff;
+ border-bottom: 1px solid #ddd;
+ position: relative;
+ min-height: 41px;
+
+ &:first-child {
+ border-top: none;
+ }
+ &:last-child {
+ border-bottom: none;
+ }
+ img {
+ float: right;
+ }
+ .message {
+ overflow-x: auto;
+ width: 690px;
+ padding-top: 3px;
+ }
+ .avatar img {
+ height: 30px;
+ width: 30px;
+ margin-top: 5px;
+ .border-radius(3px);
+ }
+ .expand {
+ position: absolute;
+ top: 17px;
+ right: -18px;
+ font-size: 14px;
+ font-weight: bold;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ .colorboxed {
+ display: none;
+ }
+ }
+ .signoff {
+ .signoff-opinion {
+ float: right;
+ font: bold 30px/1 @font-normal;
+ text-transform: capitalize;
+ margin-right: 15px;
+ margin-top: 5px;
+
+ &.yes { color: saturate(@c-green - #111, 20%); }
+ &.no { color: #c00; }
+ &.neutral { color: @c-cream - #333; }
+ }
+ .author {
+ .opinion {
+ font-weight: bold;
+ }
+ }
+ .message {
+ overflow-x: auto;
+ width: 620px;
+ }
+ }
+ }
+ .add-review-comment {
+ margin-bottom: 40px;
+ }
+
+ .add-signoff {
+ margin-bottom: 40px;
+
+ form {
+ .sign-off-as {
+ font-weight: bold;
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ .file {
+ .filename {
+ h3 {
+ a {
+ color: @c-dark;
+ }
+ }
+ }
+ .add-file-comment {
+ margin-bottom: 24px;
+ }
+ }
+ .diff {
+ overflow-x: auto;
+ border: 1px solid #ddd;
+ margin-bottom: 24px;
+
+ table {
+ width: 100%;
+ background-color: #f9f9f9;
+ margin-bottom: 0;
+
+ td.code:after {
+ color: #ccc;
+ content: "↩";
+ vertical-align: -1px;
+ line-height: 1;
+ }
+ tr {
+ &.rem {
+ color: black;
+ background-color: @c-diff-rem;
+
+ td.code:after {
+ color: white;
+ }
+ td.linenumber {
+ color: @c-dark;
+ }
+ }
+ &.add {
+ color: black;
+ background-color: @c-diff-add;
+
+ td.code:after {
+ color: white;
+ }
+ td.linenumber {
+ color: @c-dark;
+ }
+ }
+ &.viewing {
+ background-color: lighten(@c-cream, 5%);
+ }
+ &.commentable {
+ cursor: pointer;
+ }
+ &.chosen {
+ background-color: @c-cream;
+ }
+ &.comment-form td {
+ line-height: 15px;
+ padding: 10px 10px 15px 56px;
+ border-bottom: 1px solid #ddd;
+ border-top: 1px solid #ddd;
+ background-color: #f4f4f4;
+
+ textarea {
+ width: 500px;
+ height: 50px;
+ border: 1px solid @c-light;
+ }
+ }
+ }
+
+ td {
+ border: none;
+ line-height: 1;
+ padding: 1px 5px;
+
+ &.addrem-add, &.addrem-rem {
+ font-weight: bold;
+ text-align: center;
+ width: 10px;
+ }
+ &.linenumber {
+ width: 25px;
+ text-align: right;
+ font-family: @font-mono;
+ color: @c-light;
+ }
+ &.skip {
+ text-align: right;
+ color: @c-light;
+ }
+ &.code {
+ white-space: pre;
+ }
+ &.comment {
+ line-height: 15px;
+ padding: 10px 10px 10px 56px;
+ border-bottom: 1px solid #ddd;
+ border-top: 1px solid #ddd;
+ background-color: #f4f4f4;
+
+ .avatar {
+ float: right;
+ margin-top: 2px;
+
+ img {
+ height: 30px;
+ width: 30px;
+ .border-radius(3px);
+ }
+ }
+ .author {
+ padding-bottom: 3px;
+ }
+ .comment-content {
+ position: relative;
+ }
+ .expand {
+ position: absolute;
+ top: -13px;
+ right: -8px;
+ font-size: 14px;
+ font-weight: bold;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ .colorboxed {
+ display: none;
+ }
+ }
+ }
+ }
+ }
+}
+
+#colorbox {
+ #cboxLoadedContent {
+ padding: 10px;
+ }
+ #cboxContent {
+ position: relative;
+ }
+ .avatar {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+
+ }
+ .author {
+ font-size: 20px;
+ width: 400px;
+ margin-bottom: 20px;
+ }
+ .message {
+ overflow-x: auto;
+ max-width: 910px;
+ padding: 0 0 5px;
+ font-family: @font-mono;
+ white-space: pre;
+ }
+ .context {
+ .context-head {
+ color: @c-light;
+ font-size: 20px;
+ margin-top: -16px;
+ margin-bottom: 18px;
+ }
+ }
+}
--- a/review/static/ui.js Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-$(function() {
- $("a.fold").toggle(function(event) {
- $(event.target).closest(".file").find(".file-review-contents").slideUp("fast", function () {
- $(event.target).closest("h3").find(".status").html("→");
- });
- return false;
- }, function(event) {
- $(event.target).closest(".file").find(".file-review-contents").slideDown("fast", function() {
- $(event.target).closest("h3").find(".status").html("↓");
- });
- return false;
- });
-
- $(".submit").live('click', function() {
- $(this).closest("form").submit();
- });
-});
--- a/review/static/underscore.min.js Fri Aug 20 16:29:35 2010 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-(function(){var n=this,A=n._,r=typeof StopIteration!=="undefined"?StopIteration:"__break__",j=Array.prototype,l=Object.prototype,o=j.slice,B=j.unshift,C=l.toString,p=l.hasOwnProperty,s=j.forEach,t=j.map,u=j.reduce,v=j.reduceRight,w=j.filter,x=j.every,y=j.some,m=j.indexOf,z=j.lastIndexOf;l=Array.isArray;var D=Object.keys,b=function(a){return new k(a)};if(typeof exports!=="undefined")exports._=b;n._=b;b.VERSION="1.0.4";var i=b.forEach=function(a,c,d){try{if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=
-0,f=a.length;e<f;e++)c.call(d,a[e],e,a);else for(e in a)p.call(a,e)&&c.call(d,a[e],e,a)}catch(g){if(g!=r)throw g;}return a};b.map=function(a,c,d){if(t&&a.map===t)return a.map(c,d);var e=[];i(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(u&&a.reduce===u)return a.reduce(b.bind(d,e),c);i(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(v&&a.reduceRight===v)return a.reduceRight(b.bind(d,e),c);a=b.clone(b.toArray(a)).reverse();return b.reduce(a,
-c,d,e)};b.detect=function(a,c,d){var e;i(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.filter=function(a,c,d){if(w&&a.filter===w)return a.filter(c,d);var e=[];i(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];i(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.every=function(a,c,d){c=c||b.identity;if(x&&a.every===x)return a.every(c,d);var e=true;i(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.some=
-function(a,c,d){c=c||b.identity;if(y&&a.some===y)return a.some(c,d);var e=false;i(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c)!=-1;var d=false;i(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,
-a);var e={computed:-Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){var g=e.criteria,h=f.criteria;return g<h?-1:g>h?
-1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return o.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?o.call(a,0,c):a[0]};b.rest=function(a,c,d){return o.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};
-b.compact=function(a){return b.filter(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.filter(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.filter(b.uniq(a),function(d){return b.every(c,
-function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(m&&a.indexOf===m)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(z&&a.lastIndexOf===z)return a.lastIndexOf(c);for(var d=a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=
-1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||{},d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);i(c,function(d){a[d]=b.bind(a[d],a)});return a};b.memoize=function(a,c){var d={};c=c||b.identity;return function(){var e=c.apply(this,arguments);return e in
-d?d[e]:(d[e]=a.apply(this,arguments))}};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=D||function(a){if(b.isArray(a))return b.range(0,
-a.length);var c=[];for(var d in a)p.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){i(b.rest(arguments),function(c){for(var d in c)a[d]=c[d]});return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;
-if(a==c)return true;if(!a&&c||a&&!c)return false;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return false;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!(f in c)||!b.isEqual(a[f],
-c[f]))return false;return true};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(p.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=l||function(a){return!!(a&&a.concat&&a.unshift&&!a.callee)};b.isArguments=function(a){return a&&a.callee};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===
-+a||C.call(a)==="[object Number]"};b.isBoolean=function(a){return a===true||a===false};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){n._=A;return this};b.identity=function(a){return a};b.times=function(a,c,d){for(var e=
-0;e<a;e++)c.call(d,e)};b.breakLoop=function(){throw r;};b.mixin=function(a){i(b.functions(a),function(c){E(c,b[c]=a[c])})};var F=0;b.uniqueId=function(a){var c=F++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings,e=new RegExp("'(?=[^"+d.end.substr(0,1)+"]*"+d.end.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")+")","g");d=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g,
-" ").replace(e,"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?d(c):d};b.each=b.forEach;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.select=b.filter;b.all=b.every;b.any=b.some;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var k=function(a){this._wrapped=a},q=function(a,c){return c?b(a).chain():a},E=function(a,c){k.prototype[a]=function(){var d=b.toArray(arguments);
-B.call(d,this._wrapped);return q(c.apply(b,d),this._chain)}};b.mixin(b);i(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=j[a];k.prototype[a]=function(){c.apply(this._wrapped,arguments);return q(this._wrapped,this._chain)}});i(["concat","join","slice"],function(a){var c=j[a];k.prototype[a]=function(){return q(c.apply(this._wrapped,arguments),this._chain)}});k.prototype.chain=function(){this._chain=true;return this};k.prototype.value=function(){return this._wrapped}})();
\ No newline at end of file
--- a/review/templates/base.html Fri Aug 20 16:29:35 2010 +0200
+++ b/review/templates/base.html Wed Jun 29 19:47:16 2011 -0400
@@ -4,17 +4,18 @@
<html>
<head>
<title>{% block title %}{% endblock %}Reviewing {{ title }} with hg-review</title>
- <link rel="stylesheet" href="/static/aal.css" type="text/css" media="screen" />
- <link rel="stylesheet" href="/static/colorbox/colorbox.css" type="text/css" media="screen" />
- <link rel="stylesheet" href="/static/style.css" type="text/css" media="screen" />
- <link rel="stylesheet" href="/static/extra.css" type="text/css" media="screen" />
+ <link rel="stylesheet" href="/static/styles/aal.css" type="text/css" media="screen" />
+ <link rel="stylesheet" href="/static/scripts/colorbox/colorbox.css" type="text/css" media="screen" />
+ <link rel="stylesheet/less" href="/static/styles/style.less" type="text/css" media="screen" />
+ <link rel="stylesheet" href="/static/styles/extra.css" type="text/css" media="screen" />
- <script type="text/javascript" src="/static/jquery-1.4.2.min.js"></script>
- <script type="text/javascript" src="/static/jquery.infieldlabel.min.js"></script>
- <script type="text/javascript" src="/static/underscore.min.js"></script>
- <script type="text/javascript" src="/static/colorbox/jquery.colorbox.js"></script>
- <script type="text/javascript" src="/static/ui.js"></script>
- <script type="text/javascript" src="/static/comments.js"></script>
+ <script src="/static/scripts/less.js" type="text/javascript"></script>
+ <script type="text/javascript" src="/static/scripts/jquery.js"></script>
+ <script type="text/javascript" src="/static/scripts/jquery.infieldlabels.js"></script>
+ <script type="text/javascript" src="/static/scripts/underscore.js"></script>
+ <script type="text/javascript" src="/static/scripts/colorbox/jquery.colorbox.js"></script>
+ <script type="text/javascript" src="/static/scripts/ui.js"></script>
+ <script type="text/javascript" src="/static/scripts/comments.js"></script>
</head>
<body id="{% block id %}{% endblock %}">
--- a/review/templates/changeset.html Fri Aug 20 16:29:35 2010 +0200
+++ b/review/templates/changeset.html Wed Jun 29 19:47:16 2011 -0400
@@ -34,9 +34,7 @@
<a href="mailto:{{ utils['email'](rev.user()) }}">{{ utils['person'](rev.user()) }}</a>
</h2>
- {% if utils['len'](rev.description().splitlines()) > 1 %}
- <div class="fulldesc">{{ "\n".join(rev.description().splitlines()[1:]).strip() }}</div>
- {% endif %}
+ <div class="fulldesc">{{ rev.description() }}</div>
</div>
{% with %}
--- a/review/templates/index.html Fri Aug 20 16:29:35 2010 +0200
+++ b/review/templates/index.html Wed Jun 29 19:47:16 2011 -0400
@@ -22,7 +22,7 @@
{% set signoffs = utils['categorize_signoffs'](rcset.signoffs) %}
{% if comments %}
- <a class="badge comments" href="{{ link }}">{{ utils['len'](comments) }} comment{% if utils['len'](comments) > 1 %}s{% endif %}</a>
+ <a class="badge comments" href="{{ link }}">{{ utils['len'](comments) }} comment{% if utils['len'](comments) > 1 %}s{% endif %}</a>
{% endif %}
{% if signoffs['yes'] %}
--- a/review/web.py Fri Aug 20 16:29:35 2010 +0200
+++ b/review/web.py Wed Jun 29 19:47:16 2011 -0400
@@ -250,10 +250,4 @@
app.title = os.path.basename(repo.root)
app.project_url = None
- if app.debug:
- try:
- from flaskext.lesscss import lesscss
- lesscss(app)
- except ImportError:
- ui.warn('Could not find flask-lesscss, not auto-rendering .less files!\n')
app.run(host=address, port=port)