Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Silva <sebastian@sugarlabs.org>2011-10-12 00:54:31 (GMT)
committer Sebastian Silva <sebastian@sugarlabs.org>2011-10-12 00:54:31 (GMT)
commitfe1a1eb79bf0f1df8bbc56d2402e32061af79d06 (patch)
treed39e3b7780e4b6949250d490a4a7a874f788981c
parent5861585e94a32b3032ac473804bf90c6e1363940 (diff)
Tidy up code a bit - added documentation
-rw-r--r--icons/document-generic.pngbin0 -> 722 bytes
-rw-r--r--icons/folder.pngbin0 -> 1132 bytes
-rw-r--r--icons/image-x-generic.pngbin0 -> 1172 bytes
-rw-r--r--icons/text-uri-list.pngbin0 -> 1445 bytes
-rw-r--r--icons/text-x-generic.pngbin0 -> 761 bytes
-rw-r--r--icons/text-x-python.pngbin0 -> 1601 bytes
-rwxr-xr-xrun.sh5
-rw-r--r--studio/__init__.py0
-rw-r--r--studio/static/css/main.css111
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.pngbin0 -> 211 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.pngbin0 -> 178 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.pngbin0 -> 180 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.pngbin0 -> 209 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.pngbin0 -> 22287 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.pngbin0 -> 97 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.pngbin0 -> 148 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-icons_000000_256x240.pngbin0 -> 4369 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--studio/static/css/sugar-theme/images/ui-icons_ffffff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--studio/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css568
-rw-r--r--studio/static/doc/flask-docs/.buildinfo4
-rw-r--r--studio/static/doc/flask-docs/_images/debugger.pngbin0 -> 123545 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/debugger1.pngbin0 -> 123545 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/debugger2.pngbin0 -> 123545 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/debugger3.pngbin0 -> 123545 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/debugger4.pngbin0 -> 123545 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/debugger5.pngbin0 -> 123545 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/flaskr.pngbin0 -> 53571 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/logo-full.pngbin0 -> 20806 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/logo-full1.pngbin0 -> 20806 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/logo-full2.pngbin0 -> 20806 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/no.pngbin0 -> 317 bytes
-rw-r--r--studio/static/doc/flask-docs/_images/yes.pngbin0 -> 277 bytes
-rw-r--r--studio/static/doc/flask-docs/_sources/api.txt624
-rw-r--r--studio/static/doc/flask-docs/_sources/becomingbig.txt88
-rw-r--r--studio/static/doc/flask-docs/_sources/blueprints.txt203
-rw-r--r--studio/static/doc/flask-docs/_sources/changelog.txt1
-rw-r--r--studio/static/doc/flask-docs/_sources/config.txt384
-rw-r--r--studio/static/doc/flask-docs/_sources/deploying/cgi.txt46
-rw-r--r--studio/static/doc/flask-docs/_sources/deploying/fastcgi.txt164
-rw-r--r--studio/static/doc/flask-docs/_sources/deploying/index.txt23
-rw-r--r--studio/static/doc/flask-docs/_sources/deploying/mod_wsgi.txt167
-rw-r--r--studio/static/doc/flask-docs/_sources/deploying/others.txt102
-rw-r--r--studio/static/doc/flask-docs/_sources/deploying/uwsgi.txt68
-rw-r--r--studio/static/doc/flask-docs/_sources/design.txt191
-rw-r--r--studio/static/doc/flask-docs/_sources/errorhandling.txt237
-rw-r--r--studio/static/doc/flask-docs/_sources/extensiondev.txt387
-rw-r--r--studio/static/doc/flask-docs/_sources/extensions.txt48
-rw-r--r--studio/static/doc/flask-docs/_sources/foreword.txt100
-rw-r--r--studio/static/doc/flask-docs/_sources/htmlfaq.txt207
-rw-r--r--studio/static/doc/flask-docs/_sources/index.txt30
-rw-r--r--studio/static/doc/flask-docs/_sources/installation.txt175
-rw-r--r--studio/static/doc/flask-docs/_sources/latexindex.txt6
-rw-r--r--studio/static/doc/flask-docs/_sources/license.txt48
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/appdispatch.txt170
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/appfactories.txt75
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/caching.txt69
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/deferredcallbacks.txt73
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/distribute.txt166
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/errorpages.txt77
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/fabric.txt196
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/favicon.txt53
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/fileuploads.txt181
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/flashing.txt119
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/index.txt39
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/jquery.txt167
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/lazyloading.txt104
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/mongokit.txt144
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/packages.txt115
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/sqlalchemy.txt214
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/sqlite3.txt119
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/streaming.txt61
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/templateinheritance.txt69
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/urlprocessors.txt126
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/viewdecorators.txt168
-rw-r--r--studio/static/doc/flask-docs/_sources/patterns/wtforms.txt124
-rw-r--r--studio/static/doc/flask-docs/_sources/quickstart.txt824
-rw-r--r--studio/static/doc/flask-docs/_sources/reqcontext.txt239
-rw-r--r--studio/static/doc/flask-docs/_sources/security.txt175
-rw-r--r--studio/static/doc/flask-docs/_sources/shell.txt93
-rw-r--r--studio/static/doc/flask-docs/_sources/signals.txt255
-rw-r--r--studio/static/doc/flask-docs/_sources/styleguide.txt200
-rw-r--r--studio/static/doc/flask-docs/_sources/templating.txt188
-rw-r--r--studio/static/doc/flask-docs/_sources/testing.txt307
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/css.txt31
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/dbcon.txt57
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/dbinit.txt67
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/folders.txt23
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/index.txt32
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/introduction.txt33
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/schema.txt25
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/setup.txt90
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/templates.txt111
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/testing.txt10
-rw-r--r--studio/static/doc/flask-docs/_sources/tutorial/views.txt98
-rw-r--r--studio/static/doc/flask-docs/_sources/unicode.txt107
-rw-r--r--studio/static/doc/flask-docs/_sources/upgrading.txt324
-rw-r--r--studio/static/doc/flask-docs/_sources/views.txt227
-rw-r--r--studio/static/doc/flask-docs/_static/ajax-loader.gifbin0 -> 673 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/basic.css540
-rw-r--r--studio/static/doc/flask-docs/_static/comment-bright.pngbin0 -> 3500 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/comment-close.pngbin0 -> 3578 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/comment.pngbin0 -> 3445 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/debugger.pngbin0 -> 123545 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/doctools.js247
-rw-r--r--studio/static/doc/flask-docs/_static/down-pressed.pngbin0 -> 368 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/down.pngbin0 -> 363 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/file.pngbin0 -> 392 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/flask.pngbin0 -> 9925 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/flaskr.pngbin0 -> 53571 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/flasky.css387
-rw-r--r--studio/static/doc/flask-docs/_static/jquery.js154
-rw-r--r--studio/static/doc/flask-docs/_static/logo-full.pngbin0 -> 20806 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/minus.pngbin0 -> 199 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/no.pngbin0 -> 317 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/plus.pngbin0 -> 199 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/pygments.css70
-rw-r--r--studio/static/doc/flask-docs/_static/searchtools.js556
-rw-r--r--studio/static/doc/flask-docs/_static/small_flask.css70
-rw-r--r--studio/static/doc/flask-docs/_static/touch-icon.pngbin0 -> 3624 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/underscore.js23
-rw-r--r--studio/static/doc/flask-docs/_static/up-pressed.pngbin0 -> 372 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/up.pngbin0 -> 363 bytes
-rw-r--r--studio/static/doc/flask-docs/_static/websupport.js808
-rw-r--r--studio/static/doc/flask-docs/_static/yes.pngbin0 -> 277 bytes
-rw-r--r--studio/static/doc/flask-docs/api.html3475
-rw-r--r--studio/static/doc/flask-docs/becomingbig.html187
-rw-r--r--studio/static/doc/flask-docs/blueprints.html306
-rw-r--r--studio/static/doc/flask-docs/changelog.html418
-rw-r--r--studio/static/doc/flask-docs/config.html505
-rw-r--r--studio/static/doc/flask-docs/deploying/cgi.html153
-rw-r--r--studio/static/doc/flask-docs/deploying/fastcgi.html259
-rw-r--r--studio/static/doc/flask-docs/deploying/index.html150
-rw-r--r--studio/static/doc/flask-docs/deploying/mod_wsgi.html255
-rw-r--r--studio/static/doc/flask-docs/deploying/others.html200
-rw-r--r--studio/static/doc/flask-docs/deploying/uwsgi.html167
-rw-r--r--studio/static/doc/flask-docs/design.html276
-rw-r--r--studio/static/doc/flask-docs/errorhandling.html347
-rw-r--r--studio/static/doc/flask-docs/extensiondev.html475
-rw-r--r--studio/static/doc/flask-docs/extensions.html153
-rw-r--r--studio/static/doc/flask-docs/foreword.html198
-rw-r--r--studio/static/doc/flask-docs/genindex.html1287
-rw-r--r--studio/static/doc/flask-docs/htmlfaq.html357
-rw-r--r--studio/static/doc/flask-docs/index.html426
-rw-r--r--studio/static/doc/flask-docs/installation.html249
-rw-r--r--studio/static/doc/flask-docs/latexindex.html406
-rw-r--r--studio/static/doc/flask-docs/license.html220
-rw-r--r--studio/static/doc/flask-docs/objects.invbin0 -> 3272 bytes
-rw-r--r--studio/static/doc/flask-docs/patterns/appdispatch.html273
-rw-r--r--studio/static/doc/flask-docs/patterns/appfactories.html186
-rw-r--r--studio/static/doc/flask-docs/patterns/caching.html178
-rw-r--r--studio/static/doc/flask-docs/patterns/deferredcallbacks.html180
-rw-r--r--studio/static/doc/flask-docs/patterns/distribute.html254
-rw-r--r--studio/static/doc/flask-docs/patterns/errorpages.html182
-rw-r--r--studio/static/doc/flask-docs/patterns/fabric.html298
-rw-r--r--studio/static/doc/flask-docs/patterns/favicon.html162
-rw-r--r--studio/static/doc/flask-docs/patterns/fileuploads.html283
-rw-r--r--studio/static/doc/flask-docs/patterns/flashing.html221
-rw-r--r--studio/static/doc/flask-docs/patterns/index.html240
-rw-r--r--studio/static/doc/flask-docs/patterns/jquery.html261
-rw-r--r--studio/static/doc/flask-docs/patterns/lazyloading.html214
-rw-r--r--studio/static/doc/flask-docs/patterns/mongokit.html241
-rw-r--r--studio/static/doc/flask-docs/patterns/packages.html219
-rw-r--r--studio/static/doc/flask-docs/patterns/sqlalchemy.html310
-rw-r--r--studio/static/doc/flask-docs/patterns/sqlite3.html226
-rw-r--r--studio/static/doc/flask-docs/patterns/streaming.html172
-rw-r--r--studio/static/doc/flask-docs/patterns/templateinheritance.html173
-rw-r--r--studio/static/doc/flask-docs/patterns/urlprocessors.html237
-rw-r--r--studio/static/doc/flask-docs/patterns/viewdecorators.html274
-rw-r--r--studio/static/doc/flask-docs/patterns/wtforms.html224
-rw-r--r--studio/static/doc/flask-docs/quickstart.html875
-rw-r--r--studio/static/doc/flask-docs/reqcontext.html330
-rw-r--r--studio/static/doc/flask-docs/search.html101
-rw-r--r--studio/static/doc/flask-docs/searchindex.js1
-rw-r--r--studio/static/doc/flask-docs/security.html260
-rw-r--r--studio/static/doc/flask-docs/shell.html194
-rw-r--r--studio/static/doc/flask-docs/signals.html358
-rw-r--r--studio/static/doc/flask-docs/styleguide.html321
-rw-r--r--studio/static/doc/flask-docs/templating.html293
-rw-r--r--studio/static/doc/flask-docs/testing.html391
-rw-r--r--studio/static/doc/flask-docs/tutorial/css.html132
-rw-r--r--studio/static/doc/flask-docs/tutorial/dbcon.html154
-rw-r--r--studio/static/doc/flask-docs/tutorial/dbinit.html166
-rw-r--r--studio/static/doc/flask-docs/tutorial/folders.html122
-rw-r--r--studio/static/doc/flask-docs/tutorial/index.html137
-rw-r--r--studio/static/doc/flask-docs/tutorial/introduction.html128
-rw-r--r--studio/static/doc/flask-docs/tutorial/schema.html125
-rw-r--r--studio/static/doc/flask-docs/tutorial/setup.html183
-rw-r--r--studio/static/doc/flask-docs/tutorial/templates.html212
-rw-r--r--studio/static/doc/flask-docs/tutorial/testing.html113
-rw-r--r--studio/static/doc/flask-docs/tutorial/views.html206
-rw-r--r--studio/static/doc/flask-docs/unicode.html215
-rw-r--r--studio/static/doc/flask-docs/upgrading.html421
-rw-r--r--studio/static/doc/flask-docs/views.html355
-rw-r--r--studio/static/doc/myosa/META-INF/container.xml6
-rw-r--r--studio/static/doc/myosa/ch000_table_of_contents.xhtml3
-rw-r--r--studio/static/doc/myosa/ch001_introduction.xhtml45
-rw-r--r--studio/static/doc/myosa/ch002_what-is-sugar.xhtml61
-rw-r--r--studio/static/doc/myosa/ch003_what-is-a-sugar-activity.xhtml22
-rw-r--r--studio/static/doc/myosa/ch004_what-do-i-need-to-know-to-write-a-sugar-activity.xhtml41
-rw-r--r--studio/static/doc/myosa/ch006_setting-up-a-development-environment.xhtml133
-rw-r--r--studio/static/doc/myosa/ch007_creating-your-first-activity.xhtml49
-rw-r--r--studio/static/doc/myosa/ch008_a-standalone-python-program-for-reading-etexts.xhtml244
-rw-r--r--studio/static/doc/myosa/ch009_inherit-from-sugaractivityactivity.xhtml279
-rw-r--r--studio/static/doc/myosa/ch010_package-the-activity.xhtml208
-rw-r--r--studio/static/doc/myosa/ch011_add-refinements.xhtml625
-rw-r--r--studio/static/doc/myosa/ch012_add-your-activity-code-to-version-control.xhtml336
-rw-r--r--studio/static/doc/myosa/ch013_going-international-with-pootle.xhtml137
-rw-r--r--studio/static/doc/myosa/ch014_distribute-your-activity.xhtml92
-rw-r--r--studio/static/doc/myosa/ch015_debugging-sugar-activities.xhtml259
-rw-r--r--studio/static/doc/myosa/ch017_making-shared-activities.xhtml2185
-rw-r--r--studio/static/doc/myosa/ch018_adding-text-to-speech.xhtml695
-rw-r--r--studio/static/doc/myosa/ch019_fun-with-the-journal.xhtml1085
-rw-r--r--studio/static/doc/myosa/ch020_making-activities-using-pygame.xhtml536
-rw-r--r--studio/static/doc/myosa/ch021_making-new-style-toolbars.xhtml1150
-rw-r--r--studio/static/doc/myosa/ch023_where-to-go-from-here.xhtml46
-rw-r--r--studio/static/doc/myosa/ch024_about-the-authors.xhtml31
-rw-r--r--studio/static/doc/myosa/ch025_credits.xhtml176
-rw-r--r--studio/static/doc/myosa/content.opf126
-rw-r--r--studio/static/doc/myosa/mimetype1
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-CoverImage600x450-en.pngbin0 -> 205403 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_01_1-en.jpgbin0 -> 34804 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_02-en.jpgbin0 -> 45789 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_03-en.jpgbin0 -> 33653 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_04-en.jpgbin0 -> 31931 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_05-en.jpgbin0 -> 32213 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander1-en.jpgbin0 -> 37783 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander2-en.jpgbin0 -> 48574 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-SpeakActivity-en.pngbin0 -> 36205 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-analyzeactivity_1-en.jpgbin0 -> 62877 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-batallanaval-en.jpgbin0 -> 39257 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-collab1_1-en.jpgbin0 -> 31212 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-collab2_1-en.jpgbin0 -> 14702 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-collab3_1-en.jpgbin0 -> 19833 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-collab4-en.jpgbin0 -> 23415 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-collab5-en.jpgbin0 -> 9911 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-collab6-en.jpgbin0 -> 6280 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle1-en.jpgbin0 -> 26578 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle2_1-en.jpgbin0 -> 26965 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-eric-en.jpgbin0 -> 81814 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak-en.jpgbin0 -> 54496 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak2_1-en.jpgbin0 -> 59308 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak3-en.jpgbin0 -> 87646 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-gimp1-en.jpgbin0 -> 23869 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git1-en.jpgbin0 -> 11035 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git10-en.jpgbin0 -> 75087 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git11_1-en.jpgbin0 -> 78179 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git12-en.jpgbin0 -> 38527 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git13-en.jpgbin0 -> 18516 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git14-en.jpgbin0 -> 67245 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git2-en.jpgbin0 -> 32774 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git3-en.jpgbin0 -> 49867 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git4-en.jpgbin0 -> 10053 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git5-en.jpgbin0 -> 54709 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git6-en.jpgbin0 -> 57414 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git7-en.jpgbin0 -> 69235 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git8-en.jpgbin0 -> 70787 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-git9-en.jpgbin0 -> 47536 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-inkscape-en.jpgbin0 -> 57170 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-journal_main_screen-en.pngbin0 -> 61870 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity1-en.jpgbin0 -> 89934 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity2-en.jpgbin0 -> 55389 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar1_1-en.jpgbin0 -> 5352 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar2-en.jpgbin0 -> 10164 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar3-en.jpgbin0 -> 8582 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar4-en.jpgbin0 -> 8952 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar5-en.jpgbin0 -> 69720 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-oldtoolbar-en.jpgbin0 -> 6277 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle1-en.jpgbin0 -> 18451 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle2-en.jpgbin0 -> 42319 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle3-en.jpgbin0 -> 50011 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-scribble-en.jpgbin0 -> 26461 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-spe-en.jpgbin0 -> 43229 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-sugargame_1-en.jpgbin0 -> 9868 bytes
-rw-r--r--studio/static/doc/myosa/static/ActivitiesGuideSugar-thegimp-en.jpgbin0 -> 73629 bytes
-rw-r--r--studio/static/doc/myosa/static/Floss-100-en.gifbin0 -> 1290 bytes
-rw-r--r--studio/static/doc/myosa/static/MYOSA_Cover.pngbin0 -> 229733 bytes
-rw-r--r--studio/static/doc/myosa/toc.ncx170
-rw-r--r--studio/static/doc/sugar-theme.html369
l---------studio/static/icons1
-rw-r--r--studio/static/images/logo-flask-s.pngbin0 -> 7120 bytes
-rw-r--r--studio/static/images/logo-jqueryui.gifbin0 -> 2891 bytes
-rw-r--r--studio/static/images/logo-sugar-trans.pngbin0 -> 10355 bytes
-rw-r--r--studio/static/images/throbber.gifbin0 -> 10701 bytes
-rw-r--r--studio/static/init.html26
-rw-r--r--studio/static/js/ace/ace-uncompressed.js15284
-rw-r--r--studio/static/js/ace/ace.js1
-rw-r--r--studio/static/js/ace/cockpit-uncompressed.js2504
-rw-r--r--studio/static/js/ace/cockpit.js1
-rw-r--r--studio/static/js/ace/keybinding-emacs.js1
-rw-r--r--studio/static/js/ace/keybinding-vim.js1
-rw-r--r--studio/static/js/ace/mode-c_cpp.js1
-rw-r--r--studio/static/js/ace/mode-clojure.js1
-rw-r--r--studio/static/js/ace/mode-coffee.js1
-rw-r--r--studio/static/js/ace/mode-csharp.js1
-rw-r--r--studio/static/js/ace/mode-css.js1
-rw-r--r--studio/static/js/ace/mode-html.js1
-rw-r--r--studio/static/js/ace/mode-java.js1
-rw-r--r--studio/static/js/ace/mode-javascript.js1
-rw-r--r--studio/static/js/ace/mode-json.js1
-rw-r--r--studio/static/js/ace/mode-perl.js1
-rw-r--r--studio/static/js/ace/mode-php.js1
-rw-r--r--studio/static/js/ace/mode-python.js1
-rw-r--r--studio/static/js/ace/mode-ruby.js1
-rw-r--r--studio/static/js/ace/mode-scss.js1
-rw-r--r--studio/static/js/ace/mode-svg.js1
-rw-r--r--studio/static/js/ace/mode-xml.js1
-rw-r--r--studio/static/js/ace/theme-clouds.js1
-rw-r--r--studio/static/js/ace/theme-clouds_midnight.js1
-rw-r--r--studio/static/js/ace/theme-cobalt.js1
-rw-r--r--studio/static/js/ace/theme-dawn.js1
-rw-r--r--studio/static/js/ace/theme-eclipse.js1
-rw-r--r--studio/static/js/ace/theme-idle_fingers.js1
-rw-r--r--studio/static/js/ace/theme-kr_theme.js1
-rw-r--r--studio/static/js/ace/theme-merbivore.js1
-rw-r--r--studio/static/js/ace/theme-merbivore_soft.js1
-rw-r--r--studio/static/js/ace/theme-mono_industrial.js1
-rw-r--r--studio/static/js/ace/theme-monokai.js1
-rw-r--r--studio/static/js/ace/theme-pastel_on_dark.js1
-rw-r--r--studio/static/js/ace/theme-twilight.js1
-rw-r--r--studio/static/js/ace/theme-vibrant_ink.js1
-rw-r--r--studio/static/js/ace/worker-coffee.js1
-rw-r--r--studio/static/js/ace/worker-css.js1
-rw-r--r--studio/static/js/ace/worker-javascript.js1
-rw-r--r--studio/static/js/jquery-1.6.2.js8981
-rw-r--r--studio/static/js/jquery-ui-1.8.16.sugar.min.js791
-rw-r--r--studio/static/js/jquery.corner.js249
-rw-r--r--studio/static/js/jquery.plugin.svgimg.js33
-rw-r--r--studio/static/js/jquery.quicksand.js307
-rw-r--r--studio/static/js/websdk-1.js6
-rw-r--r--studio/static/js/wymeditor/iframe/.svn/entries31
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/entries402
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-blockquote.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h1.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h2.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h3.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h4.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h5.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h6.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-p.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-pre.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-basebin0 -> 196 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-basebin0 -> 166 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-basebin0 -> 172 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-basebin0 -> 170 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-basebin0 -> 172 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-basebin0 -> 172 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-basebin0 -> 171 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-basebin0 -> 3607 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-basebin0 -> 177 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.css.svn-base90
-rw-r--r--studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.html.svn-base26
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-blockquote.pngbin0 -> 196 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-h1.pngbin0 -> 166 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-h2.pngbin0 -> 172 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-h3.pngbin0 -> 170 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-h4.pngbin0 -> 172 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-h5.pngbin0 -> 172 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-h6.pngbin0 -> 171 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-p.pngbin0 -> 3607 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/lbl-pre.pngbin0 -> 177 bytes
-rw-r--r--studio/static/js/wymeditor/iframe/default/wymiframe.css90
-rw-r--r--studio/static/js/wymeditor/iframe/default/wymiframe.html26
-rw-r--r--studio/static/js/wymeditor/jquery.wymeditor.explorer.js218
-rw-r--r--studio/static/js/wymeditor/jquery.wymeditor.js3860
-rw-r--r--studio/static/js/wymeditor/jquery.wymeditor.min.js1
-rw-r--r--studio/static/js/wymeditor/jquery.wymeditor.mozilla.js278
-rw-r--r--studio/static/js/wymeditor/jquery.wymeditor.opera.js119
-rw-r--r--studio/static/js/wymeditor/jquery.wymeditor.pack.js1
-rw-r--r--studio/static/js/wymeditor/jquery.wymeditor.safari.js247
-rw-r--r--studio/static/js/wymeditor/lang/.svn/entries912
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/bg.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/ca.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/cs.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/cy.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/de.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/en.js.svn-base46
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/es.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/fa.js.svn-base46
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/fi.js.svn-base44
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/fr.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/gl.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/he.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/hr.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/hu.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/it.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/ja.js.svn-base44
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/nb.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/nl.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/nn.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/pl.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/pt-br.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/pt.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/ru.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/sv.js.svn-base46
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/tr.js.svn-base45
-rw-r--r--studio/static/js/wymeditor/lang/.svn/text-base/zh_cn.js.svn-base47
-rw-r--r--studio/static/js/wymeditor/lang/bg.js45
-rw-r--r--studio/static/js/wymeditor/lang/ca.js45
-rw-r--r--studio/static/js/wymeditor/lang/cs.js45
-rw-r--r--studio/static/js/wymeditor/lang/cy.js45
-rw-r--r--studio/static/js/wymeditor/lang/de.js45
-rw-r--r--studio/static/js/wymeditor/lang/en.js46
-rw-r--r--studio/static/js/wymeditor/lang/es.js45
-rw-r--r--studio/static/js/wymeditor/lang/fa.js46
-rw-r--r--studio/static/js/wymeditor/lang/fi.js44
-rw-r--r--studio/static/js/wymeditor/lang/fr.js45
-rw-r--r--studio/static/js/wymeditor/lang/gl.js45
-rw-r--r--studio/static/js/wymeditor/lang/he.js45
-rw-r--r--studio/static/js/wymeditor/lang/hr.js45
-rw-r--r--studio/static/js/wymeditor/lang/hu.js45
-rw-r--r--studio/static/js/wymeditor/lang/it.js45
-rw-r--r--studio/static/js/wymeditor/lang/ja.js44
-rw-r--r--studio/static/js/wymeditor/lang/nb.js45
-rw-r--r--studio/static/js/wymeditor/lang/nl.js45
-rw-r--r--studio/static/js/wymeditor/lang/nn.js45
-rw-r--r--studio/static/js/wymeditor/lang/pl.js45
-rw-r--r--studio/static/js/wymeditor/lang/pt-br.js45
-rw-r--r--studio/static/js/wymeditor/lang/pt.js45
-rw-r--r--studio/static/js/wymeditor/lang/ru.js45
-rw-r--r--studio/static/js/wymeditor/lang/sv.js46
-rw-r--r--studio/static/js/wymeditor/lang/tr.js45
-rw-r--r--studio/static/js/wymeditor/lang/zh_cn.js47
-rw-r--r--studio/static/js/wymeditor/plugins/.svn/entries43
-rw-r--r--studio/static/js/wymeditor/plugins/embed/.svn/entries62
-rw-r--r--studio/static/js/wymeditor/plugins/embed/.svn/text-base/jquery.wymeditor.embed.js.svn-base52
-rw-r--r--studio/static/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js52
-rw-r--r--studio/static/js/wymeditor/plugins/fullscreen/.svn/entries96
-rw-r--r--studio/static/js/wymeditor/plugins/fullscreen/.svn/prop-base/icon_fullscreen.gif.svn-base5
-rw-r--r--studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-basebin0 -> 509 bytes
-rw-r--r--studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/jquery.wymeditor.fullscreen.js.svn-base127
-rw-r--r--studio/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gifbin0 -> 509 bytes
-rw-r--r--studio/static/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js127
-rw-r--r--studio/static/js/wymeditor/plugins/hovertools/.svn/entries62
-rw-r--r--studio/static/js/wymeditor/plugins/hovertools/.svn/text-base/jquery.wymeditor.hovertools.js.svn-base57
-rw-r--r--studio/static/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js57
-rw-r--r--studio/static/js/wymeditor/plugins/resizable/.svn/entries96
-rw-r--r--studio/static/js/wymeditor/plugins/resizable/.svn/text-base/jquery.wymeditor.resizable.js.svn-base91
-rw-r--r--studio/static/js/wymeditor/plugins/resizable/.svn/text-base/readme.txt.svn-base124
-rw-r--r--studio/static/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js91
-rw-r--r--studio/static/js/wymeditor/plugins/resizable/readme.txt124
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/.svn/entries164
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/.svn/prop-base/wand.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/.svn/text-base/README.svn-base19
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/.svn/text-base/jquery.wymeditor.tidy.js.svn-base82
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/.svn/text-base/tidy.php.svn-base36
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-basebin0 -> 715 bytes
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/README19
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js82
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/tidy.php36
-rw-r--r--studio/static/js/wymeditor/plugins/tidy/wand.pngbin0 -> 715 bytes
-rw-r--r--studio/static/js/wymeditor/skins/.svn/entries77
-rw-r--r--studio/static/js/wymeditor/skins/.svn/prop-base/wymeditor_icon.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-basebin0 -> 1028 bytes
-rw-r--r--studio/static/js/wymeditor/skins/compact/.svn/entries130
-rw-r--r--studio/static/js/wymeditor/skins/compact/.svn/prop-base/icons.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-basebin0 -> 3651 bytes
-rw-r--r--studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.css.svn-base134
-rw-r--r--studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.js.svn-base35
-rw-r--r--studio/static/js/wymeditor/skins/compact/icons.pngbin0 -> 3651 bytes
-rw-r--r--studio/static/js/wymeditor/skins/compact/skin.css134
-rw-r--r--studio/static/js/wymeditor/skins/compact/skin.js35
-rw-r--r--studio/static/js/wymeditor/skins/default/.svn/entries130
-rw-r--r--studio/static/js/wymeditor/skins/default/.svn/prop-base/icons.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-basebin0 -> 3651 bytes
-rw-r--r--studio/static/js/wymeditor/skins/default/.svn/text-base/skin.css.svn-base133
-rw-r--r--studio/static/js/wymeditor/skins/default/.svn/text-base/skin.js.svn-base40
-rw-r--r--studio/static/js/wymeditor/skins/default/icons.pngbin0 -> 3651 bytes
-rw-r--r--studio/static/js/wymeditor/skins/default/skin.css133
-rw-r--r--studio/static/js/wymeditor/skins/default/skin.js40
-rw-r--r--studio/static/js/wymeditor/skins/minimal/.svn/entries99
-rw-r--r--studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.css.svn-base131
-rw-r--r--studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.js.svn-base30
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/entries164
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.header.gif.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.selector.silver.gif.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.wymeditor.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/icons.silver.gif.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-basebin0 -> 781 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.selector.silver.gif.svn-basebin0 -> 1621 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-basebin0 -> 498 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-basebin0 -> 15382 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/bg.header.gifbin0 -> 781 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gifbin0 -> 1621 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/bg.wymeditor.pngbin0 -> 498 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/images/icons.silver.gifbin0 -> 15382 bytes
-rw-r--r--studio/static/js/wymeditor/skins/minimal/skin.css131
-rw-r--r--studio/static/js/wymeditor/skins/minimal/skin.js30
-rw-r--r--studio/static/js/wymeditor/skins/silver/.svn/entries167
-rw-r--r--studio/static/js/wymeditor/skins/silver/.svn/prop-base/COPYING.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/silver/.svn/prop-base/README.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/silver/.svn/text-base/COPYING.svn-base674
-rw-r--r--studio/static/js/wymeditor/skins/silver/.svn/text-base/README.svn-base27
-rw-r--r--studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.css.svn-base297
-rw-r--r--studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.js.svn-base61
-rwxr-xr-xstudio/static/js/wymeditor/skins/silver/COPYING674
-rwxr-xr-xstudio/static/js/wymeditor/skins/silver/README27
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/entries164
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.header.gif.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.selector.silver.gif.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.wymeditor.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/icons.silver.gif.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-basebin0 -> 781 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.selector.silver.gif.svn-basebin0 -> 1621 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-basebin0 -> 498 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-basebin0 -> 15382 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/bg.header.gifbin0 -> 781 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/bg.selector.silver.gifbin0 -> 1621 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/bg.wymeditor.pngbin0 -> 498 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/images/icons.silver.gifbin0 -> 15382 bytes
-rw-r--r--studio/static/js/wymeditor/skins/silver/skin.css297
-rw-r--r--studio/static/js/wymeditor/skins/silver/skin.js61
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/.svn/entries130
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/.svn/prop-base/icons.png.svn-base5
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-basebin0 -> 3651 bytes
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.css.svn-base134
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.js.svn-base39
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/icons.pngbin0 -> 3651 bytes
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/skin.css134
-rw-r--r--studio/static/js/wymeditor/skins/twopanels/skin.js39
-rw-r--r--studio/static/js/wymeditor/skins/wymeditor_icon.pngbin0 -> 1028 bytes
l---------studio/static/static1
-rw-r--r--studio/studio.py124
-rw-r--r--studio/templates/editor.html67
-rw-r--r--studio/templates/filer.html60
-rw-r--r--studio/templates/index.html128
-rw-r--r--studio/templates/skel.html21
-rw-r--r--studio/templates/split-view.html10
-rw-r--r--studio/templates/wysiwyg-editor.html54
-rw-r--r--websdk/browser.py59
-rw-r--r--websdk/inspector.py79
-rw-r--r--websdk/skel.py30
534 files changed, 87221 insertions, 0 deletions
diff --git a/icons/document-generic.png b/icons/document-generic.png
new file mode 100644
index 0000000..1518d8a
--- /dev/null
+++ b/icons/document-generic.png
Binary files differ
diff --git a/icons/folder.png b/icons/folder.png
new file mode 100644
index 0000000..bc757d4
--- /dev/null
+++ b/icons/folder.png
Binary files differ
diff --git a/icons/image-x-generic.png b/icons/image-x-generic.png
new file mode 100644
index 0000000..962b684
--- /dev/null
+++ b/icons/image-x-generic.png
Binary files differ
diff --git a/icons/text-uri-list.png b/icons/text-uri-list.png
new file mode 100644
index 0000000..64e848d
--- /dev/null
+++ b/icons/text-uri-list.png
Binary files differ
diff --git a/icons/text-x-generic.png b/icons/text-x-generic.png
new file mode 100644
index 0000000..0eaf1f1
--- /dev/null
+++ b/icons/text-x-generic.png
Binary files differ
diff --git a/icons/text-x-python.png b/icons/text-x-python.png
new file mode 100644
index 0000000..a5f7984
--- /dev/null
+++ b/icons/text-x-python.png
Binary files differ
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000..8322998
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+export PYTHONPATH=`pwd`/websdk:$PYTHONPATH
+exec python studio/studio.py 5000 &
+sleep 2
+exec python -c "import webbrowser;webbrowser.open(\"http://localhost:5000\")"
diff --git a/studio/__init__.py b/studio/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/studio/__init__.py
diff --git a/studio/static/css/main.css b/studio/static/css/main.css
new file mode 100644
index 0000000..b094667
--- /dev/null
+++ b/studio/static/css/main.css
@@ -0,0 +1,111 @@
+body {
+ background-color: #c0c0c0;
+ height: 100%;
+ margin: 0;
+ font-size: 88%;
+ font-family: DejaVu Sans;
+}
+
+p.subtitle {
+ font-size: 10pt;
+ margin-top: 5px;
+}
+
+li {
+ margin-bottom: 1em
+}
+
+hr {
+ border: 0;
+ color: #9E9E9E;
+ background-color: #9E9E9E;
+ height: 1px;
+ width: 100%;
+ text-align: left;
+}
+
+#content {
+ margin: 30px;
+}
+
+#beta {
+ font-size: 12pt;
+ color: red;
+ display: none;
+}
+
+#editor {
+ margin: 0;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 85%;
+ margin-left: 15%;
+}
+
+#editor .wymeditor {
+}
+
+#editor-sidebar {
+ width: 15%;
+ padding: 5px;
+}
+
+.bling {
+ display: none;
+}
+
+div#filer {
+ display: none;
+}
+
+#filer ul{
+ list-style: none;
+ width: 100%;
+ padding: 0;
+ margin-left: 10px;
+}
+
+#filer ul li {
+ text-align: center;
+ vertical-align: top;
+}
+
+
+#filer ul li a:hover{
+ color: white;
+ background-color: #808080;
+}
+
+#filer ul li a{
+ color: black;
+ text-decoration: none;
+ font-size: 8pt;
+ width: 50px;
+ height: 70px;
+ float:left;
+ padding:22px;
+ padding-top:6px;
+}
+
+#filer ul li a img {
+ border: none;
+ height: 56px;
+}
+
+#filer-header {
+ background-color: black;
+ color: white;
+ margin: 0;
+ padding: 0;
+ padding-top: 1px;
+ padding-bottom: 7px;
+ padding-left: 15px;
+ width: 100%;
+}
+
+#filer-header div{
+ padding-left: 7px;
+}
diff --git a/studio/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.png b/studio/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.png
new file mode 100644
index 0000000..299f267
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.png b/studio/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.png
new file mode 100644
index 0000000..5b5dab2
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.png b/studio/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.png
new file mode 100644
index 0000000..ac8b229
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.png b/studio/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.png
new file mode 100644
index 0000000..89c6362
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.png b/studio/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.png
new file mode 100644
index 0000000..6864463
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.png b/studio/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.png
new file mode 100644
index 0000000..d05eb5b
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.png b/studio/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.png
new file mode 100644
index 0000000..68a36c5
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.png b/studio/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.png
new file mode 100644
index 0000000..81722a4
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-icons_000000_256x240.png b/studio/static/css/sugar-theme/images/ui-icons_000000_256x240.png
new file mode 100644
index 0000000..7c211aa
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-icons_000000_256x240.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.png b/studio/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 0000000..09d1cdc
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.png b/studio/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 0000000..2ab019b
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/images/ui-icons_ffffff_256x240.png b/studio/static/css/sugar-theme/images/ui-icons_ffffff_256x240.png
new file mode 100644
index 0000000..42f8f99
--- /dev/null
+++ b/studio/static/css/sugar-theme/images/ui-icons_ffffff_256x240.png
Binary files differ
diff --git a/studio/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css b/studio/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css
new file mode 100644
index 0000000..c3877c2
--- /dev/null
+++ b/studio/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css
@@ -0,0 +1,568 @@
+/*
+ * jQuery UI CSS Framework 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+
+/*
+ * jQuery UI CSS Framework 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=DejaVu%20Sans&fwDefault=normal&fsDefault=1.1em&cornerRadius=10px&bgColorHeader=282828&bgTextureHeader=01_flat.png&bgImgOpacityHeader=75&borderColorHeader=282828&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=e5e5e5&bgTextureContent=01_flat.png&bgImgOpacityContent=100&borderColorContent=c0c0c0&fcContent=000000&iconColorContent=000000&bgColorDefault=808080&bgTextureDefault=01_flat.png&bgImgOpacityDefault=75&borderColorDefault=808080&fcDefault=ffffff&iconColorDefault=ffffff&bgColorHover=808080&bgTextureHover=01_flat.png&bgImgOpacityHover=75&borderColorHover=808080&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=c0c0c0&fcActive=000000&iconColorActive=000000&bgColorHighlight=282828&bgTextureHighlight=04_highlight_hard.png&bgImgOpacityHighlight=5&borderColorHighlight=000&fcHighlight=fff&iconColorHighlight=2e83ff&bgColorError=c0c0c0&bgTextureError=04_highlight_hard.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=282828&bgTextureOverlay=21_glow_ball.png&bgImgOpacityOverlay=20&opacityOverlay=80&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=50&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: DejaVu Sans; font-size: 1.1em; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: DejaVu Sans; font-size: 1em; }
+.ui-widget-content { border: 1px solid #c0c0c0; background: #e5e5e5 url(images/ui-bg_flat_100_e5e5e5_40x100.png) 50% 50% repeat-x; color: #000000; }
+.ui-widget-content a { color: #000000; }
+.ui-widget-header { border: 1px solid #282828; background: #282828 url(images/ui-bg_flat_75_282828_40x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
+.ui-widget-header a { color: #ffffff; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #808080; background: #808080 url(images/ui-bg_flat_75_808080_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #ffffff; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #808080; background: #808080 url(images/ui-bg_flat_75_808080_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #ffffff; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #c0c0c0; background: #ffffff url(images/ui-bg_flat_65_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #000000; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #000000; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #000; background: #282828 url(images/ui-bg_highlight-hard_5_282828_1x100.png) 50% top repeat-x; color: #fff; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #fff; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #c0c0c0 url(images/ui-bg_highlight-hard_95_c0c0c0_1x100.png) 50% top repeat-x; color: #cd0a0a; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_000000_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_ffffff_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 10px; -webkit-border-top-left-radius: 10px; -khtml-border-top-left-radius: 10px; border-top-left-radius: 10px; }
+.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 10px; -webkit-border-top-right-radius: 10px; -khtml-border-top-right-radius: 10px; border-top-right-radius: 10px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 10px; -webkit-border-bottom-left-radius: 10px; -khtml-border-bottom-left-radius: 10px; border-bottom-left-radius: 10px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 10px; -webkit-border-bottom-right-radius: 10px; -khtml-border-bottom-right-radius: 10px; border-bottom-right-radius: 10px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #282828 url(images/ui-bg_glow-ball_20_282828_600x600.png) 50% 35% repeat-x; opacity: .80;filter:Alpha(Opacity=80); }
+.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_50_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
+ * jQuery UI Resizable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
+ * jQuery UI Selectable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
+/*
+ * jQuery UI Accordion 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }
+/*
+ * jQuery UI Autocomplete 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.16
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+ float: left;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ zoom: 1;
+ float: left;
+ clear: left;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+ zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+/*
+ * jQuery UI Button 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
+.ui-button { display: inline-block; position: relative; padding: 0; margin-top: 6pt; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
+.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-buttonset { margin-right: 7px; }
+.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+/*
+ * jQuery UI Dialog 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/*
+ * jQuery UI Slider 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
+ * jQuery UI Tabs 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*
+ * jQuery UI Datepicker 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+ display: none; /*sorry for IE5*/
+ display/**/: block; /*sorry for IE5*/
+ position: absolute; /*must have*/
+ z-index: -1; /*must have*/
+ filter: mask(); /*must have*/
+ top: -4px; /*must have*/
+ left: -4px; /*must have*/
+ width: 200px; /*must have*/
+ height: 200px; /*must have*/
+}/*
+ * jQuery UI Progressbar 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
diff --git a/studio/static/doc/flask-docs/.buildinfo b/studio/static/doc/flask-docs/.buildinfo
new file mode 100644
index 0000000..ad0796f
--- /dev/null
+++ b/studio/static/doc/flask-docs/.buildinfo
@@ -0,0 +1,4 @@
+# Sphinx build info version 1
+# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: 39b2407f81d28483972c20d3689be37c
+tags: fbb0d17656682115ca4d033fb2f83ba1
diff --git a/studio/static/doc/flask-docs/_images/debugger.png b/studio/static/doc/flask-docs/_images/debugger.png
new file mode 100644
index 0000000..4f47229
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/debugger.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/debugger1.png b/studio/static/doc/flask-docs/_images/debugger1.png
new file mode 100644
index 0000000..4f47229
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/debugger1.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/debugger2.png b/studio/static/doc/flask-docs/_images/debugger2.png
new file mode 100644
index 0000000..4f47229
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/debugger2.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/debugger3.png b/studio/static/doc/flask-docs/_images/debugger3.png
new file mode 100644
index 0000000..4f47229
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/debugger3.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/debugger4.png b/studio/static/doc/flask-docs/_images/debugger4.png
new file mode 100644
index 0000000..4f47229
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/debugger4.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/debugger5.png b/studio/static/doc/flask-docs/_images/debugger5.png
new file mode 100644
index 0000000..4f47229
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/debugger5.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/flaskr.png b/studio/static/doc/flask-docs/_images/flaskr.png
new file mode 100644
index 0000000..07d027d
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/flaskr.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/logo-full.png b/studio/static/doc/flask-docs/_images/logo-full.png
new file mode 100644
index 0000000..5deaf1b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/logo-full.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/logo-full1.png b/studio/static/doc/flask-docs/_images/logo-full1.png
new file mode 100644
index 0000000..5deaf1b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/logo-full1.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/logo-full2.png b/studio/static/doc/flask-docs/_images/logo-full2.png
new file mode 100644
index 0000000..5deaf1b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/logo-full2.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/no.png b/studio/static/doc/flask-docs/_images/no.png
new file mode 100644
index 0000000..4ac1083
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/no.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_images/yes.png b/studio/static/doc/flask-docs/_images/yes.png
new file mode 100644
index 0000000..ac27c4e
--- /dev/null
+++ b/studio/static/doc/flask-docs/_images/yes.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_sources/api.txt b/studio/static/doc/flask-docs/_sources/api.txt
new file mode 100644
index 0000000..7695788
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/api.txt
@@ -0,0 +1,624 @@
+.. _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:
+
+
+Blueprint Objects
+-----------------
+
+.. autoclass:: Blueprint
+ :members:
+ :inherited-members:
+
+Incoming Request Data
+---------------------
+
+.. autoclass:: Request
+ :members:
+
+ .. attribute:: form
+
+ A :class:`~werkzeug.datastructures.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.datastructures.MultiDict` with the parsed contents of the query
+ string. (The part in the URL after the question mark).
+
+ .. attribute:: values
+
+ A :class:`~werkzeug.datastructures.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:: headers
+
+ The incoming request headers as a dictionary like object.
+
+ .. 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.datastructures.MultiDict` with files uploaded as part of a
+ `POST` or `PUT` request. Each file is stored as
+ :class:`~werkzeug.datastructures.FileStorage` object. It basically behaves like a
+ standard file object you know from Python, with the difference that
+ it also has a :meth:`~werkzeug.datastructures.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.
+
+.. 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.wrappers.Request`
+ subclass and provides all of the attributes Werkzeug defines. This
+ just shows a quick overview of the most important ones.
+
+
+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 they know 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.
+
+
+Session Interface
+-----------------
+
+.. versionadded:: 0.8
+
+The session interface provides a simple way to replace the session
+implementation that Flask is using.
+
+.. currentmodule:: flask.sessions
+
+.. autoclass:: SessionInterface
+ :members:
+
+.. autoclass:: SecureCookieSessionInterface
+ :members:
+
+.. autoclass:: NullSession
+ :members:
+
+.. autoclass:: SessionMixin
+ :members:
+
+.. admonition:: Notice
+
+ The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer
+ starting with Flask 0.8. Either catch this down yourself or use
+ the :attr:`~flask.Flask.permanent_session_lifetime` attribute on the
+ app which converts the result to an integer automatically.
+
+
+Test Client
+-----------
+
+.. currentmodule:: flask.testing
+
+.. autoclass:: FlaskClient
+ :members:
+
+
+Application Globals
+-------------------
+
+.. currentmodule:: flask
+
+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:: has_request_context
+
+.. autofunction:: url_for
+
+.. function:: abort(code)
+
+ Raises an :exc:`~werkzeug.exceptions.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:: safe_join
+
+.. 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:
+
+Extensions
+----------
+
+.. data:: flask.ext
+
+ This module acts as redirect import module to Flask extensions. It was
+ added in 0.8 as the canonical way to import Flask extensions and makes
+ it possible for us to have more flexibility in how we distribute
+ extensions.
+
+ If you want to use an extension named “Flask-Foo” you would import it
+ from :data:`~flask.ext` as follows::
+
+ from flask.ext import foo
+
+ .. versionadded:: 0.8
+
+Useful Internals
+----------------
+
+.. autoclass:: flask.ctx.RequestContext
+ :members:
+
+.. data:: _request_ctx_stack
+
+ The internal :class:`~werkzeug.local.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
+
+.. autoclass:: flask.blueprints.BlueprintSetupState
+ :members:
+
+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`.
+
+.. data:: request_tearing_down
+
+ This signal is sent when the application is tearing down the request.
+ This is always called, even if an error happened. No arguments are
+ provided.
+
+.. 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
+
+Class Based Views
+-----------------
+
+.. versionadded:: 0.7
+
+.. currentmodule:: None
+
+.. autoclass:: flask.views.View
+ :members:
+
+.. autoclass:: flask.views.MethodView
+ :members:
+
+.. _url-route-registrations:
+
+URL Route Registrations
+-----------------------
+
+Generally there are three ways to define rules for the routing system:
+
+1. You can use the :meth:`flask.Flask.route` decorator.
+2. You can use the :meth:`flask.Flask.add_url_rule` function.
+3. You can directly access the underlying Werkzeug routing system
+ which is exposed as :attr:`flask.Flask.url_map`.
+
+Variable 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 available:
+
+=========== ===============================================
+`unicode` accepts any text without a slash (the default)
+`int` accepts integers
+`float` like `int` but for floating point values
+`path` like the default but also accepts slashes
+=========== ===============================================
+
+Here are 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 requests 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.
+
+You can also define multiple rules for the same function. They have to be
+unique however. Defaults can also be specified. Here for example is a
+definition for a URL that accepts an optional page::
+
+ @app.route('/users/', defaults={'page': 1})
+ @app.route('/users/page/<int:page>')
+ def show_users(page):
+ pass
+
+This specifies that ``/users/`` will be the URL for page one and
+``/users/page/N`` will be the URL for page `N`.
+
+Here are the parameters that :meth:`~flask.Flask.route` and
+:meth:`~flask.Flask.add_url_rule` accept. The only difference is that
+with the route parameter the view function is defined with the decorator
+instead of the `view_func` parameter.
+
+=============== ==========================================================
+`rule` the URL roule as string
+`endpoint` the endpoint for the registered URL rule. Flask itself
+ assumes that the name of the view function is the name
+ of the endpoint if not explicitly stated.
+`view_func` the function to call when serving a request to the
+ provided endpoint. If this is not provided one can
+ specify the function later by storing it in the
+ :attr:`~flask.Flask.view_functions` dictionary with the
+ endpoint as key.
+`defaults` A dictionary with defaults for this rule. See the
+ example above for how defaults work.
+`subdomain` specifies the rule for the subdomain in case subdomain
+ matching is in use. If not specified the default
+ subdomain is assumed.
+`**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. They have to be specified as keyword arguments.
+=============== ==========================================================
+
+.. _view-func-options:
+
+View Function Options
+---------------------
+
+For internal usage the view functions can have some attributes attached to
+customize behavior the view function would normally not have control over.
+The following attributes can be provided optionally to either override
+some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior:
+
+- `__name__`: The name of a function is by default used as endpoint. If
+ endpoint is provided explicitly this value is used. Additionally this
+ will be prefixed with the name of the blueprint by default which
+ cannot be customized from the function itself.
+
+- `methods`: If methods are not provided when the URL rule is added,
+ Flask will look on the view function object itself is an `methods`
+ attribute exists. If it does, it will pull the information for the
+ methods from there.
+
+- `provide_automatic_options`: if this attribute is set Flask will
+ either force enable or disable the automatic implementation of the
+ HTTP `OPTIONS` response. This can be useful when working with
+ decorators that want to customize the `OPTIONS` response on a per-view
+ basis.
+
+Full example::
+
+ def index():
+ if request.method == 'OPTIONS':
+ # custom options handling here
+ ...
+ return 'Hello World!'
+ index.provide_automatic_options = False
+ index.methods = ['GET', 'OPTIONS']
+
+ app.add_url_rule('/', index)
+
+.. versionadded:: 0.8
+ The `provide_automatic_options` functionality was added.
diff --git a/studio/static/doc/flask-docs/_sources/becomingbig.txt b/studio/static/doc/flask-docs/_sources/becomingbig.txt
new file mode 100644
index 0000000..20a0186
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/becomingbig.txt
@@ -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.
diff --git a/studio/static/doc/flask-docs/_sources/blueprints.txt b/studio/static/doc/flask-docs/_sources/blueprints.txt
new file mode 100644
index 0000000..9422fd0
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/blueprints.txt
@@ -0,0 +1,203 @@
+.. _blueprints:
+
+Modular Applications with Blueprints
+====================================
+
+.. versionadded:: 0.7
+
+Flask uses a concept of *blueprints* for making application components and
+supporting common patterns within an application or across applications.
+Blueprints can greatly simplify how large applications work and provide a
+central means for Flask extensions to register operations on applications.
+A :class:`Blueprint` object works similarly to a :class:`Flask`
+application object, but it is not actually an application. Rather it is a
+*blueprint* of how to construct or extend an application.
+
+Why Blueprints?
+---------------
+
+Blueprints in Flask are intended for these cases:
+
+* Factor an application into a set of blueprints. This is ideal for
+ larger applications; a project could instantiate an application object,
+ initialize several extensions, and register a collection of blueprints.
+* Register a blueprint on an application at a URL prefix and/or subdomain.
+ Parameters in the URL prefix/subdomain become common view arguments
+ (with defaults) across all view functions in the blueprint.
+* Register a blueprint multiple times on an application with different URL
+ rules.
+* Provide template filters, static files, templates, and other utilities
+ through blueprints. A blueprint does not have to implement applications
+ or view functions.
+* Register a blueprint on an application for any of these cases when
+ initializing a Flask extension.
+
+A blueprint in Flask is not a pluggable app because it is not actually an
+application -- it's a set of operations which can be registered on an
+application, even multiple times. Why not have multiple application
+objects? You can do that (see :ref:`app-dispatch`), but your applications
+will have separate configs and will be managed at the WSGI layer.
+
+Blueprints instead provide separation at the Flask level, share
+application config, and can change an application object as necessary with
+being registered. The downside is that you cannot unregister a blueprint
+once an application was created without having to destroy the whole
+application object.
+
+The Concept of Blueprints
+-------------------------
+
+The basic concept of blueprints is that they record operations to execute
+when registered on an application. Flask associates view functions with
+blueprints when dispatching requests and generating URLs from one endpoint
+to another.
+
+My First Blueprint
+------------------
+
+This is what a very basic blueprint looks like. In this case we want to
+implement a blueprint that does simple rendering of static templates::
+
+ from flask import Blueprint, render_template, abort
+ from jinja2 import TemplateNotFound
+
+ simple_page = Blueprint('simple_page', __name__)
+
+ @simple_page.route('/', defaults={'page': 'index'})
+ @simple_page.route('/<page>')
+ def show(page):
+ try:
+ return render_template('pages/%s.html' % page)
+ except TemplateNotFound:
+ abort(404)
+
+When you bind a function with the help of the ``@simple_page.route``
+decorator the blueprint will record the intention of registering the
+function `show` on the application when it's later registered.
+Additionally it will prefix the endpoint of the function with the
+name of the blueprint which was given to the :class:`Blueprint`
+constructor (in this case also ``simple_page``).
+
+Registering Blueprints
+----------------------
+
+So how do you register that blueprint? Like this::
+
+ from flask import Flask
+ from yourapplication.simple_page import simple_page
+
+ app = Flask(__name__)
+ app.register_blueprint(simple_page)
+
+If you check the rules registered on the application, you will find
+these::
+
+ [<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
+ <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
+ <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>]
+
+The first one is obviously from the application ifself for the static
+files. The other two are for the `show` function of the ``simple_page``
+blueprint. As you can see, they are also prefixed with the name of the
+blueprint and separated by a dot (``.``).
+
+Blueprints however can also be mounted at different locations::
+
+ app.register_blueprint(simple_page, url_prefix='/pages')
+
+And sure enough, these are the generated rules::
+
+ [<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
+ <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
+ <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>]
+
+On top of that you can register blueprints multiple times though not every
+blueprint might respond properly to that. In fact it depends on how the
+blueprint is implemented if it can be mounted more than once.
+
+Blueprint Resources
+-------------------
+
+Blueprints can provide resources as well. Sometimes you might want to
+introduce a blueprint only for the resources it provides.
+
+Blueprint Resource Folder
+`````````````````````````
+
+Like for regular applications, blueprints are considered to be contained
+in a folder. While multiple blueprints can originate from the same folder,
+it does not have to be the case and it's usually not recommended.
+
+The folder is inferred from the second argument to :class:`Blueprint` which
+is usually `__name__`. This argument specifies what logical Python
+module or package corresponds to the blueprint. If it points to an actual
+Python package that package (which is a folder on the filesystem) is the
+resource folder. If it's a module, the package the module is contained in
+will be the resource folder. You can access the
+:attr:`Blueprint.root_path` property to see what the resource folder is::
+
+ >>> simple_page.root_path
+ '/Users/username/TestProject/yourapplication'
+
+To quickly open sources from this folder you can use the
+:meth:`~Blueprint.open_resource` function::
+
+ with simple_page.open_resource('static/style.css') as f:
+ code = f.read()
+
+Static Files
+````````````
+
+A blueprint can expose a folder with static files by providing a path to a
+folder on the filesystem via the `static_folder` keyword argument. It can
+either be an absolute path or one relative to the folder of the
+blueprint::
+
+ admin = Blueprint('admin', __name__, static_folder='static')
+
+By default the rightmost part of the path is where it is exposed on the
+web. Because the folder is called ``static`` here it will be available at
+the location of the blueprint + ``/static``. Say the blueprint is
+registered for ``/admin`` the static folder will be at ``/admin/static``.
+
+The endpoint is named `blueprint_name.static` so you can generate URLs to
+it like you would do to the static folder of the application::
+
+ url_for('admin.static', filename='style.css')
+
+Templates
+`````````
+
+If you want the blueprint to expose templates you can do that by providing
+the `template_folder` parameter to the :class:`Blueprint` constructor::
+
+ admin = Blueprint('admin', __name__, template_folder='templates')
+
+As for static files, the path can be absolute or relative to the blueprint
+resource folder. The template folder is added to the searchpath of
+templates but with a lower priority than the actual application's template
+folder. That way you can easily override templates that a blueprint
+provides in the actual application.
+
+So if you have a blueprint in the folder ``yourapplication/admin`` and you
+want to render the template ``'admin/index.html'`` and you have provided
+``templates`` as a `template_folder` you will have to create a file like
+this: ``yourapplication/admin/templates/admin/index.html``.
+
+Building URLs
+-------------
+
+If you want to link from one page to another you can use the
+:func:`url_for` function just like you normally would do just that you
+prefix the URL endpoint with the name of the blueprint and a dot (``.``)::
+
+ url_for('admin.index')
+
+Additionally if you are in a view function of a blueprint or a rendered
+template and you want to link to another endpoint of the same blueprint,
+you can use relative redirects by prefixing the endpoint with a dot only::
+
+ url_for('.index')
+
+This will link to ``admin.index`` for instance in case the current request
+was dispatched to any other admin blueprint endpoint.
diff --git a/studio/static/doc/flask-docs/_sources/changelog.txt b/studio/static/doc/flask-docs/_sources/changelog.txt
new file mode 100644
index 0000000..d6c5f48
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/changelog.txt
@@ -0,0 +1 @@
+.. include:: ../CHANGES
diff --git a/studio/static/doc/flask-docs/_sources/config.txt b/studio/static/doc/flask-docs/_sources/config.txt
new file mode 100644
index 0000000..ca724dc
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/config.txt
@@ -0,0 +1,384 @@
+.. _config:
+
+Configuration Handling
+======================
+
+.. versionadded:: 0.3
+
+Applications need some kind of configuration. There are different settings
+you might want to change depending on the application environment like
+toggling the debug mode, setting the secret key, and other such
+environment-specific 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 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.
+``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in
+ debug mode the request context is not
+ popped on exceptions to enable debuggers
+ to introspect the data. This can be
+ disabled by this key. You can also use
+ this setting to force-enable it for non
+ debug execution which might be useful to
+ debug production applications (but also
+ very risky).
+``SECRET_KEY`` the secret key
+``SESSION_COOKIE_NAME`` the name of the session cookie
+``SESSION_COOKIE_DOMAIN`` the domain for the session cookie. If
+ this is not set, the cookie will be
+ valid for all subdomains of
+ ``SERVER_NAME``.
+``SESSION_COOKIE_PATH`` the path for the session cookie. If
+ this is not set the cookie will be valid
+ for all of ``APPLICATION_ROOT`` or if
+ that is not set for ``'/'``.
+``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set
+ with the httponly flag. Defaults to
+ `True`.
+``SESSION_COOKIE_SECURE`` controls if the cookie should be set
+ with the secure flag. Defaults to
+ `False`.
+``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
+ :class:`datetime.timedelta` object.
+ Starting with Flask 0.8 this can also be
+ an integer representing seconds.
+``USE_X_SENDFILE`` enable/disable x-sendfile
+``LOGGER_NAME`` the name of the logger
+``SERVER_NAME`` the name and port number of the server.
+ Required for subdomain support (e.g.:
+ ``'myapp.dev:5000'``) Note that
+ localhost does not support subdomains so
+ setting this to “localhost” does not
+ help.
+``APPLICATION_ROOT`` If the application does not occupy
+ a whole domain or subdomain this can
+ be set to the path where the application
+ is configured to live. This is for
+ session cookie as path value. If
+ domains are used, this should be
+ ``None``.
+``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.
+``TRAP_HTTP_EXCEPTIONS`` If this is set to ``True`` Flask will
+ not execute the error handlers of HTTP
+ exceptions but instead treat the
+ exception like any other and bubble it
+ through the exception stack. This is
+ helpful for hairy debugging situations
+ where you have to find out where an HTTP
+ exception is coming from.
+``TRAP_BAD_REQUEST_ERRORS`` Werkzeug's internal data structures that
+ deal with request specific data will
+ raise special key errors that are also
+ bad request exceptions. Likewise many
+ operations can implicitly fail with a
+ BadRequest exception for consistency.
+ Since it's nice for debugging to know
+ why exactly it failed this flag can be
+ used to debug those situations. If this
+ config is set to ``True`` you will get
+ a regular traceback instead.
+================================= =========================================
+
+.. 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``, ``PRESERVE_CONTEXT_ON_EXCEPTION``
+
+.. versionadded:: 0.8
+ ``TRAP_BAD_REQUEST_ERRORS``, ``TRAP_HTTP_EXCEPTIONS``,
+ ``APPLICATION_ROOT``, ``SESSION_COOKIE_DOMAIN``,
+ ``SESSION_COOKIE_PATH``, ``SESSION_COOKIE_HTTPONLY``,
+ ``SESSION_COOKIE_SECURE``
+
+Configuring from Files
+----------------------
+
+Configuration becomes more useful if you can store it in a separate file,
+ideally located outside the actual application package. This makes
+packaging and distributing your application possible via various package
+handling tools (:ref:`distribute-deployment`) and finally modifying the
+configuration 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 of a configuration file::
+
+ # Example configuration
+ 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 single 100% solution for this problem in
+general, but there are a couple of things you can keep in mind to improve
+that experience:
+
+1. create your application in a function and register blueprints 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 should be at
+least separate configurations for the production server and the one used
+during development. The easiest way to handle this is to use a default
+configuration that is always loaded and part of the 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 TestingConfig(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:`fabric-deployment` pattern.
+
+.. _fabric: http://fabfile.org/
+
+
+.. _instance-folders:
+
+Instance Folders
+----------------
+
+.. versionadded:: 0.8
+
+Flask 0.8 introduces instance folders. Flask for a long time made it
+possible to refer to paths relative to the application's folder directly
+(via :attr:`Flask.root_path`). This was also how many developers loaded
+configurations stored next to the application. Unfortunately however this
+only works well if applications are not packages in which case the root
+path refers to the contents of the package.
+
+With Flask 0.8 a new attribute was introduced:
+:attr:`Flask.instance_path`. It refers to a new concept called the
+“instance folder”. The instance folder is designed to not be under
+version control and be deployment specific. It's the perfect place to
+drop things that either change at runtime or configuration files.
+
+You can either explicitly provide the path of the instance folder when
+creating the Flask application or you can let Flask autodetect the
+instance folder. For explicit configuration use the `instance_path`
+parameter::
+
+ app = Flask(__name__, instance_path='/path/to/instance/folder')
+
+Please keep in mind that this path *must* be absolute when provided.
+
+If the `instance_path` parameter is not provided the following default
+locations are used:
+
+- Uninstalled module::
+
+ /myapp.py
+ /instance
+
+- Uninstalled package::
+
+ /myapp
+ /__init__.py
+ /instance
+
+- Installed module or package::
+
+ $PREFIX/lib/python2.X/site-packages/myapp
+ $PREFIX/var/myapp-instance
+
+ ``$PREFIX`` is the prefix of your Python installation. This can be
+ ``/usr`` or the path to your virtualenv. You can print the value of
+ ``sys.prefix`` to see what the prefix is set to.
+
+Since the config object provided loading of configuration files from
+relative filenames we made it possible to change the loading via filenames
+to be relative to the instance path if wanted. The behavior of relative
+paths in config files can be flipped between “relative to the application
+root” (the default) to “relative to instance folder” via the
+`instance_relative_config` switch to the application constructor::
+
+ app = Flask(__name__, instance_relative_config=True)
+
+Here is a full example of how to configure Flask to preload the config
+from a module and then override the config from a file in the config
+folder if it exists::
+
+ app = Flask(__name__, instance_relative_config=True)
+ app.config.from_object('yourapplication.default_settings')
+ app.config.from_pyfile('application.cfg', silent=True)
+
+The path to the instance folder can be found via the
+:attr:`Flask.instance_path`. Flask also provides a shortcut to open a
+file from the instance folder with :meth:`Flask.open_instance_resource`.
+
+Example usage for both::
+
+ filename = os.path.join(app.instance_root, 'application.cfg')
+ with open(filename) as f:
+ config = f.read()
+
+ # or via open_instance_resource:
+ with app.open_instance_resource('application.cfg') as f:
+ config = f.read()
diff --git a/studio/static/doc/flask-docs/_sources/deploying/cgi.txt b/studio/static/doc/flask-docs/_sources/deploying/cgi.txt
new file mode 100644
index 0000000..a2fba90
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/deploying/cgi.txt
@@ -0,0 +1,46 @@
+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 sub-optimal
+performance.
+
+This is also the way you can use a Flask application on Google's `App
+Engine`_, where execution happens in a CGI-like environment.
+
+.. admonition:: Watch Out
+
+ Please make sure in advance that any ``app.run()`` calls you might
+ have in your application file are inside an ``if __name__ ==
+ '__main__':`` block 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.
+
+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.
+
+.. _App Engine: http://code.google.com/appengine/
diff --git a/studio/static/doc/flask-docs/_sources/deploying/fastcgi.txt b/studio/static/doc/flask-docs/_sources/deploying/fastcgi.txt
new file mode 100644
index 0000000..6dace1a
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/deploying/fastcgi.txt
@@ -0,0 +1,164 @@
+.. _deploying-fastcgi:
+
+FastCGI
+=======
+
+FastCGI is a deployment option on servers like `nginx`_, `lighttpd`_,
+and `cherokee`_; see :ref:`deploying-uwsgi` and
+:ref:`deploying-other-servers` for other options. 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 to follow along.
+
+.. admonition:: Watch Out
+
+ Please make sure in advance that any ``app.run()`` calls you might
+ have in your application file are inside an ``if __name__ ==
+ '__main__':`` block 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
+
+ if __name__ == '__main__':
+ WSGIServer(app).run()
+
+This is enough for Apache to work, however nginx and older versions of
+lighttpd need a socket to be explicitly passed 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.fcgi" =>
+ ((
+ "socket" => "/tmp/yourapplication-fcgi.sock",
+ "bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
+ "check-local" => "disable",
+ "max-procs" => 1
+ ))
+ )
+
+ alias.url = (
+ "/static/" => "/path/to/your/static"
+ )
+
+ url.rewrite-once = (
+ "^(/static.*)$" => "$1",
+ "^(/.*)$" => "/yourapplication.fcgi$1"
+
+Remember to enable the FastCGI, alias and rewrite modules. 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. Also, see the Lighty docs for more information on `FastCGI and
+Python <http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModFastCGI>`_
+(note that explicitly passing a socket to run() is no longer necessary).
+
+
+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;
+ }
+
+Running FastCGI Processes
+-------------------------
+
+Since Nginx and others do not load FastCGI apps, you have to do it by
+yourself. `Supervisor can manage FastCGI processes.
+<http://supervisord.org/configuration.html#fcgi-program-x-section-settings>`_
+You can look around for other FastCGI process managers or write a script
+to run your `.fcgi` file at boot, e.g. using a SysV ``init.d`` script.
+For a temporary solution, you can always run the ``.fcgi`` script inside
+GNU screen. See ``man screen`` for details, and note that this is a
+manual solution which does not persist across system restart::
+
+ $ 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.
+
+.. _nginx: http://nginx.org/
+.. _lighttpd: http://www.lighttpd.net/
+.. _cherokee: http://www.cherokee-project.com/
+.. _flup: http://trac.saddi.com/flup
diff --git a/studio/static/doc/flask-docs/_sources/deploying/index.txt b/studio/static/doc/flask-docs/_sources/deploying/index.txt
new file mode 100644
index 0000000..d258df8
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/deploying/index.txt
@@ -0,0 +1,23 @@
+.. _deployment:
+
+Deployment Options
+==================
+
+Depending on what you have available there are multiple ways to run
+Flask applications. You can use the builtin server during development,
+but you should use a full deployment option for production applications.
+(Do not use the builtin development server in production.) Several
+options are available and documented here.
+
+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
+:class:`Flask` application object is the actual WSGI application.
+
+.. toctree::
+ :maxdepth: 2
+
+ mod_wsgi
+ cgi
+ fastcgi
+ uwsgi
+ others
diff --git a/studio/static/doc/flask-docs/_sources/deploying/mod_wsgi.txt b/studio/static/doc/flask-docs/_sources/deploying/mod_wsgi.txt
new file mode 100644
index 0000000..c85ed64
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/deploying/mod_wsgi.txt
@@ -0,0 +1,167 @@
+.. _mod_wsgi-deployment:
+
+mod_wsgi (Apache)
+=================
+
+If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
+
+.. admonition:: Watch Out
+
+ Please make sure in advance that any ``app.run()`` calls you might
+ have in your application file are inside an ``if __name__ ==
+ '__main__':`` block 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.
diff --git a/studio/static/doc/flask-docs/_sources/deploying/others.txt b/studio/static/doc/flask-docs/_sources/deploying/others.txt
new file mode 100644
index 0000000..6f3e5cc
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/deploying/others.txt
@@ -0,0 +1,102 @@
+.. _deploying-other-servers:
+
+Other Servers
+=============
+
+There are popular servers written in Python that allow the execution of WSGI
+applications as well. These servers stand alone when they run; you can proxy
+to them from your web server.
+
+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`_ provides many command-line options -- see ``gunicorn -h``.
+For example, to run a Flask application with 4 worker processes (``-w
+4``) binding to localhost port 4000 (``-b 127.0.0.1:4000``)::
+
+ gunicorn -w 4 -b 127.0.0.1:4000 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 using one of these servers 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-Forwarded-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)
diff --git a/studio/static/doc/flask-docs/_sources/deploying/uwsgi.txt b/studio/static/doc/flask-docs/_sources/deploying/uwsgi.txt
new file mode 100644
index 0000000..bdee15b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/deploying/uwsgi.txt
@@ -0,0 +1,68 @@
+.. _deploying-uwsgi:
+
+uWSGI
+=====
+
+uWSGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
+`cherokee`_; see :ref:`deploying-fastcgi` and
+:ref:`deploying-other-servers` for other options. To use your WSGI
+application with uWSGI protocol you will need a uWSGI server
+first. uWSGI is both a protocol and an application server; the
+application server can serve uWSGI, FastCGI, and HTTP protocols.
+
+The most popular uWSGI server is `uwsgi`_, which we will use for this
+guide. Make sure to have it installed to follow along.
+
+.. admonition:: Watch Out
+
+ Please make sure in advance that any ``app.run()`` calls you might
+ have in your application file are inside an ``if __name__ ==
+ '__main__':`` block 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 uWSGI.
+
+Starting your app with uwsgi
+----------------------------
+
+`uwsgi` is designed to operate on WSGI callables found in python modules.
+
+Given a flask application in myapp.py, use the following command:
+
+.. sourcecode:: text
+
+ $ uwsgi -s /tmp/uwsgi.sock --module myapp --callable app
+
+Or, if you prefer:
+
+.. sourcecode:: text
+
+ $ uwsgi -s /tmp/uwsgi.sock -w myapp:app
+
+Configuring nginx
+-----------------
+
+A basic flask uWSGI configuration for nginx looks like this::
+
+ location = /yourapplication { rewrite ^ /yourapplication/; }
+ location /yourapplication { try_files $uri @yourapplication; }
+ location @yourapplication {
+ include uwsgi_params;
+ uwsgi_param SCRIPT_NAME /yourapplication;
+ uwsgi_modifier1 30;
+ uwsgi_pass unix:/tmp/uwsgi.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 tell
+it the WSGI `SCRIPT_NAME` or set the uwsgi modifier to make use of it::
+
+ location / { try_files $uri @yourapplication; }
+ location @yourapplication {
+ include uwsgi_params;
+ uwsgi_pass unix:/tmp/uwsgi.sock;
+ }
+
+.. _nginx: http://nginx.org/
+.. _lighttpd: http://www.lighttpd.net/
+.. _cherokee: http://www.cherokee-project.com/
+.. _uwsgi: http://projects.unbit.it/uwsgi/
diff --git a/studio/static/doc/flask-docs/_sources/design.txt b/studio/static/doc/flask-docs/_sources/design.txt
new file mode 100644
index 0000000..6ca363a
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/design.txt
@@ -0,0 +1,191 @@
+.. _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`).
+
+The Routing System
+------------------
+
+Flask uses the Werkzeug routing system which has was designed to
+automatically order routes by complexity. This means that you can declare
+routes in arbitrary order and they will still work as expected. This is a
+requirement if you want to properly implement decorator based routing
+since decorators could be fired in undefined order when the application is
+split into multiple modules.
+
+Another design decision with the Werkzeug routing system is that routes
+in Werkzeug try to ensure that there is that URLs are unique. Werkzeug
+will go quite far with that in that it will automatically redirect to a
+canonical URL if a route is ambiguous.
+
+
+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.
diff --git a/studio/static/doc/flask-docs/_sources/errorhandling.txt b/studio/static/doc/flask-docs/_sources/errorhandling.txt
new file mode 100644
index 0000000..debb9d7
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/errorhandling.txt
@@ -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 TheHandlerYouWant
+ 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)
diff --git a/studio/static/doc/flask-docs/_sources/extensiondev.txt b/studio/static/doc/flask-docs/_sources/extensiondev.txt
new file mode 100644
index 0000000..ee0d5e6
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/extensiondev.txt
@@ -0,0 +1,387 @@
+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 ``flask_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 ``flask_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.
+
+Flask sets up a redirect package called :data:`flask.ext` where users
+should import the extensions from. If you for instance have a package
+called ``flask_something`` users would import it as
+``flask.ext.something``. This is done to transition from the old
+namespace packages. See :ref:`ext-import-transition` for more details.
+
+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.
+
+First we create the following folder structure::
+
+ flask-sqlite3/
+ flask_sqlite3.py
+ LICENSE
+ README
+
+Here's the contents of the most important files:
+
+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__,
+ py_modules=['flask_sqlite3'],
+ # if you would be using a package instead use packages instead
+ # of py_modules:
+ # packages=['flask_sqlite3'],
+ zip_safe=False,
+ include_package_data=True,
+ 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.
+
+flask_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 use the class based approach because it will provide users with a
+manager object that handles opening and closing database connections.
+
+The Extension Code
+------------------
+
+Here's the contents of the `flask_sqlite3.py` for copy/paste::
+
+ from __future__ import absolute_import
+ import sqlite3
+
+ from flask import _request_ctx_stack
+
+ class SQLite3(object):
+
+ def __init__(self, app):
+ self.app = app
+ self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
+ self.app.teardown_request(self.teardown_request)
+ self.app.before_request(self.before_request)
+
+ def connect(self):
+ return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
+
+ def before_request(self):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db = self.connect()
+
+ def teardown_request(self, exception):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db.close()
+
+ def get_db(self):
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.sqlite3_db
+
+So here's what these lines of code do:
+
+1. The ``__future__`` import is necessary to activate absolute imports.
+ 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 requires a supplied `app` object,
+ sets a configuration for the database if it's not there
+ (:meth:`dict.setdefault`), and attaches `before_request` and
+ `teardown_request` handlers.
+3. Next, we define a `connect` function that opens a database connection.
+4. Then we set up the request handlers we bound to the app above. Note here
+ that we're attaching our database connection to the top request context via
+ `_request_ctx_stack.top`. Extensions should use the top context and not the
+ `g` object to store things like database connections.
+5. Finally, we add a `get_db` function that simplifies access to the context's
+ database.
+
+So why did we decide on a class based approach here? Because using our
+extension looks something like this::
+
+ from flask import Flask
+ from flask_sqlite3 import SQLite3
+
+ app = Flask(__name__)
+ app.config.from_pyfile('the-config.cfg')
+ manager = SQLite3(app)
+ db = manager.get_db()
+
+You can then use the database from views like this::
+
+ @app.route('/')
+ def show_all():
+ cur = db.cursor()
+ cur.execute(...)
+
+Opening a database connection from outside a view function is simple.
+
+>>> from yourapplication import db
+>>> cur = db.cursor()
+>>> cur.execute(...)
+
+Adding an `init_app` Function
+-----------------------------
+
+In practice, you'll almost always want to permit users to initialize your
+extension and provide an app object after the fact. This can help avoid
+circular import problems when a user is breaking their app into multiple files.
+Our extension could add an `init_app` function as follows::
+
+ class SQLite3(object):
+
+ def __init__(self, app=None):
+ if app is not None:
+ self.app = app
+ self.init_app(self.app)
+ else:
+ self.app = None
+
+ def init_app(self, app):
+ self.app = app
+ self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
+ self.app.teardown_request(self.teardown_request)
+ self.app.before_request(self.before_request)
+
+ def connect(self):
+ return sqlite3.connect(app.config['SQLITE3_DATABASE'])
+
+ def before_request(self):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db = self.connect()
+
+ def teardown_request(self, exception):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db.close()
+
+ def get_db(self):
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.sqlite3_db
+
+The user could then initialize the extension in one file::
+
+ manager = SQLite3()
+
+and bind their app to the extension in another file::
+
+ manager.init_app(app)
+
+End-Of-Request Behavior
+-----------------------
+
+Due to the change in Flask 0.7 regarding functions that are run at the end
+of the request your extension will have to be extra careful there if it
+wants to continue to support older versions of Flask. The following
+pattern is a good way to support both::
+
+ def close_connection(response):
+ ctx = _request_ctx_stack.top
+ ctx.sqlite3_db.close()
+ return response
+
+ if hasattr(app, 'teardown_request'):
+ app.teardown_request(close_connection)
+ else:
+ app.after_request(close_connection)
+
+Strictly speaking the above code is wrong, because teardown functions are
+passed the exception and typically don't return anything. However because
+the return value is discarded this will just work assuming that the code
+in between does not touch the passed parameter.
+
+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
+ named ``flask_extensionname``. They might also reside inside a
+ ``flaskext`` namespace packages though this is discouraged now.
+2. It must ship a testing suite that can either be invoked with ``make test``
+ or ``python setup.py test``. For test suites 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 test suite 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
+
+
+.. _ext-import-transition:
+
+Extension Import Transition
+---------------------------
+
+For a while we recommended using namespace packages for Flask extensions.
+This turned out to be problematic in practice because many different
+competing namespace package systems exist and pip would automatically
+switch between different systems and this caused a lot of problems for
+users.
+
+Instead we now recommend naming packages ``flask_foo`` instead of the now
+deprecated ``flaskext.foo``. Flask 0.8 introduces a redirect import
+system that lets uses import from ``flask.ext.foo`` and it will try
+``flask_foo`` first and if that fails ``flaskext.foo``.
+
+Flask extensions should urge users to import from ``flask.ext.foo``
+instead of ``flask_foo`` or ``flaskext_foo`` so that extensions can
+transition to the new package name without affecting users.
+
+
+.. _OAuth extension: http://packages.python.org/Flask-OAuth/
+.. _mailinglist: http://flask.pocoo.org/mailinglist/
+.. _IRC channel: http://flask.pocoo.org/community/irc/
diff --git a/studio/static/doc/flask-docs/_sources/extensions.txt b/studio/static/doc/flask-docs/_sources/extensions.txt
new file mode 100644
index 0000000..53dca56
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/extensions.txt
@@ -0,0 +1,48 @@
+Flask Extensions
+================
+
+Flask extensions extend the functionality of Flask in various different
+ways. For instance they add support for databases and other common tasks.
+
+Finding Extensions
+------------------
+
+Flask extensions are listed on the `Flask Extension Registry`_ and can be
+downloaded with ``easy_install`` or ``pip``. If you add a Flask extension
+as dependency to your ``requirements.rst`` or ``setup.py`` file they are
+usually installed with a simple command or when your application installs.
+
+Using Extensions
+----------------
+
+Extensions typically have documentation that goes along that shows how to
+use it. There are no general rules in how extensions are supposed to
+behave but they are imported from common locations. If you have an
+extension called ``Flask-Foo`` or ``Foo-Flask`` it will be always
+importable from ``flask.ext.foo``::
+
+ from flask.ext import foo
+
+Flask Before 0.8
+----------------
+
+If you are using Flask 0.7 or earlier the :data:`flask.ext` package will not
+exist, instead you have to import from ``flaskext.foo`` or ``flask_foo``
+depending on how the extension is distributed. If you want to develop an
+application that supports Flask 0.7 or earlier you should still import
+from the :data:`flask.ext` package. We provide you with a compatibility
+module that provides this package for older versions of Flask. You can
+download it from github: `flaskext_compat.py`_
+
+And here is how you can use it::
+
+ import flaskext_compat
+ flaskext_compat.activate()
+
+ from flask.ext import foo
+
+Once the ``flaskext_compat`` module is activated the :data:`flask.ext` will
+exist and you can start importing from there.
+
+.. _Flask Extension Registry: http://flask.pocoo.org/extensions/
+.. _flaskext_compat.py: https://github.com/mitsuhiko/flask/raw/master/scripts/flaskext_compat.py
diff --git a/studio/static/doc/flask-docs/_sources/foreword.txt b/studio/static/doc/flask-docs/_sources/foreword.txt
new file mode 100644
index 0000000..10b886b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/foreword.txt
@@ -0,0 +1,100 @@
+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 the fact that it does not make much
+decisions for you. While Flask does pick a templating engine for you, we
+won't make such decisions for your datastore or other parts.
+
+For us however the term “micro” does not mean that the whole implementation
+has to fit into a single Python file.
+
+One of the design decisions with Flask was that simple tasks should be
+simple and not take up a lot of code and yet not limit yourself. Because
+of that we took a few design choices that some people might find
+surprising or unorthodox. 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. In order to solve these
+problems we don't hide the thread locals for you but instead embrace them
+and provide you with a lot of tools to make it as pleasant as possible to
+work with them.
+
+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. While this can be changed you usually don't have to.
+
+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.
+
+Since Flask is based on a very solid foundation there is not a lot of code
+in Flask itself. As such it's easy to adapt even for lage applications
+and we are making sure that you can either configure it as much as
+possible by subclassing things or by forking the entire codebase. 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`.
+
+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. While the
+situation is greatly improving there are still some issues that make it
+hard for us to switch over to Python 3 just now. These problems are
+partially caused by changes in the language that went unreviewed for too
+long, partially also because we have not quite worked out how the lower
+level API should change for the unicode differences in Python3.
+
+Werkzeug and Flask will be ported to Python 3 as soon as a solution for
+the changes 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. If you plan on upgrading to Python 3 in the near future we
+strongly recommend that you read `How to write forwards compatible
+Python code <http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/>`_.
diff --git a/studio/static/doc/flask-docs/_sources/htmlfaq.txt b/studio/static/doc/flask-docs/_sources/htmlfaq.txt
new file mode 100644
index 0000000..1da25f3
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/htmlfaq.txt
@@ -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.
diff --git a/studio/static/doc/flask-docs/_sources/index.txt b/studio/static/doc/flask-docs/_sources/index.txt
new file mode 100644
index 0000000..c4ded1f
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/index.txt
@@ -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
diff --git a/studio/static/doc/flask-docs/_sources/installation.txt b/studio/static/doc/flask-docs/_sources/installation.txt
new file mode 100644
index 0000000..eb645bd
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/installation.txt
@@ -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
diff --git a/studio/static/doc/flask-docs/_sources/latexindex.txt b/studio/static/doc/flask-docs/_sources/latexindex.txt
new file mode 100644
index 0000000..288197c
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/latexindex.txt
@@ -0,0 +1,6 @@
+:orphan:
+
+Flask Documentation
+===================
+
+.. include:: contents.rst.inc
diff --git a/studio/static/doc/flask-docs/_sources/license.txt b/studio/static/doc/flask-docs/_sources/license.txt
new file mode 100644
index 0000000..38777e6
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/license.txt
@@ -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
diff --git a/studio/static/doc/flask-docs/_sources/patterns/appdispatch.txt b/studio/static/doc/flask-docs/_sources/patterns/appdispatch.txt
new file mode 100644
index 0000000..93b4af9
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/appdispatch.txt
@@ -0,0 +1,170 @@
+.. _app-dispatch:
+
+Application Dispatching
+=======================
+
+Application dispatching is the process of combining multiple Flask
+applications on the WSGI level. You can not only combine Flask
+applications into something larger but any WSGI application. This would
+even allow you to run a Django and a Flask application in the same
+interpreter side by side if you want. The usefulness of this depends on
+how the applications work internally.
+
+The fundamental difference from the :ref:`module approach
+<larger-applications>` is that in this case you are running the same or
+different Flask applications that are entirely isolated from each other.
+They run different configurations and are dispatched on the WSGI level.
+
+
+Working with this Document
+--------------------------
+
+Each of the techniques and examples below results in an ``application`` object
+that can be run with any WSGI server. For production, see :ref:`deployment`.
+For development, Werkzeug provides a builtin server for development available
+at :func:`werkzeug.serving.run_simple`::
+
+ from werkzeug.serving import run_simple
+ run_simple('localhost', 5000, application, use_reloader=True)
+
+Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for
+use in production. Use a :ref:`full-blown WSGI server <deployment>`.
+
+
+Combining Applications
+----------------------
+
+If you have entirely separated applications and you want them to work next
+to each other in the same Python interpreter process you can take
+advantage of the :class:`werkzeug.wsgi.DispatcherMiddleware`. The idea
+here is that each Flask application is a valid WSGI application and they
+are combined by the dispatcher middleware into a larger one that
+dispatched based on prefix.
+
+For example you could have your main application run on `/` and your
+backend interface on `/backend`::
+
+ from werkzeug.wsgi import DispatcherMiddleware
+ from frontend_app import application as frontend
+ from backend_app import application as backend
+
+ application = DispatcherMiddleware(frontend, {
+ '/backend': backend
+ })
+
+
+Dispatch by Subdomain
+---------------------
+
+Sometimes you might want to use multiple instances of the same application
+with different configurations. Assuming the application is created inside
+a function and you can call that function to instanciate it, that is
+really easy to implement. In order to develop your application to support
+creating new instances in functions have a look at the
+:ref:`app-factories` pattern.
+
+A very common example would be creating applications per subdomain. For
+instance you configure your webserver to dispatch all requests for all
+subdomains to your application and you then use the subdomain information
+to create user-specific instances. Once you have your server set up to
+listen on all subdomains you can use a very simple WSGI application to do
+the dynamic application creation.
+
+The perfect level for abstraction in that regard is the WSGI layer. You
+write your own WSGI application that looks at the request that comes and
+and delegates it to your Flask application. If that application does not
+exist yet, it is dynamically created and remembered::
+
+ from threading import Lock
+
+ class SubdomainDispatcher(object):
+
+ def __init__(self, domain, create_app):
+ self.domain = domain
+ self.create_app = create_app
+ self.lock = Lock()
+ self.instances = {}
+
+ def get_application(self, host):
+ host = host.split(':')[0]
+ assert host.endswith(self.domain), 'Configuration error'
+ subdomain = host[:-len(self.domain)].rstrip('.')
+ with self.lock:
+ app = self.instances.get(subdomain)
+ if app is None:
+ app = self.create_app(subdomain)
+ self.instances[subdomain] = app
+ return app
+
+ def __call__(self, environ, start_response):
+ app = self.get_application(environ['HTTP_HOST'])
+ return app(environ, start_response)
+
+
+This dispatcher can then be used like this::
+
+ from myapplication import create_app, get_user_for_subdomain
+ from werkzeug.exceptions import NotFound
+
+ def make_app(subdomain):
+ user = get_user_for_subdomain(subdomain)
+ if user is None:
+ # if there is no user for that subdomain we still have
+ # to return a WSGI application that handles that request.
+ # We can then just return the NotFound() exception as
+ # application which will render a default 404 page.
+ # You might also redirect the user to the main page then
+ return NotFound()
+
+ # otherwise create the application for the specific user
+ return create_app(user)
+
+ application = SubdomainDispatcher('example.com', make_app)
+
+
+Dispatch by Path
+----------------
+
+Dispatching by a path on the URL is very similar. Instead of looking at
+the `Host` header to figure out the subdomain one simply looks at the
+request path up to the first slash::
+
+ from threading import Lock
+ from werkzeug.wsgi import pop_path_info, peek_path_info
+
+ class PathDispatcher(object):
+
+ def __init__(self, default_app, create_app):
+ self.default_app = default_app
+ self.create_app = create_app
+ self.lock = Lock()
+ self.instances = {}
+
+ def get_application(self, prefix):
+ with self.lock:
+ app = self.instances.get(prefix)
+ if app is None:
+ app = self.create_app(prefix)
+ if app is not None:
+ self.instances[prefix] = app
+ return app
+
+ def __call__(self, environ, start_response):
+ app = self.get_application(peek_path_info(environ))
+ if app is not None:
+ pop_path_info(environ)
+ else:
+ app = self.default_app
+ return app(environ, start_response)
+
+The big difference between this and the subdomain one is that this one
+falls back to another application if the creator function returns `None`::
+
+ from myapplication import create_app, default_app, get_user_for_prefix
+
+ def make_app(prefix):
+ user = get_user_for_prefix(prefix)
+ if user is not None:
+ return create_app(user)
+
+ application = PathDispatcher(default_app, make_app)
diff --git a/studio/static/doc/flask-docs/_sources/patterns/appfactories.txt b/studio/static/doc/flask-docs/_sources/patterns/appfactories.txt
new file mode 100644
index 0000000..2a6190e
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/appfactories.txt
@@ -0,0 +1,75 @@
+.. _app-factories:
+
+Application Factories
+=====================
+
+If you are already using packages and blueprints for your application
+(:ref:`blueprints`) there are a couple of really nice ways to further improve
+the experience. A common pattern is creating the application object when
+the blueprint 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_blueprint(admin)
+ app.register_blueprint(frontend)
+
+ return app
+
+The downside is that you cannot use the application object in the blueprints
+at import time. You can however use it from within a request. How do you
+get access to the application with the config? Use
+:data:`~flask.current_app`::
+
+ from flask import current_app, Blueprint, render_template
+ admin = Blueprint('admin', __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 blueprint 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.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/caching.txt b/studio/static/doc/flask-docs/_sources/patterns/caching.txt
new file mode 100644
index 0000000..5817aa2
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/caching.txt
@@ -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
diff --git a/studio/static/doc/flask-docs/_sources/patterns/deferredcallbacks.txt b/studio/static/doc/flask-docs/_sources/patterns/deferredcallbacks.txt
new file mode 100644
index 0000000..917c512
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/deferredcallbacks.txt
@@ -0,0 +1,73 @@
+.. _deferred-callbacks:
+
+Deferred Request Callbacks
+==========================
+
+One of the design principles of Flask is that response objects are created
+and passed down a chain of potential callbacks that can modify them or
+replace them. When the request handling starts, there is no response
+object yet. It is created as necessary either by a view function or by
+some other component in the system.
+
+But what happens if you want to modify the response at a point where the
+response does not exist yet? A common example for that would be a
+before-request function that wants to set a cookie on the response object.
+
+One way is to avoid the situation. Very often that is possible. For
+instance you can try to move that logic into an after-request callback
+instead. Sometimes however moving that code there is just not a very
+pleasant experience or makes code look very awkward.
+
+As an alternative possibility you can attach a bunch of callback functions
+to the :data:`~flask.g` object and call then at the end of the request.
+This way you can defer code execution from anywhere in the application.
+
+
+The Decorator
+-------------
+
+The following decorator is the key. It registers a function on a list on
+the :data:`~flask.g` object::
+
+ from flask import g
+
+ def after_this_request(f):
+ if not hasattr(g, 'after_request_callbacks'):
+ g.after_request_callbacks = []
+ g.after_request_callbacks.append(f)
+ return f
+
+
+Calling the Deferred
+--------------------
+
+Now you can use the `after_this_request` decorator to mark a function to
+be called at the end of the request. But we still need to call them. For
+this the following function needs to be registered as
+:meth:`~flask.Flask.after_request` callback::
+
+ @app.after_request
+ def call_after_request_callbacks(response):
+ for callback in getattr(g, 'after_request_callbacks', ()):
+ response = callback(response)
+ return response
+
+
+A Practical Example
+-------------------
+
+Now we can easily at any point in time register a function to be called at
+the end of this particular request. For example you can remember the
+current language of the user in a cookie in the before-request function::
+
+ from flask import request
+
+ @app.before_request
+ def detect_user_language():
+ language = request.cookies.get('user_lang')
+ if language is None:
+ language = guess_language_from_request()
+ @after_this_request
+ def remember_language(response):
+ response.set_cookie('user_lang', language)
+ g.language = language
diff --git a/studio/static/doc/flask-docs/_sources/patterns/distribute.txt b/studio/static/doc/flask-docs/_sources/patterns/distribute.txt
new file mode 100644
index 0000000..b6f6a5e
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/distribute.txt
@@ -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
diff --git a/studio/static/doc/flask-docs/_sources/patterns/errorpages.txt b/studio/static/doc/flask-docs/_sources/patterns/errorpages.txt
new file mode 100644
index 0000000..ddf73c9
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/errorpages.txt
@@ -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 they try to access a forbidden resource.
+
+*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 they were
+ 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 %}
diff --git a/studio/static/doc/flask-docs/_sources/patterns/fabric.txt b/studio/static/doc/flask-docs/_sources/patterns/fabric.txt
new file mode 100644
index 0000000..b02ad27
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/fabric.txt
@@ -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 pre-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', capture=True).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 /tmp/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/
diff --git a/studio/static/doc/flask-docs/_sources/patterns/favicon.txt b/studio/static/doc/flask-docs/_sources/patterns/favicon.txt
new file mode 100644
index 0000000..f7b2f9c
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/favicon.txt
@@ -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
diff --git a/studio/static/doc/flask-docs/_sources/patterns/fileuploads.txt b/studio/static/doc/flask-docs/_sources/patterns/fileuploads.txt
new file mode 100644
index 0000000..d237b10
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/fileuploads.txt
@@ -0,0 +1,181 @@
+.. _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.datastructures.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__)
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
+
+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(app.config['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.utils.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.utils.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 or she 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.wsgi.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': app.config['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 it has transmitted
+already. Do you realize the irony? The client is asking for something it
+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/
diff --git a/studio/static/doc/flask-docs/_sources/patterns/flashing.txt b/studio/static/doc/flask-docs/_sources/patterns/flashing.txt
new file mode 100644
index 0000000..7abe716
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/flashing.txt
@@ -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 they 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.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/index.txt b/studio/static/doc/flask-docs/_sources/patterns/index.txt
new file mode 100644
index 0000000..964b1e1
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/index.txt
@@ -0,0 +1,39 @@
+.. _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
+ appdispatch
+ urlprocessors
+ distribute
+ fabric
+ sqlite3
+ sqlalchemy
+ fileuploads
+ caching
+ viewdecorators
+ wtforms
+ templateinheritance
+ flashing
+ jquery
+ errorpages
+ lazyloading
+ mongokit
+ favicon
+ streaming
+ deferredcallbacks
diff --git a/studio/static/doc/flask-docs/_sources/patterns/jquery.txt b/studio/static/doc/flask-docs/_sources/patterns/jquery.txt
new file mode 100644
index 0000000..f3c46e3
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/jquery.txt
@@ -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 the bottom of your `<body>` 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 src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js"></script>
+ <script>window.jQuery || document.write('<script src="{{
+ url_for('static', filename='jquery.js') }}">\x3C/script>')</script>
+
+In this case you have to put jQuery into your static folder as a fallback, but it will
+first try to load it directly from Google. 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.
+
+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.datastructures.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.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/lazyloading.txt b/studio/static/doc/flask-docs/_sources/patterns/lazyloading.txt
new file mode 100644
index 0000000..50ad6fa
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/lazyloading.txt
@@ -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.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/mongokit.txt b/studio/static/doc/flask-docs/_sources/patterns/mongokit.txt
new file mode 100644
index 0000000..a9c4eef
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/mongokit.txt
@@ -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/>`_.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/packages.txt b/studio/static/doc/flask-docs/_sources/patterns/packages.txt
new file mode 100644
index 0000000..79fd2c5
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/packages.txt
@@ -0,0 +1,115 @@
+.. _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. Import the view module
+ **after the application object is created**.
+
+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
+ /runserver.py
+ /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 Blueprints
+-----------------------
+
+If you have larger applications it's recommended to divide them into
+smaller groups where each group is implemented with the help of a
+blueprint. For a gentle introduction into this topic refer to the
+:ref:`blueprints` chapter of the documentation.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/sqlalchemy.txt b/studio/static/doc/flask-docs/_sources/patterns/sqlalchemy.txt
new file mode 100644
index 0000000..5a33d1f
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/sqlalchemy.txt
@@ -0,0 +1,214 @@
+.. _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.teardown_request
+ def shutdown_session(exception=None):
+ db_session.remove()
+
+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/orm/extensions/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.teardown_request
+ def shutdown_session(exception=None):
+ db_session.remove()
+
+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/>`_.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/sqlite3.txt b/studio/static/doc/flask-docs/_sources/patterns/sqlite3.txt
new file mode 100644
index 0000000..0d02e46
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/sqlite3.txt
@@ -0,0 +1,119 @@
+.. _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.teardown_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.teardown_request
+ def teardown_request(exception):
+ if hasattr(g, 'db'):
+ g.db.close()
+
+.. note::
+
+ Please keep in mind that the teardown request functions are always
+ executed, even if a before-request handler failed or was never
+ executed. Because of this we have to make sure here that the database
+ is there before we close it.
+
+Connect on Demand
+-----------------
+
+The downside of this approach is that this will only work if Flask
+executed the before-request handlers for you. If you are attempting to
+use the database from a script or the interactive Python shell you would
+have to do something like this::
+
+ with app.test_request_context():
+ app.preprocess_request()
+ # now you can use the g.db object
+
+In order to trigger the execution of the connection code. You won't be
+able to drop the dependency on the request context this way, but you could
+make it so that the application connects when necessary::
+
+ def get_connection():
+ db = getattr(g, '_db', None)
+ if db is None:
+ db = g._db = connect_db()
+ return db
+
+Downside here is that you have to use ``db = get_connection()`` instead of
+just being able to use ``g.db`` directly.
+
+.. _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()
diff --git a/studio/static/doc/flask-docs/_sources/patterns/streaming.txt b/studio/static/doc/flask-docs/_sources/patterns/streaming.txt
new file mode 100644
index 0000000..8393b00
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/streaming.txt
@@ -0,0 +1,61 @@
+Streaming Contents
+==================
+
+Sometimes you want to send an enormous amount of data to the client, much
+more than you want to keep in memory. When you are generating the data on
+the fly though, how do you send that back to the client without the
+roundtrip to the filesystem?
+
+The answer is by using generators and direct responses.
+
+Basic Usage
+-----------
+
+This is a basic view function that generates a lot of CSV data on the fly.
+The trick is to have an inner function that uses a generator to generate
+data and to then invoke that function and pass it to a response object::
+
+ from flask import Response
+
+ @app.route('/large.csv')
+ def generate_large_csv():
+ def generate():
+ for row in iter_all_rows():
+ yield ','.join(row) + '\n'
+ return Response(generate(), mimetype='text/csv')
+
+Each ``yield`` expression is directly sent to the browser. Now though
+that some WSGI middlewares might break streaming, so be careful there in
+debug environments with profilers and other things you might have enabled.
+
+Streaming from Templates
+------------------------
+
+The Jinja2 template engine also supports rendering templates piece by
+piece. This functionality is not directly exposed by Flask because it is
+quite uncommon, but you can easily do it yourself::
+
+ from flask import Response
+
+ def stream_template(template_name, **context):
+ app.update_template_context(context)
+ t = app.jinja_env.get_template(template_name)
+ rv = t.stream(context)
+ rv.enable_buffering(5)
+ return rv
+
+ @app.route('/my-large-page.html')
+ def render_large_template():
+ rows = iter_all_rows()
+ return Response(stream_template('the_template.html', rows=rows))
+
+The trick here is to get the template object from the Jinja2 environment
+on the application and to call :meth:`~jinja2.Template.stream` instead of
+:meth:`~jinja2.Template.render` which returns a stream object instead of a
+string. Since we're bypassing the Flask template render functions and
+using the template object itself we have to make sure to update the render
+context ourselves by calling :meth:`~flask.Flask.update_template_context`.
+The template is then evaluated as the stream is iterated over. Since each
+time you do a yield the server will flush the content to the client you
+might want to buffer up a few items in the template which you can do with
+``rv.enable_buffering(size)``. ``5`` is a sane default.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/templateinheritance.txt b/studio/static/doc/flask-docs/_sources/patterns/templateinheritance.txt
new file mode 100644
index 0000000..70015ec
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/templateinheritance.txt
@@ -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 %}
+ &copy; 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 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() }}``.
diff --git a/studio/static/doc/flask-docs/_sources/patterns/urlprocessors.txt b/studio/static/doc/flask-docs/_sources/patterns/urlprocessors.txt
new file mode 100644
index 0000000..778a5a6
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/urlprocessors.txt
@@ -0,0 +1,126 @@
+Using URL Processors
+====================
+
+.. versionadded:: 0.7
+
+Flask 0.7 introduces the concept of URL processors. The idea is that you
+might have a bunch of resources with common parts in the URL that you
+don't always explicitly want to provide. For instance you might have a
+bunch of URLs that have the language code in it but you don't want to have
+to handle it in every single function yourself.
+
+URL processors are especially helpful when combined with blueprints. We
+will handle both application specific URL processors here as well as
+blueprint specifics.
+
+Internationalized Application URLs
+----------------------------------
+
+Consider an application like this::
+
+ from flask import Flask, g
+
+ app = Flask(__name__)
+
+ @app.route('/<lang_code>/')
+ def index(lang_code):
+ g.lang_code = lang_code
+ ...
+
+ @app.route('/<lang_code>/about')
+ def about(lang_code):
+ g.lang_code = lang_code
+ ...
+
+This is an awful lot of repetition as you have to handle the language code
+setting on the :data:`~flask.g` object yourself in every single function.
+Sure, a decorator could be used to simplify this, but if you want to
+generate URLs from one function to another you would have to still provide
+the language code explicitly which can be annoying.
+
+For the latter, this is where :func:`~flask.Flask.url_defaults` functions
+come in. They can automatically inject values into a call for
+:func:`~flask.url_for` automatically. The code below checks if the
+language code is not yet in the dictionary of URL values and if the
+endpoint wants a value named ``'lang_code'``::
+
+ @app.url_defaults
+ def add_language_code(endpoint, values):
+ if 'lang_code' in values or not g.lang_code:
+ return
+ if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
+ values['lang_code'] = g.lang_code
+
+The method :meth:`~werkzeug.routing.Map.is_endpoint_expecting` of the URL
+map can be used to figure out if it would make sense to provide a language
+code for the given endpoint.
+
+The reverse of that function are
+:meth:`~flask.Flask.url_value_preprocessor`\s. They are executed right
+after the request was matched and can execute code based on the URL
+values. The idea is that they pull information out of the values
+dictionary and put it somewhere else::
+
+ @app.url_value_preprocessor
+ def pull_lang_code(endpoint, values):
+ g.lang_code = values.pop('lang_code', None)
+
+That way you no longer have to do the `lang_code` assigment to
+:data:`~flask.g` in every function. You can further improve that by
+writing your own decorator that prefixes URLs with the language code, but
+the more beautiful solution is using a blueprint. Once the
+``'lang_code'`` is popped from the values dictionary and it will no longer
+be forwarded to the view function reducing the code to this::
+
+ from flask import Flask, g
+
+ app = Flask(__name__)
+
+ @app.url_defaults
+ def add_language_code(endpoint, values):
+ if 'lang_code' in values or not g.lang_code:
+ return
+ if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
+ values['lang_code'] = g.lang_code
+
+ @app.url_value_preprocessor
+ def pull_lang_code(endpoint, values):
+ g.lang_code = values.pop('lang_code', None)
+
+ @app.route('/<lang_code>/')
+ def index():
+ ...
+
+ @app.route('/<lang_code>/about')
+ def about():
+ ...
+
+Internationalized Blueprint URLs
+--------------------------------
+
+Because blueprints can automatically prefix all URLs with a common string
+it's easy to automatically do that for every function. Furthermore
+blueprints can have per-blueprint URL processors which removes a whole lot
+of logic from the :meth:`~flask.Flask.url_defaults` function because it no
+longer has to check if the URL is really interested in a ``'lang_code'``
+parameter::
+
+ from flask import Blueprint, g
+
+ bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>')
+
+ @bp.url_defaults
+ def add_language_code(endpoint, values):
+ values.setdefault('lang_code', g.lang_code)
+
+ @bp.url_value_preprocessor
+ def pull_lang_code(endpoint, values):
+ g.lang_code = values.pop('lang_code')
+
+ @bp.route('/')
+ def index():
+ ...
+
+ @bp.route('/about')
+ def about():
+ ...
diff --git a/studio/static/doc/flask-docs/_sources/patterns/viewdecorators.txt b/studio/static/doc/flask-docs/_sources/patterns/viewdecorators.txt
new file mode 100644
index 0000000..a094857
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/viewdecorators.txt
@@ -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, they 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"
+
+
+
diff --git a/studio/static/doc/flask-docs/_sources/patterns/wtforms.txt b/studio/static/doc/flask-docs/_sources/patterns/wtforms.txt
new file mode 100644
index 0000000..93824df
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/patterns/wtforms.txt
@@ -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/
diff --git a/studio/static/doc/flask-docs/_sources/quickstart.txt b/studio/static/doc/flask-docs/_sources/quickstart.txt
new file mode 100644
index 0000000..34aa3be
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/quickstart.txt
@@ -0,0 +1,824 @@
+.. _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 this::
+
+ 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
+
+.. admonition:: Working With Other Debuggers
+
+ Debuggers interfere with each other. If you are using another debugger
+ (e.g. PyDev or IntelliJ), you may need to set ``app.debug = False``.
+
+
+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 they 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 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 &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
+>>> Markup.escape('<blink>hacker</blink>')
+Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
+>>> Markup('<em>Marked up</em> &raquo; 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.datastructures.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.datastructures.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.utils.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. To set cookies you can use the
+:attr:`~flask.Response.set_cookie` method of response objects. The
+:attr:`~flask.Request.cookies` attribute of request objects 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.
+
+Reading cookies::
+
+ from flask import request
+
+ @app.route('/')
+ def index():
+ username = request.cookies.get('username')
+ # use cookies.get(key) instead of cookies[key] to not get a
+ # KeyError if the cookie is missing.
+
+Storing cookies::
+
+ from flask import make_response
+
+ @app.route('/')
+ def index():
+ resp = make_response(render_template(...))
+ resp.set_cookie('username', 'the username')
+ return resp
+
+Note that cookies are set on response objects. Since you normally you
+just return strings from the view functions Flask will convert them into
+response objects for you. If you explicitly want to do that you can use
+the :meth:`~flask.make_response` function and then modify it.
+
+Sometimes you might want to set a cookie at a point where the response
+object does not exist yet. This is possible by utilizing the
+:ref:`deferred-callbacks` pattern.
+
+For this also see :ref:`about-responses`.
+
+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 they 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.
+
+.. _about-responses:
+
+About Responses
+---------------
+
+The return value from a view function is automatically converted into a
+response object for you. If the return value is a string it's converted
+into a response object with the string as response body, an ``200 OK``
+error code and a ``text/html`` mimetype. The logic that Flask applies to
+converting return values into response objects is as follows:
+
+1. If a response object of the correct type is returned it's directly
+ returned from the view.
+2. If it's a string, a response object is created with that data and the
+ default parameters.
+3. If a tuple is returned the response object is created by passing the
+ tuple as arguments to the response object's constructor.
+4. If neither of that works, Flask will assume the return value is a
+ valid WSGI application and converts that into a response object.
+
+If you want to get hold of the resulting response object inside the view
+you can use the :func:`~flask.make_response` function.
+
+Imagine you have a view like this:
+
+.. sourcecode:: python
+
+ @app.errorhandler(404)
+ def not_found(error):
+ return render_template('error.html'), 404
+
+You just need to wrap the return expression with
+:func:`~flask.make_response` and get the result object to modify it, then
+return it:
+
+.. sourcecode:: python
+
+ @app.errorhandler(404)
+ def not_found(error):
+ resp = make_response(render_template('error.html'), 404)
+ resp.headers['X-Something'] = 'A value'
+ return resp
+
+.. _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 they know 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 they 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)
diff --git a/studio/static/doc/flask-docs/_sources/reqcontext.txt b/studio/static/doc/flask-docs/_sources/reqcontext.txt
new file mode 100644
index 0000000..0249b88
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/reqcontext.txt
@@ -0,0 +1,239 @@
+.. _request-context:
+
+The Request Context
+===================
+
+This document describes the behavior in Flask 0.7 which is mostly in line
+with the old behavior but has some small, subtle differences.
+
+One of the design ideas behind Flask is that there are two different
+“states” in which code is executed. The application setup state in which
+the application implicitly is on the module level. It starts when the
+:class:`Flask` object is instantiated, and it implicitly ends when the
+first request comes in. While the application is in this state a few
+assumptions are true:
+
+- the programmer can modify the application object safely.
+- no request handling happened so far
+- you have to have a reference to the application object in order to
+ modify it, there is no magic proxy that can give you a reference to
+ the application object you're currently creating or modifying.
+
+On the contrast, during request handling, a couple of other rules exist:
+
+- while a request is active, the context local objects
+ (:data:`flask.request` and others) point to the current request.
+- any code can get hold of these objects at any time.
+
+The magic that makes this works is internally referred in Flask as the
+“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 :class:`~flask.ctx.RequestContext`:
+
+>>> ctx = app.test_request_context('/?next=http://example.com/')
+
+This context can be used in two ways. Either with the `with` statement
+or by calling the :meth:`~flask.ctx.RequestContext.push` and
+:meth:`~flask.ctx.RequestContext.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()
+
+Because the request context is internally maintained as a stack you can
+push and pop multiple times. This is very handy to implement things like
+internal redirects.
+
+For more information of how to utilize the request context from the
+interactive Python shell, head over to the :ref:`shell` chapter.
+
+How the Context Works
+---------------------
+
+If you look into how the Flask WSGI application internally works, you will
+find a piece of code that looks very much like this::
+
+ def wsgi_app(self, environ):
+ with self.request_context(environ):
+ try:
+ response = self.full_dispatch_request()
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ return response(environ, start_response)
+
+The method :meth:`~Flask.request_context` returns a new
+:class:`~flask.ctx.RequestContext` object and uses it in combination with
+the `with` statement to bind the context. Everything that is called from
+the same thread from this point onwards until the end of the `with`
+statement will have access to the request globals (:data:`flask.request`
+and others).
+
+The request context internally works like a stack: The topmost level on
+the stack is the current active request.
+:meth:`~flask.ctx.RequestContext.push` adds the context to the stack on
+the very top, :meth:`~flask.ctx.RequestContext.pop` removes it from the
+stack again. On popping the application's
+:func:`~flask.Flask.teardown_request` functions are also executed.
+
+.. _callbacks-and-errors:
+
+Callbacks and Errors
+--------------------
+
+What happens if an error occurs in Flask during request processing? This
+particular behavior changed in 0.7 because we wanted to make it easier to
+understand what is actually happening. The new behavior is quite simple:
+
+1. Before each request, :meth:`~flask.Flask.before_request` functions are
+ executed. If one of these functions return a response, the other
+ functions are no longer called. In any case however the return value
+ is treated as a replacement for the view's return value.
+
+2. If the :meth:`~flask.Flask.before_request` functions did not return a
+ response, the regular request handling kicks in and the view function
+ that was matched has the chance to return a response.
+
+3. The return value of the view is then converted into an actual response
+ object and handed over to the :meth:`~flask.Flask.after_request`
+ functions which have the chance to replace it or modify it in place.
+
+4. At the end of the request the :meth:`~flask.Flask.teardown_request`
+ functions are executed. This always happens, even in case of an
+ unhandled exception down the road or if a before-request handler was
+ not executed yet or at all (for example in test environments sometimes
+ you might want to not execute before-request callbacks).
+
+Now what happens on errors? In production mode if an exception is not
+caught, the 500 internal server handler is called. In development mode
+however the exception is not further processed and bubbles up to the WSGI
+server. That way things like the interactive debugger can provide helpful
+debug information.
+
+An important change in 0.7 is that the internal server error is now no
+longer post processed by the after request callbacks and after request
+callbacks are no longer guaranteed to be executed. This way the internal
+dispatching code looks cleaner and is easier to customize and understand.
+
+The new teardown functions are supposed to be used as a replacement for
+things that absolutely need to happen at the end of request.
+
+Teardown Callbacks
+------------------
+
+The teardown callbacks are special callbacks in that they are executed at
+at different point. Strictly speaking they are independent of the actual
+request handling as they are bound to the lifecycle of the
+:class:`~flask.ctx.RequestContext` object. When the request context is
+popped, the :meth:`~flask.Flask.teardown_request` functions are called.
+
+This is important to know if the life of the request context is prolonged
+by using the test client in a with statement or when using the request
+context from the command line::
+
+ with app.test_client() as client:
+ resp = client.get('/foo')
+ # the teardown functions are still not called at that point
+ # even though the response ended and you have the response
+ # object in your hand
+
+ # only when the code reaches this point the teardown functions
+ # are called. Alternatively the same thing happens if another
+ # request was triggered from the test client
+
+It's easy to see the behavior from the command line:
+
+>>> app = Flask(__name__)
+>>> @app.teardown_request
+... def teardown_request(exception=None):
+... print 'this runs after request'
+...
+>>> ctx = app.test_request_context()
+>>> ctx.push()
+>>> ctx.pop()
+this runs after request
+>>>
+
+Keep in mind that teardown callbacks are always executed, even if
+before-request callbacks were not executed yet but an exception happened.
+Certain parts of the test system might also temporarily create a request
+context without calling the before-request handlers. Make sure to write
+your teardown-request handlers in a way that they will never fail.
+
+.. _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.local.LocalProxy._get_current_object` method::
+
+ app = current_app._get_current_object()
+ my_signal.send(app)
+
+Context Preservation on Error
+-----------------------------
+
+If an error occurs or not, at the end of the request the request context
+is popped and all data associated with it is destroyed. During
+development however that can be problematic as you might want to have the
+information around for a longer time in case an exception occurred. In
+Flask 0.6 and earlier in debug mode, if an exception occurred, the
+request context was not popped so that the interactive debugger can still
+provide you with important information.
+
+Starting with Flask 0.7 you have finer control over that behavior by
+setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By
+default it's linked to the setting of ``DEBUG``. If the application is in
+debug mode the context is preserved, in production mode it's not.
+
+Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` in production mode
+as it will cause your application to leak memory on exceptions. However
+it can be useful during development to get the same error preserving
+behavior as in development mode when attempting to debug an error that
+only occurs under production settings.
diff --git a/studio/static/doc/flask-docs/_sources/security.txt b/studio/static/doc/flask-docs/_sources/security.txt
new file mode 100644
index 0000000..909ef53
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/security.txt
@@ -0,0 +1,175 @@
+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 they 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
+-------------
+
+.. admonition:: ECMAScript 5 Changes
+
+ Starting with ECMAScript 5 the behavior of literals changed. Now they
+ are not constructed with the constructor of ``Array`` and others, but
+ with the builtin constructor of ``Array`` which closes this particular
+ attack vector.
+
+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
+top-level 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 top-level 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.
diff --git a/studio/static/doc/flask-docs/_sources/shell.txt b/studio/static/doc/flask-docs/_sources/shell.txt
new file mode 100644
index 0000000..61b9dc0
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/shell.txt
@@ -0,0 +1,93 @@
+.. _shell:
+
+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.
+
+Generally it's recommended that you read the :ref:`request-context`
+chapter of the documentation first.
+
+Creating a Request Context
+--------------------------
+
+The easiest way to create a proper request context from the shell is by
+using the :attr:`~flask.Flask.test_request_context` method which creates
+us a :class:`~flask.ctx.RequestContext`:
+
+>>> ctx = app.test_request_context()
+
+Normally you would use the `with` statement to make this request object
+active, but in the shell it's easier to use the
+:meth:`~flask.ctx.RequestContext.push` and
+:meth:`~flask.ctx.RequestContext.pop` methods by hand:
+
+>>> ctx.push()
+
+From that point onwards you can work with the request object until you
+call `pop`:
+
+>>> ctx.pop()
+
+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 might result in your database
+being unavailable if you are connecting to the database in a
+before-request callback or 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()
+
+The functions registered as :meth:`~flask.Flask.teardown_request` are
+automatically called when the context is popped. So this is the perfect
+place to automatically tear down resources that were needed by the request
+context (such as database connections).
+
+
+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 *
diff --git a/studio/static/doc/flask-docs/_sources/signals.txt b/studio/static/doc/flask-docs/_sources/signals.txt
new file mode 100644
index 0000000..0d1d9ee
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/signals.txt
@@ -0,0 +1,255 @@
+.. _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 want 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)
+
+.. data:: flask.request_tearing_down
+ :noindex:
+
+ This signal is sent when the request is tearing down. This is always
+ called, even if an exception is caused. Currently functions listening
+ to this signal are called after the regular teardown handlers, but this
+ is not something you can rely on.
+
+ Example subscriber::
+
+ def close_db_connection(sender):
+ session.close()
+
+ from flask import request_tearing_down
+ request_tearing_down.connect(close_db_connection, app)
+
+.. _blinker: http://pypi.python.org/pypi/blinker
diff --git a/studio/static/doc/flask-docs/_sources/styleguide.txt b/studio/static/doc/flask-docs/_sources/styleguide.txt
new file mode 100644
index 0000000..d46ecd0
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/styleguide.txt
@@ -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 no-go:
+ 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)
diff --git a/studio/static/doc/flask-docs/_sources/templating.txt b/studio/static/doc/flask-docs/_sources/templating.txt
new file mode 100644
index 0000000..bd940b0
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/templating.txt
@@ -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.
diff --git a/studio/static/doc/flask-docs/_sources/testing.txt b/studio/static/doc/flask-docs/_sources/testing.txt
new file mode 100644
index 0000000..1e00fe8
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/testing.txt
@@ -0,0 +1,307 @@
+.. _testing:
+
+Testing Flask Applications
+==========================
+
+ **Something that is untested is broken.**
+
+The origin of this quote is unknown and while it is not entirely correct, it is also
+not 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 make changes and instantly know if anything breaks.
+
+Flask provides a way to test your application by exposing the Werkzeug
+test :class:`~werkzeug.test.Client` 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 pre-installed with Python.
+
+The Application
+---------------
+
+First, we need an application to test; 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 the application, 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()
+ flaskr.app.config['TESTING'] = True
+ 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. This function is called before
+each individual test function is run. To delete the database after the
+test, we close the file and remove it from the filesystem in the
+:meth:`~unittest.TestCase.tearDown` method. Additionally during setup the
+``TESTING`` config flag is activated. What it does is disabling the error
+catching during request handling so that you get better error reports when
+performing test requests against the application.
+
+This test client will 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 the 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 actual tests, we already know that our flaskr
+application is syntactically valid, otherwise the import would have died
+with an exception.
+
+The First Test
+--------------
+
+Now it's time to start testing the functionality of the application.
+Let's check that the application shows "No entries here so far" if we
+access the root of the application (``/``). To do this, we add a new
+test method to our class, 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
+
+Notice that our test functions begin with the word `test`; this allows
+:mod:`unittest` to automatically identify the method as a test to run.
+
+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.wrappers.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
+
+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 and out
+of the application. To do this, we fire some requests to the login and logout
+pages with the required form data (username and password). And 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 that 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
+--------------------
+
+We should 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 '&lt;Hello&gt;' 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 which contains a larger test
+suite.
+
+
+.. _MiniTwit Example:
+ http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/
+
+
+Other Testing Tricks
+--------------------
+
+Besides using the test client as shown above, there is also the
+:meth:`~flask.Flask.test_request_context` method that can be used
+in combination with the `with` statement to activate a request context
+temporarily. With this you can access the :class:`~flask.request`,
+:class:`~flask.g` and :class:`~flask.session` objects like in view
+functions. Here is a full example that demonstrates this approach::
+
+ 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 in the same
+way.
+
+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`).
+
+Note however that if you are using a test request context, the
+:meth:`~flask.Flask.before_request` functions are not automatically called
+same for :meth:`~flask.Flask.after_request` functions. However
+:meth:`~flask.Flask.teardown_request` functions are indeed executed when
+the test request context leaves the `with` block. If you do want the
+:meth:`~flask.Flask.before_request` functions to be called as well, you
+need to call :meth:`~flask.Flask.preprocess_request` yourself::
+
+ app = flask.Flask(__name__)
+
+ with app.test_request_context('/?name=Peter'):
+ app.preprocess_request()
+ ...
+
+This can be necessary to open database connections or something similar
+depending on how your application was designed.
+
+If you want to call the :meth:`~flask.Flask.after_request` functions you
+need to call into :meth:`~flask.Flask.process_response` which however
+requires that you pass it a response object::
+
+ app = flask.Flask(__name__)
+
+ with app.test_request_context('/?name=Peter'):
+ resp = Response('...')
+ resp = app.process_response(resp)
+ ...
+
+This in general is less useful because at that point you can directly
+start using the test client.
+
+
+Keeping the Context Around
+--------------------------
+
+.. versionadded:: 0.4
+
+Sometimes it is helpful to trigger a regular request but still 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 were to use just the :meth:`~flask.Flask.test_client` without
+the `with` block, the `assert` would fail with an error because `request`
+is no longer available (because you are trying to use it outside of the actual request).
+However, keep in mind that any :meth:`~flask.Flask.after_request` functions
+are already called at this point so your database connection and
+everything involved is probably already closed down.
+
+
+Accessing and Modifying Sessions
+--------------------------------
+
+.. versionadded:: 0.8
+
+Sometimes it can be very helpful to access or modify the sessions from the
+test client. Generally there are two ways for this. If you just want to
+ensure that a session has certain keys set to certain values you can just
+keep the context around and access :data:`flask.session`::
+
+ with app.test_client() as c:
+ rv = c.get('/')
+ assert flask.session['foo'] == 42
+
+This however does not make it possible to also modify the session or to
+access the session before a request was fired. Starting with Flask 0.8 we
+provide a so called “session transaction” which simulates the appropriate
+calls to open a session in the context of the test client and to modify
+it. At the end of the transaction the session is stored. This works
+independently of the session backend used::
+
+ with app.test_client() as c:
+ with c.session_transaction() as sess:
+ sess['a_key'] = 'a value'
+
+ # once this is reached the session was stored
+
+Note that in this case you have to use the ``sess`` object instead of the
+:data:`flask.session` proxy. The object however itself will provide the
+same interface.
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/css.txt b/studio/static/doc/flask-docs/_sources/tutorial/css.txt
new file mode 100644
index 0000000..03f62ed
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/css.txt
@@ -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`.
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/dbcon.txt b/studio/static/doc/flask-docs/_sources/tutorial/dbcon.txt
new file mode 100644
index 0000000..99391a2
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/dbcon.txt
@@ -0,0 +1,57 @@
+.. _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`,
+:meth:`~flask.Flask.after_request` and :meth:`~flask.Flask.teardown_request`
+decorators::
+
+ @app.before_request
+ def before_request():
+ g.db = connect_db()
+
+ @app.teardown_request
+ def teardown_request(exception):
+ g.db.close()
+
+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. They are however not guaranteed
+to be executed if an exception is raised, this is where functions marked with
+:meth:`~flask.Flask.teardown_request` come in. They get called after the
+response has been constructed. They are not allowed to modify the request, and
+their return values are ignored. If an exception occurred while the request was
+being processed, it is passed to each function; otherwise, `None` is passed in.
+
+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`.
+
+.. hint:: Where do I put this code?
+
+ If you've been following along in this tutorial, you might be wondering
+ where to put the code from this step and the next. A logical place is to
+ group these module-level functions together, and put your new
+ ``before_request`` and ``teardown_request`` functions below your existing
+ ``init_db`` function (following the tutorial line-by-line).
+
+ If you need a moment to find your bearings, take a look at how the `example
+ source`_ is organized. In Flask, you can put all of your application code
+ into a single Python module. You don't have to, and if your app :ref:`grows
+ larger <larger-applications>`, it's a good idea not to.
+
+.. _example source:
+ http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/dbinit.txt b/studio/static/doc/flask-docs/_sources/tutorial/dbinit.txt
new file mode 100644
index 0000000..b546a1a
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/dbinit.txt
@@ -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`
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/folders.txt b/studio/static/doc/flask-docs/_sources/tutorial/folders.txt
new file mode 100644
index 0000000..6108093
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/folders.txt
@@ -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/
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/index.txt b/studio/static/doc/flask-docs/_sources/tutorial/index.txt
new file mode 100644
index 0000000..3f2d659
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/index.txt
@@ -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
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/introduction.txt b/studio/static/doc/flask-docs/_sources/tutorial/introduction.txt
new file mode 100644
index 0000000..c72bbd7
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/introduction.txt
@@ -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 they 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/
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/schema.txt b/studio/static/doc/flask-docs/_sources/tutorial/schema.txt
new file mode 100644
index 0000000..c078667
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/schema.txt
@@ -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`.
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/setup.txt b/studio/static/doc/flask-docs/_sources/tutorial/setup.txt
new file mode 100644
index 0000000..e9e4d67
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/setup.txt
@@ -0,0 +1,90 @@
+.. _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. Do this with the following command::
+
+ python flaskr.py
+
+You will see a message telling you that server has started along with
+the address at which you can access it.
+
+When you head over to the server in your browser 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`.
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/templates.txt b/studio/static/doc/flask-docs/_sources/tutorial/templates.txt
new file mode 100644
index 0000000..5ec5584
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/templates.txt
@@ -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`.
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/testing.txt b/studio/static/doc/flask-docs/_sources/tutorial/testing.txt
new file mode 100644
index 0000000..34edd79
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/testing.txt
@@ -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.
diff --git a/studio/static/doc/flask-docs/_sources/tutorial/views.txt b/studio/static/doc/flask-docs/_sources/tutorial/views.txt
new file mode 100644
index 0000000..93bec3b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/tutorial/views.txt
@@ -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 they are 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`.
diff --git a/studio/static/doc/flask-docs/_sources/unicode.txt b/studio/static/doc/flask-docs/_sources/unicode.txt
new file mode 100644
index 0000000..413ea84
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/unicode.txt
@@ -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.
diff --git a/studio/static/doc/flask-docs/_sources/upgrading.txt b/studio/static/doc/flask-docs/_sources/upgrading.txt
new file mode 100644
index 0000000..0ba46c1
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/upgrading.txt
@@ -0,0 +1,324 @@
+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.8
+-----------
+
+Flask introduced a new session interface system. We also noticed that
+there was a naming collision between `flask.session` the module that
+implements sessions and :data:`flask.session` which is the global session
+object. With that introduction we moved the implementation details for
+the session system into a new module called :mod:`flask.sessions`. If you
+used the previously undocumented session support we urge you to upgrade.
+
+If invalid JSON data was submitted Flask will now raise a
+:exc:`~werkzeug.exceptions.BadRequest` exception instead of letting the
+default :exc:`ValueError` bubble up. This has the advantage that you no
+longer have to handle that error to avoid an internal server error showing
+up for the user. If you were catching this down explicitly in the past
+as `ValueError` you will need to change this.
+
+Due to a bug in the test client Flask 0.7 did not trigger teardown
+handlers when the test client was used in a with statement. This was
+since fixed but might require some changes in your testsuites if you
+relied on this behavior.
+
+Version 0.7
+-----------
+
+In Flask 0.7 we cleaned up the code base internally a lot and did some
+backwards incompatible changes that make it easier to implement larger
+applications with Flask. Because we want to make upgrading as easy as
+possible we tried to counter the problems arising from these changes by
+providing a script that can ease the transition.
+
+The script scans your whole application and generates an unified diff with
+changes it assumes are safe to apply. However as this is an automated
+tool it won't be able to find all use cases and it might miss some. We
+internally spread a lot of deprecation warnings all over the place to make
+it easy to find pieces of code that it was unable to upgrade.
+
+We strongly recommend that you hand review the generated patchfile and
+only apply the chunks that look good.
+
+If you are using git as version control system for your project we
+recommend applying the patch with ``path -p1 < patchfile.diff`` and then
+using the interactive commit feature to only apply the chunks that look
+good.
+
+To apply the upgrade script do the following:
+
+1. Download the script: `flask-07-upgrade.py
+ <https://raw.github.com/mitsuhiko/flask/master/scripts/flask-07-upgrade.py>`_
+2. Run it in the directory of your application::
+
+ python flask-07-upgrade.py > patchfile.diff
+
+3. Review the generated patchfile.
+4. Apply the patch::
+
+ patch -p1 < patchfile.diff
+
+5. If you were using per-module template folders you need to move some
+ templates around. Previously if you had a folder named ``templates``
+ next to a blueprint named ``admin`` the implicit template path
+ automatically was ``admin/index.html`` for a template file called
+ ``templates/index.html``. This no longer is the case. Now you need
+ to name the template ``templates/admin/index.html``. The tool will
+ not detect this so you will have to do that on your own.
+
+Please note that deprecation warnings are disabled by default starting
+with Python 2.7. In order to see the deprecation warnings that might be
+emitted you have to enabled them with the :mod:`warnings` module.
+
+If you are working with windows and you lack the `patch` command line
+utility you can get it as part of various Unix runtime environments for
+windows including cygwin, msysgit or ming32. Also source control systems
+like svn, hg or git have builtin support for applying unified diffs as
+generated by the tool. Check the manual of your version control system
+for more information.
+
+Bug in Request Locals
+`````````````````````
+
+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)
+
+.. _upgrading-to-new-teardown-handling:
+
+Upgrading to new Teardown Handling
+``````````````````````````````````
+
+We streamlined the behavior of the callbacks for request handling. For
+things that modify the response the :meth:`~flask.Flask.after_request`
+decorators continue to work as expected, but for things that absolutely
+must happen at the end of request we introduced the new
+:meth:`~flask.Flask.teardown_request` decorator. Unfortunately that
+change also made after-request work differently under error conditions.
+It's not consistently skipped if exceptions happen whereas previously it
+might have been called twice to ensure it is executed at the end of the
+request.
+
+If you have database connection code that looks like this::
+
+ @app.after_request
+ def after_request(response):
+ g.db.close()
+ return response
+
+You are now encouraged to use this instead::
+
+ @app.teardown_request
+ def after_request(exception):
+ if hasattr(g, 'db'):
+ g.db.close()
+
+On the upside this change greatly improves the internal code flow and
+makes it easier to customize the dispatching and error handling. This
+makes it now a lot easier to write unit tests as you can prevent closing
+down of database connections for a while. You can take advantage of the
+fact that the teardown callbacks are called when the response context is
+removed from the stack so a test can query the database after request
+handling::
+
+ with app.test_client() as client:
+ resp = client.get('/')
+ # g.db is still bound if there is such a thing
+
+ # and here it's gone
+
+Manual Error Handler Attaching
+``````````````````````````````
+
+While it is still possible to attach error handlers to
+:attr:`Flask.error_handlers` it's discouraged to do so and in fact
+deprecated. In generaly we no longer recommend custom error handler
+attaching via assignments to the underlying dictionary due to the more
+complex internal handling to support arbitrary exception classes and
+blueprints. See :meth:`Flask.errorhandler` for more information.
+
+The proper upgrade is to change this::
+
+ app.error_handlers[403] = handle_error
+
+Into this::
+
+ app.register_error_handler(403, handle_error)
+
+Alternatively you should just attach the function with a decorator::
+
+ @app.errorhandler(403)
+ def handle_error(e):
+ ...
+
+(Note that :meth:`register_error_handler` is new in Flask 0.7)
+
+Blueprint Support
+`````````````````
+
+Blueprints replace the previous concept of “Modules” in Flask. They
+provide better semantics for various features and work better with large
+applications. The update script provided should be able to upgrade your
+applications automatically, but there might be some cases where it fails
+to upgrade. What changed?
+
+- Blueprints need explicit names. Modules had an automatic name
+ guesssing scheme where the shortname for the module was taken from the
+ last part of the import module. The upgrade script tries to guess
+ that name but it might fail as this information could change at
+ runtime.
+- Blueprints have an inverse behavior for :meth:`url_for`. Previously
+ ``.foo`` told :meth:`url_for` that it should look for the endpoint
+ `foo` on the application. Now it means “relative to current module”.
+ The script will inverse all calls to :meth:`url_for` automatically for
+ you. It will do this in a very eager way so you might end up with
+ some unnecessary leading dots in your code if you're not using
+ modules.
+- Blueprints do not automatically provide static folders. They will
+ also no longer automatically export templates from a folder called
+ `templates` next to their location however but it can be enabled from
+ the constructor. Same with static files: if you want to continue
+ serving static files you need to tell the constructor explicitly the
+ path to the static folder (which can be relative to the blueprint's
+ module path).
+- Rendering templates was simplified. Now the blueprints can provide
+ template folders which are added to a general template searchpath.
+ This means that you need to add another subfolder with the blueprint's
+ name into that folder if you want ``blueprintname/template.html`` as
+ the template name.
+
+If you continue to use the `Module` object which is deprecated, Flask will
+restore the previous behavior as good as possible. However we strongly
+recommend upgrading to the new blueprints as they provide a lot of useful
+improvement such as the ability to attach a blueprint multiple times,
+blueprint specific error handlers and a lot more.
+
+
+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.
diff --git a/studio/static/doc/flask-docs/_sources/views.txt b/studio/static/doc/flask-docs/_sources/views.txt
new file mode 100644
index 0000000..441620a
--- /dev/null
+++ b/studio/static/doc/flask-docs/_sources/views.txt
@@ -0,0 +1,227 @@
+.. _views:
+
+Pluggable Views
+===============
+
+.. versionadded:: 0.7
+
+Flask 0.7 introduces pluggable views inspired by the generic views from
+Django which are based on classes instead of functions. The main
+intention is that you can replace parts of the implementations and this
+way have customizable pluggable views.
+
+Basic Principle
+---------------
+
+Consider you have a function that loads a list of objects from the
+database and renders into a template::
+
+ @app.route('/users/')
+ def show_users(page):
+ users = User.query.all()
+ return render_template('users.html', users=users)
+
+This is simple and flexible, but if you want to provide this view in a
+generic fashion that can be adapted to other models and templates as well
+you might want more flexibility. This is where pluggable class based
+views come into place. As the first step to convert this into a class
+based view you would do this::
+
+
+ from flask.views import View
+
+ class ShowUsers(View):
+
+ def dispatch_request(self):
+ users = User.query.all()
+ return render_template('users.html', objects=users)
+
+ app.add_url_rule('/users/', ShowUsers.as_view('show_users'))
+
+As you can see what you have to do is to create a subclass of
+:class:`flask.views.View` and implement
+:meth:`~flask.views.View.dispatch_request`. Then we have to convert that
+class into an actual view function by using the
+:meth:`~flask.views.View.as_view` class method. The string you pass to
+that function is the name of the endpoint that view will then have. But
+this by itself is not helpful, so let's refactor the code a bit::
+
+
+ from flask.views import View
+
+ class ListView(View):
+
+ def get_template_name(self):
+ raise NotImplementedError()
+
+ def render_template(self, context):
+ return render_template(self.get_template_name(), **context)
+
+ def dispatch_request(self):
+ context = {'objects': self.get_objects()}
+ return self.render_template(context)
+
+ class UserView(ListView):
+
+ def get_template_name(self):
+ return 'users.html'
+
+ def get_objects(self):
+ return User.query.all()
+
+This of course is not that helpful for such a small example, but it's good
+enough to explain the basic principle. When you have a class based view
+the question comes up what `self` points to. The way this works is that
+whenever the request is dispatched a new instance of the class is created
+and the :meth:`~flask.views.View.dispatch_request` method is called with
+the parameters from the URL rule. The class itself is instanciated with
+the parameters passed to the :meth:`~flask.views.View.as_view` function.
+For instance you can write a class like this::
+
+ class RenderTemplateView(View):
+ def __init__(self, template_name):
+ self.template_name = template_name
+ def dispatch_request(self):
+ return render_template(self.template_name)
+
+And then you can register it like this::
+
+ app.add_url_rule('/about', view_func=RenderTemplateView.as_view(
+ 'about_page', template_name='about.html'))
+
+Method Hints
+------------
+
+Pluggable views are attached to the application like a regular function by
+either using :func:`~flask.Flask.route` or better
+:meth:`~flask.Flask.add_url_rule`. That however also means that you would
+have to provide the names of the HTTP methods the view supports when you
+attach this. In order to move that information to the class you can
+provide a :attr:`~flask.views.View.methods` attribute that has this
+information::
+
+ class MyView(View):
+ methods = ['GET', 'POST']
+
+ def dispatch_request(self):
+ if request.method == 'POST':
+ ...
+ ...
+
+ app.add_url_rule('/myview', view_func=MyView.as_view('myview'))
+
+Method Based Dispatching
+------------------------
+
+For RESTful APIs it's especially helpful to execute a different function
+for each HTTP method. With the :class:`flask.views.MethodView` you can
+easily do that. Each HTTP method maps to a function with the same name
+(just in lowercase)::
+
+ from flask.views import MethodView
+
+ class UserAPI(MethodView):
+
+ def get(self):
+ users = User.query.all()
+ ...
+
+ def post(self):
+ user = User.from_form_data(request.form)
+ ...
+
+ app.add_url_rule('/users/', view_func=UserAPI.as_view('users'))
+
+That way you also don't have to provide the
+:attr:`~flask.views.View.methods` attribute. It's automatically set based
+on the methods defined in the class.
+
+Decorating Views
+----------------
+
+Since the view class itself is not the view function that is added to the
+routing system it does not make much sense to decorate the class itself.
+Instead you either have to decorate the return value of
+:meth:`~flask.views.View.as_view` by hand::
+
+ view = rate_limited(UserAPI.as_view('users'))
+ app.add_url_rule('/users/', view_func=view)
+
+Starting with Flask 0.8 there is also an alternative way where you can
+specify a list of decorators to apply in the class declaration::
+
+ class UserAPI(MethodView):
+ decorators = [rate_limited]
+
+Due to the implicit self from the caller's perspective you cannot use
+regular view decorators on the individual methods of the view however,
+keep this in mind.
+
+Method Views for APIs
+---------------------
+
+Web APIs are often working very closely with HTTP verbs so it makes a lot
+of sense to implement such an API based on the
+:class:`~flask.views.MethodView`. That said, you will notice that the API
+will require different URL rules that go to the same method view most of
+the time. For instance consider that you are exposing a user object on
+the web:
+
+=============== =============== ======================================
+URL Method Description
+--------------- --------------- --------------------------------------
+``/users/`` ``GET`` Gives a list of all users
+``/users/`` ``POST`` Creates a new user
+``/users/<id>`` ``GET`` Shows a single user
+``/users/<id>`` ``PUT`` Updates a single user
+``/users/<id>`` ``DELETE`` Deletes a single user
+=============== =============== ======================================
+
+So how would you go about doing that with the
+:class:`~flask.views.MethodView`? The trick is to take advantage of the
+fact that you can provide multiple rules to the same view.
+
+Let's assume for the moment the view would look like this::
+
+ class UserAPI(MethodView):
+
+ def get(self, user_id):
+ if user_id is None:
+ # return a list of users
+ pass
+ else:
+ # expose a single user
+ pass
+
+ def post(self):
+ # create a new user
+ pass
+
+ def delete(self, user_id):
+ # delete a single user
+ pass
+
+ def put(self, user_id):
+ # update a single user
+ pass
+
+So how do we hook this up with the routing system? By adding two rules
+and explicitly mentioning the methods for each::
+
+ user_view = UserAPI.as_view('user_api')
+ app.add_url_rule('/users/', defaults={'user_id': None},
+ view_func=user_view, methods=['GET', 'POST'])
+ app.add_url_rule('/users/<int:user_id>', view_func=user_view,
+ methods=['GET', 'PUT', 'DELETE'])
+
+If you have a lot of APIs that look similar you can refactor that
+registration code::
+
+ def register_api(view, endpoint, url, pk='id', pk_type='int'):
+ view_func = view.as_view(endpoint)
+ app.add_url_rule(url, defaults={pk: None},
+ view_func=view_func, methods=['GET', 'POST'])
+ app.add_url_rule('%s<%s:%s>' % (url, pk), view_func=view_func,
+ methods=['GET', 'PUT', 'DELETE'])
+
+ register_api(UserAPI, 'user_api', '/users/', pk='user_id')
diff --git a/studio/static/doc/flask-docs/_static/ajax-loader.gif b/studio/static/doc/flask-docs/_static/ajax-loader.gif
new file mode 100644
index 0000000..61faf8c
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/ajax-loader.gif
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/basic.css b/studio/static/doc/flask-docs/_static/basic.css
new file mode 100644
index 0000000..f0379f3
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/basic.css
@@ -0,0 +1,540 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+ clear: both;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+ width: 100%;
+ font-size: 90%;
+}
+
+div.related h3 {
+ display: none;
+}
+
+div.related ul {
+ margin: 0;
+ padding: 0 0 0 10px;
+ list-style: none;
+}
+
+div.related li {
+ display: inline;
+}
+
+div.related li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+ padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ float: left;
+ width: 230px;
+ margin-left: -100%;
+ font-size: 90%;
+}
+
+div.sphinxsidebar ul {
+ list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+ margin-left: 20px;
+ list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+ margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar input[type="text"] {
+ width: 170px;
+}
+
+div.sphinxsidebar input[type="submit"] {
+ width: 30px;
+}
+
+img {
+ border: 0;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li div.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 1.3em;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+ width: 100%;
+}
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+div.modindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+a.headerlink {
+ visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+ visibility: visible;
+}
+
+div.body p.caption {
+ text-align: inherit;
+}
+
+div.body td {
+ text-align: left;
+}
+
+.field-list ul {
+ padding-left: 1em;
+}
+
+.first {
+ margin-top: 0 !important;
+}
+
+p.rubric {
+ margin-top: 30px;
+ font-weight: bold;
+}
+
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 1px solid #ddb;
+ padding: 7px 7px 0 7px;
+ background-color: #ffe;
+ width: 40%;
+ float: right;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+ border: 1px solid #ccc;
+ padding: 7px 7px 0 7px;
+ margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+}
+
+div.admonition dt {
+ font-weight: bold;
+}
+
+div.admonition dl {
+ margin-bottom: 0;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+div.body p.centered {
+ text-align: center;
+ margin-top: 25px;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+ border: 0;
+ border-collapse: collapse;
+}
+
+table.docutils td, table.docutils th {
+ padding: 1px 8px 1px 5px;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+ border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+ border: 0 !important;
+}
+
+th {
+ text-align: left;
+ padding-right: 5px;
+}
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px;
+}
+
+table.citation td {
+ border-bottom: none;
+}
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+ list-style: decimal;
+}
+
+ol.loweralpha {
+ list-style: lower-alpha;
+}
+
+ol.upperalpha {
+ list-style: upper-alpha;
+}
+
+ol.lowerroman {
+ list-style: lower-roman;
+}
+
+ol.upperroman {
+ list-style: upper-roman;
+}
+
+dl {
+ margin-bottom: 15px;
+}
+
+dd p {
+ margin-top: 0px;
+}
+
+dd ul, dd table {
+ margin-bottom: 10px;
+}
+
+dd {
+ margin-top: 3px;
+ margin-bottom: 10px;
+ margin-left: 30px;
+}
+
+dt:target, .highlighted {
+ background-color: #fbe54e;
+}
+
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+.field-list ul {
+ margin: 0;
+ padding-left: 1em;
+}
+
+.field-list p {
+ margin: 0;
+}
+
+.refcount {
+ color: #060;
+}
+
+.optional {
+ font-size: 1.3em;
+}
+
+.versionmodified {
+ font-style: italic;
+}
+
+.system-message {
+ background-color: #fda;
+ padding: 5px;
+ border: 3px solid red;
+}
+
+.footnote:target {
+ background-color: #ffa;
+}
+
+.line-block {
+ display: block;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.line-block .line-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+ font-family: sans-serif;
+}
+
+.accelerator {
+ text-decoration: underline;
+}
+
+.classifier {
+ font-style: oblique;
+}
+
+abbr, acronym {
+ border-bottom: dotted 1px;
+ cursor: help;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+ overflow: auto;
+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
+}
+
+td.linenos pre {
+ padding: 5px 0px;
+ border: 0;
+ background-color: transparent;
+ color: #aaa;
+}
+
+table.highlighttable {
+ margin-left: 0.5em;
+}
+
+table.highlighttable td {
+ padding: 0 0.5em 0 0.5em;
+}
+
+tt.descname {
+ background-color: transparent;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+tt.descclassname {
+ background-color: transparent;
+}
+
+tt.xref, a tt {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+ background-color: transparent;
+}
+
+.viewcode-link {
+ float: right;
+}
+
+.viewcode-back {
+ float: right;
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ margin: -1px -10px;
+ padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+ vertical-align: middle;
+}
+
+div.body div.math p {
+ text-align: center;
+}
+
+span.eqno {
+ float: right;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+@media print {
+ div.document,
+ div.documentwrapper,
+ div.bodywrapper {
+ margin: 0 !important;
+ width: 100%;
+ }
+
+ div.sphinxsidebar,
+ div.related,
+ div.footer,
+ #top-link {
+ display: none;
+ }
+} \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/_static/comment-bright.png b/studio/static/doc/flask-docs/_static/comment-bright.png
new file mode 100644
index 0000000..551517b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/comment-bright.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/comment-close.png b/studio/static/doc/flask-docs/_static/comment-close.png
new file mode 100644
index 0000000..09b54be
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/comment-close.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/comment.png b/studio/static/doc/flask-docs/_static/comment.png
new file mode 100644
index 0000000..92feb52
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/comment.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/debugger.png b/studio/static/doc/flask-docs/_static/debugger.png
new file mode 100644
index 0000000..4f47229
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/debugger.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/doctools.js b/studio/static/doc/flask-docs/_static/doctools.js
new file mode 100644
index 0000000..8b9bd2c
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/doctools.js
@@ -0,0 +1,247 @@
+/*
+ * doctools.js
+ * ~~~~~~~~~~~
+ *
+ * Sphinx JavaScript utilties for all documentation.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/**
+ * select a different prefix for underscore
+ */
+$u = _.noConflict();
+
+/**
+ * make the code below compatible with browsers without
+ * an installed firebug like debugger
+if (!window.console || !console.firebug) {
+ var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
+ "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
+ "profile", "profileEnd"];
+ window.console = {};
+ for (var i = 0; i < names.length; ++i)
+ window.console[names[i]] = function() {};
+}
+ */
+
+/**
+ * small helper function to urldecode strings
+ */
+jQuery.urldecode = function(x) {
+ return decodeURIComponent(x).replace(/\+/g, ' ');
+}
+
+/**
+ * small helper function to urlencode strings
+ */
+jQuery.urlencode = encodeURIComponent;
+
+/**
+ * This function returns the parsed url parameters of the
+ * current request. Multiple values per key are supported,
+ * it will always return arrays of strings for the value parts.
+ */
+jQuery.getQueryParameters = function(s) {
+ if (typeof s == 'undefined')
+ s = document.location.search;
+ var parts = s.substr(s.indexOf('?') + 1).split('&');
+ var result = {};
+ for (var i = 0; i < parts.length; i++) {
+ var tmp = parts[i].split('=', 2);
+ var key = jQuery.urldecode(tmp[0]);
+ var value = jQuery.urldecode(tmp[1]);
+ if (key in result)
+ result[key].push(value);
+ else
+ result[key] = [value];
+ }
+ return result;
+};
+
+/**
+ * small function to check if an array contains
+ * a given item.
+ */
+jQuery.contains = function(arr, item) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] == item)
+ return true;
+ }
+ return false;
+};
+
+/**
+ * highlight a given string on a jquery object by wrapping it in
+ * span elements with the given class name.
+ */
+jQuery.fn.highlightText = function(text, className) {
+ function highlight(node) {
+ if (node.nodeType == 3) {
+ var val = node.nodeValue;
+ var pos = val.toLowerCase().indexOf(text);
+ if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
+ var span = document.createElement("span");
+ span.className = className;
+ span.appendChild(document.createTextNode(val.substr(pos, text.length)));
+ node.parentNode.insertBefore(span, node.parentNode.insertBefore(
+ document.createTextNode(val.substr(pos + text.length)),
+ node.nextSibling));
+ node.nodeValue = val.substr(0, pos);
+ }
+ }
+ else if (!jQuery(node).is("button, select, textarea")) {
+ jQuery.each(node.childNodes, function() {
+ highlight(this);
+ });
+ }
+ }
+ return this.each(function() {
+ highlight(this);
+ });
+};
+
+/**
+ * Small JavaScript module for the documentation.
+ */
+var Documentation = {
+
+ init : function() {
+ this.fixFirefoxAnchorBug();
+ this.highlightSearchWords();
+ this.initIndexTable();
+ },
+
+ /**
+ * i18n support
+ */
+ TRANSLATIONS : {},
+ PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
+ LOCALE : 'unknown',
+
+ // gettext and ngettext don't access this so that the functions
+ // can safely bound to a different name (_ = Documentation.gettext)
+ gettext : function(string) {
+ var translated = Documentation.TRANSLATIONS[string];
+ if (typeof translated == 'undefined')
+ return string;
+ return (typeof translated == 'string') ? translated : translated[0];
+ },
+
+ ngettext : function(singular, plural, n) {
+ var translated = Documentation.TRANSLATIONS[singular];
+ if (typeof translated == 'undefined')
+ return (n == 1) ? singular : plural;
+ return translated[Documentation.PLURALEXPR(n)];
+ },
+
+ addTranslations : function(catalog) {
+ for (var key in catalog.messages)
+ this.TRANSLATIONS[key] = catalog.messages[key];
+ this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
+ this.LOCALE = catalog.locale;
+ },
+
+ /**
+ * add context elements like header anchor links
+ */
+ addContextElements : function() {
+ $('div[id] > :header:first').each(function() {
+ $('<a class="headerlink">\u00B6</a>').
+ attr('href', '#' + this.id).
+ attr('title', _('Permalink to this headline')).
+ appendTo(this);
+ });
+ $('dt[id]').each(function() {
+ $('<a class="headerlink">\u00B6</a>').
+ attr('href', '#' + this.id).
+ attr('title', _('Permalink to this definition')).
+ appendTo(this);
+ });
+ },
+
+ /**
+ * workaround a firefox stupidity
+ */
+ fixFirefoxAnchorBug : function() {
+ if (document.location.hash && $.browser.mozilla)
+ window.setTimeout(function() {
+ document.location.href += '';
+ }, 10);
+ },
+
+ /**
+ * highlight the search words provided in the url in the text
+ */
+ highlightSearchWords : function() {
+ var params = $.getQueryParameters();
+ var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
+ if (terms.length) {
+ var body = $('div.body');
+ window.setTimeout(function() {
+ $.each(terms, function() {
+ body.highlightText(this.toLowerCase(), 'highlighted');
+ });
+ }, 10);
+ $('<li class="highlight-link"><a href="javascript:Documentation.' +
+ 'hideSearchWords()">' + _('Hide Search Matches') + '</a></li>')
+ .appendTo($('.sidebar .this-page-menu'));
+ }
+ },
+
+ /**
+ * init the domain index toggle buttons
+ */
+ initIndexTable : function() {
+ var togglers = $('img.toggler').click(function() {
+ var src = $(this).attr('src');
+ var idnum = $(this).attr('id').substr(7);
+ $('tr.cg-' + idnum).toggle();
+ if (src.substr(-9) == 'minus.png')
+ $(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
+ else
+ $(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
+ }).css('display', '');
+ if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
+ togglers.click();
+ }
+ },
+
+ /**
+ * helper function to hide the search marks again
+ */
+ hideSearchWords : function() {
+ $('.sidebar .this-page-menu li.highlight-link').fadeOut(300);
+ $('span.highlighted').removeClass('highlighted');
+ },
+
+ /**
+ * make the url absolute
+ */
+ makeURL : function(relativeURL) {
+ return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
+ },
+
+ /**
+ * get the current relative url
+ */
+ getCurrentURL : function() {
+ var path = document.location.pathname;
+ var parts = path.split(/\//);
+ $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
+ if (this == '..')
+ parts.pop();
+ });
+ var url = parts.join('/');
+ return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
+ }
+};
+
+// quick alias for translations
+_ = Documentation.gettext;
+
+$(document).ready(function() {
+ Documentation.init();
+});
diff --git a/studio/static/doc/flask-docs/_static/down-pressed.png b/studio/static/doc/flask-docs/_static/down-pressed.png
new file mode 100644
index 0000000..6f7ad78
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/down-pressed.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/down.png b/studio/static/doc/flask-docs/_static/down.png
new file mode 100644
index 0000000..3003a88
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/down.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/file.png b/studio/static/doc/flask-docs/_static/file.png
new file mode 100644
index 0000000..d18082e
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/file.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/flask.png b/studio/static/doc/flask-docs/_static/flask.png
new file mode 100644
index 0000000..5c603cc
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/flask.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/flaskr.png b/studio/static/doc/flask-docs/_static/flaskr.png
new file mode 100644
index 0000000..07d027d
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/flaskr.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/flasky.css b/studio/static/doc/flask-docs/_static/flasky.css
new file mode 100644
index 0000000..48bebc8
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/flasky.css
@@ -0,0 +1,387 @@
+/*
+ * flasky.css_t
+ * ~~~~~~~~~~~~
+ *
+ * :copyright: Copyright 2010 by Armin Ronacher.
+ * :license: Flask Design License, see LICENSE for details.
+ */
+
+
+
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: 'Georgia', serif;
+ font-size: 17px;
+ background-color: #c0c0c0;
+ color: #000;
+ margin: 0;
+ padding: 0;
+}
+
+div.document {
+ width: 940px;
+ margin: 30px auto 0 auto;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 220px;
+}
+
+div.sphinxsidebar {
+ width: 220px;
+}
+
+hr {
+ border: 1px solid #B1B4B6;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #3E4349;
+ padding: 0 30px 0 30px;
+}
+
+img.floatingflask {
+ padding: 0 0 10px 10px;
+ float: right;
+}
+
+div.footer {
+ width: 940px;
+ margin: 20px auto 30px auto;
+ font-size: 14px;
+ color: #888;
+ text-align: right;
+}
+
+div.footer a {
+ color: #888;
+}
+
+div.related {
+ display: none;
+}
+
+div.sphinxsidebar a {
+ color: #444;
+ text-decoration: none;
+ border-bottom: 1px dotted #999;
+}
+
+div.sphinxsidebar a:hover {
+ border-bottom: 1px solid #999;
+}
+
+div.sphinxsidebar {
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+div.sphinxsidebarwrapper {
+ padding: 18px 10px;
+}
+
+div.sphinxsidebarwrapper p.logo {
+ padding: 0 0 20px 0;
+ margin: 0;
+ text-align: center;
+}
+
+div.sphinxsidebar h3,
+div.sphinxsidebar h4 {
+ font-family: 'Garamond', 'Georgia', serif;
+ color: #444;
+ font-size: 24px;
+ font-weight: normal;
+ margin: 0 0 5px 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h4 {
+ font-size: 20px;
+}
+
+div.sphinxsidebar h3 a {
+ color: #444;
+}
+
+div.sphinxsidebar p.logo a,
+div.sphinxsidebar h3 a,
+div.sphinxsidebar p.logo a:hover,
+div.sphinxsidebar h3 a:hover {
+ border: none;
+}
+
+div.sphinxsidebar p {
+ color: #555;
+ margin: 10px 0;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px 0;
+ padding: 0;
+ color: #000;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #ccc;
+ font-family: 'Georgia', serif;
+ font-size: 1em;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+a {
+ color: #004B6B;
+ text-decoration: underline;
+}
+
+a:hover {
+ color: #6D4100;
+ text-decoration: underline;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: 'Garamond', 'Georgia', serif;
+ font-weight: normal;
+ margin: 30px 0px 10px 0px;
+ padding: 0;
+}
+
+div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
+div.body h2 { font-size: 180%; }
+div.body h3 { font-size: 150%; }
+div.body h4 { font-size: 130%; }
+div.body h5 { font-size: 100%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #ddd;
+ padding: 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ color: #444;
+ background: #eaeaea;
+}
+
+div.body p, div.body dd, div.body li {
+ line-height: 1.4em;
+}
+
+div.admonition {
+ background: #fafafa;
+ margin: 20px -30px;
+ padding: 10px 30px;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+}
+
+div.admonition tt.xref, div.admonition a tt {
+ border-bottom: 1px solid #fafafa;
+}
+
+dd div.admonition {
+ margin-left: -60px;
+ padding-left: 60px;
+}
+
+div.admonition p.admonition-title {
+ font-family: 'Garamond', 'Georgia', serif;
+ font-weight: normal;
+ font-size: 24px;
+ margin: 0 0 10px 0;
+ padding: 0;
+ line-height: 1;
+}
+
+div.admonition p.last {
+ margin-bottom: 0;
+}
+
+div.highlight {
+ background-color: white;
+}
+
+dt:target, .highlight {
+ background: #FAF3E8;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre, tt {
+ font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+ font-size: 0.9em;
+}
+
+img.screenshot {
+}
+
+tt.descname, tt.descclassname {
+ font-size: 0.95em;
+}
+
+tt.descname {
+ padding-right: 0.08em;
+}
+
+img.screenshot {
+ -moz-box-shadow: 2px 2px 4px #eee;
+ -webkit-box-shadow: 2px 2px 4px #eee;
+ box-shadow: 2px 2px 4px #eee;
+}
+
+table.docutils {
+ border: 1px solid #888;
+ -moz-box-shadow: 2px 2px 4px #eee;
+ -webkit-box-shadow: 2px 2px 4px #eee;
+ box-shadow: 2px 2px 4px #eee;
+}
+
+table.docutils td, table.docutils th {
+ border: 1px solid #888;
+ padding: 0.25em 0.7em;
+}
+
+table.field-list, table.footnote {
+ border: none;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+table.footnote {
+ margin: 15px 0;
+ width: 100%;
+ border: 1px solid #eee;
+ background: #fdfdfd;
+ font-size: 0.9em;
+}
+
+table.footnote + table.footnote {
+ margin-top: -15px;
+ border-top: none;
+}
+
+table.field-list th {
+ padding: 0 0.8em 0 0;
+}
+
+table.field-list td {
+ padding: 0;
+}
+
+table.footnote td.label {
+ width: 0px;
+ padding: 0.3em 0 0.3em 0.5em;
+}
+
+table.footnote td {
+ padding: 0.3em 0.5em;
+}
+
+dl {
+ margin: 0;
+ padding: 0;
+}
+
+dl dd {
+ margin-left: 30px;
+}
+
+blockquote {
+ margin: 0 0 0 30px;
+ padding: 0;
+}
+
+ul, ol {
+ margin: 10px 0 10px 30px;
+ padding: 0;
+}
+
+pre {
+ background: #eee;
+ padding: 7px 30px;
+ margin: 15px -30px;
+ line-height: 1.3em;
+}
+
+dl pre, blockquote pre, li pre {
+ margin-left: -60px;
+ padding-left: 60px;
+}
+
+dl dl pre {
+ margin-left: -90px;
+ padding-left: 90px;
+}
+
+tt {
+ background-color: #ecf0f3;
+ color: #222;
+ /* padding: 1px 2px; */
+}
+
+tt.xref, a tt {
+ background-color: #FBFBFB;
+ border-bottom: 1px solid white;
+}
+
+a.reference {
+ text-decoration: none;
+ border-bottom: 1px dotted #004B6B;
+}
+
+a.reference:hover {
+ border-bottom: 1px solid #6D4100;
+}
+
+a.footnote-reference {
+ text-decoration: none;
+ font-size: 0.7em;
+ vertical-align: top;
+ border-bottom: 1px dotted #004B6B;
+}
+
+a.footnote-reference:hover {
+ border-bottom: 1px solid #6D4100;
+}
+
+a:hover tt {
+ background: #EEE;
+}
diff --git a/studio/static/doc/flask-docs/_static/jquery.js b/studio/static/doc/flask-docs/_static/jquery.js
new file mode 100644
index 0000000..7c24308
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/jquery.js
@@ -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);
diff --git a/studio/static/doc/flask-docs/_static/logo-full.png b/studio/static/doc/flask-docs/_static/logo-full.png
new file mode 100644
index 0000000..5deaf1b
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/logo-full.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/minus.png b/studio/static/doc/flask-docs/_static/minus.png
new file mode 100644
index 0000000..da1c562
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/minus.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/no.png b/studio/static/doc/flask-docs/_static/no.png
new file mode 100644
index 0000000..4ac1083
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/no.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/plus.png b/studio/static/doc/flask-docs/_static/plus.png
new file mode 100644
index 0000000..b3cb374
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/plus.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/pygments.css b/studio/static/doc/flask-docs/_static/pygments.css
new file mode 100644
index 0000000..ee96fae
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/pygments.css
@@ -0,0 +1,70 @@
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #8f5902; font-style: italic } /* Comment */
+.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */
+.highlight .g { color: #000000 } /* Generic */
+.highlight .k { color: #004461; font-weight: bold } /* Keyword */
+.highlight .l { color: #000000 } /* Literal */
+.highlight .n { color: #000000 } /* Name */
+.highlight .o { color: #582800 } /* Operator */
+.highlight .x { color: #000000 } /* Other */
+.highlight .p { color: #000000; font-weight: bold } /* Punctuation */
+.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #8f5902 } /* Comment.Preproc */
+.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #a40000 } /* Generic.Deleted */
+.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #ef2929 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #808080 } /* Generic.Output */
+.highlight .gp { color: #745334 } /* Generic.Prompt */
+.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */
+.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */
+.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */
+.highlight .ld { color: #000000 } /* Literal.Date */
+.highlight .m { color: #990000 } /* Literal.Number */
+.highlight .s { color: #4e9a06 } /* Literal.String */
+.highlight .na { color: #c4a000 } /* Name.Attribute */
+.highlight .nb { color: #004461 } /* Name.Builtin */
+.highlight .nc { color: #000000 } /* Name.Class */
+.highlight .no { color: #000000 } /* Name.Constant */
+.highlight .nd { color: #808080 } /* Name.Decorator */
+.highlight .ni { color: #ce5c00 } /* Name.Entity */
+.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #000000 } /* Name.Function */
+.highlight .nl { color: #f57900 } /* Name.Label */
+.highlight .nn { color: #000000 } /* Name.Namespace */
+.highlight .nx { color: #000000 } /* Name.Other */
+.highlight .py { color: #000000 } /* Name.Property */
+.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #000000 } /* Name.Variable */
+.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */
+.highlight .mf { color: #990000 } /* Literal.Number.Float */
+.highlight .mh { color: #990000 } /* Literal.Number.Hex */
+.highlight .mi { color: #990000 } /* Literal.Number.Integer */
+.highlight .mo { color: #990000 } /* Literal.Number.Oct */
+.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */
+.highlight .sc { color: #4e9a06 } /* Literal.String.Char */
+.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */
+.highlight .se { color: #4e9a06 } /* Literal.String.Escape */
+.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */
+.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */
+.highlight .sx { color: #4e9a06 } /* Literal.String.Other */
+.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */
+.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */
+.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */
+.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #000000 } /* Name.Variable.Class */
+.highlight .vg { color: #000000 } /* Name.Variable.Global */
+.highlight .vi { color: #000000 } /* Name.Variable.Instance */
+.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/_static/searchtools.js b/studio/static/doc/flask-docs/_static/searchtools.js
new file mode 100644
index 0000000..6ed5ac8
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/searchtools.js
@@ -0,0 +1,556 @@
+/*
+ * searchtools.js_t
+ * ~~~~~~~~~~~~~~~~
+ *
+ * Sphinx JavaScript utilties for the full-text search.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/**
+ * helper function to return a node containing the
+ * search summary for a given text. keywords is a list
+ * of stemmed words, hlwords is the list of normal, unstemmed
+ * words. the first one is used to find the occurance, the
+ * latter for highlighting it.
+ */
+
+jQuery.makeSearchSummary = function(text, keywords, hlwords) {
+ var textLower = text.toLowerCase();
+ var start = 0;
+ $.each(keywords, function() {
+ var i = textLower.indexOf(this.toLowerCase());
+ if (i > -1)
+ start = i;
+ });
+ start = Math.max(start - 120, 0);
+ var excerpt = ((start > 0) ? '...' : '') +
+ $.trim(text.substr(start, 240)) +
+ ((start + 240 - text.length) ? '...' : '');
+ var rv = $('<div class="context"></div>').text(excerpt);
+ $.each(hlwords, function() {
+ rv = rv.highlightText(this, 'highlighted');
+ });
+ return rv;
+}
+
+
+/**
+ * Porter Stemmer
+ */
+var Stemmer = function() {
+
+ var step2list = {
+ ational: 'ate',
+ tional: 'tion',
+ enci: 'ence',
+ anci: 'ance',
+ izer: 'ize',
+ bli: 'ble',
+ alli: 'al',
+ entli: 'ent',
+ eli: 'e',
+ ousli: 'ous',
+ ization: 'ize',
+ ation: 'ate',
+ ator: 'ate',
+ alism: 'al',
+ iveness: 'ive',
+ fulness: 'ful',
+ ousness: 'ous',
+ aliti: 'al',
+ iviti: 'ive',
+ biliti: 'ble',
+ logi: 'log'
+ };
+
+ var step3list = {
+ icate: 'ic',
+ ative: '',
+ alize: 'al',
+ iciti: 'ic',
+ ical: 'ic',
+ ful: '',
+ ness: ''
+ };
+
+ var c = "[^aeiou]"; // consonant
+ var v = "[aeiouy]"; // vowel
+ var C = c + "[^aeiouy]*"; // consonant sequence
+ var V = v + "[aeiou]*"; // vowel sequence
+
+ var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
+ var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
+ var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
+ var s_v = "^(" + C + ")?" + v; // vowel in stem
+
+ this.stemWord = function (w) {
+ var stem;
+ var suffix;
+ var firstch;
+ var origword = w;
+
+ if (w.length < 3)
+ return w;
+
+ var re;
+ var re2;
+ var re3;
+ var re4;
+
+ firstch = w.substr(0,1);
+ if (firstch == "y")
+ w = firstch.toUpperCase() + w.substr(1);
+
+ // Step 1a
+ re = /^(.+?)(ss|i)es$/;
+ re2 = /^(.+?)([^s])s$/;
+
+ if (re.test(w))
+ w = w.replace(re,"$1$2");
+ else if (re2.test(w))
+ w = w.replace(re2,"$1$2");
+
+ // Step 1b
+ re = /^(.+?)eed$/;
+ re2 = /^(.+?)(ed|ing)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ re = new RegExp(mgr0);
+ if (re.test(fp[1])) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+ }
+ else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1];
+ re2 = new RegExp(s_v);
+ if (re2.test(stem)) {
+ w = stem;
+ re2 = /(at|bl|iz)$/;
+ re3 = new RegExp("([^aeiouylsz])\\1$");
+ re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+ if (re2.test(w))
+ w = w + "e";
+ else if (re3.test(w)) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+ else if (re4.test(w))
+ w = w + "e";
+ }
+ }
+
+ // Step 1c
+ re = /^(.+?)y$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(s_v);
+ if (re.test(stem))
+ w = stem + "i";
+ }
+
+ // Step 2
+ re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = new RegExp(mgr0);
+ if (re.test(stem))
+ w = stem + step2list[suffix];
+ }
+
+ // Step 3
+ re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = new RegExp(mgr0);
+ if (re.test(stem))
+ w = stem + step3list[suffix];
+ }
+
+ // Step 4
+ re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
+ re2 = /^(.+?)(s|t)(ion)$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(mgr1);
+ if (re.test(stem))
+ w = stem;
+ }
+ else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1] + fp[2];
+ re2 = new RegExp(mgr1);
+ if (re2.test(stem))
+ w = stem;
+ }
+
+ // Step 5
+ re = /^(.+?)e$/;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = new RegExp(mgr1);
+ re2 = new RegExp(meq1);
+ re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+ if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
+ w = stem;
+ }
+ re = /ll$/;
+ re2 = new RegExp(mgr1);
+ if (re.test(w) && re2.test(w)) {
+ re = /.$/;
+ w = w.replace(re,"");
+ }
+
+ // and turn initial Y back to y
+ if (firstch == "y")
+ w = firstch.toLowerCase() + w.substr(1);
+ return w;
+ }
+}
+
+
+/**
+ * Search Module
+ */
+var Search = {
+
+ _index : null,
+ _queued_query : null,
+ _pulse_status : -1,
+
+ init : function() {
+ var params = $.getQueryParameters();
+ if (params.q) {
+ var query = params.q[0];
+ $('input[name="q"]')[0].value = query;
+ this.performSearch(query);
+ }
+ },
+
+ loadIndex : function(url) {
+ $.ajax({type: "GET", url: url, data: null, success: null,
+ dataType: "script", cache: true});
+ },
+
+ setIndex : function(index) {
+ var q;
+ this._index = index;
+ if ((q = this._queued_query) !== null) {
+ this._queued_query = null;
+ Search.query(q);
+ }
+ },
+
+ hasIndex : function() {
+ return this._index !== null;
+ },
+
+ deferQuery : function(query) {
+ this._queued_query = query;
+ },
+
+ stopPulse : function() {
+ this._pulse_status = 0;
+ },
+
+ startPulse : function() {
+ if (this._pulse_status >= 0)
+ return;
+ function pulse() {
+ Search._pulse_status = (Search._pulse_status + 1) % 4;
+ var dotString = '';
+ for (var i = 0; i < Search._pulse_status; i++)
+ dotString += '.';
+ Search.dots.text(dotString);
+ if (Search._pulse_status > -1)
+ window.setTimeout(pulse, 500);
+ };
+ pulse();
+ },
+
+ /**
+ * perform a search for something
+ */
+ performSearch : function(query) {
+ // create the required interface elements
+ this.out = $('#search-results');
+ this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
+ this.dots = $('<span></span>').appendTo(this.title);
+ this.status = $('<p style="display: none"></p>').appendTo(this.out);
+ this.output = $('<ul class="search"/>').appendTo(this.out);
+
+ $('#search-progress').text(_('Preparing search...'));
+ this.startPulse();
+
+ // index already loaded, the browser was quick!
+ if (this.hasIndex())
+ this.query(query);
+ else
+ this.deferQuery(query);
+ },
+
+ query : function(query) {
+ var stopwords = ["and","then","into","it","as","are","in","if","for","no","there","their","was","is","be","to","that","but","they","not","such","with","by","a","on","these","of","will","this","near","the","or","at"];
+
+ // Stem the searchterms and add them to the correct list
+ var stemmer = new Stemmer();
+ var searchterms = [];
+ var excluded = [];
+ var hlterms = [];
+ var tmp = query.split(/\s+/);
+ var objectterms = [];
+ for (var i = 0; i < tmp.length; i++) {
+ if (tmp[i] != "") {
+ objectterms.push(tmp[i].toLowerCase());
+ }
+
+ if ($u.indexOf(stopwords, tmp[i]) != -1 || tmp[i].match(/^\d+$/) ||
+ tmp[i] == "") {
+ // skip this "word"
+ continue;
+ }
+ // stem the word
+ var word = stemmer.stemWord(tmp[i]).toLowerCase();
+ // select the correct list
+ if (word[0] == '-') {
+ var toAppend = excluded;
+ word = word.substr(1);
+ }
+ else {
+ var toAppend = searchterms;
+ hlterms.push(tmp[i].toLowerCase());
+ }
+ // only add if not already in the list
+ if (!$.contains(toAppend, word))
+ toAppend.push(word);
+ };
+ var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
+
+ // console.debug('SEARCH: searching for:');
+ // console.info('required: ', searchterms);
+ // console.info('excluded: ', excluded);
+
+ // prepare search
+ var filenames = this._index.filenames;
+ var titles = this._index.titles;
+ var terms = this._index.terms;
+ var fileMap = {};
+ var files = null;
+ // different result priorities
+ var importantResults = [];
+ var objectResults = [];
+ var regularResults = [];
+ var unimportantResults = [];
+ $('#search-progress').empty();
+
+ // lookup as object
+ for (var i = 0; i < objectterms.length; i++) {
+ var others = [].concat(objectterms.slice(0,i),
+ objectterms.slice(i+1, objectterms.length))
+ var results = this.performObjectSearch(objectterms[i], others);
+ // Assume first word is most likely to be the object,
+ // other words more likely to be in description.
+ // Therefore put matches for earlier words first.
+ // (Results are eventually used in reverse order).
+ objectResults = results[0].concat(objectResults);
+ importantResults = results[1].concat(importantResults);
+ unimportantResults = results[2].concat(unimportantResults);
+ }
+
+ // perform the search on the required terms
+ for (var i = 0; i < searchterms.length; i++) {
+ var word = searchterms[i];
+ // no match but word was a required one
+ if ((files = terms[word]) == null)
+ break;
+ if (files.length == undefined) {
+ files = [files];
+ }
+ // create the mapping
+ for (var j = 0; j < files.length; j++) {
+ var file = files[j];
+ if (file in fileMap)
+ fileMap[file].push(word);
+ else
+ fileMap[file] = [word];
+ }
+ }
+
+ // now check if the files don't contain excluded terms
+ for (var file in fileMap) {
+ var valid = true;
+
+ // check if all requirements are matched
+ if (fileMap[file].length != searchterms.length)
+ continue;
+
+ // ensure that none of the excluded terms is in the
+ // search result.
+ for (var i = 0; i < excluded.length; i++) {
+ if (terms[excluded[i]] == file ||
+ $.contains(terms[excluded[i]] || [], file)) {
+ valid = false;
+ break;
+ }
+ }
+
+ // if we have still a valid result we can add it
+ // to the result list
+ if (valid)
+ regularResults.push([filenames[file], titles[file], '', null]);
+ }
+
+ // delete unused variables in order to not waste
+ // memory until list is retrieved completely
+ delete filenames, titles, terms;
+
+ // now sort the regular results descending by title
+ regularResults.sort(function(a, b) {
+ var left = a[1].toLowerCase();
+ var right = b[1].toLowerCase();
+ return (left > right) ? -1 : ((left < right) ? 1 : 0);
+ });
+
+ // combine all results
+ var results = unimportantResults.concat(regularResults)
+ .concat(objectResults).concat(importantResults);
+
+ // print the results
+ var resultCount = results.length;
+ function displayNextItem() {
+ // results left, load the summary and display it
+ if (results.length) {
+ var item = results.pop();
+ var listItem = $('<li style="display:none"></li>');
+ if (DOCUMENTATION_OPTIONS.FILE_SUFFIX == '') {
+ // dirhtml builder
+ var dirname = item[0] + '/';
+ if (dirname.match(/\/index\/$/)) {
+ dirname = dirname.substring(0, dirname.length-6);
+ } else if (dirname == 'index/') {
+ dirname = '';
+ }
+ listItem.append($('<a/>').attr('href',
+ DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
+ highlightstring + item[2]).html(item[1]));
+ } else {
+ // normal html builders
+ listItem.append($('<a/>').attr('href',
+ item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
+ highlightstring + item[2]).html(item[1]));
+ }
+ if (item[3]) {
+ listItem.append($('<span> (' + item[3] + ')</span>'));
+ Search.output.append(listItem);
+ listItem.slideDown(5, function() {
+ displayNextItem();
+ });
+ } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
+ $.get(DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' +
+ item[0] + '.txt', function(data) {
+ if (data != '') {
+ listItem.append($.makeSearchSummary(data, searchterms, hlterms));
+ Search.output.append(listItem);
+ }
+ listItem.slideDown(5, function() {
+ displayNextItem();
+ });
+ }, "text");
+ } else {
+ // no source available, just display title
+ Search.output.append(listItem);
+ listItem.slideDown(5, function() {
+ displayNextItem();
+ });
+ }
+ }
+ // search finished, update title and status message
+ else {
+ Search.stopPulse();
+ Search.title.text(_('Search Results'));
+ if (!resultCount)
+ Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
+ else
+ Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
+ Search.status.fadeIn(500);
+ }
+ }
+ displayNextItem();
+ },
+
+ performObjectSearch : function(object, otherterms) {
+ var filenames = this._index.filenames;
+ var objects = this._index.objects;
+ var objnames = this._index.objnames;
+ var titles = this._index.titles;
+
+ var importantResults = [];
+ var objectResults = [];
+ var unimportantResults = [];
+
+ for (var prefix in objects) {
+ for (var name in objects[prefix]) {
+ var fullname = (prefix ? prefix + '.' : '') + name;
+ if (fullname.toLowerCase().indexOf(object) > -1) {
+ var match = objects[prefix][name];
+ var objname = objnames[match[1]];
+ var title = titles[match[0]];
+ // If more than one term searched for, we require other words to be
+ // found in the name/title/description
+ if (otherterms.length > 0) {
+ var haystack = (prefix + ' ' + name + ' ' + objname + ' ' + title).toLowerCase();
+ var allfound = true;
+ for (var i = 0; i < otherterms.length; i++) {
+ if (haystack.indexOf(otherterms[i]) == -1) {
+ allfound = false;
+ break;
+ }
+ }
+ if (!allfound) {
+ continue;
+ }
+ }
+ var descr = objname + _(', in ') + title;
+ // XXX the generated anchors are not generally correct
+ // XXX there may be custom prefixes
+ result = [filenames[match[0]], fullname, '#'+fullname, descr];
+ switch (match[2]) {
+ case 1: objectResults.push(result); break;
+ case 0: importantResults.push(result); break;
+ case 2: unimportantResults.push(result); break;
+ }
+ }
+ }
+ }
+
+ // sort results descending
+ objectResults.sort(function(a, b) {
+ return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
+ });
+
+ importantResults.sort(function(a, b) {
+ return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
+ });
+
+ unimportantResults.sort(function(a, b) {
+ return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
+ });
+
+ return [importantResults, objectResults, unimportantResults]
+ }
+}
+
+$(document).ready(function() {
+ Search.init();
+}); \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/_static/small_flask.css b/studio/static/doc/flask-docs/_static/small_flask.css
new file mode 100644
index 0000000..1c6df30
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/small_flask.css
@@ -0,0 +1,70 @@
+/*
+ * small_flask.css_t
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * :copyright: Copyright 2010 by Armin Ronacher.
+ * :license: Flask Design License, see LICENSE for details.
+ */
+
+body {
+ margin: 0;
+ padding: 20px 30px;
+}
+
+div.documentwrapper {
+ float: none;
+ background: white;
+}
+
+div.sphinxsidebar {
+ display: block;
+ float: none;
+ width: 102.5%;
+ margin: 50px -30px -20px -30px;
+ padding: 10px 20px;
+ background: #333;
+ color: white;
+}
+
+div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
+div.sphinxsidebar h3 a {
+ color: white;
+}
+
+div.sphinxsidebar a {
+ color: #aaa;
+}
+
+div.sphinxsidebar p.logo {
+ display: none;
+}
+
+div.document {
+ width: 100%;
+ margin: 0;
+}
+
+div.related {
+ display: block;
+ margin: 0;
+ padding: 10px 0 20px 0;
+}
+
+div.related ul,
+div.related ul li {
+ margin: 0;
+ padding: 0;
+}
+
+div.footer {
+ display: none;
+}
+
+div.bodywrapper {
+ margin: 0;
+}
+
+div.body {
+ min-height: 0;
+ padding: 0;
+}
diff --git a/studio/static/doc/flask-docs/_static/touch-icon.png b/studio/static/doc/flask-docs/_static/touch-icon.png
new file mode 100644
index 0000000..cd1e91e
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/touch-icon.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/underscore.js b/studio/static/doc/flask-docs/_static/underscore.js
new file mode 100644
index 0000000..5d89914
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/underscore.js
@@ -0,0 +1,23 @@
+// Underscore.js 0.5.5
+// (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore is freely distributable under the terms of the MIT license.
+// Portions of Underscore are inspired by or borrowed from Prototype.js,
+// Oliver Steele's Functional, and John Resig's Micro-Templating.
+// For all details and documentation:
+// http://documentcloud.github.com/underscore/
+(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.5";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
+a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
+var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
+d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(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};b.each(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};b.each(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){e=e.criteria;f=f.criteria;return e<f?-1:e>f?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 k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
+0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(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.select(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.select(b.uniq(a),function(d){return b.all(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(a.indexOf)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(a.lastIndexOf)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;1;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||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
+return a};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=function(a){if(b.isArray(a))return b.range(0,a.length);
+var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,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 true;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(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
+0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!b.isArray(a)&&!r.call(a,"length")};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 p.call(a)==="[object Number]"};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(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.template=function(a,c){a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g,
+" ").replace(/'(?=[^%]*%>)/g,"\t").split("'").join("\\'").split("\t").join("'").replace(/<%=(.+?)%>/g,"',$1,'").split("<%").join("');").split("%>").join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);
+o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();
diff --git a/studio/static/doc/flask-docs/_static/up-pressed.png b/studio/static/doc/flask-docs/_static/up-pressed.png
new file mode 100644
index 0000000..8bd587a
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/up-pressed.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/up.png b/studio/static/doc/flask-docs/_static/up.png
new file mode 100644
index 0000000..b946256
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/up.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/_static/websupport.js b/studio/static/doc/flask-docs/_static/websupport.js
new file mode 100644
index 0000000..cbb6092
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/websupport.js
@@ -0,0 +1,808 @@
+/*
+ * websupport.js
+ * ~~~~~~~~~~~~~
+ *
+ * sphinx.websupport utilties for all documentation.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+(function($) {
+ $.fn.autogrow = function() {
+ return this.each(function() {
+ var textarea = this;
+
+ $.fn.autogrow.resize(textarea);
+
+ $(textarea)
+ .focus(function() {
+ textarea.interval = setInterval(function() {
+ $.fn.autogrow.resize(textarea);
+ }, 500);
+ })
+ .blur(function() {
+ clearInterval(textarea.interval);
+ });
+ });
+ };
+
+ $.fn.autogrow.resize = function(textarea) {
+ var lineHeight = parseInt($(textarea).css('line-height'), 10);
+ var lines = textarea.value.split('\n');
+ var columns = textarea.cols;
+ var lineCount = 0;
+ $.each(lines, function() {
+ lineCount += Math.ceil(this.length / columns) || 1;
+ });
+ var height = lineHeight * (lineCount + 1);
+ $(textarea).css('height', height);
+ };
+})(jQuery);
+
+(function($) {
+ var comp, by;
+
+ function init() {
+ initEvents();
+ initComparator();
+ }
+
+ function initEvents() {
+ $('a.comment-close').live("click", function(event) {
+ event.preventDefault();
+ hide($(this).attr('id').substring(2));
+ });
+ $('a.vote').live("click", function(event) {
+ event.preventDefault();
+ handleVote($(this));
+ });
+ $('a.reply').live("click", function(event) {
+ event.preventDefault();
+ openReply($(this).attr('id').substring(2));
+ });
+ $('a.close-reply').live("click", function(event) {
+ event.preventDefault();
+ closeReply($(this).attr('id').substring(2));
+ });
+ $('a.sort-option').live("click", function(event) {
+ event.preventDefault();
+ handleReSort($(this));
+ });
+ $('a.show-proposal').live("click", function(event) {
+ event.preventDefault();
+ showProposal($(this).attr('id').substring(2));
+ });
+ $('a.hide-proposal').live("click", function(event) {
+ event.preventDefault();
+ hideProposal($(this).attr('id').substring(2));
+ });
+ $('a.show-propose-change').live("click", function(event) {
+ event.preventDefault();
+ showProposeChange($(this).attr('id').substring(2));
+ });
+ $('a.hide-propose-change').live("click", function(event) {
+ event.preventDefault();
+ hideProposeChange($(this).attr('id').substring(2));
+ });
+ $('a.accept-comment').live("click", function(event) {
+ event.preventDefault();
+ acceptComment($(this).attr('id').substring(2));
+ });
+ $('a.delete-comment').live("click", function(event) {
+ event.preventDefault();
+ deleteComment($(this).attr('id').substring(2));
+ });
+ $('a.comment-markup').live("click", function(event) {
+ event.preventDefault();
+ toggleCommentMarkupBox($(this).attr('id').substring(2));
+ });
+ }
+
+ /**
+ * Set comp, which is a comparator function used for sorting and
+ * inserting comments into the list.
+ */
+ function setComparator() {
+ // If the first three letters are "asc", sort in ascending order
+ // and remove the prefix.
+ if (by.substring(0,3) == 'asc') {
+ var i = by.substring(3);
+ comp = function(a, b) { return a[i] - b[i]; };
+ } else {
+ // Otherwise sort in descending order.
+ comp = function(a, b) { return b[by] - a[by]; };
+ }
+
+ // Reset link styles and format the selected sort option.
+ $('a.sel').attr('href', '#').removeClass('sel');
+ $('a.by' + by).removeAttr('href').addClass('sel');
+ }
+
+ /**
+ * Create a comp function. If the user has preferences stored in
+ * the sortBy cookie, use those, otherwise use the default.
+ */
+ function initComparator() {
+ by = 'rating'; // Default to sort by rating.
+ // If the sortBy cookie is set, use that instead.
+ if (document.cookie.length > 0) {
+ var start = document.cookie.indexOf('sortBy=');
+ if (start != -1) {
+ start = start + 7;
+ var end = document.cookie.indexOf(";", start);
+ if (end == -1) {
+ end = document.cookie.length;
+ by = unescape(document.cookie.substring(start, end));
+ }
+ }
+ }
+ setComparator();
+ }
+
+ /**
+ * Show a comment div.
+ */
+ function show(id) {
+ $('#ao' + id).hide();
+ $('#ah' + id).show();
+ var context = $.extend({id: id}, opts);
+ var popup = $(renderTemplate(popupTemplate, context)).hide();
+ popup.find('textarea[name="proposal"]').hide();
+ popup.find('a.by' + by).addClass('sel');
+ var form = popup.find('#cf' + id);
+ form.submit(function(event) {
+ event.preventDefault();
+ addComment(form);
+ });
+ $('#s' + id).after(popup);
+ popup.slideDown('fast', function() {
+ getComments(id);
+ });
+ }
+
+ /**
+ * Hide a comment div.
+ */
+ function hide(id) {
+ $('#ah' + id).hide();
+ $('#ao' + id).show();
+ var div = $('#sc' + id);
+ div.slideUp('fast', function() {
+ div.remove();
+ });
+ }
+
+ /**
+ * Perform an ajax request to get comments for a node
+ * and insert the comments into the comments tree.
+ */
+ function getComments(id) {
+ $.ajax({
+ type: 'GET',
+ url: opts.getCommentsURL,
+ data: {node: id},
+ success: function(data, textStatus, request) {
+ var ul = $('#cl' + id);
+ var speed = 100;
+ $('#cf' + id)
+ .find('textarea[name="proposal"]')
+ .data('source', data.source);
+
+ if (data.comments.length === 0) {
+ ul.html('<li>No comments yet.</li>');
+ ul.data('empty', true);
+ } else {
+ // If there are comments, sort them and put them in the list.
+ var comments = sortComments(data.comments);
+ speed = data.comments.length * 100;
+ appendComments(comments, ul);
+ ul.data('empty', false);
+ }
+ $('#cn' + id).slideUp(speed + 200);
+ ul.slideDown(speed);
+ },
+ error: function(request, textStatus, error) {
+ showError('Oops, there was a problem retrieving the comments.');
+ },
+ dataType: 'json'
+ });
+ }
+
+ /**
+ * Add a comment via ajax and insert the comment into the comment tree.
+ */
+ function addComment(form) {
+ var node_id = form.find('input[name="node"]').val();
+ var parent_id = form.find('input[name="parent"]').val();
+ var text = form.find('textarea[name="comment"]').val();
+ var proposal = form.find('textarea[name="proposal"]').val();
+
+ if (text == '') {
+ showError('Please enter a comment.');
+ return;
+ }
+
+ // Disable the form that is being submitted.
+ form.find('textarea,input').attr('disabled', 'disabled');
+
+ // Send the comment to the server.
+ $.ajax({
+ type: "POST",
+ url: opts.addCommentURL,
+ dataType: 'json',
+ data: {
+ node: node_id,
+ parent: parent_id,
+ text: text,
+ proposal: proposal
+ },
+ success: function(data, textStatus, error) {
+ // Reset the form.
+ if (node_id) {
+ hideProposeChange(node_id);
+ }
+ form.find('textarea')
+ .val('')
+ .add(form.find('input'))
+ .removeAttr('disabled');
+ var ul = $('#cl' + (node_id || parent_id));
+ if (ul.data('empty')) {
+ $(ul).empty();
+ ul.data('empty', false);
+ }
+ insertComment(data.comment);
+ var ao = $('#ao' + node_id);
+ ao.find('img').attr({'src': opts.commentBrightImage});
+ if (node_id) {
+ // if this was a "root" comment, remove the commenting box
+ // (the user can get it back by reopening the comment popup)
+ $('#ca' + node_id).slideUp();
+ }
+ },
+ error: function(request, textStatus, error) {
+ form.find('textarea,input').removeAttr('disabled');
+ showError('Oops, there was a problem adding the comment.');
+ }
+ });
+ }
+
+ /**
+ * Recursively append comments to the main comment list and children
+ * lists, creating the comment tree.
+ */
+ function appendComments(comments, ul) {
+ $.each(comments, function() {
+ var div = createCommentDiv(this);
+ ul.append($(document.createElement('li')).html(div));
+ appendComments(this.children, div.find('ul.comment-children'));
+ // To avoid stagnating data, don't store the comments children in data.
+ this.children = null;
+ div.data('comment', this);
+ });
+ }
+
+ /**
+ * After adding a new comment, it must be inserted in the correct
+ * location in the comment tree.
+ */
+ function insertComment(comment) {
+ var div = createCommentDiv(comment);
+
+ // To avoid stagnating data, don't store the comments children in data.
+ comment.children = null;
+ div.data('comment', comment);
+
+ var ul = $('#cl' + (comment.node || comment.parent));
+ var siblings = getChildren(ul);
+
+ var li = $(document.createElement('li'));
+ li.hide();
+
+ // Determine where in the parents children list to insert this comment.
+ for(i=0; i < siblings.length; i++) {
+ if (comp(comment, siblings[i]) <= 0) {
+ $('#cd' + siblings[i].id)
+ .parent()
+ .before(li.html(div));
+ li.slideDown('fast');
+ return;
+ }
+ }
+
+ // If we get here, this comment rates lower than all the others,
+ // or it is the only comment in the list.
+ ul.append(li.html(div));
+ li.slideDown('fast');
+ }
+
+ function acceptComment(id) {
+ $.ajax({
+ type: 'POST',
+ url: opts.acceptCommentURL,
+ data: {id: id},
+ success: function(data, textStatus, request) {
+ $('#cm' + id).fadeOut('fast');
+ $('#cd' + id).removeClass('moderate');
+ },
+ error: function(request, textStatus, error) {
+ showError('Oops, there was a problem accepting the comment.');
+ }
+ });
+ }
+
+ function deleteComment(id) {
+ $.ajax({
+ type: 'POST',
+ url: opts.deleteCommentURL,
+ data: {id: id},
+ success: function(data, textStatus, request) {
+ var div = $('#cd' + id);
+ if (data == 'delete') {
+ // Moderator mode: remove the comment and all children immediately
+ div.slideUp('fast', function() {
+ div.remove();
+ });
+ return;
+ }
+ // User mode: only mark the comment as deleted
+ div
+ .find('span.user-id:first')
+ .text('[deleted]').end()
+ .find('div.comment-text:first')
+ .text('[deleted]').end()
+ .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
+ ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
+ .remove();
+ var comment = div.data('comment');
+ comment.username = '[deleted]';
+ comment.text = '[deleted]';
+ div.data('comment', comment);
+ },
+ error: function(request, textStatus, error) {
+ showError('Oops, there was a problem deleting the comment.');
+ }
+ });
+ }
+
+ function showProposal(id) {
+ $('#sp' + id).hide();
+ $('#hp' + id).show();
+ $('#pr' + id).slideDown('fast');
+ }
+
+ function hideProposal(id) {
+ $('#hp' + id).hide();
+ $('#sp' + id).show();
+ $('#pr' + id).slideUp('fast');
+ }
+
+ function showProposeChange(id) {
+ $('#pc' + id).hide();
+ $('#hc' + id).show();
+ var textarea = $('#pt' + id);
+ textarea.val(textarea.data('source'));
+ $.fn.autogrow.resize(textarea[0]);
+ textarea.slideDown('fast');
+ }
+
+ function hideProposeChange(id) {
+ $('#hc' + id).hide();
+ $('#pc' + id).show();
+ var textarea = $('#pt' + id);
+ textarea.val('').removeAttr('disabled');
+ textarea.slideUp('fast');
+ }
+
+ function toggleCommentMarkupBox(id) {
+ $('#mb' + id).toggle();
+ }
+
+ /** Handle when the user clicks on a sort by link. */
+ function handleReSort(link) {
+ var classes = link.attr('class').split(/\s+/);
+ for (var i=0; i<classes.length; i++) {
+ if (classes[i] != 'sort-option') {
+ by = classes[i].substring(2);
+ }
+ }
+ setComparator();
+ // Save/update the sortBy cookie.
+ var expiration = new Date();
+ expiration.setDate(expiration.getDate() + 365);
+ document.cookie= 'sortBy=' + escape(by) +
+ ';expires=' + expiration.toUTCString();
+ $('ul.comment-ul').each(function(index, ul) {
+ var comments = getChildren($(ul), true);
+ comments = sortComments(comments);
+ appendComments(comments, $(ul).empty());
+ });
+ }
+
+ /**
+ * Function to process a vote when a user clicks an arrow.
+ */
+ function handleVote(link) {
+ if (!opts.voting) {
+ showError("You'll need to login to vote.");
+ return;
+ }
+
+ var id = link.attr('id');
+ if (!id) {
+ // Didn't click on one of the voting arrows.
+ return;
+ }
+ // If it is an unvote, the new vote value is 0,
+ // Otherwise it's 1 for an upvote, or -1 for a downvote.
+ var value = 0;
+ if (id.charAt(1) != 'u') {
+ value = id.charAt(0) == 'u' ? 1 : -1;
+ }
+ // The data to be sent to the server.
+ var d = {
+ comment_id: id.substring(2),
+ value: value
+ };
+
+ // Swap the vote and unvote links.
+ link.hide();
+ $('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id)
+ .show();
+
+ // The div the comment is displayed in.
+ var div = $('div#cd' + d.comment_id);
+ var data = div.data('comment');
+
+ // If this is not an unvote, and the other vote arrow has
+ // already been pressed, unpress it.
+ if ((d.value !== 0) && (data.vote === d.value * -1)) {
+ $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide();
+ $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show();
+ }
+
+ // Update the comments rating in the local data.
+ data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
+ data.vote = d.value;
+ div.data('comment', data);
+
+ // Change the rating text.
+ div.find('.rating:first')
+ .text(data.rating + ' point' + (data.rating == 1 ? '' : 's'));
+
+ // Send the vote information to the server.
+ $.ajax({
+ type: "POST",
+ url: opts.processVoteURL,
+ data: d,
+ error: function(request, textStatus, error) {
+ showError('Oops, there was a problem casting that vote.');
+ }
+ });
+ }
+
+ /**
+ * Open a reply form used to reply to an existing comment.
+ */
+ function openReply(id) {
+ // Swap out the reply link for the hide link
+ $('#rl' + id).hide();
+ $('#cr' + id).show();
+
+ // Add the reply li to the children ul.
+ var div = $(renderTemplate(replyTemplate, {id: id})).hide();
+ $('#cl' + id)
+ .prepend(div)
+ // Setup the submit handler for the reply form.
+ .find('#rf' + id)
+ .submit(function(event) {
+ event.preventDefault();
+ addComment($('#rf' + id));
+ closeReply(id);
+ })
+ .find('input[type=button]')
+ .click(function() {
+ closeReply(id);
+ });
+ div.slideDown('fast', function() {
+ $('#rf' + id).find('textarea').focus();
+ });
+ }
+
+ /**
+ * Close the reply form opened with openReply.
+ */
+ function closeReply(id) {
+ // Remove the reply div from the DOM.
+ $('#rd' + id).slideUp('fast', function() {
+ $(this).remove();
+ });
+
+ // Swap out the hide link for the reply link
+ $('#cr' + id).hide();
+ $('#rl' + id).show();
+ }
+
+ /**
+ * Recursively sort a tree of comments using the comp comparator.
+ */
+ function sortComments(comments) {
+ comments.sort(comp);
+ $.each(comments, function() {
+ this.children = sortComments(this.children);
+ });
+ return comments;
+ }
+
+ /**
+ * Get the children comments from a ul. If recursive is true,
+ * recursively include childrens' children.
+ */
+ function getChildren(ul, recursive) {
+ var children = [];
+ ul.children().children("[id^='cd']")
+ .each(function() {
+ var comment = $(this).data('comment');
+ if (recursive)
+ comment.children = getChildren($(this).find('#cl' + comment.id), true);
+ children.push(comment);
+ });
+ return children;
+ }
+
+ /** Create a div to display a comment in. */
+ function createCommentDiv(comment) {
+ if (!comment.displayed && !opts.moderator) {
+ return $('<div class="moderate">Thank you! Your comment will show up '
+ + 'once it is has been approved by a moderator.</div>');
+ }
+ // Prettify the comment rating.
+ comment.pretty_rating = comment.rating + ' point' +
+ (comment.rating == 1 ? '' : 's');
+ // Make a class (for displaying not yet moderated comments differently)
+ comment.css_class = comment.displayed ? '' : ' moderate';
+ // Create a div for this comment.
+ var context = $.extend({}, opts, comment);
+ var div = $(renderTemplate(commentTemplate, context));
+
+ // If the user has voted on this comment, highblight the correct arrow.
+ if (comment.vote) {
+ var direction = (comment.vote == 1) ? 'u' : 'd';
+ div.find('#' + direction + 'v' + comment.id).hide();
+ div.find('#' + direction + 'u' + comment.id).show();
+ }
+
+ if (opts.moderator || comment.text != '[deleted]') {
+ div.find('a.reply').show();
+ if (comment.proposal_diff)
+ div.find('#sp' + comment.id).show();
+ if (opts.moderator && !comment.displayed)
+ div.find('#cm' + comment.id).show();
+ if (opts.moderator || (opts.username == comment.username))
+ div.find('#dc' + comment.id).show();
+ }
+ return div;
+ }
+
+ /**
+ * A simple template renderer. Placeholders such as <%id%> are replaced
+ * by context['id'] with items being escaped. Placeholders such as <#id#>
+ * are not escaped.
+ */
+ function renderTemplate(template, context) {
+ var esc = $(document.createElement('div'));
+
+ function handle(ph, escape) {
+ var cur = context;
+ $.each(ph.split('.'), function() {
+ cur = cur[this];
+ });
+ return escape ? esc.text(cur || "").html() : cur;
+ }
+
+ return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
+ return handle(arguments[2], arguments[1] == '%' ? true : false);
+ });
+ }
+
+ /** Flash an error message briefly. */
+ function showError(message) {
+ $(document.createElement('div')).attr({'class': 'popup-error'})
+ .append($(document.createElement('div'))
+ .attr({'class': 'error-message'}).text(message))
+ .appendTo('body')
+ .fadeIn("slow")
+ .delay(2000)
+ .fadeOut("slow");
+ }
+
+ /** Add a link the user uses to open the comments popup. */
+ $.fn.comment = function() {
+ return this.each(function() {
+ var id = $(this).attr('id').substring(1);
+ var count = COMMENT_METADATA[id];
+ var title = count + ' comment' + (count == 1 ? '' : 's');
+ var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
+ var addcls = count == 0 ? ' nocomment' : '';
+ $(this)
+ .append(
+ $(document.createElement('a')).attr({
+ href: '#',
+ 'class': 'sphinx-comment-open' + addcls,
+ id: 'ao' + id
+ })
+ .append($(document.createElement('img')).attr({
+ src: image,
+ alt: 'comment',
+ title: title
+ }))
+ .click(function(event) {
+ event.preventDefault();
+ show($(this).attr('id').substring(2));
+ })
+ )
+ .append(
+ $(document.createElement('a')).attr({
+ href: '#',
+ 'class': 'sphinx-comment-close hidden',
+ id: 'ah' + id
+ })
+ .append($(document.createElement('img')).attr({
+ src: opts.closeCommentImage,
+ alt: 'close',
+ title: 'close'
+ }))
+ .click(function(event) {
+ event.preventDefault();
+ hide($(this).attr('id').substring(2));
+ })
+ );
+ });
+ };
+
+ var opts = {
+ processVoteURL: '/_process_vote',
+ addCommentURL: '/_add_comment',
+ getCommentsURL: '/_get_comments',
+ acceptCommentURL: '/_accept_comment',
+ deleteCommentURL: '/_delete_comment',
+ commentImage: '/static/_static/comment.png',
+ closeCommentImage: '/static/_static/comment-close.png',
+ loadingImage: '/static/_static/ajax-loader.gif',
+ commentBrightImage: '/static/_static/comment-bright.png',
+ upArrow: '/static/_static/up.png',
+ downArrow: '/static/_static/down.png',
+ upArrowPressed: '/static/_static/up-pressed.png',
+ downArrowPressed: '/static/_static/down-pressed.png',
+ voting: false,
+ moderator: false
+ };
+
+ if (typeof COMMENT_OPTIONS != "undefined") {
+ opts = jQuery.extend(opts, COMMENT_OPTIONS);
+ }
+
+ var popupTemplate = '\
+ <div class="sphinx-comments" id="sc<%id%>">\
+ <p class="sort-options">\
+ Sort by:\
+ <a href="#" class="sort-option byrating">best rated</a>\
+ <a href="#" class="sort-option byascage">newest</a>\
+ <a href="#" class="sort-option byage">oldest</a>\
+ </p>\
+ <div class="comment-header">Comments</div>\
+ <div class="comment-loading" id="cn<%id%>">\
+ loading comments... <img src="<%loadingImage%>" alt="" /></div>\
+ <ul id="cl<%id%>" class="comment-ul"></ul>\
+ <div id="ca<%id%>">\
+ <p class="add-a-comment">Add a comment\
+ (<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
+ <div class="comment-markup-box" id="mb<%id%>">\
+ reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
+ <tt>``code``</tt>, \
+ code blocks: <tt>::</tt> and an indented block after blank line</div>\
+ <form method="post" id="cf<%id%>" class="comment-form" action="">\
+ <textarea name="comment" cols="80"></textarea>\
+ <p class="propose-button">\
+ <a href="#" id="pc<%id%>" class="show-propose-change">\
+ Propose a change &#9657;\
+ </a>\
+ <a href="#" id="hc<%id%>" class="hide-propose-change">\
+ Propose a change &#9663;\
+ </a>\
+ </p>\
+ <textarea name="proposal" id="pt<%id%>" cols="80"\
+ spellcheck="false"></textarea>\
+ <input type="submit" value="Add comment" />\
+ <input type="hidden" name="node" value="<%id%>" />\
+ <input type="hidden" name="parent" value="" />\
+ </form>\
+ </div>\
+ </div>';
+
+ var commentTemplate = '\
+ <div id="cd<%id%>" class="sphinx-comment<%css_class%>">\
+ <div class="vote">\
+ <div class="arrow">\
+ <a href="#" id="uv<%id%>" class="vote" title="vote up">\
+ <img src="<%upArrow%>" />\
+ </a>\
+ <a href="#" id="uu<%id%>" class="un vote" title="vote up">\
+ <img src="<%upArrowPressed%>" />\
+ </a>\
+ </div>\
+ <div class="arrow">\
+ <a href="#" id="dv<%id%>" class="vote" title="vote down">\
+ <img src="<%downArrow%>" id="da<%id%>" />\
+ </a>\
+ <a href="#" id="du<%id%>" class="un vote" title="vote down">\
+ <img src="<%downArrowPressed%>" />\
+ </a>\
+ </div>\
+ </div>\
+ <div class="comment-content">\
+ <p class="tagline comment">\
+ <span class="user-id"><%username%></span>\
+ <span class="rating"><%pretty_rating%></span>\
+ <span class="delta"><%time.delta%></span>\
+ </p>\
+ <div class="comment-text comment"><#text#></div>\
+ <p class="comment-opts comment">\
+ <a href="#" class="reply hidden" id="rl<%id%>">reply &#9657;</a>\
+ <a href="#" class="close-reply" id="cr<%id%>">reply &#9663;</a>\
+ <a href="#" id="sp<%id%>" class="show-proposal">proposal &#9657;</a>\
+ <a href="#" id="hp<%id%>" class="hide-proposal">proposal &#9663;</a>\
+ <a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\
+ <span id="cm<%id%>" class="moderation hidden">\
+ <a href="#" id="ac<%id%>" class="accept-comment">accept</a>\
+ </span>\
+ </p>\
+ <pre class="proposal" id="pr<%id%>">\
+<#proposal_diff#>\
+ </pre>\
+ <ul class="comment-children" id="cl<%id%>"></ul>\
+ </div>\
+ <div class="clearleft"></div>\
+ </div>\
+ </div>';
+
+ var replyTemplate = '\
+ <li>\
+ <div class="reply-div" id="rd<%id%>">\
+ <form id="rf<%id%>">\
+ <textarea name="comment" cols="80"></textarea>\
+ <input type="submit" value="Add reply" />\
+ <input type="button" value="Cancel" />\
+ <input type="hidden" name="parent" value="<%id%>" />\
+ <input type="hidden" name="node" value="" />\
+ </form>\
+ </div>\
+ </li>';
+
+ $(document).ready(function() {
+ init();
+ });
+})(jQuery);
+
+$(document).ready(function() {
+ // add comment anchors for all paragraphs that are commentable
+ $('.sphinx-has-comment').comment();
+
+ // highlight search words in search results
+ $("div.context").each(function() {
+ var params = $.getQueryParameters();
+ var terms = (params.q) ? params.q[0].split(/\s+/) : [];
+ var result = $(this);
+ $.each(terms, function() {
+ result.highlightText(this.toLowerCase(), 'highlighted');
+ });
+ });
+
+ // directly open comment window if requested
+ var anchor = document.location.hash;
+ if (anchor.substring(0, 9) == '#comment-') {
+ $('#ao' + anchor.substring(9)).click();
+ document.location.hash = '#s' + anchor.substring(9);
+ }
+});
diff --git a/studio/static/doc/flask-docs/_static/yes.png b/studio/static/doc/flask-docs/_static/yes.png
new file mode 100644
index 0000000..ac27c4e
--- /dev/null
+++ b/studio/static/doc/flask-docs/_static/yes.png
Binary files differ
diff --git a/studio/static/doc/flask-docs/api.html b/studio/static/doc/flask-docs/api.html
new file mode 100644
index 0000000..f130677
--- /dev/null
+++ b/studio/static/doc/flask-docs/api.html
@@ -0,0 +1,3475 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>API &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Design Decisions in Flask" href="design.html" />
+ <link rel="prev" title="Becoming Big" href="becomingbig.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="design.html" title="Design Decisions in Flask"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="becomingbig.html" title="Becoming Big"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="api">
+<span id="id1"></span><h1>API<a class="headerlink" href="#api" title="Permalink to this headline">¶</a></h1>
+<span class="target" id="module-flask"></span><p>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.</p>
+<div class="section" id="application-object">
+<h2>Application Object<a class="headerlink" href="#application-object" title="Permalink to this headline">¶</a></h2>
+<dl class="class">
+<dt id="flask.Flask">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">Flask</tt><big>(</big><em>import_name</em>, <em>static_path=None</em>, <em>static_url_path=None</em>, <em>static_folder='static'</em>, <em>template_folder='templates'</em>, <em>instance_path=None</em>, <em>instance_relative_config=False</em><big>)</big><a class="headerlink" href="#flask.Flask" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+<p>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 <cite>__init__.py</cite> file inside) or a standard module (just a <cite>.py</cite> file).</p>
+<p>For more information about resource loading, see <a class="reference internal" href="#flask.Flask.open_resource" title="flask.Flask.open_resource"><tt class="xref py py-func docutils literal"><span class="pre">open_resource()</span></tt></a>.</p>
+<p>Usually you create a <a class="reference internal" href="#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> instance in your main module or
+in the <cite>__init__.py</cite> file of your package like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+</pre></div>
+</div>
+<div class="admonition-about-the-first-parameter admonition ">
+<p class="first admonition-title">About the First Parameter</p>
+<p>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.</p>
+<p>So it&#8217;s important what you provide there. If you are using a single
+module, <cite>__name__</cite> is always the correct value. If you however are
+using a package, it&#8217;s usually recommended to hardcode the name of
+your package there.</p>
+<p>For example if your application is defined in <cite>yourapplication/app.py</cite>
+you should create it with one of the two versions below:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="s">&#39;yourapplication&#39;</span><span class="p">)</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">)[</span><span class="mi">0</span><span class="p">])</span>
+</pre></div>
+</div>
+<p class="last">Why is that? The application will work even with <cite>__name__</cite>, 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 <cite>yourapplication.app</cite> and not
+<cite>yourapplication.views.frontend</cite>)</p>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7: </span>The <cite>static_url_path</cite>, <cite>static_folder</cite>, and <cite>template_folder</cite>
+parameters were added.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8: </span>The <cite>instance_path</cite> and <cite>instance_relative_config</cite> parameters were
+added.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>import_name</strong> &#8211; the name of the application package</li>
+<li><strong>static_url_path</strong> &#8211; can be used to specify a different path for the
+static files on the web. Defaults to the name
+of the <cite>static_folder</cite> folder.</li>
+<li><strong>static_folder</strong> &#8211; the folder with static files that should be served
+at <cite>static_url_path</cite>. Defaults to the <tt class="docutils literal"><span class="pre">'static'</span></tt>
+folder in the root path of the application.</li>
+<li><strong>template_folder</strong> &#8211; the folder that contains the templates that should
+be used by the application. Defaults to
+<tt class="docutils literal"><span class="pre">'templates'</span></tt> folder in the root path of the
+application.</li>
+<li><strong>instance_path</strong> &#8211; An alternative instance path for the application.
+By default the folder <tt class="docutils literal"><span class="pre">'instance'</span></tt> next to the
+package or module is assumed to be the instance
+path.</li>
+<li><strong>instance_relative_config</strong> &#8211; if set to <cite>True</cite> relative filenames
+for loading the config are assumed to
+be relative to the instance path instead
+of the application root.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+<dl class="method">
+<dt id="flask.Flask.add_url_rule">
+<tt class="descname">add_url_rule</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.add_url_rule" title="Permalink to this definition">¶</a></dt>
+<dd><p>Connects a URL rule. Works exactly like the <a class="reference internal" href="#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a>
+decorator. If a view_func is provided it will be registered with the
+endpoint.</p>
+<p>Basically this example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>Is equivalent to the following:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">pass</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="s">&#39;index&#39;</span><span class="p">,</span> <span class="n">index</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>If the view_func is not provided you will need to connect the endpoint
+to a view function like so:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">view_functions</span><span class="p">[</span><span class="s">&#39;index&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">index</span>
+</pre></div>
+</div>
+<p>Internally <a class="reference internal" href="#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a> invokes <a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a> so if you want
+to customize the behavior via subclassing you only need to change
+this method.</p>
+<p>For more information refer to <a class="reference internal" href="#url-route-registrations"><em>URL Route Registrations</em></a>.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.2: </span><cite>view_func</cite> parameter added.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.6: </span><cite>OPTIONS</cite> is added automatically as method.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>rule</strong> &#8211; the URL rule as string</li>
+<li><strong>endpoint</strong> &#8211; the endpoint for the registered URL rule. Flask
+itself assumes the name of the view function as
+endpoint</li>
+<li><strong>view_func</strong> &#8211; the function to call when serving a request to the
+provided endpoint</li>
+<li><strong>options</strong> &#8211; the options to be forwarded to the underlying
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/routing/#werkzeug.routing.Rule" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Rule</span></tt></a> object. A change
+to Werkzeug is handling of method options. methods
+is a list of methods this rule should be limited
+to (<cite>GET</cite>, <cite>POST</cite> etc.). By default a rule
+just listens for <cite>GET</cite> (and implicitly <cite>HEAD</cite>).
+Starting with Flask 0.6, <cite>OPTIONS</cite> is implicitly
+added and handled by the standard request handling.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.after_request">
+<tt class="descname">after_request</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.after_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Register a function to be run after each request. Your function
+must take one parameter, a <a class="reference internal" href="#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-attr docutils literal"><span class="pre">response_class</span></tt></a> object and return
+a new response object or the same (see <a class="reference internal" href="#flask.Flask.process_response" title="flask.Flask.process_response"><tt class="xref py py-meth docutils literal"><span class="pre">process_response()</span></tt></a>).</p>
+<p>As of Flask 0.7 this function might not be executed at the end of the
+request in case an unhandled exception ocurred.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.after_request_funcs">
+<tt class="descname">after_request_funcs</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.after_request_funcs" title="Permalink to this definition">¶</a></dt>
+<dd><p>A dictionary with lists of functions that should be called after
+each request. The key of the dictionary is the name of the blueprint
+this function is active for, <cite>None</cite> 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
+<a class="reference internal" href="#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> decorator.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.auto_find_instance_path">
+<tt class="descname">auto_find_instance_path</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.auto_find_instance_path" title="Permalink to this definition">¶</a></dt>
+<dd><p>Tries to locate the instance path if it was not provided to the
+constructor of the application class. It will basically calculate
+the path to a folder named <tt class="docutils literal"><span class="pre">instance</span></tt> next to your main file or
+the package.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.before_first_request">
+<tt class="descname">before_first_request</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.before_first_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a function to be run before the first request to this
+instance of the application.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.before_first_request_funcs">
+<tt class="descname">before_first_request_funcs</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.before_first_request_funcs" title="Permalink to this definition">¶</a></dt>
+<dd><p>A lists of functions that should be called at the beginning of the
+first request to this instance. To register a function here, use
+the <a class="reference internal" href="#flask.Flask.before_first_request" title="flask.Flask.before_first_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_first_request()</span></tt></a> decorator.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.before_request">
+<tt class="descname">before_request</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.before_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a function to run before each request.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.before_request_funcs">
+<tt class="descname">before_request_funcs</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.before_request_funcs" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 blueprint this function is active for, <cite>None</cite> 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 <a class="reference internal" href="#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> decorator.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.blueprints">
+<tt class="descname">blueprints</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.blueprints" title="Permalink to this definition">¶</a></dt>
+<dd><p>all the attached blueprints in a directory by name. Blueprints
+can be attached multiple times so this dictionary does not tell
+you how often they got attached.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.config">
+<tt class="descname">config</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.config" title="Permalink to this definition">¶</a></dt>
+<dd><p>The configuration dictionary as <a class="reference internal" href="#flask.Config" title="flask.Config"><tt class="xref py py-class docutils literal"><span class="pre">Config</span></tt></a>. This behaves
+exactly like a regular dictionary but supports additional methods
+to load a config from files.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.context_processor">
+<tt class="descname">context_processor</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.context_processor" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a template context processor function.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.create_global_jinja_loader">
+<tt class="descname">create_global_jinja_loader</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.create_global_jinja_loader" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates the loader for the Jinja2 environment. Can be used to
+override just the loader and keeping the rest unchanged. It&#8217;s
+discouraged to override this function. Instead one should override
+the <a class="reference internal" href="#flask.Flask.jinja_loader" title="flask.Flask.jinja_loader"><tt class="xref py py-meth docutils literal"><span class="pre">jinja_loader()</span></tt></a> function instead.</p>
+<p>The global loader dispatches between the loaders of the application
+and the individual blueprints.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.create_jinja_environment">
+<tt class="descname">create_jinja_environment</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.create_jinja_environment" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates the Jinja2 environment based on <a class="reference internal" href="#flask.Flask.jinja_options" title="flask.Flask.jinja_options"><tt class="xref py py-attr docutils literal"><span class="pre">jinja_options</span></tt></a>
+and <a class="reference internal" href="#flask.Flask.select_jinja_autoescape" title="flask.Flask.select_jinja_autoescape"><tt class="xref py py-meth docutils literal"><span class="pre">select_jinja_autoescape()</span></tt></a>. Since 0.7 this also adds
+the Jinja2 globals and filters after initialization. Override
+this function to customize the behavior.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.create_url_adapter">
+<tt class="descname">create_url_adapter</tt><big>(</big><em>request</em><big>)</big><a class="headerlink" href="#flask.Flask.create_url_adapter" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.debug">
+<tt class="descname">debug</tt><a class="headerlink" href="#flask.Flask.debug" title="Permalink to this definition">¶</a></dt>
+<dd><p>The debug flag. Set this to <cite>True</cite> 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.</p>
+<p>This attribute can also be configured from the config with the <cite>DEBUG</cite>
+configuration key. Defaults to <cite>False</cite>.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.debug_log_format">
+<tt class="descname">debug_log_format</tt><em class="property"> = '--------------------------------------------------------------------------------\n%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s\n--------------------------------------------------------------------------------'</em><a class="headerlink" href="#flask.Flask.debug_log_format" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.3.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.default_config">
+<tt class="descname">default_config</tt><em class="property"> = ImmutableDict({'USE_X_SENDFILE': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_NAME': 'session', 'LOGGER_NAME': None, 'DEBUG': False, 'SECRET_KEY': None, 'MAX_CONTENT_LENGTH': None, 'APPLICATION_ROOT': None, 'SERVER_NAME': None, 'TESTING': False, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'PROPAGATE_EXCEPTIONS': None, 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_HTTPONLY': True})</em><a class="headerlink" href="#flask.Flask.default_config" title="Permalink to this definition">¶</a></dt>
+<dd><p>Default configuration parameters.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.dispatch_request">
+<tt class="descname">dispatch_request</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.dispatch_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference internal" href="#flask.make_response" title="flask.make_response"><tt class="xref py py-func docutils literal"><span class="pre">make_response()</span></tt></a>.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.7: </span>This no longer does the exception handling, this code was
+moved to the new <a class="reference internal" href="#flask.Flask.full_dispatch_request" title="flask.Flask.full_dispatch_request"><tt class="xref py py-meth docutils literal"><span class="pre">full_dispatch_request()</span></tt></a>.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.do_teardown_request">
+<tt class="descname">do_teardown_request</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.do_teardown_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Called after the actual request dispatching and will
+call every as <a class="reference internal" href="#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> decorated function. This is
+not actually called by the <a class="reference internal" href="#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> object itself but is always
+triggered when the request context is popped. That way we have a
+tighter control over certain resources under testing environments.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.enable_modules">
+<tt class="descname">enable_modules</tt><em class="property"> = True</em><a class="headerlink" href="#flask.Flask.enable_modules" title="Permalink to this definition">¶</a></dt>
+<dd><p>Enable the deprecated module support? This is active by default
+in 0.7 but will be changed to False in 0.8. With Flask 1.0 modules
+will be removed in favor of Blueprints</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.endpoint">
+<tt class="descname">endpoint</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.endpoint" title="Permalink to this definition">¶</a></dt>
+<dd><p>A decorator to register a function as an endpoint.
+Example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.endpoint</span><span class="p">(</span><span class="s">&#39;example.endpoint&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">example</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&quot;example&quot;</span>
+</pre></div>
+</div>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>endpoint</strong> &#8211; the name of the endpoint</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.error_handler_spec">
+<tt class="descname">error_handler_spec</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.error_handler_spec" title="Permalink to this definition">¶</a></dt>
+<dd><p>A dictionary of all registered error handlers. The key is <cite>None</cite>
+for error handlers active on the application, otherwise the key is
+the name of the blueprint. Each key points to another dictionary
+where they key is the status code of the http exception. The
+special key <cite>None</cite> points to a list of tuples where the first item
+is the class for the instance check and the second the error handler
+function.</p>
+<p>To register a error handler, use the <a class="reference internal" href="#flask.Flask.errorhandler" title="flask.Flask.errorhandler"><tt class="xref py py-meth docutils literal"><span class="pre">errorhandler()</span></tt></a>
+decorator.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.errorhandler">
+<tt class="descname">errorhandler</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.errorhandler" title="Permalink to this definition">¶</a></dt>
+<dd><p>A decorator that is used to register a function give a given
+error code. Example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">page_not_found</span><span class="p">(</span><span class="n">error</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;This page does not exist&#39;</span><span class="p">,</span> <span class="mi">404</span>
+</pre></div>
+</div>
+<p>You can also register handlers for arbitrary exceptions:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="n">DatabaseError</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">special_exception_handler</span><span class="p">(</span><span class="n">error</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;Database connection failed&#39;</span><span class="p">,</span> <span class="mi">500</span>
+</pre></div>
+</div>
+<p>You can also register a function as error handler without using
+the <a class="reference internal" href="#flask.Flask.errorhandler" title="flask.Flask.errorhandler"><tt class="xref py py-meth docutils literal"><span class="pre">errorhandler()</span></tt></a> decorator. The following example is
+equivalent to the one above:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">page_not_found</span><span class="p">(</span><span class="n">error</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;This page does not exist&#39;</span><span class="p">,</span> <span class="mi">404</span>
+<span class="n">app</span><span class="o">.</span><span class="n">error_handler_spec</span><span class="p">[</span><span class="bp">None</span><span class="p">][</span><span class="mi">404</span><span class="p">]</span> <span class="o">=</span> <span class="n">page_not_found</span>
+</pre></div>
+</div>
+<p>Setting error handlers via assignments to <a class="reference internal" href="#flask.Flask.error_handler_spec" title="flask.Flask.error_handler_spec"><tt class="xref py py-attr docutils literal"><span class="pre">error_handler_spec</span></tt></a>
+however is discouraged as it requires fidling with nested dictionaries
+and the special case for arbitrary exception types.</p>
+<p>The first <cite>None</cite> refers to the active blueprint. If the error
+handler should be application wide <cite>None</cite> shall be used.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7: </span>One can now additionally also register custom exception types
+that do not necessarily have to be a subclass of the
+<tt class="xref py py-class docutils literal"><span class="pre">HTTPException</span></tt> class.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>code</strong> &#8211; the code as integer for the handler</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.extensions">
+<tt class="descname">extensions</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.extensions" title="Permalink to this definition">¶</a></dt>
+<dd><p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="s">&#39;extensions&#39;</span><span class="p">):</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">extensions</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="n">app</span><span class="o">.</span><span class="n">extensions</span><span class="p">[</span><span class="s">&#39;extensionname&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">SomeObject</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>The key must match the name of the <cite>flaskext</cite> module. For example in
+case of a &#8220;Flask-Foo&#8221; extension in <cite>flaskext.foo</cite>, the key would be
+<tt class="docutils literal"><span class="pre">'foo'</span></tt>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.full_dispatch_request">
+<tt class="descname">full_dispatch_request</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.full_dispatch_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Dispatches the request and on top of that performs request
+pre and postprocessing as well as HTTP exception catching and
+error handling.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.got_first_request">
+<tt class="descname">got_first_request</tt><a class="headerlink" href="#flask.Flask.got_first_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>This attribute is set to <cite>True</cite> if the application started
+handling the first request.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.handle_exception">
+<tt class="descname">handle_exception</tt><big>(</big><em>e</em><big>)</big><a class="headerlink" href="#flask.Flask.handle_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>Default exception handling that kicks in when an exception
+occours that is not caught. 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.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.handle_http_exception">
+<tt class="descname">handle_http_exception</tt><big>(</big><em>e</em><big>)</big><a class="headerlink" href="#flask.Flask.handle_http_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>Handles an HTTP exception. By default this will invoke the
+registered error handlers and fall back to returning the
+exception as response.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.handle_user_exception">
+<tt class="descname">handle_user_exception</tt><big>(</big><em>e</em><big>)</big><a class="headerlink" href="#flask.Flask.handle_user_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>This method is called whenever an exception occurs that should be
+handled. A special case are
+<tt class="xref py py-class docutils literal"><span class="pre">HTTPException</span></tt>s which are forwarded by
+this function to the <a class="reference internal" href="#flask.Flask.handle_http_exception" title="flask.Flask.handle_http_exception"><tt class="xref py py-meth docutils literal"><span class="pre">handle_http_exception()</span></tt></a> method. This
+function will either return a response value or reraise the
+exception with the same traceback.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.has_static_folder">
+<tt class="descname">has_static_folder</tt><a class="headerlink" href="#flask.Flask.has_static_folder" title="Permalink to this definition">¶</a></dt>
+<dd><p>This is <cite>True</cite> if the package bound object&#8217;s container has a
+folder named <tt class="docutils literal"><span class="pre">'static'</span></tt>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.init_jinja_globals">
+<tt class="descname">init_jinja_globals</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.init_jinja_globals" title="Permalink to this definition">¶</a></dt>
+<dd><p>Deprecated. Used to initialize the Jinja2 globals.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.7: </span>This method is deprecated with 0.7. Override
+<a class="reference internal" href="#flask.Flask.create_jinja_environment" title="flask.Flask.create_jinja_environment"><tt class="xref py py-meth docutils literal"><span class="pre">create_jinja_environment()</span></tt></a> instead.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.inject_url_defaults">
+<tt class="descname">inject_url_defaults</tt><big>(</big><em>endpoint</em>, <em>values</em><big>)</big><a class="headerlink" href="#flask.Flask.inject_url_defaults" title="Permalink to this definition">¶</a></dt>
+<dd><p>Injects the URL defaults for the given endpoint directly into
+the values dictionary passed. This is used internally and
+automatically called on URL building.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.instance_path">
+<tt class="descname">instance_path</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.instance_path" title="Permalink to this definition">¶</a></dt>
+<dd><p>Holds the path to the instance folder.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.jinja_env">
+<tt class="descname">jinja_env</tt><a class="headerlink" href="#flask.Flask.jinja_env" title="Permalink to this definition">¶</a></dt>
+<dd><p>The Jinja2 environment used to load templates.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.jinja_loader">
+<tt class="descname">jinja_loader</tt><a class="headerlink" href="#flask.Flask.jinja_loader" title="Permalink to this definition">¶</a></dt>
+<dd><p>The Jinja loader for this package bound object.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.jinja_options">
+<tt class="descname">jinja_options</tt><em class="property"> = ImmutableDict({'extensions': ['jinja2.ext.autoescape', 'jinja2.ext.with_']})</em><a class="headerlink" href="#flask.Flask.jinja_options" title="Permalink to this definition">¶</a></dt>
+<dd><p>Options that are passed directly to the Jinja2 environment.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.log_exception">
+<tt class="descname">log_exception</tt><big>(</big><em>exc_info</em><big>)</big><a class="headerlink" href="#flask.Flask.log_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>Logs an exception. This is called by <a class="reference internal" href="#flask.Flask.handle_exception" title="flask.Flask.handle_exception"><tt class="xref py py-meth docutils literal"><span class="pre">handle_exception()</span></tt></a>
+if debugging is disabled and right before the handler is called.
+The default implementation logs the exception as error on the
+<a class="reference internal" href="#flask.Flask.logger" title="flask.Flask.logger"><tt class="xref py py-attr docutils literal"><span class="pre">logger</span></tt></a>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.logger">
+<tt class="descname">logger</tt><a class="headerlink" href="#flask.Flask.logger" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.Logger" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">logging.Logger</span></tt></a> 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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&#39;A value for debugging&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s">&#39;A warning ocurred (</span><span class="si">%d</span><span class="s"> apples)&#39;</span><span class="p">,</span> <span class="mi">42</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s">&#39;An error occoured&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.3.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.logger_name">
+<tt class="descname">logger_name</tt><a class="headerlink" href="#flask.Flask.logger_name" title="Permalink to this definition">¶</a></dt>
+<dd><p>The name of the logger to use. By default the logger name is the
+package name passed to the constructor.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.4.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.make_config">
+<tt class="descname">make_config</tt><big>(</big><em>instance_relative=False</em><big>)</big><a class="headerlink" href="#flask.Flask.make_config" title="Permalink to this definition">¶</a></dt>
+<dd><p>Used to create the config attribute by the Flask constructor.
+The <cite>instance_relative</cite> parameter is passed in from the constructor
+of Flask (there named <cite>instance_relative_config</cite>) and indicates if
+the config should be relative to the instance path or the root path
+of the application.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.make_default_options_response">
+<tt class="descname">make_default_options_response</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.make_default_options_response" title="Permalink to this definition">¶</a></dt>
+<dd><p>This method is called to create the default <cite>OPTIONS</cite> response.
+This can be changed through subclassing to change the default
+behaviour of <cite>OPTIONS</cite> responses.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.make_null_session">
+<tt class="descname">make_null_session</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.make_null_session" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates a new instance of a missing session. Instead of overriding
+this method we recommend replacing the <a class="reference internal" href="#flask.Flask.session_interface" title="flask.Flask.session_interface"><tt class="xref py py-class docutils literal"><span class="pre">session_interface</span></tt></a>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.make_response">
+<tt class="descname">make_response</tt><big>(</big><em>rv</em><big>)</big><a class="headerlink" href="#flask.Flask.make_response" title="Permalink to this definition">¶</a></dt>
+<dd><p>Converts the return value from a view function to a real
+response object that is an instance of <a class="reference internal" href="#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-attr docutils literal"><span class="pre">response_class</span></tt></a>.</p>
+<p>The following types are allowed for <cite>rv</cite>:</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="35%" />
+<col width="65%" />
+</colgroup>
+<tbody valign="top">
+<tr class="row-odd"><td><a class="reference internal" href="#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-attr docutils literal"><span class="pre">response_class</span></tt></a></td>
+<td>the object is returned unchanged</td>
+</tr>
+<tr class="row-even"><td><tt class="xref py py-class docutils literal"><span class="pre">str</span></tt></td>
+<td>a response object is created with the
+string as body</td>
+</tr>
+<tr class="row-odd"><td><tt class="xref py py-class docutils literal"><span class="pre">unicode</span></tt></td>
+<td>a response object is created with the
+string encoded to utf-8 as body</td>
+</tr>
+<tr class="row-even"><td><tt class="xref py py-class docutils literal"><span class="pre">tuple</span></tt></td>
+<td>the response object is created with the
+contents of the tuple as arguments</td>
+</tr>
+<tr class="row-odd"><td>a WSGI function</td>
+<td>the function is called as WSGI application
+and buffered as response object</td>
+</tr>
+</tbody>
+</table>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>rv</strong> &#8211; the return value from the view function</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.name">
+<tt class="descname">name</tt><a class="headerlink" href="#flask.Flask.name" title="Permalink to this definition">¶</a></dt>
+<dd><p>The name of the application. This is usually the import name
+with the difference that it&#8217;s guessed from the run file if the
+import name is main. This name is used as a display name when
+Flask needs the name of the application. It can be set and overriden
+to change the value.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.open_instance_resource">
+<tt class="descname">open_instance_resource</tt><big>(</big><em>resource</em>, <em>mode='rb'</em><big>)</big><a class="headerlink" href="#flask.Flask.open_instance_resource" title="Permalink to this definition">¶</a></dt>
+<dd><p>Opens a resource from the application&#8217;s instance folder
+(<a class="reference internal" href="#flask.Flask.instance_path" title="flask.Flask.instance_path"><tt class="xref py py-attr docutils literal"><span class="pre">instance_path</span></tt></a>). Otherwise works like
+<a class="reference internal" href="#flask.Flask.open_resource" title="flask.Flask.open_resource"><tt class="xref py py-meth docutils literal"><span class="pre">open_resource()</span></tt></a>. Instance resources can also be opened for
+writing.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>resource</strong> &#8211; the name of the resource. To access resources within
+subfolders use forward slashes as separator.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.open_resource">
+<tt class="descname">open_resource</tt><big>(</big><em>resource</em>, <em>mode='rb'</em><big>)</big><a class="headerlink" href="#flask.Flask.open_resource" title="Permalink to this definition">¶</a></dt>
+<dd><p>Opens a resource from the application&#8217;s resource folder. To see
+how this works, consider the following folder structure:</p>
+<div class="highlight-python"><pre>/myapplication.py
+/schema.sql
+/static
+ /style.css
+/templates
+ /layout.html
+ /index.html</pre>
+</div>
+<p>If you want to open the <cite>schema.sql</cite> file you would do the
+following:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">open_resource</span><span class="p">(</span><span class="s">&#39;schema.sql&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">contents</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="n">do_something_with</span><span class="p">(</span><span class="n">contents</span><span class="p">)</span>
+</pre></div>
+</div>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>resource</strong> &#8211; the name of the resource. To access resources within
+subfolders use forward slashes as separator.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.open_session">
+<tt class="descname">open_session</tt><big>(</big><em>request</em><big>)</big><a class="headerlink" href="#flask.Flask.open_session" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates or opens a new session. Default implementation stores all
+session data in a signed cookie. This requires that the
+<a class="reference internal" href="#flask.Flask.secret_key" title="flask.Flask.secret_key"><tt class="xref py py-attr docutils literal"><span class="pre">secret_key</span></tt></a> is set. Instead of overriding this method
+we recommend replacing the <a class="reference internal" href="#flask.Flask.session_interface" title="flask.Flask.session_interface"><tt class="xref py py-class docutils literal"><span class="pre">session_interface</span></tt></a>.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>request</strong> &#8211; an instance of <a class="reference internal" href="#flask.Flask.request_class" title="flask.Flask.request_class"><tt class="xref py py-attr docutils literal"><span class="pre">request_class</span></tt></a>.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.permanent_session_lifetime">
+<tt class="descname">permanent_session_lifetime</tt><a class="headerlink" href="#flask.Flask.permanent_session_lifetime" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <a class="reference external" href="http://docs.python.org/dev/library/datetime.html#datetime.timedelta" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">timedelta</span></tt></a> 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.</p>
+<p>This attribute can also be configured from the config with the
+<cite>PERMANENT_SESSION_LIFETIME</cite> configuration key. Defaults to
+<tt class="docutils literal"><span class="pre">timedelta(days=31)</span></tt></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.preprocess_request">
+<tt class="descname">preprocess_request</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Flask.preprocess_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Called before the actual request dispatching and will
+call every as <a class="reference internal" href="#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> decorated function.
+If any of these function returns a value it&#8217;s handled as
+if it was the return value from the view and further
+request handling is stopped.</p>
+<p>This also triggers the <tt class="xref py py-meth docutils literal"><span class="pre">url_value_processor()</span></tt> functions before
+the actualy <a class="reference internal" href="#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> functions are called.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.preserve_context_on_exception">
+<tt class="descname">preserve_context_on_exception</tt><a class="headerlink" href="#flask.Flask.preserve_context_on_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>Returns the value of the <cite>PRESERVE_CONTEXT_ON_EXCEPTION</cite>
+configuration value in case it&#8217;s set, otherwise a sensible default
+is returned.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.process_response">
+<tt class="descname">process_response</tt><big>(</big><em>response</em><big>)</big><a class="headerlink" href="#flask.Flask.process_response" title="Permalink to this definition">¶</a></dt>
+<dd><p>Can be overridden in order to modify the response object
+before it&#8217;s sent to the WSGI server. By default this will
+call all the <a class="reference internal" href="#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> decorated functions.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.5: </span>As of Flask 0.5 the functions registered for after request
+execution are called in reverse order of registration.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>response</strong> &#8211; a <a class="reference internal" href="#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-attr docutils literal"><span class="pre">response_class</span></tt></a> object.</td>
+</tr>
+<tr class="field-even field"><th class="field-name">Returns:</th><td class="field-body">a new response object or the same, has to be an
+instance of <a class="reference internal" href="#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-attr docutils literal"><span class="pre">response_class</span></tt></a>.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.propagate_exceptions">
+<tt class="descname">propagate_exceptions</tt><a class="headerlink" href="#flask.Flask.propagate_exceptions" title="Permalink to this definition">¶</a></dt>
+<dd><p>Returns the value of the <cite>PROPAGATE_EXCEPTIONS</cite> configuration
+value in case it&#8217;s set, otherwise a sensible default is returned.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.register_blueprint">
+<tt class="descname">register_blueprint</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.register_blueprint" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a blueprint on the application.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.register_error_handler">
+<tt class="descname">register_error_handler</tt><big>(</big><em>code_or_exception</em>, <em>f</em><big>)</big><a class="headerlink" href="#flask.Flask.register_error_handler" title="Permalink to this definition">¶</a></dt>
+<dd><p>Alternative error attach function to the <a class="reference internal" href="#flask.Flask.errorhandler" title="flask.Flask.errorhandler"><tt class="xref py py-meth docutils literal"><span class="pre">errorhandler()</span></tt></a>
+decorator that is more straightforward to use for non decorator
+usage.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.register_module">
+<tt class="descname">register_module</tt><big>(</big><em>module</em>, <em>**options</em><big>)</big><a class="headerlink" href="#flask.Flask.register_module" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a module with this application. The keyword argument
+of this function are the same as the ones for the constructor of the
+<tt class="xref py py-class docutils literal"><span class="pre">Module</span></tt> class and will override the values of the module if
+provided.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.7: </span>The module system was deprecated in favor for the blueprint
+system.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.request_class">
+<tt class="descname">request_class</tt><a class="headerlink" href="#flask.Flask.request_class" title="Permalink to this definition">¶</a></dt>
+<dd><p>The class that is used for request objects. See <a class="reference internal" href="#flask.Request" title="flask.Request"><tt class="xref py py-class docutils literal"><span class="pre">Request</span></tt></a>
+for more information.</p>
+<p>alias of <a class="reference internal" href="#flask.Request" title="flask.Request"><tt class="xref py py-class docutils literal"><span class="pre">Request</span></tt></a></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.request_context">
+<tt class="descname">request_context</tt><big>(</big><em>environ</em><big>)</big><a class="headerlink" href="#flask.Flask.request_context" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates a <a class="reference internal" href="#flask.ctx.RequestContext" title="flask.ctx.RequestContext"><tt class="xref py py-class docutils literal"><span class="pre">RequestContext</span></tt></a> from the given
+environment and binds it to the current context. This must be used in
+combination with the <cite>with</cite> statement because the request is only bound
+to the current context for the duration of the <cite>with</cite> block.</p>
+<p>Example usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">request_context</span><span class="p">(</span><span class="n">environ</span><span class="p">):</span>
+ <span class="n">do_something_with</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The object returned can also be used without the <cite>with</cite> statement
+which is useful for working in the shell. The example above is
+doing exactly the same as this code:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">ctx</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">request_context</span><span class="p">(</span><span class="n">environ</span><span class="p">)</span>
+<span class="n">ctx</span><span class="o">.</span><span class="n">push</span><span class="p">()</span>
+<span class="k">try</span><span class="p">:</span>
+ <span class="n">do_something_with</span><span class="p">(</span><span class="n">request</span><span class="p">)</span>
+<span class="k">finally</span><span class="p">:</span>
+ <span class="n">ctx</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+</pre></div>
+</div>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.3: </span>Added support for non-with statement usage and <cite>with</cite> statement
+is now passed the ctx object.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>environ</strong> &#8211; a WSGI environment</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.response_class">
+<tt class="descname">response_class</tt><a class="headerlink" href="#flask.Flask.response_class" title="Permalink to this definition">¶</a></dt>
+<dd><p>The class that is used for response objects. See
+<a class="reference internal" href="#flask.Response" title="flask.Response"><tt class="xref py py-class docutils literal"><span class="pre">Response</span></tt></a> for more information.</p>
+<p>alias of <a class="reference internal" href="#flask.Response" title="flask.Response"><tt class="xref py py-class docutils literal"><span class="pre">Response</span></tt></a></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.route">
+<tt class="descname">route</tt><big>(</big><em>rule</em>, <em>**options</em><big>)</big><a class="headerlink" href="#flask.Flask.route" title="Permalink to this definition">¶</a></dt>
+<dd><p>A decorator that is used to register a view function for a
+given URL rule. This does the same thing as <a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a>
+but is intended for decorator usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&#39;Hello World&#39;</span>
+</pre></div>
+</div>
+<p>For more information refer to <a class="reference internal" href="#url-route-registrations"><em>URL Route Registrations</em></a>.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>rule</strong> &#8211; the URL rule as string</li>
+<li><strong>endpoint</strong> &#8211; the endpoint for the registered URL rule. Flask
+itself assumes the name of the view function as
+endpoint</li>
+<li><strong>view_func</strong> &#8211; the function to call when serving a request to the
+provided endpoint</li>
+<li><strong>options</strong> &#8211; the options to be forwarded to the underlying
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/routing/#werkzeug.routing.Rule" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Rule</span></tt></a> object. A change
+to Werkzeug is handling of method options. methods
+is a list of methods this rule should be limited
+to (<cite>GET</cite>, <cite>POST</cite> etc.). By default a rule
+just listens for <cite>GET</cite> (and implicitly <cite>HEAD</cite>).
+Starting with Flask 0.6, <cite>OPTIONS</cite> is implicitly
+added and handled by the standard request handling.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.run">
+<tt class="descname">run</tt><big>(</big><em>host='127.0.0.1'</em>, <em>port=5000</em>, <em>debug=None</em>, <em>**options</em><big>)</big><a class="headerlink" href="#flask.Flask.run" title="Permalink to this definition">¶</a></dt>
+<dd><p>Runs the application on a local development server. If the
+<a class="reference internal" href="#flask.Flask.debug" title="flask.Flask.debug"><tt class="xref py py-attr docutils literal"><span class="pre">debug</span></tt></a> flag is set the server will automatically reload
+for code changes and show a debugger in case an exception happened.</p>
+<p>If you want to run the application in debug mode, but disable the
+code execution on the interactive debugger, you can pass
+<tt class="docutils literal"><span class="pre">use_evalex=False</span></tt> as parameter. This will keep the debugger&#8217;s
+traceback screen active, but disable code execution.</p>
+<div class="admonition-keep-in-mind admonition ">
+<p class="first admonition-title">Keep in Mind</p>
+<p class="last">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 <a class="reference internal" href="#flask.Flask.run" title="flask.Flask.run"><tt class="xref py py-meth docutils literal"><span class="pre">run()</span></tt></a> with <tt class="docutils literal"><span class="pre">debug=True</span></tt> and <tt class="docutils literal"><span class="pre">use_reloader=False</span></tt>.
+Setting <tt class="docutils literal"><span class="pre">use_debugger</span></tt> to <cite>True</cite> without being in debug mode
+won&#8217;t catch any exceptions because there won&#8217;t be any to
+catch.</p>
+</div>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>host</strong> &#8211; the hostname to listen on. set this to <tt class="docutils literal"><span class="pre">'0.0.0.0'</span></tt>
+to have the server available externally as well.</li>
+<li><strong>port</strong> &#8211; the port of the webserver</li>
+<li><strong>debug</strong> &#8211; if given, enable or disable debug mode.
+See <a class="reference internal" href="#flask.Flask.debug" title="flask.Flask.debug"><tt class="xref py py-attr docutils literal"><span class="pre">debug</span></tt></a>.</li>
+<li><strong>options</strong> &#8211; the options to be forwarded to the underlying
+Werkzeug server. See
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/serving/#werkzeug.serving.run_simple" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">werkzeug.serving.run_simple()</span></tt></a> for more
+information.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.save_session">
+<tt class="descname">save_session</tt><big>(</big><em>session</em>, <em>response</em><big>)</big><a class="headerlink" href="#flask.Flask.save_session" title="Permalink to this definition">¶</a></dt>
+<dd><p>Saves the session if it needs updates. For the default
+implementation, check <a class="reference internal" href="#flask.Flask.open_session" title="flask.Flask.open_session"><tt class="xref py py-meth docutils literal"><span class="pre">open_session()</span></tt></a>. Instead of overriding this
+method we recommend replacing the <a class="reference internal" href="#flask.Flask.session_interface" title="flask.Flask.session_interface"><tt class="xref py py-class docutils literal"><span class="pre">session_interface</span></tt></a>.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>session</strong> &#8211; the session to be saved (a
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/contrib/securecookie/#werkzeug.contrib.securecookie.SecureCookie" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">SecureCookie</span></tt></a>
+object)</li>
+<li><strong>response</strong> &#8211; an instance of <a class="reference internal" href="#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-attr docutils literal"><span class="pre">response_class</span></tt></a></li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.secret_key">
+<tt class="descname">secret_key</tt><a class="headerlink" href="#flask.Flask.secret_key" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+<p>This attribute can also be configured from the config with the
+<cite>SECRET_KEY</cite> configuration key. Defaults to <cite>None</cite>.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.select_jinja_autoescape">
+<tt class="descname">select_jinja_autoescape</tt><big>(</big><em>filename</em><big>)</big><a class="headerlink" href="#flask.Flask.select_jinja_autoescape" title="Permalink to this definition">¶</a></dt>
+<dd><p>Returns <cite>True</cite> if autoescaping should be active for the given
+template name.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.send_static_file">
+<tt class="descname">send_static_file</tt><big>(</big><em>filename</em><big>)</big><a class="headerlink" href="#flask.Flask.send_static_file" title="Permalink to this definition">¶</a></dt>
+<dd><p>Function used internally to send static files from the static
+folder to the browser.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.session_cookie_name">
+<tt class="descname">session_cookie_name</tt><a class="headerlink" href="#flask.Flask.session_cookie_name" title="Permalink to this definition">¶</a></dt>
+<dd><p>The secure cookie uses this for the name of the session cookie.</p>
+<p>This attribute can also be configured from the config with the
+<cite>SESSION_COOKIE_NAME</cite> configuration key. Defaults to <tt class="docutils literal"><span class="pre">'session'</span></tt></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.session_interface">
+<tt class="descname">session_interface</tt><em class="property"> = &lt;flask.sessions.SecureCookieSessionInterface object at 0x103dc8090&gt;</em><a class="headerlink" href="#flask.Flask.session_interface" title="Permalink to this definition">¶</a></dt>
+<dd><p>the session interface to use. By default an instance of
+<a class="reference internal" href="#flask.sessions.SecureCookieSessionInterface" title="flask.sessions.SecureCookieSessionInterface"><tt class="xref py py-class docutils literal"><span class="pre">SecureCookieSessionInterface</span></tt></a> is used here.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.teardown_request">
+<tt class="descname">teardown_request</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.teardown_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Register a function to be run at the end of each request,
+regardless of whether there was an exception or not. These functions
+are executed when the request context is popped, even if not an
+actual request was performed.</p>
+<p>Example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">ctx</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">()</span>
+<span class="n">ctx</span><span class="o">.</span><span class="n">push</span><span class="p">()</span>
+<span class="o">...</span>
+<span class="n">ctx</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>When <tt class="docutils literal"><span class="pre">ctx.pop()</span></tt> is executed in the above example, the teardown
+functions are called just before the request context moves from the
+stack of active contexts. This becomes relevant if you are using
+such constructs in tests.</p>
+<p>Generally teardown functions must take every necesary step to avoid
+that they will fail. If they do execute code that might fail they
+will have to surround the execution of these code by try/except
+statements and log ocurring errors.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.teardown_request_funcs">
+<tt class="descname">teardown_request_funcs</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.teardown_request_funcs" title="Permalink to this definition">¶</a></dt>
+<dd><p>A dictionary with lists of functions that are called after
+each request, even if an exception has occurred. The key of the
+dictionary is the name of the blueprint this function is active for,
+<cite>None</cite> for all requests. These functions are not allowed to modify
+the request, and their return values are ignored. If an exception
+occurred while processing the request, it gets passed to each
+teardown_request function. To register a function here, use the
+<a class="reference internal" href="#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> decorator.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.template_context_processors">
+<tt class="descname">template_context_processors</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.template_context_processors" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 blueprint this function is active for, <cite>None</cite> for all
+requests. Each returns a dictionary that the template context is
+updated with. To register a function here, use the
+<a class="reference internal" href="#flask.Flask.context_processor" title="flask.Flask.context_processor"><tt class="xref py py-meth docutils literal"><span class="pre">context_processor()</span></tt></a> decorator.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.template_filter">
+<tt class="descname">template_filter</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.template_filter" title="Permalink to this definition">¶</a></dt>
+<dd><p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.template_filter</span><span class="p">()</span>
+<span class="k">def</span> <span class="nf">reverse</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">s</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
+</pre></div>
+</div>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>name</strong> &#8211; the optional name of the filter, otherwise the
+function name will be used.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.test_client">
+<tt class="descname">test_client</tt><big>(</big><em>use_cookies=True</em><big>)</big><a class="headerlink" href="#flask.Flask.test_client" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates a test client for this application. For information
+about unit testing head over to <a class="reference internal" href="testing.html#testing"><em>Testing Flask Applications</em></a>.</p>
+<p>The test client can be used in a <cite>with</cite> block to defer the closing down
+of the context until the end of the <cite>with</cite> block. This is useful if
+you want to access the context locals for testing:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span> <span class="k">as</span> <span class="n">c</span><span class="p">:</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/?vodka=42&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="s">&#39;vodka&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s">&#39;42&#39;</span>
+</pre></div>
+</div>
+<p>See <a class="reference internal" href="#flask.testing.FlaskClient" title="flask.testing.FlaskClient"><tt class="xref py py-class docutils literal"><span class="pre">FlaskClient</span></tt></a> for more information.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.4: </span>added support for <cite>with</cite> block usage for the client.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7: </span>The <cite>use_cookies</cite> parameter was added as well as the ability
+to override the client to be used by setting the
+<a class="reference internal" href="#flask.Flask.test_client_class" title="flask.Flask.test_client_class"><tt class="xref py py-attr docutils literal"><span class="pre">test_client_class</span></tt></a> attribute.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.test_client_class">
+<tt class="descname">test_client_class</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.test_client_class" title="Permalink to this definition">¶</a></dt>
+<dd><p>the test client that is used with when <cite>test_client</cite> is used.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.test_request_context">
+<tt class="descname">test_request_context</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.test_request_context" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates a WSGI environment from the given values (see
+<tt class="xref py py-func docutils literal"><span class="pre">werkzeug.test.EnvironBuilder()</span></tt> for more information, this
+function accepts the same arguments).</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.testing">
+<tt class="descname">testing</tt><a class="headerlink" href="#flask.Flask.testing" title="Permalink to this definition">¶</a></dt>
+<dd><p>The testing flag. Set this to <cite>True</cite> 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.</p>
+<p>If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the
+default it&#8217;s implicitly enabled.</p>
+<p>This attribute can also be configured from the config with the
+<cite>TESTING</cite> configuration key. Defaults to <cite>False</cite>.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.trap_http_exception">
+<tt class="descname">trap_http_exception</tt><big>(</big><em>e</em><big>)</big><a class="headerlink" href="#flask.Flask.trap_http_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>Checks if an HTTP exception should be trapped or not. By default
+this will return <cite>False</cite> for all exceptions except for a bad request
+key error if <tt class="docutils literal"><span class="pre">TRAP_BAD_REQUEST_ERRORS</span></tt> is set to <cite>True</cite>. It
+also returns <cite>True</cite> if <tt class="docutils literal"><span class="pre">TRAP_HTTP_EXCEPTIONS</span></tt> is set to <cite>True</cite>.</p>
+<p>This is called for all HTTP exceptions raised by a view function.
+If it returns <cite>True</cite> for any exception the error handler for this
+exception is not called and it shows up as regular exception in the
+traceback. This is helpful for debugging implicitly raised HTTP
+exceptions.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.update_template_context">
+<tt class="descname">update_template_context</tt><big>(</big><em>context</em><big>)</big><a class="headerlink" href="#flask.Flask.update_template_context" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>context</strong> &#8211; the context as a dictionary that is updated in place
+to add extra variables.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.url_default_functions">
+<tt class="descname">url_default_functions</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.url_default_functions" title="Permalink to this definition">¶</a></dt>
+<dd><p>A dictionary with lists of functions that can be used as URL value
+preprocessors. The key <cite>None</cite> here is used for application wide
+callbacks, otherwise the key is the name of the blueprint.
+Each of these functions has the chance to modify the dictionary
+of URL values before they are used as the keyword arguments of the
+view function. For each function registered this one should also
+provide a <a class="reference internal" href="#flask.Flask.url_defaults" title="flask.Flask.url_defaults"><tt class="xref py py-meth docutils literal"><span class="pre">url_defaults()</span></tt></a> function that adds the parameters
+automatically again that were removed that way.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.url_defaults">
+<tt class="descname">url_defaults</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.url_defaults" title="Permalink to this definition">¶</a></dt>
+<dd><p>Callback function for URL defaults for all view functions of the
+application. It&#8217;s called with the endpoint and values and should
+update the values passed in place.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.url_map">
+<tt class="descname">url_map</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.url_map" title="Permalink to this definition">¶</a></dt>
+<dd><p>The <a class="reference external" href="http://werkzeug.pocoo.org/docs/routing/#werkzeug.routing.Map" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Map</span></tt></a> for this instance. You can use
+this to change the routing converters after the class was created
+but before any routes are connected. Example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.routing</span> <span class="kn">import</span> <span class="n">BaseConverter</span>
+
+<span class="k">class</span> <span class="nc">ListConverter</span><span class="p">(</span><span class="n">BaseConverter</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">to_python</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;,&#39;</span><span class="p">)</span>
+ <span class="k">def</span> <span class="nf">to_url</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;,&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">BaseConverter</span><span class="o">.</span><span class="n">to_url</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
+ <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">)</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">url_map</span><span class="o">.</span><span class="n">converters</span><span class="p">[</span><span class="s">&#39;list&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">ListConverter</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.url_rule_class">
+<tt class="descname">url_rule_class</tt><a class="headerlink" href="#flask.Flask.url_rule_class" title="Permalink to this definition">¶</a></dt>
+<dd><p>The rule object to use for URL rules created. This is used by
+<a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a>. Defaults to <a class="reference external" href="http://werkzeug.pocoo.org/docs/routing/#werkzeug.routing.Rule" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">werkzeug.routing.Rule</span></tt></a>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+<p>alias of <tt class="xref py py-class docutils literal"><span class="pre">Rule</span></tt></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.url_value_preprocessor">
+<tt class="descname">url_value_preprocessor</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.Flask.url_value_preprocessor" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a function as URL value preprocessor for all view
+functions of the application. It&#8217;s called before the view functions
+are called and can modify the url values provided.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.url_value_preprocessors">
+<tt class="descname">url_value_preprocessors</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.url_value_preprocessors" title="Permalink to this definition">¶</a></dt>
+<dd><p>A dictionary with lists of functions that can be used as URL
+value processor functions. Whenever a URL is built these functions
+are called to modify the dictionary of values in place. The key
+<cite>None</cite> here is used for application wide
+callbacks, otherwise the key is the name of the blueprint.
+Each of these functions has the chance to modify the dictionary</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.use_x_sendfile">
+<tt class="descname">use_x_sendfile</tt><a class="headerlink" href="#flask.Flask.use_x_sendfile" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference internal" href="#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">send_file()</span></tt></a> method.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.2.</span></p>
+<p>This attribute can also be configured from the config with the
+<cite>USE_X_SENDFILE</cite> configuration key. Defaults to <cite>False</cite>.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Flask.view_functions">
+<tt class="descname">view_functions</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Flask.view_functions" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference internal" href="#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a> decorator.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Flask.wsgi_app">
+<tt class="descname">wsgi_app</tt><big>(</big><em>environ</em>, <em>start_response</em><big>)</big><a class="headerlink" href="#flask.Flask.wsgi_app" title="Permalink to this definition">¶</a></dt>
+<dd><p>The actual WSGI application. This is not implemented in
+<cite>__call__</cite> so that middlewares can be applied without losing a
+reference to the class. So instead of doing this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">MyMiddleware</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>It&#8217;s a better idea to do this instead:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span> <span class="o">=</span> <span class="n">MyMiddleware</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Then you still have the original application object around and
+can continue to call methods on it.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.7: </span>The behavior of the before and after request callbacks was changed
+under error conditions and a new callback was added that will
+always execute at the end of the request, independent on if an
+error ocurred or not. See <a class="reference internal" href="reqcontext.html#callbacks-and-errors"><em>Callbacks and Errors</em></a>.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>environ</strong> &#8211; a WSGI environment</li>
+<li><strong>start_response</strong> &#8211; a callable accepting a status code,
+a list of headers and an optional
+exception context to start the response</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="blueprint-objects">
+<h2>Blueprint Objects<a class="headerlink" href="#blueprint-objects" title="Permalink to this headline">¶</a></h2>
+<dl class="class">
+<dt id="flask.Blueprint">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">Blueprint</tt><big>(</big><em>name</em>, <em>import_name</em>, <em>static_folder=None</em>, <em>static_url_path=None</em>, <em>template_folder=None</em>, <em>url_prefix=None</em>, <em>subdomain=None</em>, <em>url_defaults=None</em><big>)</big><a class="headerlink" href="#flask.Blueprint" title="Permalink to this definition">¶</a></dt>
+<dd><p>Represents a blueprint. A blueprint is an object that records
+functions that will be called with the
+<tt class="xref py py-class docutils literal"><span class="pre">BlueprintSetupState</span></tt> later to register functions
+or other things on the main application. See <a class="reference internal" href="blueprints.html#blueprints"><em>Modular Applications with Blueprints</em></a> for more
+information.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+<dl class="method">
+<dt id="flask.Blueprint.add_url_rule">
+<tt class="descname">add_url_rule</tt><big>(</big><em>rule</em>, <em>endpoint=None</em>, <em>view_func=None</em>, <em>**options</em><big>)</big><a class="headerlink" href="#flask.Blueprint.add_url_rule" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.add_url_rule()</span></tt></a> but for a blueprint. The endpoint for
+the <a class="reference internal" href="#flask.url_for" title="flask.url_for"><tt class="xref py py-func docutils literal"><span class="pre">url_for()</span></tt></a> function is prefixed with the name of the blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.after_app_request">
+<tt class="descname">after_app_request</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.after_app_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.after_request()</span></tt></a> but for a blueprint. Such a function
+is executed after each request, even if outside of the blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.after_request">
+<tt class="descname">after_request</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.after_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.after_request()</span></tt></a> but for a blueprint. This function
+is only executed after each request that is handled by a function of
+that blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.app_context_processor">
+<tt class="descname">app_context_processor</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.app_context_processor" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.context_processor" title="flask.Flask.context_processor"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.context_processor()</span></tt></a> but for a blueprint. Such a
+function is executed each request, even if outside of the blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.app_errorhandler">
+<tt class="descname">app_errorhandler</tt><big>(</big><em>code</em><big>)</big><a class="headerlink" href="#flask.Blueprint.app_errorhandler" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.errorhandler" title="flask.Flask.errorhandler"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.errorhandler()</span></tt></a> but for a blueprint. This
+handler is used for all requests, even if outside of the blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.app_url_defaults">
+<tt class="descname">app_url_defaults</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.app_url_defaults" title="Permalink to this definition">¶</a></dt>
+<dd><p>Same as <a class="reference internal" href="#flask.Blueprint.url_defaults" title="flask.Blueprint.url_defaults"><tt class="xref py py-meth docutils literal"><span class="pre">url_defaults()</span></tt></a> but application wide.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.app_url_value_preprocessor">
+<tt class="descname">app_url_value_preprocessor</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.app_url_value_preprocessor" title="Permalink to this definition">¶</a></dt>
+<dd><p>Same as <a class="reference internal" href="#flask.Blueprint.url_value_preprocessor" title="flask.Blueprint.url_value_preprocessor"><tt class="xref py py-meth docutils literal"><span class="pre">url_value_preprocessor()</span></tt></a> but application wide.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.before_app_first_request">
+<tt class="descname">before_app_first_request</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.before_app_first_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.before_first_request" title="flask.Flask.before_first_request"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.before_first_request()</span></tt></a>. Such a function is
+executed before the first request to the application.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.before_app_request">
+<tt class="descname">before_app_request</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.before_app_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.before_request()</span></tt></a>. Such a function is executed
+before each request, even if outside of a blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.before_request">
+<tt class="descname">before_request</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.before_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.before_request()</span></tt></a> but for a blueprint. This function
+is only executed before each request that is handled by a function of
+that blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.context_processor">
+<tt class="descname">context_processor</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.context_processor" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.context_processor" title="flask.Flask.context_processor"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.context_processor()</span></tt></a> but for a blueprint. This
+function is only executed for requests handled by a blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.endpoint">
+<tt class="descname">endpoint</tt><big>(</big><em>endpoint</em><big>)</big><a class="headerlink" href="#flask.Blueprint.endpoint" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.endpoint" title="flask.Flask.endpoint"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.endpoint()</span></tt></a> but for a blueprint. This does not
+prefix the endpoint with the blueprint name, this has to be done
+explicitly by the user of this method. If the endpoint is prefixed
+with a <cite>.</cite> it will be registered to the current blueprint, otherwise
+it&#8217;s an application independent endpoint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.errorhandler">
+<tt class="descname">errorhandler</tt><big>(</big><em>code_or_exception</em><big>)</big><a class="headerlink" href="#flask.Blueprint.errorhandler" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers an error handler that becomes active for this blueprint
+only. Please be aware that routing does not happen local to a
+blueprint so an error handler for 404 usually is not handled by
+a blueprint unless it is caused inside a view function. Another
+special case is the 500 internal server error which is always looked
+up from the application.</p>
+<p>Otherwise works as the <a class="reference internal" href="#flask.Flask.errorhandler" title="flask.Flask.errorhandler"><tt class="xref py py-meth docutils literal"><span class="pre">errorhandler()</span></tt></a> decorator
+of the <a class="reference internal" href="#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> object.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Blueprint.has_static_folder">
+<tt class="descname">has_static_folder</tt><a class="headerlink" href="#flask.Blueprint.has_static_folder" title="Permalink to this definition">¶</a></dt>
+<dd><p>This is <cite>True</cite> if the package bound object&#8217;s container has a
+folder named <tt class="docutils literal"><span class="pre">'static'</span></tt>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Blueprint.jinja_loader">
+<tt class="descname">jinja_loader</tt><a class="headerlink" href="#flask.Blueprint.jinja_loader" title="Permalink to this definition">¶</a></dt>
+<dd><p>The Jinja loader for this package bound object.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.make_setup_state">
+<tt class="descname">make_setup_state</tt><big>(</big><em>app</em>, <em>options</em>, <em>first_registration=False</em><big>)</big><a class="headerlink" href="#flask.Blueprint.make_setup_state" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates an instance of <a class="reference internal" href="#flask.blueprints.BlueprintSetupState" title="flask.blueprints.BlueprintSetupState"><tt class="xref py py-meth docutils literal"><span class="pre">BlueprintSetupState()</span></tt></a>
+object that is later passed to the register callback functions.
+Subclasses can override this to return a subclass of the setup state.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.open_resource">
+<tt class="descname">open_resource</tt><big>(</big><em>resource</em>, <em>mode='rb'</em><big>)</big><a class="headerlink" href="#flask.Blueprint.open_resource" title="Permalink to this definition">¶</a></dt>
+<dd><p>Opens a resource from the application&#8217;s resource folder. To see
+how this works, consider the following folder structure:</p>
+<div class="highlight-python"><pre>/myapplication.py
+/schema.sql
+/static
+ /style.css
+/templates
+ /layout.html
+ /index.html</pre>
+</div>
+<p>If you want to open the <cite>schema.sql</cite> file you would do the
+following:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">open_resource</span><span class="p">(</span><span class="s">&#39;schema.sql&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">contents</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+ <span class="n">do_something_with</span><span class="p">(</span><span class="n">contents</span><span class="p">)</span>
+</pre></div>
+</div>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>resource</strong> &#8211; the name of the resource. To access resources within
+subfolders use forward slashes as separator.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.record">
+<tt class="descname">record</tt><big>(</big><em>func</em><big>)</big><a class="headerlink" href="#flask.Blueprint.record" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a function that is called when the blueprint is
+registered on the application. This function is called with the
+state as argument as returned by the <a class="reference internal" href="#flask.Blueprint.make_setup_state" title="flask.Blueprint.make_setup_state"><tt class="xref py py-meth docutils literal"><span class="pre">make_setup_state()</span></tt></a>
+method.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.record_once">
+<tt class="descname">record_once</tt><big>(</big><em>func</em><big>)</big><a class="headerlink" href="#flask.Blueprint.record_once" title="Permalink to this definition">¶</a></dt>
+<dd><p>Works like <a class="reference internal" href="#flask.Blueprint.record" title="flask.Blueprint.record"><tt class="xref py py-meth docutils literal"><span class="pre">record()</span></tt></a> but wraps the function in another
+function that will ensure the function is only called once. If the
+blueprint is registered a second time on the application, the
+function passed is not called.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.register">
+<tt class="descname">register</tt><big>(</big><em>app</em>, <em>options</em>, <em>first_registration=False</em><big>)</big><a class="headerlink" href="#flask.Blueprint.register" title="Permalink to this definition">¶</a></dt>
+<dd><p>Called by <a class="reference internal" href="#flask.Flask.register_blueprint" title="flask.Flask.register_blueprint"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.register_blueprint()</span></tt></a> to register a blueprint
+on the application. This can be overridden to customize the register
+behavior. Keyword arguments from
+<a class="reference internal" href="#flask.Flask.register_blueprint" title="flask.Flask.register_blueprint"><tt class="xref py py-func docutils literal"><span class="pre">register_blueprint()</span></tt></a> are directly forwarded to this
+method in the <cite>options</cite> dictionary.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.route">
+<tt class="descname">route</tt><big>(</big><em>rule</em>, <em>**options</em><big>)</big><a class="headerlink" href="#flask.Blueprint.route" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.route()</span></tt></a> but for a blueprint. The endpoint for the
+<a class="reference internal" href="#flask.url_for" title="flask.url_for"><tt class="xref py py-func docutils literal"><span class="pre">url_for()</span></tt></a> function is prefixed with the name of the blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.send_static_file">
+<tt class="descname">send_static_file</tt><big>(</big><em>filename</em><big>)</big><a class="headerlink" href="#flask.Blueprint.send_static_file" title="Permalink to this definition">¶</a></dt>
+<dd><p>Function used internally to send static files from the static
+folder to the browser.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.teardown_app_request">
+<tt class="descname">teardown_app_request</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.teardown_app_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.teardown_request()</span></tt></a> but for a blueprint. Such a
+function is executed when tearing down each request, even if outside of
+the blueprint.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.teardown_request">
+<tt class="descname">teardown_request</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.teardown_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like <a class="reference internal" href="#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">Flask.teardown_request()</span></tt></a> but for a blueprint. This
+function is only executed when tearing down requests handled by a
+function of that blueprint. Teardown request functions are executed
+when the request context is popped, even when no actual request was
+performed.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.url_defaults">
+<tt class="descname">url_defaults</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.url_defaults" title="Permalink to this definition">¶</a></dt>
+<dd><p>Callback function for URL defaults for this blueprint. It&#8217;s called
+with the endpoint and values and should update the values passed
+in place.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Blueprint.url_value_preprocessor">
+<tt class="descname">url_value_preprocessor</tt><big>(</big><em>f</em><big>)</big><a class="headerlink" href="#flask.Blueprint.url_value_preprocessor" title="Permalink to this definition">¶</a></dt>
+<dd><p>Registers a function as URL value preprocessor for this
+blueprint. It&#8217;s called before the view functions are called and
+can modify the url values provided.</p>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="incoming-request-data">
+<h2>Incoming Request Data<a class="headerlink" href="#incoming-request-data" title="Permalink to this headline">¶</a></h2>
+<dl class="class">
+<dt id="flask.Request">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">Request</tt><big>(</big><em>environ</em>, <em>populate_request=True</em>, <em>shallow=False</em><big>)</big><a class="headerlink" href="#flask.Request" title="Permalink to this definition">¶</a></dt>
+<dd><p>The request object used by default in Flask. Remembers the
+matched endpoint and view arguments.</p>
+<p>It is what ends up as <a class="reference internal" href="#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a>. If you want to replace
+the request object used you can subclass this and set
+<a class="reference internal" href="#flask.Flask.request_class" title="flask.Flask.request_class"><tt class="xref py py-attr docutils literal"><span class="pre">request_class</span></tt></a> to your subclass.</p>
+<p>The request object is a <a class="reference external" href="http://werkzeug.pocoo.org/docs/wrappers/#werkzeug.wrappers.Request" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Request</span></tt></a> subclass and
+provides all of the attributes Werkzeug defines plus a few Flask
+specific ones.</p>
+<dl class="attribute">
+<dt id="flask.Request.form">
+<tt class="descname">form</tt><a class="headerlink" href="#flask.Request.form" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.MultiDict" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">MultiDict</span></tt></a> with the parsed form data from <cite>POST</cite>
+or <cite>PUT</cite> requests. Please keep in mind that file uploads will not
+end up here, but instead in the <a class="reference internal" href="#flask.Request.files" title="flask.Request.files"><tt class="xref py py-attr docutils literal"><span class="pre">files</span></tt></a> attribute.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.args">
+<tt class="descname">args</tt><a class="headerlink" href="#flask.Request.args" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.MultiDict" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">MultiDict</span></tt></a> with the parsed contents of the query
+string. (The part in the URL after the question mark).</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.values">
+<tt class="descname">values</tt><a class="headerlink" href="#flask.Request.values" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.CombinedMultiDict" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">CombinedMultiDict</span></tt></a> with the contents of both
+<a class="reference internal" href="#flask.Request.form" title="flask.Request.form"><tt class="xref py py-attr docutils literal"><span class="pre">form</span></tt></a> and <a class="reference internal" href="#flask.Request.args" title="flask.Request.args"><tt class="xref py py-attr docutils literal"><span class="pre">args</span></tt></a>.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.cookies">
+<tt class="descname">cookies</tt><a class="headerlink" href="#flask.Request.cookies" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <a class="reference external" href="http://docs.python.org/dev/library/stdtypes.html#dict" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">dict</span></tt></a> with the contents of all cookies transmitted with
+the request.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.stream">
+<tt class="descname">stream</tt><a class="headerlink" href="#flask.Request.stream" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference internal" href="#flask.Request.data" title="flask.Request.data"><tt class="xref py py-attr docutils literal"><span class="pre">data</span></tt></a> which will give
+you that data as a string. The stream only returns the data once.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.headers">
+<tt class="descname">headers</tt><a class="headerlink" href="#flask.Request.headers" title="Permalink to this definition">¶</a></dt>
+<dd><p>The incoming request headers as a dictionary like object.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.data">
+<tt class="descname">data</tt><a class="headerlink" href="#flask.Request.data" title="Permalink to this definition">¶</a></dt>
+<dd><p>Contains the incoming request data as string in case it came with
+a mimetype Flask does not handle.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.files">
+<tt class="descname">files</tt><a class="headerlink" href="#flask.Request.files" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.MultiDict" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">MultiDict</span></tt></a> with files uploaded as part of a
+<cite>POST</cite> or <cite>PUT</cite> request. Each file is stored as
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">FileStorage</span></tt></a> object. It basically behaves like a
+standard file object you know from Python, with the difference that
+it also has a <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.save" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">save()</span></tt></a> function that can
+store the file on the filesystem.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.environ">
+<tt class="descname">environ</tt><a class="headerlink" href="#flask.Request.environ" title="Permalink to this definition">¶</a></dt>
+<dd><p>The underlying WSGI environment.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.method">
+<tt class="descname">method</tt><a class="headerlink" href="#flask.Request.method" title="Permalink to this definition">¶</a></dt>
+<dd><p>The current request method (<tt class="docutils literal"><span class="pre">POST</span></tt>, <tt class="docutils literal"><span class="pre">GET</span></tt> etc.)</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.path">
+<tt class="descname">path</tt><a class="headerlink" href="#flask.Request.path" title="Permalink to this definition">¶</a></dt>
+<dd></dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.script_root">
+<tt class="descname">script_root</tt><a class="headerlink" href="#flask.Request.script_root" title="Permalink to this definition">¶</a></dt>
+<dd></dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.url">
+<tt class="descname">url</tt><a class="headerlink" href="#flask.Request.url" title="Permalink to this definition">¶</a></dt>
+<dd></dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.base_url">
+<tt class="descname">base_url</tt><a class="headerlink" href="#flask.Request.base_url" title="Permalink to this definition">¶</a></dt>
+<dd></dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.url_root">
+<tt class="descname">url_root</tt><a class="headerlink" href="#flask.Request.url_root" title="Permalink to this definition">¶</a></dt>
+<dd><p>Provides different ways to look at the current URL. Imagine your
+application is listening on the following URL:</p>
+<div class="highlight-python"><pre>http://www.example.com/myapplication</pre>
+</div>
+<p>And a user requests the following URL:</p>
+<div class="highlight-python"><pre>http://www.example.com/myapplication/page.html?x=y</pre>
+</div>
+<p>In this case the values of the above mentioned attributes would be
+the following:</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="19%" />
+<col width="81%" />
+</colgroup>
+<tbody valign="top">
+<tr class="row-odd"><td><cite>path</cite></td>
+<td><tt class="docutils literal"><span class="pre">/page.html</span></tt></td>
+</tr>
+<tr class="row-even"><td><cite>script_root</cite></td>
+<td><tt class="docutils literal"><span class="pre">/myapplication</span></tt></td>
+</tr>
+<tr class="row-odd"><td><cite>base_url</cite></td>
+<td><tt class="docutils literal"><span class="pre">http://www.example.com/myapplication/page.html</span></tt></td>
+</tr>
+<tr class="row-even"><td><cite>url</cite></td>
+<td><tt class="docutils literal"><span class="pre">http://www.example.com/myapplication/page.html?x=y</span></tt></td>
+</tr>
+<tr class="row-odd"><td><cite>url_root</cite></td>
+<td><tt class="docutils literal"><span class="pre">http://www.example.com/myapplication/</span></tt></td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.is_xhr">
+<tt class="descname">is_xhr</tt><a class="headerlink" href="#flask.Request.is_xhr" title="Permalink to this definition">¶</a></dt>
+<dd><p><cite>True</cite> if the request was triggered via a JavaScript
+<cite>XMLHttpRequest</cite>. This only works with libraries that support the
+<tt class="docutils literal"><span class="pre">X-Requested-With</span></tt> header and set it to <cite>XMLHttpRequest</cite>.
+Libraries that do that are prototype, jQuery and Mochikit and
+probably some more.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.blueprint">
+<tt class="descname">blueprint</tt><a class="headerlink" href="#flask.Request.blueprint" title="Permalink to this definition">¶</a></dt>
+<dd><p>The name of the current blueprint</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.endpoint">
+<tt class="descname">endpoint</tt><a class="headerlink" href="#flask.Request.endpoint" title="Permalink to this definition">¶</a></dt>
+<dd><p>The endpoint that matched the request. This in combination with
+<a class="reference internal" href="#flask.Request.view_args" title="flask.Request.view_args"><tt class="xref py py-attr docutils literal"><span class="pre">view_args</span></tt></a> can be used to reconstruct the same or a
+modified URL. If an exception happened when matching, this will
+be <cite>None</cite>.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.json">
+<tt class="descname">json</tt><a class="headerlink" href="#flask.Request.json" title="Permalink to this definition">¶</a></dt>
+<dd><p>If the mimetype is <cite>application/json</cite> this will contain the
+parsed JSON data. Otherwise this will be <cite>None</cite>.</p>
+<p>This requires Python 2.6 or an installed version of simplejson.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.max_content_length">
+<tt class="descname">max_content_length</tt><a class="headerlink" href="#flask.Request.max_content_length" title="Permalink to this definition">¶</a></dt>
+<dd><p>Read-only view of the <cite>MAX_CONTENT_LENGTH</cite> config key.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.module">
+<tt class="descname">module</tt><a class="headerlink" href="#flask.Request.module" title="Permalink to this definition">¶</a></dt>
+<dd><p>The name of the current module if the request was dispatched
+to an actual module. This is deprecated functionality, use blueprints
+instead.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Request.on_json_loading_failed">
+<tt class="descname">on_json_loading_failed</tt><big>(</big><em>e</em><big>)</big><a class="headerlink" href="#flask.Request.on_json_loading_failed" title="Permalink to this definition">¶</a></dt>
+<dd><p>Called if decoding of the JSON data failed. The return value of
+this method is used by <a class="reference internal" href="#flask.json" title="flask.json"><tt class="xref py py-attr docutils literal"><span class="pre">json</span></tt></a> when an error ocurred. The
+default implementation raises a <tt class="xref py py-class docutils literal"><span class="pre">BadRequest</span></tt>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.routing_exception">
+<tt class="descname">routing_exception</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Request.routing_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference external" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.NotFound" title="(in Werkzeug v0.7)"><tt class="xref py py-exc docutils literal"><span class="pre">NotFound</span></tt></a> exception or
+something similar.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.url_rule">
+<tt class="descname">url_rule</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Request.url_rule" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 (<tt class="docutils literal"><span class="pre">request.url_rule.methods</span></tt>) etc.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6.</span></p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Request.view_args">
+<tt class="descname">view_args</tt><em class="property"> = None</em><a class="headerlink" href="#flask.Request.view_args" title="Permalink to this definition">¶</a></dt>
+<dd><p>a dict of view arguments that matched the request. If an exception
+happened when matching, this will be <cite>None</cite>.</p>
+</dd></dl>
+
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.request">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">request</tt><a class="headerlink" href="#flask.request" title="Permalink to this definition">¶</a></dt>
+<dd><p>To access incoming request data, you can use the global <cite>request</cite>
+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.</p>
+<p>This is a proxy. See <a class="reference internal" href="reqcontext.html#notes-on-proxies"><em>Notes On Proxies</em></a> for more information.</p>
+<p>The request object is an instance of a <a class="reference external" href="http://werkzeug.pocoo.org/docs/wrappers/#werkzeug.wrappers.Request" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Request</span></tt></a>
+subclass and provides all of the attributes Werkzeug defines. This
+just shows a quick overview of the most important ones.</p>
+</dd></dl>
+
+</div>
+<div class="section" id="response-objects">
+<h2>Response Objects<a class="headerlink" href="#response-objects" title="Permalink to this headline">¶</a></h2>
+<dl class="class">
+<dt id="flask.Response">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">Response</tt><big>(</big><em>response=None</em>, <em>status=None</em>, <em>headers=None</em>, <em>mimetype=None</em>, <em>content_type=None</em>, <em>direct_passthrough=False</em><big>)</big><a class="headerlink" href="#flask.Response" title="Permalink to this definition">¶</a></dt>
+<dd><p>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&#8217;t have to create this object yourself because
+<a class="reference internal" href="#flask.Flask.make_response" title="flask.Flask.make_response"><tt class="xref py py-meth docutils literal"><span class="pre">make_response()</span></tt></a> will take care of that for you.</p>
+<p>If you want to replace the response object used you can subclass this and
+set <a class="reference internal" href="#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-attr docutils literal"><span class="pre">response_class</span></tt></a> to your subclass.</p>
+<dl class="attribute">
+<dt id="flask.Response.headers">
+<tt class="descname">headers</tt><a class="headerlink" href="#flask.Response.headers" title="Permalink to this definition">¶</a></dt>
+<dd><p>A <tt class="xref py py-class docutils literal"><span class="pre">Headers</span></tt> object representing the response headers.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Response.status_code">
+<tt class="descname">status_code</tt><a class="headerlink" href="#flask.Response.status_code" title="Permalink to this definition">¶</a></dt>
+<dd><p>The response status as integer.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Response.set_cookie">
+<tt class="descname">set_cookie</tt><big>(</big><em>key</em>, <em>value=''</em>, <em>max_age=None</em>, <em>expires=None</em>, <em>path='/'</em>, <em>domain=None</em>, <em>secure=None</em>, <em>httponly=False</em><big>)</big><a class="headerlink" href="#flask.Response.set_cookie" title="Permalink to this definition">¶</a></dt>
+<dd><p>Sets a cookie. The parameters are the same as in the cookie <cite>Morsel</cite>
+object in the Python standard library but it accepts unicode data, too.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>key</strong> &#8211; the key (name) of the cookie to be set.</li>
+<li><strong>value</strong> &#8211; the value of the cookie.</li>
+<li><strong>max_age</strong> &#8211; should be a number of seconds, or <cite>None</cite> (default) if
+the cookie should last only as long as the client&#8217;s
+browser session.</li>
+<li><strong>expires</strong> &#8211; should be a <cite>datetime</cite> object or UNIX timestamp.</li>
+<li><strong>domain</strong> &#8211; if you want to set a cross-domain cookie. For example,
+<tt class="docutils literal"><span class="pre">domain=&quot;.example.com&quot;</span></tt> will set a cookie that is
+readable by the domain <tt class="docutils literal"><span class="pre">www.example.com</span></tt>,
+<tt class="docutils literal"><span class="pre">foo.example.com</span></tt> etc. Otherwise, a cookie will only
+be readable by the domain that set it.</li>
+<li><strong>path</strong> &#8211; limits the cookie to a given path, per default it will
+span the whole domain.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Response.data">
+<tt class="descname">data</tt><a class="headerlink" href="#flask.Response.data" title="Permalink to this definition">¶</a></dt>
+<dd><p>The string representation of the request body. Whenever you access
+this property the request iterable is encoded and flattened. This
+can lead to unwanted behavior if you stream big data.</p>
+<p>This behavior can be disabled by setting
+<tt class="xref py py-attr docutils literal"><span class="pre">implicit_sequence_conversion</span></tt> to <cite>False</cite>.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.Response.mimetype">
+<tt class="descname">mimetype</tt><a class="headerlink" href="#flask.Response.mimetype" title="Permalink to this definition">¶</a></dt>
+<dd><p>The mimetype (content type without charset etc.)</p>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="sessions">
+<h2>Sessions<a class="headerlink" href="#sessions" title="Permalink to this headline">¶</a></h2>
+<p>If you have the <a class="reference internal" href="#flask.Flask.secret_key" title="flask.Flask.secret_key"><tt class="xref py py-attr docutils literal"><span class="pre">Flask.secret_key</span></tt></a> 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 they know the secret key, so make sure to set that
+to something complex and unguessable.</p>
+<p>To access the current session you can use the <a class="reference internal" href="#flask.session" title="flask.session"><tt class="xref py py-class docutils literal"><span class="pre">session</span></tt></a> object:</p>
+<dl class="class">
+<dt id="flask.session">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">session</tt><a class="headerlink" href="#flask.session" title="Permalink to this definition">¶</a></dt>
+<dd><p>The session object works pretty much like an ordinary dict, with the
+difference that it keeps track on modifications.</p>
+<p>This is a proxy. See <a class="reference internal" href="reqcontext.html#notes-on-proxies"><em>Notes On Proxies</em></a> for more information.</p>
+<p>The following attributes are interesting:</p>
+<dl class="attribute">
+<dt id="flask.session.new">
+<tt class="descname">new</tt><a class="headerlink" href="#flask.session.new" title="Permalink to this definition">¶</a></dt>
+<dd><p><cite>True</cite> if the session is new, <cite>False</cite> otherwise.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.session.modified">
+<tt class="descname">modified</tt><a class="headerlink" href="#flask.session.modified" title="Permalink to this definition">¶</a></dt>
+<dd><p><cite>True</cite> 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 <cite>True</cite> yourself. Here an example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># this change is not picked up because a mutable object (here</span>
+<span class="c"># a list) is changed.</span>
+<span class="n">session</span><span class="p">[</span><span class="s">&#39;objects&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
+<span class="c"># so mark it as modified yourself</span>
+<span class="n">session</span><span class="o">.</span><span class="n">modified</span> <span class="o">=</span> <span class="bp">True</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.session.permanent">
+<tt class="descname">permanent</tt><a class="headerlink" href="#flask.session.permanent" title="Permalink to this definition">¶</a></dt>
+<dd><p>If set to <cite>True</cite> the session lives for
+<a class="reference internal" href="#flask.Flask.permanent_session_lifetime" title="flask.Flask.permanent_session_lifetime"><tt class="xref py py-attr docutils literal"><span class="pre">permanent_session_lifetime</span></tt></a> seconds. The
+default is 31 days. If set to <cite>False</cite> (which is the default) the
+session will be deleted when the user closes the browser.</p>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="session-interface">
+<h2>Session Interface<a class="headerlink" href="#session-interface" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+<p>The session interface provides a simple way to replace the session
+implementation that Flask is using.</p>
+<dl class="class">
+<dt id="flask.sessions.SessionInterface">
+<em class="property">class </em><tt class="descclassname">flask.sessions.</tt><tt class="descname">SessionInterface</tt><a class="headerlink" href="#flask.sessions.SessionInterface" title="Permalink to this definition">¶</a></dt>
+<dd><p>The basic interface you have to implement in order to replace the
+default session interface which uses werkzeug&#8217;s securecookie
+implementation. The only methods you have to implement are
+<a class="reference internal" href="#flask.sessions.SessionInterface.open_session" title="flask.sessions.SessionInterface.open_session"><tt class="xref py py-meth docutils literal"><span class="pre">open_session()</span></tt></a> and <a class="reference internal" href="#flask.sessions.SessionInterface.save_session" title="flask.sessions.SessionInterface.save_session"><tt class="xref py py-meth docutils literal"><span class="pre">save_session()</span></tt></a>, the others have
+useful defaults which you don&#8217;t need to change.</p>
+<p>The session object returned by the <a class="reference internal" href="#flask.sessions.SessionInterface.open_session" title="flask.sessions.SessionInterface.open_session"><tt class="xref py py-meth docutils literal"><span class="pre">open_session()</span></tt></a> method has to
+provide a dictionary like interface plus the properties and methods
+from the <a class="reference internal" href="#flask.sessions.SessionMixin" title="flask.sessions.SessionMixin"><tt class="xref py py-class docutils literal"><span class="pre">SessionMixin</span></tt></a>. We recommend just subclassing a dict
+and adding that mixin:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Session</span><span class="p">(</span><span class="nb">dict</span><span class="p">,</span> <span class="n">SessionMixin</span><span class="p">):</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>If <a class="reference internal" href="#flask.sessions.SessionInterface.open_session" title="flask.sessions.SessionInterface.open_session"><tt class="xref py py-meth docutils literal"><span class="pre">open_session()</span></tt></a> returns <cite>None</cite> Flask will call into
+<a class="reference internal" href="#flask.sessions.SessionInterface.make_null_session" title="flask.sessions.SessionInterface.make_null_session"><tt class="xref py py-meth docutils literal"><span class="pre">make_null_session()</span></tt></a> to create a session that acts as replacement
+if the session support cannot work because some requirement is not
+fulfilled. The default <a class="reference internal" href="#flask.sessions.NullSession" title="flask.sessions.NullSession"><tt class="xref py py-class docutils literal"><span class="pre">NullSession</span></tt></a> class that is created
+will complain that the secret key was not set.</p>
+<p>To replace the session interface on an application all you have to do
+is to assign <a class="reference internal" href="#flask.Flask.session_interface" title="flask.Flask.session_interface"><tt class="xref py py-attr docutils literal"><span class="pre">flask.Flask.session_interface</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">session_interface</span> <span class="o">=</span> <span class="n">MySessionInterface</span><span class="p">()</span>
+</pre></div>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.get_cookie_domain">
+<tt class="descname">get_cookie_domain</tt><big>(</big><em>app</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.get_cookie_domain" title="Permalink to this definition">¶</a></dt>
+<dd><p>Helpful helper method that returns the cookie domain that should
+be used for the session cookie if session cookies are used.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.get_cookie_httponly">
+<tt class="descname">get_cookie_httponly</tt><big>(</big><em>app</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.get_cookie_httponly" title="Permalink to this definition">¶</a></dt>
+<dd><p>Returns True if the session cookie should be httponly. This
+currently just returns the value of the <tt class="docutils literal"><span class="pre">SESSION_COOKIE_HTTPONLY</span></tt>
+config var.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.get_cookie_path">
+<tt class="descname">get_cookie_path</tt><big>(</big><em>app</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.get_cookie_path" title="Permalink to this definition">¶</a></dt>
+<dd><p>Returns the path for which the cookie should be valid. The
+default implementation uses the value from the SESSION_COOKIE_PATH``
+config var if it&#8217;s set, and falls back to <tt class="docutils literal"><span class="pre">APPLICATION_ROOT</span></tt> or
+uses <tt class="docutils literal"><span class="pre">/</span></tt> if it&#8217;s <cite>None</cite>.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.get_cookie_secure">
+<tt class="descname">get_cookie_secure</tt><big>(</big><em>app</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.get_cookie_secure" title="Permalink to this definition">¶</a></dt>
+<dd><p>Returns True if the cookie should be secure. This currently
+just returns the value of the <tt class="docutils literal"><span class="pre">SESSION_COOKIE_SECURE</span></tt> setting.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.get_expiration_time">
+<tt class="descname">get_expiration_time</tt><big>(</big><em>app</em>, <em>session</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.get_expiration_time" title="Permalink to this definition">¶</a></dt>
+<dd><p>A helper method that returns an expiration date for the session
+or <cite>None</cite> if the session is linked to the browser session. The
+default implementation returns now + the permanent session
+lifetime configured on the application.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.is_null_session">
+<tt class="descname">is_null_session</tt><big>(</big><em>obj</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.is_null_session" title="Permalink to this definition">¶</a></dt>
+<dd><p>Checks if a given object is a null session. Null sessions are
+not asked to be saved.</p>
+<p>This checks if the object is an instance of <a class="reference internal" href="#flask.sessions.SessionInterface.null_session_class" title="flask.sessions.SessionInterface.null_session_class"><tt class="xref py py-attr docutils literal"><span class="pre">null_session_class</span></tt></a>
+by default.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.make_null_session">
+<tt class="descname">make_null_session</tt><big>(</big><em>app</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.make_null_session" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates a null session which acts as a replacement object if the
+real session support could not be loaded due to a configuration
+error. This mainly aids the user experience because the job of the
+null session is to still support lookup without complaining but
+modifications are answered with a helpful error message of what
+failed.</p>
+<p>This creates an instance of <a class="reference internal" href="#flask.sessions.SessionInterface.null_session_class" title="flask.sessions.SessionInterface.null_session_class"><tt class="xref py py-attr docutils literal"><span class="pre">null_session_class</span></tt></a> by default.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.sessions.SessionInterface.null_session_class">
+<tt class="descname">null_session_class</tt><a class="headerlink" href="#flask.sessions.SessionInterface.null_session_class" title="Permalink to this definition">¶</a></dt>
+<dd><p><a class="reference internal" href="#flask.sessions.SessionInterface.make_null_session" title="flask.sessions.SessionInterface.make_null_session"><tt class="xref py py-meth docutils literal"><span class="pre">make_null_session()</span></tt></a> will look here for the class that should
+be created when a null session is requested. Likewise the
+<a class="reference internal" href="#flask.sessions.SessionInterface.is_null_session" title="flask.sessions.SessionInterface.is_null_session"><tt class="xref py py-meth docutils literal"><span class="pre">is_null_session()</span></tt></a> method will perform a typecheck against
+this type.</p>
+<p>alias of <a class="reference internal" href="#flask.sessions.NullSession" title="flask.sessions.NullSession"><tt class="xref py py-class docutils literal"><span class="pre">NullSession</span></tt></a></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.open_session">
+<tt class="descname">open_session</tt><big>(</big><em>app</em>, <em>request</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.open_session" title="Permalink to this definition">¶</a></dt>
+<dd><p>This method has to be implemented and must either return <cite>None</cite>
+in case the loading failed because of a configuration error or an
+instance of a session object which implements a dictionary like
+interface + the methods and attributes on <a class="reference internal" href="#flask.sessions.SessionMixin" title="flask.sessions.SessionMixin"><tt class="xref py py-class docutils literal"><span class="pre">SessionMixin</span></tt></a>.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.sessions.SessionInterface.save_session">
+<tt class="descname">save_session</tt><big>(</big><em>app</em>, <em>session</em>, <em>response</em><big>)</big><a class="headerlink" href="#flask.sessions.SessionInterface.save_session" title="Permalink to this definition">¶</a></dt>
+<dd><p>This is called for actual sessions returned by <a class="reference internal" href="#flask.sessions.SessionInterface.open_session" title="flask.sessions.SessionInterface.open_session"><tt class="xref py py-meth docutils literal"><span class="pre">open_session()</span></tt></a>
+at the end of the request. This is still called during a request
+context so if you absolutely need access to the request you can do
+that.</p>
+</dd></dl>
+
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.sessions.SecureCookieSessionInterface">
+<em class="property">class </em><tt class="descclassname">flask.sessions.</tt><tt class="descname">SecureCookieSessionInterface</tt><a class="headerlink" href="#flask.sessions.SecureCookieSessionInterface" title="Permalink to this definition">¶</a></dt>
+<dd><p>The cookie session interface that uses the Werkzeug securecookie
+as client side session backend.</p>
+<dl class="attribute">
+<dt id="flask.sessions.SecureCookieSessionInterface.session_class">
+<tt class="descname">session_class</tt><a class="headerlink" href="#flask.sessions.SecureCookieSessionInterface.session_class" title="Permalink to this definition">¶</a></dt>
+<dd><p>alias of <tt class="xref py py-class docutils literal"><span class="pre">SecureCookieSession</span></tt></p>
+</dd></dl>
+
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.sessions.NullSession">
+<em class="property">class </em><tt class="descclassname">flask.sessions.</tt><tt class="descname">NullSession</tt><big>(</big><em>data=None</em>, <em>secret_key=None</em>, <em>new=True</em><big>)</big><a class="headerlink" href="#flask.sessions.NullSession" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.sessions.SessionMixin">
+<em class="property">class </em><tt class="descclassname">flask.sessions.</tt><tt class="descname">SessionMixin</tt><a class="headerlink" href="#flask.sessions.SessionMixin" title="Permalink to this definition">¶</a></dt>
+<dd><p>Expands a basic dictionary with an accessors that are expected
+by Flask extensions and users for the session.</p>
+<dl class="attribute">
+<dt id="flask.sessions.SessionMixin.modified">
+<tt class="descname">modified</tt><em class="property"> = True</em><a class="headerlink" href="#flask.sessions.SessionMixin.modified" title="Permalink to this definition">¶</a></dt>
+<dd><p>for some backends this will always be <cite>True</cite>, but some backends will
+default this to false and detect changes in the dictionary for as
+long as changes do not happen on mutable structures in the session.
+The default mixin implementation just hardcodes <cite>True</cite> in.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.sessions.SessionMixin.new">
+<tt class="descname">new</tt><em class="property"> = False</em><a class="headerlink" href="#flask.sessions.SessionMixin.new" title="Permalink to this definition">¶</a></dt>
+<dd><p>some session backends can tell you if a session is new, but that is
+not necessarily guaranteed. Use with caution. The default mixin
+implementation just hardcodes <cite>False</cite> in.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.sessions.SessionMixin.permanent">
+<tt class="descname">permanent</tt><a class="headerlink" href="#flask.sessions.SessionMixin.permanent" title="Permalink to this definition">¶</a></dt>
+<dd><p>this reflects the <tt class="docutils literal"><span class="pre">'_permanent'</span></tt> key in the dict.</p>
+</dd></dl>
+
+</dd></dl>
+
+<div class="admonition-notice admonition ">
+<p class="first admonition-title">Notice</p>
+<p class="last">The <tt class="docutils literal"><span class="pre">PERMANENT_SESSION_LIFETIME</span></tt> config key can also be an integer
+starting with Flask 0.8. Either catch this down yourself or use
+the <a class="reference internal" href="#flask.Flask.permanent_session_lifetime" title="flask.Flask.permanent_session_lifetime"><tt class="xref py py-attr docutils literal"><span class="pre">permanent_session_lifetime</span></tt></a> attribute on the
+app which converts the result to an integer automatically.</p>
+</div>
+</div>
+<div class="section" id="test-client">
+<h2>Test Client<a class="headerlink" href="#test-client" title="Permalink to this headline">¶</a></h2>
+<dl class="class">
+<dt id="flask.testing.FlaskClient">
+<em class="property">class </em><tt class="descclassname">flask.testing.</tt><tt class="descname">FlaskClient</tt><big>(</big><em>application</em>, <em>response_wrapper=None</em>, <em>use_cookies=True</em>, <em>allow_subdomain_redirects=False</em><big>)</big><a class="headerlink" href="#flask.testing.FlaskClient" title="Permalink to this definition">¶</a></dt>
+<dd><p>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. For general information
+about how to use this class refer to <a class="reference external" href="http://werkzeug.pocoo.org/docs/test/#werkzeug.test.Client" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">werkzeug.test.Client</span></tt></a>.</p>
+<p>Basic usage is outlined in the <a class="reference internal" href="testing.html#testing"><em>Testing Flask Applications</em></a> chapter.</p>
+<dl class="method">
+<dt id="flask.testing.FlaskClient.session_transaction">
+<tt class="descname">session_transaction</tt><big>(</big><em>*args</em>, <em>**kwds</em><big>)</big><a class="headerlink" href="#flask.testing.FlaskClient.session_transaction" title="Permalink to this definition">¶</a></dt>
+<dd><p>When used in combination with a with statement this opens a
+session transaction. This can be used to modify the session that
+the test client uses. Once the with block is left the session is
+stored back.</p>
+<blockquote>
+<div><dl class="docutils">
+<dt>with client.session_transaction() as session:</dt>
+<dd>session[&#8216;value&#8217;] = 42</dd>
+</dl>
+</div></blockquote>
+<p>Internally this is implemented by going through a temporary test
+request context and since session handling could depend on
+request variables this function accepts the same arguments as
+<a class="reference internal" href="#flask.Flask.test_request_context" title="flask.Flask.test_request_context"><tt class="xref py py-meth docutils literal"><span class="pre">test_request_context()</span></tt></a> which are directly
+passed through.</p>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="application-globals">
+<h2>Application Globals<a class="headerlink" href="#application-globals" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference internal" href="#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a> and <a class="reference internal" href="#flask.session" title="flask.session"><tt class="xref py py-class docutils literal"><span class="pre">session</span></tt></a>.</p>
+<dl class="data">
+<dt id="flask.g">
+<tt class="descclassname">flask.</tt><tt class="descname">g</tt><a class="headerlink" href="#flask.g" title="Permalink to this definition">¶</a></dt>
+<dd><p>Just store on this whatever you want. For example a database
+connection or the user that is currently logged in.</p>
+<p>This is a proxy. See <a class="reference internal" href="reqcontext.html#notes-on-proxies"><em>Notes On Proxies</em></a> for more information.</p>
+</dd></dl>
+
+</div>
+<div class="section" id="useful-functions-and-classes">
+<h2>Useful Functions and Classes<a class="headerlink" href="#useful-functions-and-classes" title="Permalink to this headline">¶</a></h2>
+<dl class="data">
+<dt id="flask.current_app">
+<tt class="descclassname">flask.</tt><tt class="descname">current_app</tt><a class="headerlink" href="#flask.current_app" title="Permalink to this definition">¶</a></dt>
+<dd><p>Points to the application handling the request. This is useful for
+extensions that want to support multiple applications running side
+by side.</p>
+<p>This is a proxy. See <a class="reference internal" href="reqcontext.html#notes-on-proxies"><em>Notes On Proxies</em></a> for more information.</p>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.has_request_context">
+<tt class="descclassname">flask.</tt><tt class="descname">has_request_context</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.has_request_context" title="Permalink to this definition">¶</a></dt>
+<dd><p>If you have code that wants to test if a request context is there or
+not this function can be used. For instance if you want to take advantage
+of request information is it&#8217;s available but fail silently if the request
+object is unavailable.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">remote_addr</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">username</span>
+ <span class="k">if</span> <span class="n">remote_addr</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">and</span> <span class="n">has_request_context</span><span class="p">():</span>
+ <span class="n">remote_addr</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">remote_addr</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">remote_addr</span> <span class="o">=</span> <span class="n">remote_addr</span>
+</pre></div>
+</div>
+<p>Alternatively you can also just test any of the context bound objects
+(such as <a class="reference internal" href="#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a> or <a class="reference internal" href="#flask.g" title="flask.g"><tt class="xref py py-class docutils literal"><span class="pre">g</span></tt></a> for truthness):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">remote_addr</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">username</span> <span class="o">=</span> <span class="n">username</span>
+ <span class="k">if</span> <span class="n">remote_addr</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">and</span> <span class="n">request</span><span class="p">:</span>
+ <span class="n">remote_addr</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">remote_addr</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">remote_addr</span> <span class="o">=</span> <span class="n">remote_addr</span>
+</pre></div>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.url_for">
+<tt class="descclassname">flask.</tt><tt class="descname">url_for</tt><big>(</big><em>endpoint</em>, <em>**values</em><big>)</big><a class="headerlink" href="#flask.url_for" title="Permalink to this definition">¶</a></dt>
+<dd><p>Generates a URL to the given endpoint with the method provided.</p>
+<p>Variable arguments that are unknown to the target endpoint are appended
+to the generated URL as query arguments. If the value of a query argument
+is <cite>None</cite>, the whole pair is skipped. In case blueprints are active
+you can shortcut references to the same blueprint by prefixing the
+local endpoint with a dot (<tt class="docutils literal"><span class="pre">.</span></tt>).</p>
+<p>This will reference the index function local to the current blueprint:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;.index&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>For more information, head over to the <a class="reference internal" href="quickstart.html#url-building"><em>Quickstart</em></a>.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>endpoint</strong> &#8211; the endpoint of the URL (name of the function)</li>
+<li><strong>values</strong> &#8211; the variable arguments of the URL rule</li>
+<li><strong>_external</strong> &#8211; if set to <cite>True</cite>, an absolute URL is generated.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.abort">
+<tt class="descclassname">flask.</tt><tt class="descname">abort</tt><big>(</big><em>code</em><big>)</big><a class="headerlink" href="#flask.abort" title="Permalink to this definition">¶</a></dt>
+<dd><p>Raises an <a class="reference external" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.HTTPException" title="(in Werkzeug v0.7)"><tt class="xref py py-exc docutils literal"><span class="pre">HTTPException</span></tt></a> for the given
+status code. For example to abort request handling with a page not
+found exception, you would call <tt class="docutils literal"><span class="pre">abort(404)</span></tt>.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>code</strong> &#8211; the HTTP error code.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.redirect">
+<tt class="descclassname">flask.</tt><tt class="descname">redirect</tt><big>(</big><em>location</em>, <em>code=302</em><big>)</big><a class="headerlink" href="#flask.redirect" title="Permalink to this definition">¶</a></dt>
+<dd><p>Return a response object (a WSGI application) that, if called,
+redirects the client to the target location. Supported codes are 301,
+302, 303, 305, and 307. 300 is not supported because it&#8217;s not a real
+redirect and 304 because it&#8217;s the answer for a request with a request
+with defined If-Modified-Since headers.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6: </span>The location can now be a unicode string that is encoded using
+the <tt class="xref py py-func docutils literal"><span class="pre">iri_to_uri()</span></tt> function.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>location</strong> &#8211; the location the response should redirect to.</li>
+<li><strong>code</strong> &#8211; the redirect status code. defaults to 302.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.make_response">
+<tt class="descclassname">flask.</tt><tt class="descname">make_response</tt><big>(</big><em>*args</em><big>)</big><a class="headerlink" href="#flask.make_response" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+<p>If view looked like this and you want to add a new header:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;index.html&#39;</span><span class="p">,</span> <span class="n">foo</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>You can now do something like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="n">response</span> <span class="o">=</span> <span class="n">make_response</span><span class="p">(</span><span class="n">render_template</span><span class="p">(</span><span class="s">&#39;index.html&#39;</span><span class="p">,</span> <span class="n">foo</span><span class="o">=</span><span class="mi">42</span><span class="p">))</span>
+ <span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">&#39;X-Parachutes&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;parachutes are cool&#39;</span>
+ <span class="k">return</span> <span class="n">response</span>
+</pre></div>
+</div>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">response</span> <span class="o">=</span> <span class="n">make_response</span><span class="p">(</span><span class="n">render_template</span><span class="p">(</span><span class="s">&#39;not_found.html&#39;</span><span class="p">),</span> <span class="mi">404</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The other use case of this function is to force the return value of a
+view function into a response which is helpful with view
+decorators:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">response</span> <span class="o">=</span> <span class="n">make_response</span><span class="p">(</span><span class="n">view_function</span><span class="p">())</span>
+<span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">&#39;X-Parachutes&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;parachutes are cool&#39;</span>
+</pre></div>
+</div>
+<p>Internally this function does the following things:</p>
+<ul class="simple">
+<li>if no arguments are passed, it creates a new response argument</li>
+<li>if one argument is passed, <a class="reference internal" href="#flask.Flask.make_response" title="flask.Flask.make_response"><tt class="xref py py-meth docutils literal"><span class="pre">flask.Flask.make_response()</span></tt></a>
+is invoked with it.</li>
+<li>if more than one argument is passed, the arguments are passed
+to the <a class="reference internal" href="#flask.Flask.make_response" title="flask.Flask.make_response"><tt class="xref py py-meth docutils literal"><span class="pre">flask.Flask.make_response()</span></tt></a> function as tuple.</li>
+</ul>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6.</span></p>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.send_file">
+<tt class="descclassname">flask.</tt><tt class="descname">send_file</tt><big>(</big><em>filename_or_fp</em>, <em>mimetype=None</em>, <em>as_attachment=False</em>, <em>attachment_filename=None</em>, <em>add_etags=True</em>, <em>cache_timeout=43200</em>, <em>conditional=False</em><big>)</big><a class="headerlink" href="#flask.send_file" title="Permalink to this definition">¶</a></dt>
+<dd><p>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&#8217;s file_wrapper support. Alternatively
+you can set the application&#8217;s <a class="reference internal" href="#flask.Flask.use_x_sendfile" title="flask.Flask.use_x_sendfile"><tt class="xref py py-attr docutils literal"><span class="pre">use_x_sendfile</span></tt></a> attribute
+to <tt class="docutils literal"><span class="pre">True</span></tt> to directly emit an <cite>X-Sendfile</cite> header. This however
+requires support of the underlying webserver for <cite>X-Sendfile</cite>.</p>
+<p>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 <cite>filename</cite> or an <cite>attachment_filename</cite> to be
+provided.</p>
+<p>Please never pass filenames to this function from user sources without
+checking them first. Something like this is usually sufficient to
+avoid security problems:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">if</span> <span class="s">&#39;..&#39;</span> <span class="ow">in</span> <span class="n">filename</span> <span class="ow">or</span> <span class="n">filename</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">):</span>
+ <span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
+</pre></div>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.2.</span></p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5: </span>The <cite>add_etags</cite>, <cite>cache_timeout</cite> and <cite>conditional</cite> parameters were
+added. The default behaviour is now to attach etags.</p>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.7: </span>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</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>filename_or_fp</strong> &#8211; the filename of the file to send. This is
+relative to the <tt class="xref py py-attr docutils literal"><span class="pre">root_path</span></tt> if a
+relative path is specified.
+Alternatively a file object might be provided
+in which case <cite>X-Sendfile</cite> 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 <a class="reference internal" href="#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">send_file()</span></tt></a>.</li>
+<li><strong>mimetype</strong> &#8211; the mimetype of the file if provided, otherwise
+auto detection happens.</li>
+<li><strong>as_attachment</strong> &#8211; set to <cite>True</cite> if you want to send this file with
+a <tt class="docutils literal"><span class="pre">Content-Disposition:</span> <span class="pre">attachment</span></tt> header.</li>
+<li><strong>attachment_filename</strong> &#8211; the filename for the attachment if it
+differs from the file&#8217;s filename.</li>
+<li><strong>add_etags</strong> &#8211; set to <cite>False</cite> to disable attaching of etags.</li>
+<li><strong>conditional</strong> &#8211; set to <cite>True</cite> to enable conditional responses.</li>
+<li><strong>cache_timeout</strong> &#8211; the timeout in seconds for the headers.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.send_from_directory">
+<tt class="descclassname">flask.</tt><tt class="descname">send_from_directory</tt><big>(</big><em>directory</em>, <em>filename</em>, <em>**options</em><big>)</big><a class="headerlink" href="#flask.send_from_directory" title="Permalink to this definition">¶</a></dt>
+<dd><p>Send a file from a given directory with <a class="reference internal" href="#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">send_file()</span></tt></a>. This
+is a secure way to quickly expose static files from an upload folder
+or something similar.</p>
+<p>Example usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/uploads/&lt;path:filename&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">download_file</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">send_from_directory</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">],</span>
+ <span class="n">filename</span><span class="p">,</span> <span class="n">as_attachment</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<div class="admonition-sending-files-and-performance admonition ">
+<p class="first admonition-title">Sending files and Performance</p>
+<p class="last">It is strongly recommended to activate either <cite>X-Sendfile</cite> 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.</p>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5.</span></p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>directory</strong> &#8211; the directory where all the files are stored.</li>
+<li><strong>filename</strong> &#8211; the filename relative to that directory to
+download.</li>
+<li><strong>options</strong> &#8211; optional keyword arguments that are directly
+forwarded to <a class="reference internal" href="#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">send_file()</span></tt></a>.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.safe_join">
+<tt class="descclassname">flask.</tt><tt class="descname">safe_join</tt><big>(</big><em>directory</em>, <em>filename</em><big>)</big><a class="headerlink" href="#flask.safe_join" title="Permalink to this definition">¶</a></dt>
+<dd><p>Safely join <cite>directory</cite> and <cite>filename</cite>.</p>
+<p>Example usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/wiki/&lt;path:filename&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">wiki_page</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
+ <span class="n">filename</span> <span class="o">=</span> <span class="n">safe_join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;WIKI_FOLDER&#39;</span><span class="p">],</span> <span class="n">filename</span><span class="p">)</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">&#39;rb&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">fd</span><span class="p">:</span>
+ <span class="n">content</span> <span class="o">=</span> <span class="n">fd</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> <span class="c"># Read and process the file content...</span>
+</pre></div>
+</div>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
+<li><strong>directory</strong> &#8211; the base directory.</li>
+<li><strong>filename</strong> &#8211; the untrusted filename relative to that directory.</li>
+</ul>
+</td>
+</tr>
+<tr class="field-even field"><th class="field-name">Raises :</th><td class="field-body"><p class="first last"><tt class="xref py py-class docutils literal"><span class="pre">NotFound</span></tt> if the resulting path
+would fall out of <cite>directory</cite>.</p>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.escape">
+<tt class="descclassname">flask.</tt><tt class="descname">escape</tt><big>(</big><em>s</em><big>)</big> &rarr; markup<a class="headerlink" href="#flask.escape" title="Permalink to this definition">¶</a></dt>
+<dd><p>Convert the characters &amp;, &lt;, &gt;, &#8216;, and &#8221; in string s to HTML-safe
+sequences. Use this if you need to display text that might contain
+such characters in HTML. Marks return value as markup string.</p>
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.Markup">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">Markup</tt><a class="headerlink" href="#flask.Markup" title="Permalink to this definition">¶</a></dt>
+<dd><p>Marks a string as being safe for inclusion in HTML/XML output without
+needing to be escaped. This implements the <cite>__html__</cite> interface a couple
+of frameworks and web applications use. <a class="reference internal" href="#flask.Markup" title="flask.Markup"><tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt></a> is a direct
+subclass of <cite>unicode</cite> and provides all the methods of <cite>unicode</cite> just that
+it escapes arguments passed and always returns <cite>Markup</cite>.</p>
+<p>The <cite>escape</cite> function returns markup objects so that double escaping can&#8217;t
+happen.</p>
+<p>The constructor of the <a class="reference internal" href="#flask.Markup" title="flask.Markup"><tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt></a> class can be used for three
+different things: When passed an unicode object it&#8217;s assumed to be safe,
+when passed an object with an HTML representation (has an <cite>__html__</cite>
+method) that representation is used, otherwise the object passed is
+converted into a unicode string and then assumed to be safe:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="p">(</span><span class="s">&quot;Hello &lt;em&gt;World&lt;/em&gt;!&quot;</span><span class="p">)</span>
+<span class="go">Markup(u&#39;Hello &lt;em&gt;World&lt;/em&gt;!&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">Foo</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+<span class="gp">... </span> <span class="k">def</span> <span class="nf">__html__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+<span class="gp">... </span> <span class="k">return</span> <span class="s">&#39;&lt;a href=&quot;#&quot;&gt;foo&lt;/a&gt;&#39;</span>
+<span class="gp">... </span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="p">(</span><span class="n">Foo</span><span class="p">())</span>
+<span class="go">Markup(u&#39;&lt;a href=&quot;#&quot;&gt;foo&lt;/a&gt;&#39;)</span>
+</pre></div>
+</div>
+<p>If you want object passed being always treated as unsafe you can use the
+<a class="reference internal" href="#flask.escape" title="flask.escape"><tt class="xref py py-meth docutils literal"><span class="pre">escape()</span></tt></a> classmethod to create a <a class="reference internal" href="#flask.Markup" title="flask.Markup"><tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt></a> object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s">&quot;Hello &lt;em&gt;World&lt;/em&gt;!&quot;</span><span class="p">)</span>
+<span class="go">Markup(u&#39;Hello &amp;lt;em&amp;gt;World&amp;lt;/em&amp;gt;!&#39;)</span>
+</pre></div>
+</div>
+<p>Operations on a markup string are markup aware which means that all
+arguments are passed through the <a class="reference internal" href="#flask.escape" title="flask.escape"><tt class="xref py py-func docutils literal"><span class="pre">escape()</span></tt></a> function:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">em</span> <span class="o">=</span> <span class="n">Markup</span><span class="p">(</span><span class="s">&quot;&lt;em&gt;</span><span class="si">%s</span><span class="s">&lt;/em&gt;&quot;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">em</span> <span class="o">%</span> <span class="s">&quot;foo &amp; bar&quot;</span>
+<span class="go">Markup(u&#39;&lt;em&gt;foo &amp;amp; bar&lt;/em&gt;&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">strong</span> <span class="o">=</span> <span class="n">Markup</span><span class="p">(</span><span class="s">&quot;&lt;strong&gt;</span><span class="si">%(text)s</span><span class="s">&lt;/strong&gt;&quot;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">strong</span> <span class="o">%</span> <span class="p">{</span><span class="s">&#39;text&#39;</span><span class="p">:</span> <span class="s">&#39;&lt;blink&gt;hacker here&lt;/blink&gt;&#39;</span><span class="p">}</span>
+<span class="go">Markup(u&#39;&lt;strong&gt;&amp;lt;blink&amp;gt;hacker here&amp;lt;/blink&amp;gt;&lt;/strong&gt;&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="p">(</span><span class="s">&quot;&lt;em&gt;Hello&lt;/em&gt; &quot;</span><span class="p">)</span> <span class="o">+</span> <span class="s">&quot;&lt;foo&gt;&quot;</span>
+<span class="go">Markup(u&#39;&lt;em&gt;Hello&lt;/em&gt; &amp;lt;foo&amp;gt;&#39;)</span>
+</pre></div>
+</div>
+<dl class="classmethod">
+<dt id="flask.Markup.escape">
+<em class="property">classmethod </em><tt class="descname">escape</tt><big>(</big><em>s</em><big>)</big><a class="headerlink" href="#flask.Markup.escape" title="Permalink to this definition">¶</a></dt>
+<dd><p>Escape the string. Works like <a class="reference internal" href="#flask.escape" title="flask.escape"><tt class="xref py py-func docutils literal"><span class="pre">escape()</span></tt></a> with the difference
+that for subclasses of <a class="reference internal" href="#flask.Markup" title="flask.Markup"><tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt></a> this function would return the
+correct subclass.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Markup.unescape">
+<tt class="descname">unescape</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Markup.unescape" title="Permalink to this definition">¶</a></dt>
+<dd><p>Unescape markup again into an unicode string. This also resolves
+known HTML4 and XHTML entities:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="p">(</span><span class="s">&quot;Main &amp;raquo; &lt;em&gt;About&lt;/em&gt;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">unescape</span><span class="p">()</span>
+<span class="go">u&#39;Main \xbb &lt;em&gt;About&lt;/em&gt;&#39;</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Markup.striptags">
+<tt class="descname">striptags</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.Markup.striptags" title="Permalink to this definition">¶</a></dt>
+<dd><p>Unescape markup into an unicode string and strip all tags. This
+also resolves known HTML4 and XHTML entities. Whitespace is
+normalized to one:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="p">(</span><span class="s">&quot;Main &amp;raquo; &lt;em&gt;About&lt;/em&gt;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">striptags</span><span class="p">()</span>
+<span class="go">u&#39;Main \xbb About&#39;</span>
+</pre></div>
+</div>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="message-flashing">
+<h2>Message Flashing<a class="headerlink" href="#message-flashing" title="Permalink to this headline">¶</a></h2>
+<dl class="function">
+<dt id="flask.flash">
+<tt class="descclassname">flask.</tt><tt class="descname">flash</tt><big>(</big><em>message</em>, <em>category='message'</em><big>)</big><a class="headerlink" href="#flask.flash" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference internal" href="#flask.get_flashed_messages" title="flask.get_flashed_messages"><tt class="xref py py-func docutils literal"><span class="pre">get_flashed_messages()</span></tt></a>.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>message</strong> &#8211; the message to be flashed.</li>
+<li><strong>category</strong> &#8211; the category for the message. The following values
+are recommended: <tt class="docutils literal"><span class="pre">'message'</span></tt> for any kind of message,
+<tt class="docutils literal"><span class="pre">'error'</span></tt> for errors, <tt class="docutils literal"><span class="pre">'info'</span></tt> for information
+messages and <tt class="docutils literal"><span class="pre">'warning'</span></tt> for warnings. However any
+kind of string can be used as category.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.get_flashed_messages">
+<tt class="descclassname">flask.</tt><tt class="descname">get_flashed_messages</tt><big>(</big><em>with_categories=False</em><big>)</big><a class="headerlink" href="#flask.get_flashed_messages" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <cite>with_categories</cite> is set to <cite>True</cite>, the return value will
+be a list of tuples in the form <tt class="docutils literal"><span class="pre">(category,</span> <span class="pre">message)</span></tt> instead.</p>
+<p>Example usage:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">for</span> <span class="nv">category</span><span class="o">,</span> <span class="nv">msg</span> <span class="k">in</span> <span class="nv">get_flashed_messages</span><span class="o">(</span><span class="nv">with_categories</span><span class="o">=</span><span class="kp">true</span><span class="o">)</span> <span class="cp">%}</span>
+ <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">flash-</span><span class="cp">{{</span> <span class="nv">category</span> <span class="cp">}}</span><span class="nt">&gt;</span><span class="cp">{{</span> <span class="nv">msg</span> <span class="cp">}}</span>
+<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.3: </span><cite>with_categories</cite> parameter added.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>with_categories</strong> &#8211; set to <cite>True</cite> to also receive categories.</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+</div>
+<div class="section" id="returning-json">
+<h2>Returning JSON<a class="headerlink" href="#returning-json" title="Permalink to this headline">¶</a></h2>
+<dl class="function">
+<dt id="flask.jsonify">
+<tt class="descclassname">flask.</tt><tt class="descname">jsonify</tt><big>(</big><em>*args</em>, <em>**kwargs</em><big>)</big><a class="headerlink" href="#flask.jsonify" title="Permalink to this definition">¶</a></dt>
+<dd><p>Creates a <a class="reference internal" href="#flask.Response" title="flask.Response"><tt class="xref py py-class docutils literal"><span class="pre">Response</span></tt></a> with the JSON representation of
+the given arguments with an <cite>application/json</cite> mimetype. The arguments
+to this function are the same as to the <a class="reference external" href="http://docs.python.org/dev/library/stdtypes.html#dict" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">dict</span></tt></a> constructor.</p>
+<p>Example usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/_get_current_user&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">get_current_user</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="n">g</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">username</span><span class="p">,</span>
+ <span class="n">email</span><span class="o">=</span><span class="n">g</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
+ <span class="nb">id</span><span class="o">=</span><span class="n">g</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This will send a JSON response like this to the browser:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="p">{</span>
+ <span class="s">&quot;username&quot;</span><span class="p">:</span> <span class="s">&quot;admin&quot;</span><span class="p">,</span>
+ <span class="s">&quot;email&quot;</span><span class="p">:</span> <span class="s">&quot;admin@localhost&quot;</span><span class="p">,</span>
+ <span class="s">&quot;id&quot;</span><span class="p">:</span> <span class="mi">42</span>
+<span class="p">}</span>
+</pre></div>
+</div>
+<p>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 <a class="reference internal" href="security.html#json-security"><em>JSON Security</em></a>.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.2.</span></p>
+</dd></dl>
+
+<dl class="data">
+<dt id="flask.json">
+<tt class="descclassname">flask.</tt><tt class="descname">json</tt><a class="headerlink" href="#flask.json" title="Permalink to this definition">¶</a></dt>
+<dd><p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">try</span><span class="p">:</span>
+ <span class="kn">import</span> <span class="nn">simplejson</span> <span class="kn">as</span> <span class="nn">json</span>
+<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
+ <span class="kn">import</span> <span class="nn">json</span>
+</pre></div>
+</div>
+<p>You can instead just do this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">json</span>
+</pre></div>
+</div>
+<p>For usage examples, read the <a class="reference external" href="http://docs.python.org/dev/library/json.html#json" title="(in Python v3.3)"><tt class="xref py py-mod docutils literal"><span class="pre">json</span></tt></a> documentation.</p>
+<p>The <a class="reference external" href="http://docs.python.org/dev/library/json.html#json.dumps" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">dumps()</span></tt></a> function of this json module is also available
+as filter called <tt class="docutils literal"><span class="pre">|tojson</span></tt> in Jinja2. Note that inside <cite>script</cite>
+tags no escaping must take place, so make sure to disable escaping
+with <tt class="docutils literal"><span class="pre">|safe</span></tt> if you intend to use it inside <cite>script</cite> tags:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span><span class="nt">&gt;</span>
+ <span class="nx">doSomethingWith</span><span class="p">(</span><span class="cp">{{</span> <span class="nv">user.username</span><span class="o">|</span><span class="nf">tojson</span><span class="o">|</span><span class="nf">safe</span> <span class="cp">}}</span><span class="p">);</span>
+<span class="nt">&lt;/script&gt;</span>
+</pre></div>
+</div>
+<p>Note that the <tt class="docutils literal"><span class="pre">|tojson</span></tt> filter escapes forward slashes properly.</p>
+</dd></dl>
+
+</div>
+<div class="section" id="template-rendering">
+<h2>Template Rendering<a class="headerlink" href="#template-rendering" title="Permalink to this headline">¶</a></h2>
+<dl class="function">
+<dt id="flask.render_template">
+<tt class="descclassname">flask.</tt><tt class="descname">render_template</tt><big>(</big><em>template_name</em>, <em>**context</em><big>)</big><a class="headerlink" href="#flask.render_template" title="Permalink to this definition">¶</a></dt>
+<dd><p>Renders a template from the template folder with the given
+context.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>template_name</strong> &#8211; the name of the template to be rendered</li>
+<li><strong>context</strong> &#8211; the variables that should be available in the
+context of the template.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.render_template_string">
+<tt class="descclassname">flask.</tt><tt class="descname">render_template_string</tt><big>(</big><em>source</em>, <em>**context</em><big>)</big><a class="headerlink" href="#flask.render_template_string" title="Permalink to this definition">¶</a></dt>
+<dd><p>Renders a template from the given template source string
+with the given context.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>template_name</strong> &#8211; the sourcecode of the template to be
+rendered</li>
+<li><strong>context</strong> &#8211; the variables that should be available in the
+context of the template.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="function">
+<dt id="flask.get_template_attribute">
+<tt class="descclassname">flask.</tt><tt class="descname">get_template_attribute</tt><big>(</big><em>template_name</em>, <em>attribute</em><big>)</big><a class="headerlink" href="#flask.get_template_attribute" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <cite>_cider.html</cite> with the following contents:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">macro</span> <span class="nv">hello</span><span class="o">(</span><span class="nv">name</span><span class="o">)</span> <span class="cp">%}</span>Hello <span class="cp">{{</span> <span class="nv">name</span> <span class="cp">}}</span>!<span class="cp">{%</span> <span class="k">endmacro</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>You can access this from Python code like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">hello</span> <span class="o">=</span> <span class="n">get_template_attribute</span><span class="p">(</span><span class="s">&#39;_cider.html&#39;</span><span class="p">,</span> <span class="s">&#39;hello&#39;</span><span class="p">)</span>
+<span class="k">return</span> <span class="n">hello</span><span class="p">(</span><span class="s">&#39;World&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.2.</span></p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>template_name</strong> &#8211; the name of the template</li>
+<li><strong>attribute</strong> &#8211; the name of the variable of macro to acccess</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+</div>
+<div class="section" id="configuration">
+<h2>Configuration<a class="headerlink" href="#configuration" title="Permalink to this headline">¶</a></h2>
+<dl class="class">
+<dt id="flask.Config">
+<em class="property">class </em><tt class="descclassname">flask.</tt><tt class="descname">Config</tt><big>(</big><em>root_path</em>, <em>defaults=None</em><big>)</big><a class="headerlink" href="#flask.Config" title="Permalink to this definition">¶</a></dt>
+<dd><p>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.</p>
+<p>Either you can fill the config from a config file:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="s">&#39;yourconfig.cfg&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Or alternatively you can define the configuration options in the
+module that calls <a class="reference internal" href="#flask.Config.from_object" title="flask.Config.from_object"><tt class="xref py py-meth docutils literal"><span class="pre">from_object()</span></tt></a> 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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">DEBUG</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="n">SECRET_KEY</span> <span class="o">=</span> <span class="s">&#39;development key&#39;</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>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.</p>
+<p>Probably the most interesting way to load configurations is from an
+environment variable pointing to a file:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_envvar</span><span class="p">(</span><span class="s">&#39;YOURAPPLICATION_SETTINGS&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>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:</p>
+<div class="highlight-python"><pre>export YOURAPPLICATION_SETTINGS='/path/to/config/file'</pre>
+</div>
+<p>On windows use <cite>set</cite> instead.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>root_path</strong> &#8211; path to which files are read relative from. When the
+config object is created by the application, this is
+the application&#8217;s <tt class="xref py py-attr docutils literal"><span class="pre">root_path</span></tt>.</li>
+<li><strong>defaults</strong> &#8211; an optional dictionary of default values</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+<dl class="method">
+<dt id="flask.Config.from_envvar">
+<tt class="descname">from_envvar</tt><big>(</big><em>variable_name</em>, <em>silent=False</em><big>)</big><a class="headerlink" href="#flask.Config.from_envvar" title="Permalink to this definition">¶</a></dt>
+<dd><p>Loads a configuration from an environment variable pointing to
+a configuration file. This is basically just a shortcut with nicer
+error messages for this line of code:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;YOURAPPLICATION_SETTINGS&#39;</span><span class="p">])</span>
+</pre></div>
+</div>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
+<li><strong>variable_name</strong> &#8211; name of the environment variable</li>
+<li><strong>silent</strong> &#8211; set to <cite>True</cite> if you want silent failure for missing
+files.</li>
+</ul>
+</td>
+</tr>
+<tr class="field-even field"><th class="field-name">Returns:</th><td class="field-body"><p class="first last">bool. <cite>True</cite> if able to load config, <cite>False</cite> otherwise.</p>
+</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Config.from_object">
+<tt class="descname">from_object</tt><big>(</big><em>obj</em><big>)</big><a class="headerlink" href="#flask.Config.from_object" title="Permalink to this definition">¶</a></dt>
+<dd><p>Updates the values from the given object. An object can be of one
+of the following two types:</p>
+<ul class="simple">
+<li>a string: in this case the object with that name will be imported</li>
+<li>an actual object reference: that object is used directly</li>
+</ul>
+<p>Objects are usually either modules or classes.</p>
+<p>Just the uppercase variables in that object are stored in the config.
+Example usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="s">&#39;yourapplication.default_config&#39;</span><span class="p">)</span>
+<span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">default_config</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="n">default_config</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>You should not use this function to load the actual configuration but
+rather configuration defaults. The actual config should be loaded
+with <a class="reference internal" href="#flask.Config.from_pyfile" title="flask.Config.from_pyfile"><tt class="xref py py-meth docutils literal"><span class="pre">from_pyfile()</span></tt></a> and ideally from a location not within the
+package because the package might be installed system wide.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>obj</strong> &#8211; an import name or object</td>
+</tr>
+</tbody>
+</table>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.Config.from_pyfile">
+<tt class="descname">from_pyfile</tt><big>(</big><em>filename</em>, <em>silent=False</em><big>)</big><a class="headerlink" href="#flask.Config.from_pyfile" title="Permalink to this definition">¶</a></dt>
+<dd><p>Updates the values in the config from a Python file. This function
+behaves as if the file was imported as module with the
+<a class="reference internal" href="#flask.Config.from_object" title="flask.Config.from_object"><tt class="xref py py-meth docutils literal"><span class="pre">from_object()</span></tt></a> function.</p>
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
+<li><strong>filename</strong> &#8211; the filename of the config. This can either be an
+absolute filename or a filename relative to the
+root path.</li>
+<li><strong>silent</strong> &#8211; set to <cite>True</cite> if you want silent failure for missing
+files.</li>
+</ul>
+</td>
+</tr>
+</tbody>
+</table>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7: </span><cite>silent</cite> parameter.</p>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="extensions">
+<h2>Extensions<a class="headerlink" href="#extensions" title="Permalink to this headline">¶</a></h2>
+<dl class="data">
+<dt id="flask.flask.ext">
+<tt class="descclassname">flask.</tt><tt class="descname">ext</tt><a class="headerlink" href="#flask.flask.ext" title="Permalink to this definition">¶</a></dt>
+<dd><p>This module acts as redirect import module to Flask extensions. It was
+added in 0.8 as the canonical way to import Flask extensions and makes
+it possible for us to have more flexibility in how we distribute
+extensions.</p>
+<p>If you want to use an extension named “Flask-Foo” you would import it
+from <a class="reference internal" href="#flask.flask.ext" title="flask.flask.ext"><tt class="xref py py-data docutils literal"><span class="pre">ext</span></tt></a> as follows:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask.ext</span> <span class="kn">import</span> <span class="n">foo</span>
+</pre></div>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+</div>
+<div class="section" id="useful-internals">
+<h2>Useful Internals<a class="headerlink" href="#useful-internals" title="Permalink to this headline">¶</a></h2>
+<dl class="class">
+<dt id="flask.ctx.RequestContext">
+<em class="property">class </em><tt class="descclassname">flask.ctx.</tt><tt class="descname">RequestContext</tt><big>(</big><em>app</em>, <em>environ</em><big>)</big><a class="headerlink" href="#flask.ctx.RequestContext" title="Permalink to this definition">¶</a></dt>
+<dd><p>The request context contains all request relevant information. It is
+created at the beginning of the request and pushed to the
+<cite>_request_ctx_stack</cite> and removed at the end of it. It will create the
+URL adapter and request object for the WSGI environment provided.</p>
+<p>Do not attempt to use this class directly, instead use
+<a class="reference internal" href="#flask.Flask.test_request_context" title="flask.Flask.test_request_context"><tt class="xref py py-meth docutils literal"><span class="pre">test_request_context()</span></tt></a> and
+<a class="reference internal" href="#flask.Flask.request_context" title="flask.Flask.request_context"><tt class="xref py py-meth docutils literal"><span class="pre">request_context()</span></tt></a> to create this object.</p>
+<p>When the request context is popped, it will evaluate all the
+functions registered on the application for teardown execution
+(<a class="reference internal" href="#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a>).</p>
+<p>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 <cite>DEBUG</cite> mode. By setting
+<tt class="docutils literal"><span class="pre">'flask._preserve_context'</span></tt> to <cite>True</cite> on the WSGI environment the
+context will not pop itself at the end of the request. This is used by
+the <a class="reference internal" href="#flask.Flask.test_client" title="flask.Flask.test_client"><tt class="xref py py-meth docutils literal"><span class="pre">test_client()</span></tt></a> for example to implement the
+deferred cleanup functionality.</p>
+<p>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 <tt class="xref py py-meth docutils literal"><span class="pre">pop()</span></tt> the stack yourself in
+that situation, otherwise your unittests will leak memory.</p>
+<dl class="method">
+<dt id="flask.ctx.RequestContext.match_request">
+<tt class="descname">match_request</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.ctx.RequestContext.match_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Can be overridden by a subclass to hook into the matching
+of the request.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.ctx.RequestContext.pop">
+<tt class="descname">pop</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.ctx.RequestContext.pop" title="Permalink to this definition">¶</a></dt>
+<dd><p>Pops the request context and unbinds it by doing that. This will
+also trigger the execution of functions registered by the
+<a class="reference internal" href="#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> decorator.</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.ctx.RequestContext.push">
+<tt class="descname">push</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.ctx.RequestContext.push" title="Permalink to this definition">¶</a></dt>
+<dd><p>Binds the request context to the current context.</p>
+</dd></dl>
+
+</dd></dl>
+
+<dl class="data">
+<dt id="flask._request_ctx_stack">
+<tt class="descclassname">flask.</tt><tt class="descname">_request_ctx_stack</tt><a class="headerlink" href="#flask._request_ctx_stack" title="Permalink to this definition">¶</a></dt>
+<dd><p>The internal <a class="reference external" href="http://werkzeug.pocoo.org/docs/local/#werkzeug.local.LocalStack" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">LocalStack</span></tt></a> 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.</p>
+<p>The following attributes are always present on each layer of the
+stack:</p>
+<dl class="docutils">
+<dt><cite>app</cite></dt>
+<dd>the active Flask application.</dd>
+<dt><cite>url_adapter</cite></dt>
+<dd>the URL adapter that was used to match the request.</dd>
+<dt><cite>request</cite></dt>
+<dd>the current request object.</dd>
+<dt><cite>session</cite></dt>
+<dd>the active session object.</dd>
+<dt><cite>g</cite></dt>
+<dd>an object with all the attributes of the <a class="reference internal" href="#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">flask.g</span></tt></a> object.</dd>
+<dt><cite>flashes</cite></dt>
+<dd>an internal cache for the flashed messages.</dd>
+</dl>
+<p>Example usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">_request_ctx_stack</span>
+
+<span class="k">def</span> <span class="nf">get_session</span><span class="p">():</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="k">if</span> <span class="n">ctx</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">ctx</span><span class="o">.</span><span class="n">session</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.blueprints.BlueprintSetupState">
+<em class="property">class </em><tt class="descclassname">flask.blueprints.</tt><tt class="descname">BlueprintSetupState</tt><big>(</big><em>blueprint</em>, <em>app</em>, <em>options</em>, <em>first_registration</em><big>)</big><a class="headerlink" href="#flask.blueprints.BlueprintSetupState" title="Permalink to this definition">¶</a></dt>
+<dd><p>Temporary holder object for registering a blueprint with the
+application. An instance of this class is created by the
+<a class="reference internal" href="#flask.Blueprint.make_setup_state" title="flask.Blueprint.make_setup_state"><tt class="xref py py-meth docutils literal"><span class="pre">make_setup_state()</span></tt></a> method and later passed
+to all register callback functions.</p>
+<dl class="method">
+<dt id="flask.blueprints.BlueprintSetupState.add_url_rule">
+<tt class="descname">add_url_rule</tt><big>(</big><em>rule</em>, <em>endpoint=None</em>, <em>view_func=None</em>, <em>**options</em><big>)</big><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.add_url_rule" title="Permalink to this definition">¶</a></dt>
+<dd><p>A helper method to register a rule (and optionally a view function)
+to the application. The endpoint is automatically prefixed with the
+blueprint&#8217;s name.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.blueprints.BlueprintSetupState.app">
+<tt class="descname">app</tt><em class="property"> = None</em><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.app" title="Permalink to this definition">¶</a></dt>
+<dd><p>a reference to the current application</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.blueprints.BlueprintSetupState.blueprint">
+<tt class="descname">blueprint</tt><em class="property"> = None</em><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.blueprint" title="Permalink to this definition">¶</a></dt>
+<dd><p>a reference to the blurprint that created this setup state.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.blueprints.BlueprintSetupState.first_registration">
+<tt class="descname">first_registration</tt><em class="property"> = None</em><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.first_registration" title="Permalink to this definition">¶</a></dt>
+<dd><p>as blueprints can be registered multiple times with the
+application and not everything wants to be registered
+multiple times on it, this attribute can be used to figure
+out if the blueprint was registered in the past already.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.blueprints.BlueprintSetupState.options">
+<tt class="descname">options</tt><em class="property"> = None</em><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.options" title="Permalink to this definition">¶</a></dt>
+<dd><p>a dictionary with all options that were passed to the
+<a class="reference internal" href="#flask.Flask.register_blueprint" title="flask.Flask.register_blueprint"><tt class="xref py py-meth docutils literal"><span class="pre">register_blueprint()</span></tt></a> method.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.blueprints.BlueprintSetupState.subdomain">
+<tt class="descname">subdomain</tt><em class="property"> = None</em><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.subdomain" title="Permalink to this definition">¶</a></dt>
+<dd><p>The subdomain that the blueprint should be active for, <cite>None</cite>
+otherwise.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.blueprints.BlueprintSetupState.url_defaults">
+<tt class="descname">url_defaults</tt><em class="property"> = None</em><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.url_defaults" title="Permalink to this definition">¶</a></dt>
+<dd><p>A dictionary with URL defaults that is added to each and every
+URL that was defined with the blueprint.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.blueprints.BlueprintSetupState.url_prefix">
+<tt class="descname">url_prefix</tt><em class="property"> = None</em><a class="headerlink" href="#flask.blueprints.BlueprintSetupState.url_prefix" title="Permalink to this definition">¶</a></dt>
+<dd><p>The prefix that should be used for all URLs defined on the
+blueprint.</p>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="signals">
+<h2>Signals<a class="headerlink" href="#signals" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6.</span></p>
+<dl class="data">
+<dt id="flask.signals_available">
+<tt class="descclassname">flask.</tt><tt class="descname">signals_available</tt><a class="headerlink" href="#flask.signals_available" title="Permalink to this definition">¶</a></dt>
+<dd><p><cite>True</cite> if the signalling system is available. This is the case
+when <a class="reference external" href="http://pypi.python.org/pypi/blinker">blinker</a> is installed.</p>
+</dd></dl>
+
+<dl class="data">
+<dt id="flask.template_rendered">
+<tt class="descclassname">flask.</tt><tt class="descname">template_rendered</tt><a class="headerlink" href="#flask.template_rendered" title="Permalink to this definition">¶</a></dt>
+<dd><p>This signal is sent when a template was successfully rendered. The
+signal is invoked with the instance of the template as <cite>template</cite>
+and the context as dictionary (named <cite>context</cite>).</p>
+</dd></dl>
+
+<dl class="data">
+<dt id="flask.request_started">
+<tt class="descclassname">flask.</tt><tt class="descname">request_started</tt><a class="headerlink" href="#flask.request_started" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference internal" href="#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a>.</p>
+</dd></dl>
+
+<dl class="data">
+<dt id="flask.request_finished">
+<tt class="descclassname">flask.</tt><tt class="descname">request_finished</tt><a class="headerlink" href="#flask.request_finished" title="Permalink to this definition">¶</a></dt>
+<dd><p>This signal is sent right before the response is sent to the client.
+It is passed the response to be sent named <cite>response</cite>.</p>
+</dd></dl>
+
+<dl class="data">
+<dt id="flask.got_request_exception">
+<tt class="descclassname">flask.</tt><tt class="descname">got_request_exception</tt><a class="headerlink" href="#flask.got_request_exception" title="Permalink to this definition">¶</a></dt>
+<dd><p>This signal is sent when an exception happens during request processing.
+It is sent <em>before</em> 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 <cite>exception</cite>.</p>
+</dd></dl>
+
+<dl class="data">
+<dt id="flask.request_tearing_down">
+<tt class="descclassname">flask.</tt><tt class="descname">request_tearing_down</tt><a class="headerlink" href="#flask.request_tearing_down" title="Permalink to this definition">¶</a></dt>
+<dd><p>This signal is sent when the application is tearing down the request.
+This is always called, even if an error happened. No arguments are
+provided.</p>
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.signals.Namespace">
+<em class="property">class </em><tt class="descclassname">flask.signals.</tt><tt class="descname">Namespace</tt><a class="headerlink" href="#flask.signals.Namespace" title="Permalink to this definition">¶</a></dt>
+<dd><p>An alias for <a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Namespace" title="(in Blinker v1.1)"><tt class="xref py py-class docutils literal"><span class="pre">blinker.base.Namespace</span></tt></a> 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.</p>
+<dl class="method">
+<dt id="flask.signals.Namespace.signal">
+<tt class="descname">signal</tt><big>(</big><em>name</em>, <em>doc=None</em><big>)</big><a class="headerlink" href="#flask.signals.Namespace.signal" title="Permalink to this definition">¶</a></dt>
+<dd><p>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 <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#RuntimeError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">RuntimeError</span></tt></a> for all other
+operations, including connecting.</p>
+</dd></dl>
+
+</dd></dl>
+
+</div>
+<div class="section" id="class-based-views">
+<h2>Class Based Views<a class="headerlink" href="#class-based-views" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+<dl class="class">
+<dt id="flask.views.View">
+<em class="property">class </em><tt class="descclassname">flask.views.</tt><tt class="descname">View</tt><a class="headerlink" href="#flask.views.View" title="Permalink to this definition">¶</a></dt>
+<dd><p>Alternative way to use view functions. A subclass has to implement
+<tt class="xref py py-meth docutils literal"><span class="pre">dispatch_request()</span></tt> which is called with the view arguments from
+the URL routing system. If <tt class="xref py py-attr docutils literal"><span class="pre">methods</span></tt> is provided the methods
+do not have to be passed to the <a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a>
+method explicitly:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">MyView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
+ <span class="n">methods</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">]</span>
+
+ <span class="k">def</span> <span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;Hello </span><span class="si">%s</span><span class="s">!&#39;</span> <span class="o">%</span> <span class="n">name</span>
+
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/hello/&lt;name&gt;&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">MyView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s">&#39;myview&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>When you want to decorate a pluggable view you will have to either do that
+when the view function is created (by wrapping the return value of
+<tt class="xref py py-meth docutils literal"><span class="pre">as_view()</span></tt>) or you can use the <tt class="xref py py-attr docutils literal"><span class="pre">decorators</span></tt> attribute:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">SecretView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
+ <span class="n">methods</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">]</span>
+ <span class="n">decorators</span> <span class="o">=</span> <span class="p">[</span><span class="n">superuser_required</span><span class="p">]</span>
+
+ <span class="k">def</span> <span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+<p>The decorators stored in the decorators list are applied one after another
+when the view function is created. Note that you can <em>not</em> use the class
+based decorators since those would decorate the view class and not the
+generated view function!</p>
+<dl class="classmethod">
+<dt id="flask.views.View.as_view">
+<em class="property">classmethod </em><tt class="descname">as_view</tt><big>(</big><em>name</em>, <em>*class_args</em>, <em>**class_kwargs</em><big>)</big><a class="headerlink" href="#flask.views.View.as_view" title="Permalink to this definition">¶</a></dt>
+<dd><p>Converts the class into an actual view function that can be
+used with the routing system. What it does internally is generating
+a function on the fly that will instanciate the <tt class="xref py py-class docutils literal"><span class="pre">View</span></tt>
+on each request and call the <tt class="xref py py-meth docutils literal"><span class="pre">dispatch_request()</span></tt> method on it.</p>
+<p>The arguments passed to <tt class="xref py py-meth docutils literal"><span class="pre">as_view()</span></tt> are forwarded to the
+constructor of the class.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.views.View.decorators">
+<tt class="descname">decorators</tt><em class="property"> = []</em><a class="headerlink" href="#flask.views.View.decorators" title="Permalink to this definition">¶</a></dt>
+<dd><p>The canonical way to decorate class based views is to decorate the
+return value of as_view(). However since this moves parts of the
+logic from the class declaration to the place where it&#8217;s hooked
+into the routing system.</p>
+<p>You can place one or more decorators in this list and whenever the
+view function is created the result is automatically decorated.</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+</dd></dl>
+
+<dl class="method">
+<dt id="flask.views.View.dispatch_request">
+<tt class="descname">dispatch_request</tt><big>(</big><big>)</big><a class="headerlink" href="#flask.views.View.dispatch_request" title="Permalink to this definition">¶</a></dt>
+<dd><p>Subclasses have to override this method to implement the
+actual view function code. This method is called with all
+the arguments from the URL rule.</p>
+</dd></dl>
+
+<dl class="attribute">
+<dt id="flask.views.View.methods">
+<tt class="descname">methods</tt><em class="property"> = None</em><a class="headerlink" href="#flask.views.View.methods" title="Permalink to this definition">¶</a></dt>
+<dd><p>A for which methods this pluggable view can handle.</p>
+</dd></dl>
+
+</dd></dl>
+
+<dl class="class">
+<dt id="flask.views.MethodView">
+<em class="property">class </em><tt class="descclassname">flask.views.</tt><tt class="descname">MethodView</tt><a class="headerlink" href="#flask.views.MethodView" title="Permalink to this definition">¶</a></dt>
+<dd><p>Like a regular class based view but that dispatches requests to
+particular methods. For instance if you implement a method called
+<tt class="xref py py-meth docutils literal"><span class="pre">get()</span></tt> it means you will response to <tt class="docutils literal"><span class="pre">'GET'</span></tt> requests and
+the <tt class="xref py py-meth docutils literal"><span class="pre">dispatch_request()</span></tt> implementation will automatically
+forward your request to that. Also <tt class="xref py py-attr docutils literal"><span class="pre">options</span></tt> is set for you
+automatically:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">CounterAPI</span><span class="p">(</span><span class="n">MethodView</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;counter&#39;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">session</span><span class="p">[</span><span class="s">&#39;counter&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;counter&#39;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>
+ <span class="k">return</span> <span class="s">&#39;OK&#39;</span>
+
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/counter&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">CounterAPI</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s">&#39;counter&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+</dd></dl>
+
+</div>
+<div class="section" id="url-route-registrations">
+<span id="id2"></span><h2>URL Route Registrations<a class="headerlink" href="#url-route-registrations" title="Permalink to this headline">¶</a></h2>
+<p>Generally there are three ways to define rules for the routing system:</p>
+<ol class="arabic simple">
+<li>You can use the <a class="reference internal" href="#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">flask.Flask.route()</span></tt></a> decorator.</li>
+<li>You can use the <a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">flask.Flask.add_url_rule()</span></tt></a> function.</li>
+<li>You can directly access the underlying Werkzeug routing system
+which is exposed as <a class="reference internal" href="#flask.Flask.url_map" title="flask.Flask.url_map"><tt class="xref py py-attr docutils literal"><span class="pre">flask.Flask.url_map</span></tt></a>.</li>
+</ol>
+<p>Variable parts in the route can be specified with angular brackets
+(<tt class="docutils literal"><span class="pre">/user/&lt;username&gt;</span></tt>). 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 <tt class="docutils literal"><span class="pre">&lt;converter:name&gt;</span></tt>.</p>
+<p>Variable parts are passed to the view function as keyword arguments.</p>
+<p>The following converters are available:</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="19%" />
+<col width="81%" />
+</colgroup>
+<tbody valign="top">
+<tr class="row-odd"><td><cite>unicode</cite></td>
+<td>accepts any text without a slash (the default)</td>
+</tr>
+<tr class="row-even"><td><cite>int</cite></td>
+<td>accepts integers</td>
+</tr>
+<tr class="row-odd"><td><cite>float</cite></td>
+<td>like <cite>int</cite> but for floating point values</td>
+</tr>
+<tr class="row-even"><td><cite>path</cite></td>
+<td>like the default but also accepts slashes</td>
+</tr>
+</tbody>
+</table>
+<p>Here are some examples:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&lt;username&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_user</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
+ <span class="k">pass</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/post/&lt;int:post_id&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_post</span><span class="p">(</span><span class="n">post_id</span><span class="p">):</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>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:</p>
+<ol class="arabic simple">
+<li>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.</li>
+<li>If a rule does not end with a trailing slash and the user requests the
+page with a trailing slash, a 404 not found is raised.</li>
+</ol>
+<p>This is consistent with how web servers deal with static files. This
+also makes it possible to use relative link targets safely.</p>
+<p>You can also define multiple rules for the same function. They have to be
+unique however. Defaults can also be specified. Here for example is a
+definition for a URL that accepts an optional page:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">&#39;page&#39;</span><span class="p">:</span> <span class="mi">1</span><span class="p">})</span>
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/users/page/&lt;int:page&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_users</span><span class="p">(</span><span class="n">page</span><span class="p">):</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>This specifies that <tt class="docutils literal"><span class="pre">/users/</span></tt> will be the URL for page one and
+<tt class="docutils literal"><span class="pre">/users/page/N</span></tt> will be the URL for page <cite>N</cite>.</p>
+<p>Here are the parameters that <a class="reference internal" href="#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a> and
+<a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a> accept. The only difference is that
+with the route parameter the view function is defined with the decorator
+instead of the <cite>view_func</cite> parameter.</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="21%" />
+<col width="79%" />
+</colgroup>
+<tbody valign="top">
+<tr class="row-odd"><td><cite>rule</cite></td>
+<td>the URL roule as string</td>
+</tr>
+<tr class="row-even"><td><cite>endpoint</cite></td>
+<td>the endpoint for the registered URL rule. Flask itself
+assumes that the name of the view function is the name
+of the endpoint if not explicitly stated.</td>
+</tr>
+<tr class="row-odd"><td><cite>view_func</cite></td>
+<td>the function to call when serving a request to the
+provided endpoint. If this is not provided one can
+specify the function later by storing it in the
+<a class="reference internal" href="#flask.Flask.view_functions" title="flask.Flask.view_functions"><tt class="xref py py-attr docutils literal"><span class="pre">view_functions</span></tt></a> dictionary with the
+endpoint as key.</td>
+</tr>
+<tr class="row-even"><td><cite>defaults</cite></td>
+<td>A dictionary with defaults for this rule. See the
+example above for how defaults work.</td>
+</tr>
+<tr class="row-odd"><td><cite>subdomain</cite></td>
+<td>specifies the rule for the subdomain in case subdomain
+matching is in use. If not specified the default
+subdomain is assumed.</td>
+</tr>
+<tr class="row-even"><td><cite>**options</cite></td>
+<td>the options to be forwarded to the underlying
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/routing/#werkzeug.routing.Rule" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Rule</span></tt></a> object. A change to
+Werkzeug is handling of method options. methods is a list
+of methods this rule should be limited to (<cite>GET</cite>, <cite>POST</cite>
+etc.). By default a rule just listens for <cite>GET</cite> (and
+implicitly <cite>HEAD</cite>). Starting with Flask 0.6, <cite>OPTIONS</cite> is
+implicitly added and handled by the standard request
+handling. They have to be specified as keyword arguments.</td>
+</tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="view-function-options">
+<span id="view-func-options"></span><h2>View Function Options<a class="headerlink" href="#view-function-options" title="Permalink to this headline">¶</a></h2>
+<p>For internal usage the view functions can have some attributes attached to
+customize behavior the view function would normally not have control over.
+The following attributes can be provided optionally to either override
+some defaults to <a class="reference internal" href="#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a> or general behavior:</p>
+<ul class="simple">
+<li><cite>__name__</cite>: The name of a function is by default used as endpoint. If
+endpoint is provided explicitly this value is used. Additionally this
+will be prefixed with the name of the blueprint by default which
+cannot be customized from the function itself.</li>
+<li><cite>methods</cite>: If methods are not provided when the URL rule is added,
+Flask will look on the view function object itself is an <cite>methods</cite>
+attribute exists. If it does, it will pull the information for the
+methods from there.</li>
+<li><cite>provide_automatic_options</cite>: if this attribute is set Flask will
+either force enable or disable the automatic implementation of the
+HTTP <cite>OPTIONS</cite> response. This can be useful when working with
+decorators that want to customize the <cite>OPTIONS</cite> response on a per-view
+basis.</li>
+</ul>
+<p>Full example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;OPTIONS&#39;</span><span class="p">:</span>
+ <span class="c"># custom options handling here</span>
+ <span class="o">...</span>
+ <span class="k">return</span> <span class="s">&#39;Hello World!&#39;</span>
+<span class="n">index</span><span class="o">.</span><span class="n">provide_automatic_options</span> <span class="o">=</span> <span class="bp">False</span>
+<span class="n">index</span><span class="o">.</span><span class="n">methods</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;OPTIONS&#39;</span><span class="p">]</span>
+
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">index</span><span class="p">)</span>
+</pre></div>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8: </span>The <cite>provide_automatic_options</cite> functionality was added.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">API</a><ul>
+<li><a class="reference internal" href="#application-object">Application Object</a></li>
+<li><a class="reference internal" href="#blueprint-objects">Blueprint Objects</a></li>
+<li><a class="reference internal" href="#incoming-request-data">Incoming Request Data</a></li>
+<li><a class="reference internal" href="#response-objects">Response Objects</a></li>
+<li><a class="reference internal" href="#sessions">Sessions</a></li>
+<li><a class="reference internal" href="#session-interface">Session Interface</a></li>
+<li><a class="reference internal" href="#test-client">Test Client</a></li>
+<li><a class="reference internal" href="#application-globals">Application Globals</a></li>
+<li><a class="reference internal" href="#useful-functions-and-classes">Useful Functions and Classes</a></li>
+<li><a class="reference internal" href="#message-flashing">Message Flashing</a></li>
+<li><a class="reference internal" href="#returning-json">Returning JSON</a></li>
+<li><a class="reference internal" href="#template-rendering">Template Rendering</a></li>
+<li><a class="reference internal" href="#configuration">Configuration</a></li>
+<li><a class="reference internal" href="#extensions">Extensions</a></li>
+<li><a class="reference internal" href="#useful-internals">Useful Internals</a></li>
+<li><a class="reference internal" href="#signals">Signals</a></li>
+<li><a class="reference internal" href="#class-based-views">Class Based Views</a></li>
+<li><a class="reference internal" href="#url-route-registrations">URL Route Registrations</a></li>
+<li><a class="reference internal" href="#view-function-options">View Function Options</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="becomingbig.html" title="previous chapter">Becoming Big</a></li>
+ <li>Next: <a href="design.html" title="next chapter">Design Decisions in Flask</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/api.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/becomingbig.html b/studio/static/doc/flask-docs/becomingbig.html
new file mode 100644
index 0000000..1d44906
--- /dev/null
+++ b/studio/static/doc/flask-docs/becomingbig.html
@@ -0,0 +1,187 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Becoming Big &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="API" href="api.html" />
+ <link rel="prev" title="Other Servers" href="deploying/others.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="api.html" title="API"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="deploying/others.html" title="Other Servers"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="becoming-big">
+<span id="becomingbig"></span><h1>Becoming Big<a class="headerlink" href="#becoming-big" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>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&#8217;s very easy to take the
+code from Flask and put it into a new module within the applications and
+expand on that.</p>
+<p>Flask is designed to be extended and modified in a couple of different
+ways:</p>
+<ul class="simple">
+<li>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.</li>
+<li>Subclassing. The majority of functionality can be changed by creating
+a new subclass of the <a class="reference internal" href="api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> class and overriding
+methods provided for this exact purpose.</li>
+<li>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&#8217;s code and rename it (for example to
+<cite>framework</cite>). Then you can start modifying the code in there.</li>
+</ul>
+<div class="section" id="why-consider-forking">
+<h2>Why consider Forking?<a class="headerlink" href="#why-consider-forking" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>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&#8217;t have to contribute any
+changes back if you decide to modify the framework.</p>
+<p>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.</p>
+</div>
+<div class="section" id="scaling-like-a-pro">
+<h2>Scaling like a Pro<a class="headerlink" href="#scaling-like-a-pro" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>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&#8217;t perform any better or would not even
+support a second server.</p>
+<p>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.</p>
+</div>
+<div class="section" id="dialogue-with-the-community">
+<h2>Dialogue with the Community<a class="headerlink" href="#dialogue-with-the-community" title="Permalink to this headline">¶</a></h2>
+<p>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&#8217;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.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Becoming Big</a><ul>
+<li><a class="reference internal" href="#why-consider-forking">Why consider Forking?</a></li>
+<li><a class="reference internal" href="#scaling-like-a-pro">Scaling like a Pro</a></li>
+<li><a class="reference internal" href="#dialogue-with-the-community">Dialogue with the Community</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="deploying/others.html" title="previous chapter">Other Servers</a></li>
+ <li>Next: <a href="api.html" title="next chapter">API</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/becomingbig.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/blueprints.html b/studio/static/doc/flask-docs/blueprints.html
new file mode 100644
index 0000000..271aa3d
--- /dev/null
+++ b/studio/static/doc/flask-docs/blueprints.html
@@ -0,0 +1,306 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Modular Applications with Blueprints &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Flask Extensions" href="extensions.html" />
+ <link rel="prev" title="The Request Context" href="reqcontext.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="extensions.html" title="Flask Extensions"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="reqcontext.html" title="The Request Context"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="modular-applications-with-blueprints">
+<span id="blueprints"></span><h1>Modular Applications with Blueprints<a class="headerlink" href="#modular-applications-with-blueprints" title="Permalink to this headline">¶</a></h1>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+<p>Flask uses a concept of <em>blueprints</em> for making application components and
+supporting common patterns within an application or across applications.
+Blueprints can greatly simplify how large applications work and provide a
+central means for Flask extensions to register operations on applications.
+A <tt class="xref py py-class docutils literal"><span class="pre">Blueprint</span></tt> object works similarly to a <tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt>
+application object, but it is not actually an application. Rather it is a
+<em>blueprint</em> of how to construct or extend an application.</p>
+<div class="section" id="why-blueprints">
+<h2>Why Blueprints?<a class="headerlink" href="#why-blueprints" title="Permalink to this headline">¶</a></h2>
+<p>Blueprints in Flask are intended for these cases:</p>
+<ul class="simple">
+<li>Factor an application into a set of blueprints. This is ideal for
+larger applications; a project could instantiate an application object,
+initialize several extensions, and register a collection of blueprints.</li>
+<li>Register a blueprint on an application at a URL prefix and/or subdomain.
+Parameters in the URL prefix/subdomain become common view arguments
+(with defaults) across all view functions in the blueprint.</li>
+<li>Register a blueprint multiple times on an application with different URL
+rules.</li>
+<li>Provide template filters, static files, templates, and other utilities
+through blueprints. A blueprint does not have to implement applications
+or view functions.</li>
+<li>Register a blueprint on an application for any of these cases when
+initializing a Flask extension.</li>
+</ul>
+<p>A blueprint in Flask is not a pluggable app because it is not actually an
+application &#8211; it&#8217;s a set of operations which can be registered on an
+application, even multiple times. Why not have multiple application
+objects? You can do that (see <a class="reference internal" href="patterns/appdispatch.html#app-dispatch"><em>Application Dispatching</em></a>), but your applications
+will have separate configs and will be managed at the WSGI layer.</p>
+<p>Blueprints instead provide separation at the Flask level, share
+application config, and can change an application object as necessary with
+being registered. The downside is that you cannot unregister a blueprint
+once an application was created without having to destroy the whole
+application object.</p>
+</div>
+<div class="section" id="the-concept-of-blueprints">
+<h2>The Concept of Blueprints<a class="headerlink" href="#the-concept-of-blueprints" title="Permalink to this headline">¶</a></h2>
+<p>The basic concept of blueprints is that they record operations to execute
+when registered on an application. Flask associates view functions with
+blueprints when dispatching requests and generating URLs from one endpoint
+to another.</p>
+</div>
+<div class="section" id="my-first-blueprint">
+<h2>My First Blueprint<a class="headerlink" href="#my-first-blueprint" title="Permalink to this headline">¶</a></h2>
+<p>This is what a very basic blueprint looks like. In this case we want to
+implement a blueprint that does simple rendering of static templates:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Blueprint</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">abort</span>
+<span class="kn">from</span> <span class="nn">jinja2</span> <span class="kn">import</span> <span class="n">TemplateNotFound</span>
+
+<span class="n">simple_page</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">&#39;simple_page&#39;</span><span class="p">,</span> <span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@simple_page.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">&#39;page&#39;</span><span class="p">:</span> <span class="s">&#39;index&#39;</span><span class="p">})</span>
+<span class="nd">@simple_page.route</span><span class="p">(</span><span class="s">&#39;/&lt;page&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show</span><span class="p">(</span><span class="n">page</span><span class="p">):</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;pages/</span><span class="si">%s</span><span class="s">.html&#39;</span> <span class="o">%</span> <span class="n">page</span><span class="p">)</span>
+ <span class="k">except</span> <span class="n">TemplateNotFound</span><span class="p">:</span>
+ <span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>When you bind a function with the help of the <tt class="docutils literal"><span class="pre">&#64;simple_page.route</span></tt>
+decorator the blueprint will record the intention of registering the
+function <cite>show</cite> on the application when it&#8217;s later registered.
+Additionally it will prefix the endpoint of the function with the
+name of the blueprint which was given to the <tt class="xref py py-class docutils literal"><span class="pre">Blueprint</span></tt>
+constructor (in this case also <tt class="docutils literal"><span class="pre">simple_page</span></tt>).</p>
+</div>
+<div class="section" id="registering-blueprints">
+<h2>Registering Blueprints<a class="headerlink" href="#registering-blueprints" title="Permalink to this headline">¶</a></h2>
+<p>So how do you register that blueprint? Like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="kn">from</span> <span class="nn">yourapplication.simple_page</span> <span class="kn">import</span> <span class="n">simple_page</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">register_blueprint</span><span class="p">(</span><span class="n">simple_page</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>If you check the rules registered on the application, you will find
+these:</p>
+<div class="highlight-python"><pre>[&lt;Rule '/static/&lt;filename&gt;' (HEAD, OPTIONS, GET) -&gt; static&gt;,
+ &lt;Rule '/&lt;page&gt;' (HEAD, OPTIONS, GET) -&gt; simple_page.show&gt;,
+ &lt;Rule '/' (HEAD, OPTIONS, GET) -&gt; simple_page.show&gt;]</pre>
+</div>
+<p>The first one is obviously from the application ifself for the static
+files. The other two are for the <cite>show</cite> function of the <tt class="docutils literal"><span class="pre">simple_page</span></tt>
+blueprint. As you can see, they are also prefixed with the name of the
+blueprint and separated by a dot (<tt class="docutils literal"><span class="pre">.</span></tt>).</p>
+<p>Blueprints however can also be mounted at different locations:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">register_blueprint</span><span class="p">(</span><span class="n">simple_page</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s">&#39;/pages&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>And sure enough, these are the generated rules:</p>
+<div class="highlight-python"><pre>[&lt;Rule '/static/&lt;filename&gt;' (HEAD, OPTIONS, GET) -&gt; static&gt;,
+ &lt;Rule '/pages/&lt;page&gt;' (HEAD, OPTIONS, GET) -&gt; simple_page.show&gt;,
+ &lt;Rule '/pages/' (HEAD, OPTIONS, GET) -&gt; simple_page.show&gt;]</pre>
+</div>
+<p>On top of that you can register blueprints multiple times though not every
+blueprint might respond properly to that. In fact it depends on how the
+blueprint is implemented if it can be mounted more than once.</p>
+</div>
+<div class="section" id="blueprint-resources">
+<h2>Blueprint Resources<a class="headerlink" href="#blueprint-resources" title="Permalink to this headline">¶</a></h2>
+<p>Blueprints can provide resources as well. Sometimes you might want to
+introduce a blueprint only for the resources it provides.</p>
+<div class="section" id="blueprint-resource-folder">
+<h3>Blueprint Resource Folder<a class="headerlink" href="#blueprint-resource-folder" title="Permalink to this headline">¶</a></h3>
+<p>Like for regular applications, blueprints are considered to be contained
+in a folder. While multiple blueprints can originate from the same folder,
+it does not have to be the case and it&#8217;s usually not recommended.</p>
+<p>The folder is inferred from the second argument to <tt class="xref py py-class docutils literal"><span class="pre">Blueprint</span></tt> which
+is usually <cite>__name__</cite>. This argument specifies what logical Python
+module or package corresponds to the blueprint. If it points to an actual
+Python package that package (which is a folder on the filesystem) is the
+resource folder. If it&#8217;s a module, the package the module is contained in
+will be the resource folder. You can access the
+<tt class="xref py py-attr docutils literal"><span class="pre">Blueprint.root_path</span></tt> property to see what the resource folder is:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">simple_page</span><span class="o">.</span><span class="n">root_path</span>
+<span class="go">&#39;/Users/username/TestProject/yourapplication&#39;</span>
+</pre></div>
+</div>
+<p>To quickly open sources from this folder you can use the
+<tt class="xref py py-meth docutils literal"><span class="pre">open_resource()</span></tt> function:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">simple_page</span><span class="o">.</span><span class="n">open_resource</span><span class="p">(</span><span class="s">&#39;static/style.css&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">code</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="static-files">
+<h3>Static Files<a class="headerlink" href="#static-files" title="Permalink to this headline">¶</a></h3>
+<p>A blueprint can expose a folder with static files by providing a path to a
+folder on the filesystem via the <cite>static_folder</cite> keyword argument. It can
+either be an absolute path or one relative to the folder of the
+blueprint:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">admin</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">static_folder</span><span class="o">=</span><span class="s">&#39;static&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>By default the rightmost part of the path is where it is exposed on the
+web. Because the folder is called <tt class="docutils literal"><span class="pre">static</span></tt> here it will be available at
+the location of the blueprint + <tt class="docutils literal"><span class="pre">/static</span></tt>. Say the blueprint is
+registered for <tt class="docutils literal"><span class="pre">/admin</span></tt> the static folder will be at <tt class="docutils literal"><span class="pre">/admin/static</span></tt>.</p>
+<p>The endpoint is named <cite>blueprint_name.static</cite> so you can generate URLs to
+it like you would do to the static folder of the application:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;admin.static&#39;</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s">&#39;style.css&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="templates">
+<h3>Templates<a class="headerlink" href="#templates" title="Permalink to this headline">¶</a></h3>
+<p>If you want the blueprint to expose templates you can do that by providing
+the <cite>template_folder</cite> parameter to the <tt class="xref py py-class docutils literal"><span class="pre">Blueprint</span></tt> constructor:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">admin</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">template_folder</span><span class="o">=</span><span class="s">&#39;templates&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>As for static files, the path can be absolute or relative to the blueprint
+resource folder. The template folder is added to the searchpath of
+templates but with a lower priority than the actual application&#8217;s template
+folder. That way you can easily override templates that a blueprint
+provides in the actual application.</p>
+<p>So if you have a blueprint in the folder <tt class="docutils literal"><span class="pre">yourapplication/admin</span></tt> and you
+want to render the template <tt class="docutils literal"><span class="pre">'admin/index.html'</span></tt> and you have provided
+<tt class="docutils literal"><span class="pre">templates</span></tt> as a <cite>template_folder</cite> you will have to create a file like
+this: <tt class="docutils literal"><span class="pre">yourapplication/admin/templates/admin/index.html</span></tt>.</p>
+</div>
+</div>
+<div class="section" id="building-urls">
+<h2>Building URLs<a class="headerlink" href="#building-urls" title="Permalink to this headline">¶</a></h2>
+<p>If you want to link from one page to another you can use the
+<tt class="xref py py-func docutils literal"><span class="pre">url_for()</span></tt> function just like you normally would do just that you
+prefix the URL endpoint with the name of the blueprint and a dot (<tt class="docutils literal"><span class="pre">.</span></tt>):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;admin.index&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Additionally if you are in a view function of a blueprint or a rendered
+template and you want to link to another endpoint of the same blueprint,
+you can use relative redirects by prefixing the endpoint with a dot only:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;.index&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This will link to <tt class="docutils literal"><span class="pre">admin.index</span></tt> for instance in case the current request
+was dispatched to any other admin blueprint endpoint.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Modular Applications with Blueprints</a><ul>
+<li><a class="reference internal" href="#why-blueprints">Why Blueprints?</a></li>
+<li><a class="reference internal" href="#the-concept-of-blueprints">The Concept of Blueprints</a></li>
+<li><a class="reference internal" href="#my-first-blueprint">My First Blueprint</a></li>
+<li><a class="reference internal" href="#registering-blueprints">Registering Blueprints</a></li>
+<li><a class="reference internal" href="#blueprint-resources">Blueprint Resources</a><ul>
+<li><a class="reference internal" href="#blueprint-resource-folder">Blueprint Resource Folder</a></li>
+<li><a class="reference internal" href="#static-files">Static Files</a></li>
+<li><a class="reference internal" href="#templates">Templates</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#building-urls">Building URLs</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="reqcontext.html" title="previous chapter">The Request Context</a></li>
+ <li>Next: <a href="extensions.html" title="next chapter">Flask Extensions</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/blueprints.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/changelog.html b/studio/static/doc/flask-docs/changelog.html
new file mode 100644
index 0000000..482e029
--- /dev/null
+++ b/studio/static/doc/flask-docs/changelog.html
@@ -0,0 +1,418 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Flask Changelog &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="License" href="license.html" />
+ <link rel="prev" title="Upgrading to Newer Releases" href="upgrading.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="license.html" title="License"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="upgrading.html" title="Upgrading to Newer Releases"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="flask-changelog">
+<h1>Flask Changelog<a class="headerlink" href="#flask-changelog" title="Permalink to this headline">¶</a></h1>
+<p>Here you can see the full list of changes between each Flask release.</p>
+<div class="section" id="version-0-9">
+<h2>Version 0.9<a class="headerlink" href="#version-0-9" title="Permalink to this headline">¶</a></h2>
+<p>Relase date to be decided, codename to be chosen.</p>
+</div>
+<div class="section" id="version-0-8">
+<h2>Version 0.8<a class="headerlink" href="#version-0-8" title="Permalink to this headline">¶</a></h2>
+<p>Released on September 29th 2011, codename Rakija</p>
+<ul class="simple">
+<li>Refactored session support into a session interface so that
+the implementation of the sessions can be changed without
+having to override the Flask class.</li>
+<li>Empty session cookies are now deleted properly automatically.</li>
+<li>View functions can now opt out of getting the automatic
+OPTIONS implementation.</li>
+<li>HTTP exceptions and Bad Request errors can now be trapped so that they
+show up normally in the traceback.</li>
+<li>Flask in debug mode is now detecting some common problems and tries to
+warn you about them.</li>
+<li>Flask in debug mode will now complain with an assertion error if a view
+was attached after the first request was handled. This gives earlier
+feedback when users forget to import view code ahead of time.</li>
+<li>Added the ability to register callbacks that are only triggered once at
+the beginning of the first request. (<tt class="xref py py-meth docutils literal"><span class="pre">Flask.before_first_request()</span></tt>)</li>
+<li>Malformed JSON data will now trigger a bad request HTTP exception instead
+of a value error which usually would result in a 500 internal server
+error if not handled. This is a backwards incompatible change.</li>
+<li>Applications now not only have a root path where the resources and modules
+are located but also an instane path which is the designated place to
+drop files that are modified at runtime (uploads etc.). Also this is
+conceptionally only instance depending and outside version control so it&#8217;s
+the perfect place to put configuration files etc. For more information
+see <a class="reference internal" href="config.html#instance-folders"><em>Instance Folders</em></a>.</li>
+<li>Added the <tt class="docutils literal"><span class="pre">APPLICATION_ROOT</span></tt> configuration variable.</li>
+<li>Implemented <tt class="xref py py-meth docutils literal"><span class="pre">session_transaction()</span></tt> to
+easily modify sessions from the test environment.</li>
+<li>Refactored test client internally. The <tt class="docutils literal"><span class="pre">APPLICATION_ROOT</span></tt> configuration
+variable as well as <tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt> are now properly used by the test client
+as defaults.</li>
+<li>Added <a class="reference internal" href="api.html#flask.views.View.decorators" title="flask.views.View.decorators"><tt class="xref py py-attr docutils literal"><span class="pre">flask.views.View.decorators</span></tt></a> to support simpler decorating of
+pluggable (class based) views.</li>
+<li>Fixed an issue where the test client if used with the with statement did not
+trigger the execution of the teardown handlers.</li>
+<li>Added finer control over the session cookie parameters.</li>
+<li>HEAD requests to a method view now automatically dispatch to the <cite>get</cite>
+method if no handler was implemented.</li>
+<li>Implemented the virtual <tt class="xref py py-mod docutils literal"><span class="pre">flask.ext</span></tt> package to import extensions from.</li>
+<li>The context preservation on exceptions is now an integral component of
+Flask itself and no longer of the test client. This cleaned up some
+internal logic and lowers the odds of runaway request contexts in unittests.</li>
+</ul>
+</div>
+<div class="section" id="version-0-7-3">
+<h2>Version 0.7.3<a class="headerlink" href="#version-0-7-3" title="Permalink to this headline">¶</a></h2>
+<p>Bugfix release, release date to be decided</p>
+<ul class="simple">
+<li>Fixed the Jinja2 environment&#8217;s list_templates method not returning the
+correct names when blueprints or modules were involved.</li>
+</ul>
+</div>
+<div class="section" id="version-0-7-2">
+<h2>Version 0.7.2<a class="headerlink" href="#version-0-7-2" title="Permalink to this headline">¶</a></h2>
+<p>Bugfix release, released on July 6th 2011</p>
+<ul class="simple">
+<li>Fixed an issue with URL processors not properly working on
+blueprints.</li>
+</ul>
+</div>
+<div class="section" id="version-0-7-1">
+<h2>Version 0.7.1<a class="headerlink" href="#version-0-7-1" title="Permalink to this headline">¶</a></h2>
+<p>Bugfix release, released on June 29th 2011</p>
+<ul class="simple">
+<li>Added missing future import that broke 2.5 compatibility.</li>
+<li>Fixed an infinite redirect issue with blueprints.</li>
+</ul>
+</div>
+<div class="section" id="version-0-7">
+<h2>Version 0.7<a class="headerlink" href="#version-0-7" title="Permalink to this headline">¶</a></h2>
+<p>Released on June 28th 2011, codename Grappa</p>
+<ul class="simple">
+<li>Added <a class="reference internal" href="api.html#flask.Flask.make_default_options_response" title="flask.Flask.make_default_options_response"><tt class="xref py py-meth docutils literal"><span class="pre">make_default_options_response()</span></tt></a>
+which can be used by subclasses to alter the default
+behaviour for <cite>OPTIONS</cite> responses.</li>
+<li>Unbound locals now raise a proper <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#RuntimeError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">RuntimeError</span></tt></a> instead
+of an <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#AttributeError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">AttributeError</span></tt></a>.</li>
+<li>Mimetype guessing and etag support based on file objects is now
+deprecated for <a class="reference internal" href="api.html#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">flask.send_file()</span></tt></a> because it was unreliable.
+Pass filenames instead or attach your own etags and provide a
+proper mimetype by hand.</li>
+<li>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&#8217;s App Engine. Until
+1.0 the old behaviour will continue to work but issue dependency
+warnings.</li>
+<li>fixed a problem for Flask to run on jython.</li>
+<li>added a <cite>PROPAGATE_EXCEPTIONS</cite> configuration variable that can be
+used to flip the setting of exception propagation which previously
+was linked to <cite>DEBUG</cite> alone and is now linked to either <cite>DEBUG</cite> or
+<cite>TESTING</cite>.</li>
+<li>Flask no longer internally depends on rules being added through the
+<cite>add_url_rule</cite> function and can now also accept regular werkzeug
+rules added to the url map.</li>
+<li>Added an <cite>endpoint</cite> method to the flask application object which
+allows one to register a callback to an arbitrary endpoint with
+a decorator.</li>
+<li>Use Last-Modified for static file sending instead of Date which
+was incorrectly introduced in 0.6.</li>
+<li>Added <cite>create_jinja_loader</cite> to override the loader creation process.</li>
+<li>Implemented a silent flag for <cite>config.from_pyfile</cite>.</li>
+<li>Added <cite>teardown_request</cite> decorator, for functions that should run at the end
+of a request regardless of whether an exception occurred. Also the behavior
+for <cite>after_request</cite> was changed. It&#8217;s now no longer executed when an exception
+is raised. See <a class="reference internal" href="upgrading.html#upgrading-to-new-teardown-handling"><em>Upgrading to new Teardown Handling</em></a></li>
+<li>Implemented <a class="reference internal" href="api.html#flask.has_request_context" title="flask.has_request_context"><tt class="xref py py-func docutils literal"><span class="pre">flask.has_request_context()</span></tt></a></li>
+<li>Deprecated <cite>init_jinja_globals</cite>. Override the
+<a class="reference internal" href="api.html#flask.Flask.create_jinja_environment" title="flask.Flask.create_jinja_environment"><tt class="xref py py-meth docutils literal"><span class="pre">create_jinja_environment()</span></tt></a> method instead to
+achieve the same functionality.</li>
+<li>Added <a class="reference internal" href="api.html#flask.safe_join" title="flask.safe_join"><tt class="xref py py-func docutils literal"><span class="pre">flask.safe_join()</span></tt></a></li>
+<li>The automatic JSON request data unpacking now looks at the charset
+mimetype parameter.</li>
+<li>Don&#8217;t modify the session on <a class="reference internal" href="api.html#flask.get_flashed_messages" title="flask.get_flashed_messages"><tt class="xref py py-func docutils literal"><span class="pre">flask.get_flashed_messages()</span></tt></a> if there
+are no messages in the session.</li>
+<li><cite>before_request</cite> handlers are now able to abort requests with errors.</li>
+<li>it is not possible to define user exception handlers. That way you can
+provide custom error messages from a central hub for certain errors that
+might occur during request processing (for instance database connection
+errors, timeouts from remote resources etc.).</li>
+<li>Blueprints can provide blueprint specific error handlers.</li>
+<li>Implemented generic <a class="reference internal" href="views.html#views"><em>Pluggable Views</em></a> (class based views).</li>
+</ul>
+</div>
+<div class="section" id="version-0-6-1">
+<h2>Version 0.6.1<a class="headerlink" href="#version-0-6-1" title="Permalink to this headline">¶</a></h2>
+<p>Bugfix release, released on December 31st 2010</p>
+<ul class="simple">
+<li>Fixed an issue where the default <cite>OPTIONS</cite> response was
+not exposing all valid methods in the <cite>Allow</cite> header.</li>
+<li>Jinja2 template loading syntax now allows &#8221;./&#8221; in front of
+a template load path. Previously this caused issues with
+module setups.</li>
+<li>Fixed an issue where the subdomain setting for modules was
+ignored for the static folder.</li>
+<li>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.</li>
+</ul>
+</div>
+<div class="section" id="version-0-6">
+<h2>Version 0.6<a class="headerlink" href="#version-0-6" title="Permalink to this headline">¶</a></h2>
+<p>Released on July 27th 2010, codename Whisky</p>
+<ul class="simple">
+<li>after request functions are now called in reverse order of
+registration.</li>
+<li>OPTIONS is now automatically implemented by Flask unless the
+application explicitly adds &#8216;OPTIONS&#8217; as method to the URL rule.
+In this case no automatic OPTIONS handling kicks in.</li>
+<li>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&#8217;s part of a mapping in the .yml
+file.</li>
+<li>the <a class="reference internal" href="api.html#flask.Flask.config" title="flask.Flask.config"><tt class="xref py py-attr docutils literal"><span class="pre">config</span></tt></a> is now available in the templates
+as <cite>config</cite>.</li>
+<li>context processors will no longer override values passed directly
+to the render function.</li>
+<li>added the ability to limit the incoming request data with the
+new <tt class="docutils literal"><span class="pre">MAX_CONTENT_LENGTH</span></tt> configuration value.</li>
+<li>the endpoint for the <tt class="xref py py-meth docutils literal"><span class="pre">flask.Module.add_url_rule()</span></tt> method
+is now optional to be consistent with the function of the
+same name on the application object.</li>
+<li>added a <a class="reference internal" href="api.html#flask.make_response" title="flask.make_response"><tt class="xref py py-func docutils literal"><span class="pre">flask.make_response()</span></tt></a> function that simplifies
+creating response object instances in views.</li>
+<li>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 <a class="reference external" href="http://pypi.python.org/pypi/blinker">blinker</a> installed.</li>
+<li>refactored the way URL adapters are created. This process is now
+fully customizable with the <a class="reference internal" href="api.html#flask.Flask.create_url_adapter" title="flask.Flask.create_url_adapter"><tt class="xref py py-meth docutils literal"><span class="pre">create_url_adapter()</span></tt></a>
+method.</li>
+<li>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.</li>
+</ul>
+</div>
+<div class="section" id="version-0-5-2">
+<h2>Version 0.5.2<a class="headerlink" href="#version-0-5-2" title="Permalink to this headline">¶</a></h2>
+<p>Bugfix Release, released on July 15th 2010</p>
+<ul class="simple">
+<li>fixed another issue with loading templates from directories when
+modules were used.</li>
+</ul>
+</div>
+<div class="section" id="version-0-5-1">
+<h2>Version 0.5.1<a class="headerlink" href="#version-0-5-1" title="Permalink to this headline">¶</a></h2>
+<p>Bugfix Release, released on July 6th 2010</p>
+<ul class="simple">
+<li>fixes an issue with template loading from directories when modules
+where used.</li>
+</ul>
+</div>
+<div class="section" id="version-0-5">
+<h2>Version 0.5<a class="headerlink" href="#version-0-5" title="Permalink to this headline">¶</a></h2>
+<p>Released on July 6th 2010, codename Calvados</p>
+<ul class="simple">
+<li>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 <cite>SERVER_NAME</cite> config key. This key is now also used to set
+the session cookie cross-subdomain wide.</li>
+<li>autoescaping is no longer active for all templates. Instead it
+is only active for <tt class="docutils literal"><span class="pre">.html</span></tt>, <tt class="docutils literal"><span class="pre">.htm</span></tt>, <tt class="docutils literal"><span class="pre">.xml</span></tt> and <tt class="docutils literal"><span class="pre">.xhtml</span></tt>.
+Inside templates this behaviour can be changed with the
+<tt class="docutils literal"><span class="pre">autoescape</span></tt> tag.</li>
+<li>refactored Flask internally. It now consists of more than a
+single file.</li>
+<li><a class="reference internal" href="api.html#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">flask.send_file()</span></tt></a> now emits etags and has the ability to
+do conditional responses builtin.</li>
+<li>(temporarily) dropped support for zipped applications. This was a
+rarely used feature and led to some confusing behaviour.</li>
+<li>added support for per-package template and static-file directories.</li>
+<li>removed support for <cite>create_jinja_loader</cite> which is no longer used
+in 0.5 due to the improved module support.</li>
+<li>added a helper function to expose files from any directory.</li>
+</ul>
+</div>
+<div class="section" id="version-0-4">
+<h2>Version 0.4<a class="headerlink" href="#version-0-4" title="Permalink to this headline">¶</a></h2>
+<p>Released on June 18th 2010, codename Rakia</p>
+<ul class="simple">
+<li>added the ability to register application wide error handlers
+from modules.</li>
+<li><a class="reference internal" href="api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> handlers are now also invoked
+if the request dies with an exception and an error handling page
+kicks in.</li>
+<li>test client has not the ability to preserve the request context
+for a little longer. This can also be used to trigger custom
+requests that do not pop the request stack for testing.</li>
+<li>because the Python standard library caches loggers, the name of
+the logger is configurable now to better support unittests.</li>
+<li>added <cite>TESTING</cite> switch that can activate unittesting helpers.</li>
+<li>the logger switches to <cite>DEBUG</cite> mode now if debug is enabled.</li>
+</ul>
+</div>
+<div class="section" id="version-0-3-1">
+<h2>Version 0.3.1<a class="headerlink" href="#version-0-3-1" title="Permalink to this headline">¶</a></h2>
+<p>Bugfix release, released on May 28th 2010</p>
+<ul class="simple">
+<li>fixed a error reporting bug with <a class="reference internal" href="api.html#flask.Config.from_envvar" title="flask.Config.from_envvar"><tt class="xref py py-meth docutils literal"><span class="pre">flask.Config.from_envvar()</span></tt></a></li>
+<li>removed some unused code from flask</li>
+<li>release does no longer include development leftover files (.git
+folder for themes, built documentation in zip and pdf file and
+some .pyc files)</li>
+</ul>
+</div>
+<div class="section" id="version-0-3">
+<h2>Version 0.3<a class="headerlink" href="#version-0-3" title="Permalink to this headline">¶</a></h2>
+<p>Released on May 28th 2010, codename Schnaps</p>
+<ul class="simple">
+<li>added support for categories for flashed messages.</li>
+<li>the application now configures a <tt class="xref py py-class docutils literal"><span class="pre">logging.Handler</span></tt> and will
+log request handling exceptions to that logger when not in debug
+mode. This makes it possible to receive mails on server errors
+for example.</li>
+<li>added support for context binding that does not require the use of
+the with statement for playing in the console.</li>
+<li>the request context is now available within the with statement making
+it possible to further push the request context or pop it.</li>
+<li>added support for configurations.</li>
+</ul>
+</div>
+<div class="section" id="version-0-2">
+<h2>Version 0.2<a class="headerlink" href="#version-0-2" title="Permalink to this headline">¶</a></h2>
+<p>Released on May 12th 2010, codename Jägermeister</p>
+<ul class="simple">
+<li>various bugfixes</li>
+<li>integrated JSON support</li>
+<li>added <a class="reference internal" href="api.html#flask.get_template_attribute" title="flask.get_template_attribute"><tt class="xref py py-func docutils literal"><span class="pre">get_template_attribute()</span></tt></a> helper function.</li>
+<li><a class="reference internal" href="api.html#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a> can now also register a
+view function.</li>
+<li>refactored internal request dispatching.</li>
+<li>server listens on 127.0.0.1 by default now to fix issues with chrome.</li>
+<li>added external URL support.</li>
+<li>added support for <a class="reference internal" href="api.html#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">send_file()</span></tt></a></li>
+<li>module support and internal request handling refactoring
+to better support pluggable applications.</li>
+<li>sessions can be set to be permanent now on a per-session basis.</li>
+<li>better error reporting on missing secret keys.</li>
+<li>added support for Google Appengine.</li>
+</ul>
+</div>
+<div class="section" id="version-0-1">
+<h2>Version 0.1<a class="headerlink" href="#version-0-1" title="Permalink to this headline">¶</a></h2>
+<p>First public preview release.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Flask Changelog</a><ul>
+<li><a class="reference internal" href="#version-0-9">Version 0.9</a></li>
+<li><a class="reference internal" href="#version-0-8">Version 0.8</a></li>
+<li><a class="reference internal" href="#version-0-7-3">Version 0.7.3</a></li>
+<li><a class="reference internal" href="#version-0-7-2">Version 0.7.2</a></li>
+<li><a class="reference internal" href="#version-0-7-1">Version 0.7.1</a></li>
+<li><a class="reference internal" href="#version-0-7">Version 0.7</a></li>
+<li><a class="reference internal" href="#version-0-6-1">Version 0.6.1</a></li>
+<li><a class="reference internal" href="#version-0-6">Version 0.6</a></li>
+<li><a class="reference internal" href="#version-0-5-2">Version 0.5.2</a></li>
+<li><a class="reference internal" href="#version-0-5-1">Version 0.5.1</a></li>
+<li><a class="reference internal" href="#version-0-5">Version 0.5</a></li>
+<li><a class="reference internal" href="#version-0-4">Version 0.4</a></li>
+<li><a class="reference internal" href="#version-0-3-1">Version 0.3.1</a></li>
+<li><a class="reference internal" href="#version-0-3">Version 0.3</a></li>
+<li><a class="reference internal" href="#version-0-2">Version 0.2</a></li>
+<li><a class="reference internal" href="#version-0-1">Version 0.1</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="upgrading.html" title="previous chapter">Upgrading to Newer Releases</a></li>
+ <li>Next: <a href="license.html" title="next chapter">License</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/changelog.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/config.html b/studio/static/doc/flask-docs/config.html
new file mode 100644
index 0000000..c569e8f
--- /dev/null
+++ b/studio/static/doc/flask-docs/config.html
@@ -0,0 +1,505 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Configuration Handling &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Signals" href="signals.html" />
+ <link rel="prev" title="Handling Application Errors" href="errorhandling.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="signals.html" title="Signals"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="errorhandling.html" title="Handling Application Errors"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="configuration-handling">
+<span id="config"></span><h1>Configuration Handling<a class="headerlink" href="#configuration-handling" title="Permalink to this headline">¶</a></h1>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.3.</span></p>
+<p>Applications need some kind of configuration. There are different settings
+you might want to change depending on the application environment like
+toggling the debug mode, setting the secret key, and other such
+environment-specific things.</p>
+<p>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.</p>
+<p>Independent of how you load your config, there is a config object
+available which holds the loaded configuration values:
+The <a class="reference internal" href="api.html#flask.Flask.config" title="flask.Flask.config"><tt class="xref py py-attr docutils literal"><span class="pre">config</span></tt></a> attribute of the <a class="reference internal" href="api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a>
+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.</p>
+<div class="section" id="configuration-basics">
+<h2>Configuration Basics<a class="headerlink" href="#configuration-basics" title="Permalink to this headline">¶</a></h2>
+<p>The <a class="reference internal" href="api.html#flask.Flask.config" title="flask.Flask.config"><tt class="xref py py-attr docutils literal"><span class="pre">config</span></tt></a> is actually a subclass of a dictionary and
+can be modified just like any dictionary:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;DEBUG&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span>
+</pre></div>
+</div>
+<p>Certain configuration values are also forwarded to the
+<a class="reference internal" href="api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-attr docutils literal"><span class="pre">Flask</span></tt></a> object so you can read and write them from there:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">debug</span> <span class="o">=</span> <span class="bp">True</span>
+</pre></div>
+</div>
+<p>To update multiple keys at once you can use the <a class="reference external" href="http://docs.python.org/dev/library/stdtypes.html#dict.update" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">dict.update()</span></tt></a>
+method:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
+ <span class="n">DEBUG</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">SECRET_KEY</span><span class="o">=</span><span class="s">&#39;...&#39;</span>
+<span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="builtin-configuration-values">
+<h2>Builtin Configuration Values<a class="headerlink" href="#builtin-configuration-values" title="Permalink to this headline">¶</a></h2>
+<p>The following configuration values are used internally by Flask:</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="45%" />
+<col width="55%" />
+</colgroup>
+<tbody valign="top">
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">DEBUG</span></tt></td>
+<td>enable/disable debug mode</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">TESTING</span></tt></td>
+<td>enable/disable testing mode</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">PROPAGATE_EXCEPTIONS</span></tt></td>
+<td>explicitly enable or disable the
+propagation of exceptions. If not set or
+explicitly set to <cite>None</cite> this is
+implicitly true if either <cite>TESTING</cite> or
+<cite>DEBUG</cite> is true.</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">PRESERVE_CONTEXT_ON_EXCEPTION</span></tt></td>
+<td>By default if the application is in
+debug mode the request context is not
+popped on exceptions to enable debuggers
+to introspect the data. This can be
+disabled by this key. You can also use
+this setting to force-enable it for non
+debug execution which might be useful to
+debug production applications (but also
+very risky).</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">SECRET_KEY</span></tt></td>
+<td>the secret key</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">SESSION_COOKIE_NAME</span></tt></td>
+<td>the name of the session cookie</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">SESSION_COOKIE_DOMAIN</span></tt></td>
+<td>the domain for the session cookie. If
+this is not set, the cookie will be
+valid for all subdomains of
+<tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt>.</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">SESSION_COOKIE_PATH</span></tt></td>
+<td>the path for the session cookie. If
+this is not set the cookie will be valid
+for all of <tt class="docutils literal"><span class="pre">APPLICATION_ROOT</span></tt> or if
+that is not set for <tt class="docutils literal"><span class="pre">'/'</span></tt>.</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">SESSION_COOKIE_HTTPONLY</span></tt></td>
+<td>controls if the cookie should be set
+with the httponly flag. Defaults to
+<cite>True</cite>.</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">SESSION_COOKIE_SECURE</span></tt></td>
+<td>controls if the cookie should be set
+with the secure flag. Defaults to
+<cite>False</cite>.</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">PERMANENT_SESSION_LIFETIME</span></tt></td>
+<td>the lifetime of a permanent session as
+<a class="reference external" href="http://docs.python.org/dev/library/datetime.html#datetime.timedelta" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">datetime.timedelta</span></tt></a> object.
+Starting with Flask 0.8 this can also be
+an integer representing seconds.</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">USE_X_SENDFILE</span></tt></td>
+<td>enable/disable x-sendfile</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">LOGGER_NAME</span></tt></td>
+<td>the name of the logger</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt></td>
+<td>the name and port number of the server.
+Required for subdomain support (e.g.:
+<tt class="docutils literal"><span class="pre">'myapp.dev:5000'</span></tt>) Note that
+localhost does not support subdomains so
+setting this to “localhost” does not
+help.</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">APPLICATION_ROOT</span></tt></td>
+<td>If the application does not occupy
+a whole domain or subdomain this can
+be set to the path where the application
+is configured to live. This is for
+session cookie as path value. If
+domains are used, this should be
+<tt class="docutils literal"><span class="pre">None</span></tt>.</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">MAX_CONTENT_LENGTH</span></tt></td>
+<td>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.</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">TRAP_HTTP_EXCEPTIONS</span></tt></td>
+<td>If this is set to <tt class="docutils literal"><span class="pre">True</span></tt> Flask will
+not execute the error handlers of HTTP
+exceptions but instead treat the
+exception like any other and bubble it
+through the exception stack. This is
+helpful for hairy debugging situations
+where you have to find out where an HTTP
+exception is coming from.</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">TRAP_BAD_REQUEST_ERRORS</span></tt></td>
+<td>Werkzeug&#8217;s internal data structures that
+deal with request specific data will
+raise special key errors that are also
+bad request exceptions. Likewise many
+operations can implicitly fail with a
+BadRequest exception for consistency.
+Since it&#8217;s nice for debugging to know
+why exactly it failed this flag can be
+used to debug those situations. If this
+config is set to <tt class="docutils literal"><span class="pre">True</span></tt> you will get
+a regular traceback instead.</td>
+</tr>
+</tbody>
+</table>
+<div class="admonition-more-on-server-name admonition ">
+<p class="first admonition-title">More on <tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt></p>
+<p>The <tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt> 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.</p>
+<p class="last">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
+<tt class="docutils literal"><span class="pre">'localhost'</span></tt> you will not be able to set a cookie for
+<tt class="docutils literal"><span class="pre">'localhost'</span></tt> and every subdomain of it. Please chose a different
+server name in that case, like <tt class="docutils literal"><span class="pre">'myapplication.local'</span></tt> and add
+this name + the subdomains you want to use into your host config
+or setup a local <a class="reference external" href="https://www.isc.org/software/bind">bind</a>.</p>
+</div>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.4: </span><tt class="docutils literal"><span class="pre">LOGGER_NAME</span></tt></p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.5: </span><tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt></p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6: </span><tt class="docutils literal"><span class="pre">MAX_CONTENT_LENGTH</span></tt></p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7: </span><tt class="docutils literal"><span class="pre">PROPAGATE_EXCEPTIONS</span></tt>, <tt class="docutils literal"><span class="pre">PRESERVE_CONTEXT_ON_EXCEPTION</span></tt></p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8: </span><tt class="docutils literal"><span class="pre">TRAP_BAD_REQUEST_ERRORS</span></tt>, <tt class="docutils literal"><span class="pre">TRAP_HTTP_EXCEPTIONS</span></tt>,
+<tt class="docutils literal"><span class="pre">APPLICATION_ROOT</span></tt>, <tt class="docutils literal"><span class="pre">SESSION_COOKIE_DOMAIN</span></tt>,
+<tt class="docutils literal"><span class="pre">SESSION_COOKIE_PATH</span></tt>, <tt class="docutils literal"><span class="pre">SESSION_COOKIE_HTTPONLY</span></tt>,
+<tt class="docutils literal"><span class="pre">SESSION_COOKIE_SECURE</span></tt></p>
+</div>
+<div class="section" id="configuring-from-files">
+<h2>Configuring from Files<a class="headerlink" href="#configuring-from-files" title="Permalink to this headline">¶</a></h2>
+<p>Configuration becomes more useful if you can store it in a separate file,
+ideally located outside the actual application package. This makes
+packaging and distributing your application possible via various package
+handling tools (<a class="reference internal" href="patterns/distribute.html#distribute-deployment"><em>Deploying with Distribute</em></a>) and finally modifying the
+configuration file afterwards.</p>
+<p>So a common pattern is this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="s">&#39;yourapplication.default_settings&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_envvar</span><span class="p">(</span><span class="s">&#39;YOURAPPLICATION_SETTINGS&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This first loads the configuration from the
+<cite>yourapplication.default_settings</cite> module and then overrides the values
+with the contents of the file the <span class="target" id="index-0"></span><tt class="xref std std-envvar docutils literal"><span class="pre">YOURAPPLICATION_SETTINGS</span></tt>
+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:</p>
+<div class="highlight-python"><pre>$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
+$ python run-app.py
+ * Running on http://127.0.0.1:5000/
+ * Restarting with reloader...</pre>
+</div>
+<p>On Windows systems use the <cite>set</cite> builtin instead:</p>
+<div class="highlight-python"><pre>&gt;set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg</pre>
+</div>
+<p>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.</p>
+<p>Here is an example of a configuration file:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># Example configuration</span>
+<span class="n">DEBUG</span> <span class="o">=</span> <span class="bp">False</span>
+<span class="n">SECRET_KEY</span> <span class="o">=</span> <span class="s">&#39;?</span><span class="se">\xbf</span><span class="s">,</span><span class="se">\xb4\x8d\xa3</span><span class="s">&quot;&lt;</span><span class="se">\x9c\xb0</span><span class="s">@</span><span class="se">\x0f</span><span class="s">5</span><span class="se">\xab</span><span class="s">,w</span><span class="se">\xee\x8d</span><span class="s">$0</span><span class="se">\x13\x8b</span><span class="s">83&#39;</span>
+</pre></div>
+</div>
+<p>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 <a class="reference internal" href="api.html#flask.Config" title="flask.Config"><tt class="xref py py-class docutils literal"><span class="pre">Config</span></tt></a> object&#8217;s
+documentation.</p>
+</div>
+<div class="section" id="configuration-best-practices">
+<h2>Configuration Best Practices<a class="headerlink" href="#configuration-best-practices" title="Permalink to this headline">¶</a></h2>
+<p>The downside with the approach mentioned earlier is that it makes testing
+a little harder. There is no single 100% solution for this problem in
+general, but there are a couple of things you can keep in mind to improve
+that experience:</p>
+<ol class="arabic simple">
+<li>create your application in a function and register blueprints 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.</li>
+<li>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.</li>
+</ol>
+</div>
+<div class="section" id="development-production">
+<h2>Development / Production<a class="headerlink" href="#development-production" title="Permalink to this headline">¶</a></h2>
+<p>Most applications need more than one configuration. There should be at
+least separate configurations for the production server and the one used
+during development. The easiest way to handle this is to use a default
+configuration that is always loaded and part of the version control, and a
+separate configuration that overrides the values as necessary as mentioned
+in the example above:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="s">&#39;yourapplication.default_settings&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_envvar</span><span class="p">(</span><span class="s">&#39;YOURAPPLICATION_SETTINGS&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Then you just have to add a separate <cite>config.py</cite> file and export
+<tt class="docutils literal"><span class="pre">YOURAPPLICATION_SETTINGS=/path/to/config.py</span></tt> and you are done. However
+there are alternative ways as well. For example you could use imports or
+subclassing.</p>
+<p>What is very popular in the Django world is to make the import explicit in
+the config file by adding an <tt class="docutils literal"><span class="pre">from</span> <span class="pre">yourapplication.default_settings</span>
+<span class="pre">import</span> <span class="pre">*</span></tt> to the top of the file and then overriding the changes by hand.
+You could also inspect an environment variable like
+<tt class="docutils literal"><span class="pre">YOURAPPLICATION_MODE</span></tt> and set that to <cite>production</cite>, <cite>development</cite> etc
+and import different hardcoded files based on that.</p>
+<p>An interesting pattern is also to use classes and inheritance for
+configuration:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="n">DEBUG</span> <span class="o">=</span> <span class="bp">False</span>
+ <span class="n">TESTING</span> <span class="o">=</span> <span class="bp">False</span>
+ <span class="n">DATABASE_URI</span> <span class="o">=</span> <span class="s">&#39;sqlite://:memory:&#39;</span>
+
+<span class="k">class</span> <span class="nc">ProductionConfig</span><span class="p">(</span><span class="n">Config</span><span class="p">):</span>
+ <span class="n">DATABASE_URI</span> <span class="o">=</span> <span class="s">&#39;mysql://user@localhost/foo&#39;</span>
+
+<span class="k">class</span> <span class="nc">DevelopmentConfig</span><span class="p">(</span><span class="n">Config</span><span class="p">):</span>
+ <span class="n">DEBUG</span> <span class="o">=</span> <span class="bp">True</span>
+
+<span class="k">class</span> <span class="nc">TestingConfig</span><span class="p">(</span><span class="n">Config</span><span class="p">):</span>
+ <span class="n">TESTING</span> <span class="o">=</span> <span class="bp">True</span>
+</pre></div>
+</div>
+<p>To enable such a config you just have to call into
+<a class="reference internal" href="api.html#flask.Config.from_object" title="flask.Config.from_object"><tt class="xref py py-meth docutils literal"><span class="pre">from_object()</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="s">&#39;configmodule.ProductionConfig&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>There are many different ways and it&#8217;s up to you how you want to manage
+your configuration files. However here a list of good recommendations:</p>
+<ul class="simple">
+<li>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.</li>
+<li>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.</li>
+<li>Use a tool like <a class="reference external" href="http://fabfile.org/">fabric</a> 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
+<a class="reference internal" href="patterns/fabric.html#fabric-deployment"><em>Deploying with Fabric</em></a> pattern.</li>
+</ul>
+</div>
+<div class="section" id="instance-folders">
+<span id="id1"></span><h2>Instance Folders<a class="headerlink" href="#instance-folders" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+<p>Flask 0.8 introduces instance folders. Flask for a long time made it
+possible to refer to paths relative to the application&#8217;s folder directly
+(via <tt class="xref py py-attr docutils literal"><span class="pre">Flask.root_path</span></tt>). This was also how many developers loaded
+configurations stored next to the application. Unfortunately however this
+only works well if applications are not packages in which case the root
+path refers to the contents of the package.</p>
+<p>With Flask 0.8 a new attribute was introduced:
+<tt class="xref py py-attr docutils literal"><span class="pre">Flask.instance_path</span></tt>. It refers to a new concept called the
+“instance folder”. The instance folder is designed to not be under
+version control and be deployment specific. It&#8217;s the perfect place to
+drop things that either change at runtime or configuration files.</p>
+<p>You can either explicitly provide the path of the instance folder when
+creating the Flask application or you can let Flask autodetect the
+instance folder. For explicit configuration use the <cite>instance_path</cite>
+parameter:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">,</span> <span class="n">instance_path</span><span class="o">=</span><span class="s">&#39;/path/to/instance/folder&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Please keep in mind that this path <em>must</em> be absolute when provided.</p>
+<p>If the <cite>instance_path</cite> parameter is not provided the following default
+locations are used:</p>
+<ul>
+<li><p class="first">Uninstalled module:</p>
+<div class="highlight-python"><pre>/myapp.py
+/instance</pre>
+</div>
+</li>
+<li><p class="first">Uninstalled package:</p>
+<div class="highlight-python"><pre>/myapp
+ /__init__.py
+/instance</pre>
+</div>
+</li>
+<li><p class="first">Installed module or package:</p>
+<div class="highlight-python"><pre>$PREFIX/lib/python2.X/site-packages/myapp
+$PREFIX/var/myapp-instance</pre>
+</div>
+<p><tt class="docutils literal"><span class="pre">$PREFIX</span></tt> is the prefix of your Python installation. This can be
+<tt class="docutils literal"><span class="pre">/usr</span></tt> or the path to your virtualenv. You can print the value of
+<tt class="docutils literal"><span class="pre">sys.prefix</span></tt> to see what the prefix is set to.</p>
+</li>
+</ul>
+<p>Since the config object provided loading of configuration files from
+relative filenames we made it possible to change the loading via filenames
+to be relative to the instance path if wanted. The behavior of relative
+paths in config files can be flipped between “relative to the application
+root” (the default) to “relative to instance folder” via the
+<cite>instance_relative_config</cite> switch to the application constructor:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">,</span> <span class="n">instance_relative_config</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Here is a full example of how to configure Flask to preload the config
+from a module and then override the config from a file in the config
+folder if it exists:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">,</span> <span class="n">instance_relative_config</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="s">&#39;yourapplication.default_settings&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="s">&#39;application.cfg&#39;</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The path to the instance folder can be found via the
+<tt class="xref py py-attr docutils literal"><span class="pre">Flask.instance_path</span></tt>. Flask also provides a shortcut to open a
+file from the instance folder with <tt class="xref py py-meth docutils literal"><span class="pre">Flask.open_instance_resource()</span></tt>.</p>
+<p>Example usage for both:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">filename</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">instance_root</span><span class="p">,</span> <span class="s">&#39;application.cfg&#39;</span><span class="p">)</span>
+<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">config</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+
+<span class="c"># or via open_instance_resource:</span>
+<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">open_instance_resource</span><span class="p">(</span><span class="s">&#39;application.cfg&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">config</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Configuration Handling</a><ul>
+<li><a class="reference internal" href="#configuration-basics">Configuration Basics</a></li>
+<li><a class="reference internal" href="#builtin-configuration-values">Builtin Configuration Values</a></li>
+<li><a class="reference internal" href="#configuring-from-files">Configuring from Files</a></li>
+<li><a class="reference internal" href="#configuration-best-practices">Configuration Best Practices</a></li>
+<li><a class="reference internal" href="#development-production">Development / Production</a></li>
+<li><a class="reference internal" href="#instance-folders">Instance Folders</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="errorhandling.html" title="previous chapter">Handling Application Errors</a></li>
+ <li>Next: <a href="signals.html" title="next chapter">Signals</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/config.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/deploying/cgi.html b/studio/static/doc/flask-docs/deploying/cgi.html
new file mode 100644
index 0000000..0471329
--- /dev/null
+++ b/studio/static/doc/flask-docs/deploying/cgi.html
@@ -0,0 +1,153 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>CGI &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Deployment Options" href="index.html" />
+ <link rel="next" title="FastCGI" href="fastcgi.html" />
+ <link rel="prev" title="mod_wsgi (Apache)" href="mod_wsgi.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="fastcgi.html" title="FastCGI"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="mod_wsgi.html" title="mod_wsgi (Apache)"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Deployment Options</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="cgi">
+<h1>CGI<a class="headerlink" href="#cgi" title="Permalink to this headline">¶</a></h1>
+<p>If all other deployment methods do not work, CGI will work for sure.
+CGI is supported by all major servers but usually has a sub-optimal
+performance.</p>
+<p>This is also the way you can use a Flask application on Google&#8217;s <a class="reference external" href="http://code.google.com/appengine/">App
+Engine</a>, where execution happens in a CGI-like environment.</p>
+<div class="admonition-watch-out admonition ">
+<p class="first admonition-title">Watch Out</p>
+<p class="last">Please make sure in advance that any <tt class="docutils literal"><span class="pre">app.run()</span></tt> calls you might
+have in your application file are inside an <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span>
+<span class="pre">'__main__':</span></tt> block or moved to a separate file. Just make sure it&#8217;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.</p>
+</div>
+<div class="section" id="creating-a-cgi-file">
+<h2>Creating a <cite>.cgi</cite> file<a class="headerlink" href="#creating-a-cgi-file" title="Permalink to this headline">¶</a></h2>
+<p>First you need to create the CGI application file. Let&#8217;s call it
+<cite>yourapplication.cgi</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c">#!/usr/bin/python</span>
+<span class="kn">from</span> <span class="nn">wsgiref.handlers</span> <span class="kn">import</span> <span class="n">CGIHandler</span>
+<span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span>
+
+<span class="n">CGIHandler</span><span class="p">()</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="server-setup">
+<h2>Server Setup<a class="headerlink" href="#server-setup" title="Permalink to this headline">¶</a></h2>
+<p>Usually there are two ways to configure the server. Either just copy the
+<cite>.cgi</cite> into a <cite>cgi-bin</cite> (and use <cite>mod_rewrite</cite> or something similar to
+rewrite the URL) or let the server point to the file directly.</p>
+<p>In Apache for example you can put a like like this into the config:</p>
+<div class="highlight-apache"><div class="highlight"><pre><span class="nb">ScriptAlias</span> <span class="sx">/app</span> <span class="sx">/path/to/the/application.cgi</span>
+</pre></div>
+</div>
+<p>For more information consult the documentation of your webserver.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">CGI</a><ul>
+<li><a class="reference internal" href="#creating-a-cgi-file">Creating a <cite>.cgi</cite> file</a></li>
+<li><a class="reference internal" href="#server-setup">Server Setup</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Deployment Options</a><ul>
+ <li>Previous: <a href="mod_wsgi.html" title="previous chapter">mod_wsgi (Apache)</a></li>
+ <li>Next: <a href="fastcgi.html" title="next chapter">FastCGI</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/deploying/cgi.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/deploying/fastcgi.html b/studio/static/doc/flask-docs/deploying/fastcgi.html
new file mode 100644
index 0000000..7431826
--- /dev/null
+++ b/studio/static/doc/flask-docs/deploying/fastcgi.html
@@ -0,0 +1,259 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>FastCGI &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Deployment Options" href="index.html" />
+ <link rel="next" title="uWSGI" href="uwsgi.html" />
+ <link rel="prev" title="CGI" href="cgi.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="uwsgi.html" title="uWSGI"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="cgi.html" title="CGI"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Deployment Options</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="fastcgi">
+<span id="deploying-fastcgi"></span><h1>FastCGI<a class="headerlink" href="#fastcgi" title="Permalink to this headline">¶</a></h1>
+<p>FastCGI is a deployment option on servers like <a class="reference external" href="http://nginx.org/">nginx</a>, <a class="reference external" href="http://www.lighttpd.net/">lighttpd</a>,
+and <a class="reference external" href="http://www.cherokee-project.com/">cherokee</a>; see <a class="reference internal" href="uwsgi.html#deploying-uwsgi"><em>uWSGI</em></a> and
+<a class="reference internal" href="others.html#deploying-other-servers"><em>Other Servers</em></a> for other options. To use your WSGI
+application with any of them you will need a FastCGI server first. The
+most popular one is <a class="reference external" href="http://trac.saddi.com/flup">flup</a> which we will use for this guide. Make sure
+to have it installed to follow along.</p>
+<div class="admonition-watch-out admonition ">
+<p class="first admonition-title">Watch Out</p>
+<p class="last">Please make sure in advance that any <tt class="docutils literal"><span class="pre">app.run()</span></tt> calls you might
+have in your application file are inside an <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span>
+<span class="pre">'__main__':</span></tt> block or moved to a separate file. Just make sure it&#8217;s
+not called because this will always start a local WSGI server which
+we do not want if we deploy that application to FastCGI.</p>
+</div>
+<div class="section" id="creating-a-fcgi-file">
+<h2>Creating a <cite>.fcgi</cite> file<a class="headerlink" href="#creating-a-fcgi-file" title="Permalink to this headline">¶</a></h2>
+<p>First you need to create the FastCGI server file. Let&#8217;s call it
+<cite>yourapplication.fcgi</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c">#!/usr/bin/python</span>
+<span class="kn">from</span> <span class="nn">flup.server.fcgi</span> <span class="kn">import</span> <span class="n">WSGIServer</span>
+<span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span>
+
+<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
+ <span class="n">WSGIServer</span><span class="p">(</span><span class="n">app</span><span class="p">)</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>This is enough for Apache to work, however nginx and older versions of
+lighttpd need a socket to be explicitly passed to communicate with the
+FastCGI server. For that to work you need to pass the path to the
+socket to the <tt class="xref py py-class docutils literal"><span class="pre">WSGIServer</span></tt>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">WSGIServer</span><span class="p">(</span><span class="n">application</span><span class="p">,</span> <span class="n">bindAddress</span><span class="o">=</span><span class="s">&#39;/path/to/fcgi.sock&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>The path has to be the exact same path you define in the server
+config.</p>
+<p>Save the <cite>yourapplication.fcgi</cite> file somewhere you will find it again.
+It makes sense to have that in <cite>/var/www/yourapplication</cite> or something
+similar.</p>
+<p>Make sure to set the executable bit on that file so that the servers
+can execute it:</p>
+<div class="highlight-text"><div class="highlight"><pre># chmod +x /var/www/yourapplication/yourapplication.fcgi
+</pre></div>
+</div>
+</div>
+<div class="section" id="configuring-lighttpd">
+<h2>Configuring lighttpd<a class="headerlink" href="#configuring-lighttpd" title="Permalink to this headline">¶</a></h2>
+<p>A basic FastCGI configuration for lighttpd looks like that:</p>
+<div class="highlight-python"><pre>fastcgi.server = ("/yourapplication.fcgi" =&gt;
+ ((
+ "socket" =&gt; "/tmp/yourapplication-fcgi.sock",
+ "bin-path" =&gt; "/var/www/yourapplication/yourapplication.fcgi",
+ "check-local" =&gt; "disable",
+ "max-procs" =&gt; 1
+ ))
+)
+
+alias.url = (
+ "/static/" =&gt; "/path/to/your/static"
+)
+
+url.rewrite-once = (
+ "^(/static.*)$" =&gt; "$1",
+ "^(/.*)$" =&gt; "/yourapplication.fcgi$1"</pre>
+</div>
+<p>Remember to enable the FastCGI, alias and rewrite modules. This
+configuration binds the application to <cite>/yourapplication</cite>. If you want
+the application to work in the URL root you have to work around a
+lighttpd bug with the
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/contrib/fixers/#werkzeug.contrib.fixers.LighttpdCGIRootFix" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">LighttpdCGIRootFix</span></tt></a> middleware.</p>
+<p>Make sure to apply it only if you are mounting the application the URL
+root. Also, see the Lighty docs for more information on <a class="reference external" href="http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModFastCGI">FastCGI and
+Python</a>
+(note that explicitly passing a socket to run() is no longer necessary).</p>
+</div>
+<div class="section" id="configuring-nginx">
+<h2>Configuring nginx<a class="headerlink" href="#configuring-nginx" title="Permalink to this headline">¶</a></h2>
+<p>Installing FastCGI applications on nginx is a bit different because by
+default no FastCGI parameters are forwarded.</p>
+<p>A basic flask FastCGI configuration for nginx looks like this:</p>
+<div class="highlight-python"><pre>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;
+}</pre>
+</div>
+<p>This configuration binds the application to <cite>/yourapplication</cite>. If you
+want to have it in the URL root it&#8217;s a bit simpler because you don&#8217;t
+have to figure out how to calculate <cite>PATH_INFO</cite> and <cite>SCRIPT_NAME</cite>:</p>
+<div class="highlight-python"><pre>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;
+}</pre>
+</div>
+</div>
+<div class="section" id="running-fastcgi-processes">
+<h2>Running FastCGI Processes<a class="headerlink" href="#running-fastcgi-processes" title="Permalink to this headline">¶</a></h2>
+<p>Since Nginx and others do not load FastCGI apps, you have to do it by
+yourself. <a class="reference external" href="http://supervisord.org/configuration.html#fcgi-program-x-section-settings">Supervisor can manage FastCGI processes.</a>
+You can look around for other FastCGI process managers or write a script
+to run your <cite>.fcgi</cite> file at boot, e.g. using a SysV <tt class="docutils literal"><span class="pre">init.d</span></tt> script.
+For a temporary solution, you can always run the <tt class="docutils literal"><span class="pre">.fcgi</span></tt> script inside
+GNU screen. See <tt class="docutils literal"><span class="pre">man</span> <span class="pre">screen</span></tt> for details, and note that this is a
+manual solution which does not persist across system restart:</p>
+<div class="highlight-python"><pre>$ screen
+$ /var/www/yourapplication/yourapplication.fcgi</pre>
+</div>
+</div>
+<div class="section" id="debugging">
+<h2>Debugging<a class="headerlink" href="#debugging" title="Permalink to this headline">¶</a></h2>
+<p>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 &#8220;premature end of headers&#8221;. 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.</p>
+<p>This example assumes your application is called <cite>application.fcgi</cite> and
+that your webserver user is <cite>www-data</cite>:</p>
+<div class="highlight-python"><pre>$ su www-data
+$ cd /var/www/yourapplication
+$ python application.fcgi
+Traceback (most recent call last):
+ File "yourapplication.fcgi", line 4, in &lt;module&gt;
+ImportError: No module named yourapplication</pre>
+</div>
+<p>In this case the error seems to be &#8220;yourapplication&#8221; not being on the
+python path. Common problems are:</p>
+<ul class="simple">
+<li>Relative paths being used. Don&#8217;t rely on the current working directory</li>
+<li>The code depending on environment variables that are not set by the
+web server.</li>
+<li>Different python interpreters being used.</li>
+</ul>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">FastCGI</a><ul>
+<li><a class="reference internal" href="#creating-a-fcgi-file">Creating a <cite>.fcgi</cite> file</a></li>
+<li><a class="reference internal" href="#configuring-lighttpd">Configuring lighttpd</a></li>
+<li><a class="reference internal" href="#configuring-nginx">Configuring nginx</a></li>
+<li><a class="reference internal" href="#running-fastcgi-processes">Running FastCGI Processes</a></li>
+<li><a class="reference internal" href="#debugging">Debugging</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Deployment Options</a><ul>
+ <li>Previous: <a href="cgi.html" title="previous chapter">CGI</a></li>
+ <li>Next: <a href="uwsgi.html" title="next chapter">uWSGI</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/deploying/fastcgi.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/deploying/index.html b/studio/static/doc/flask-docs/deploying/index.html
new file mode 100644
index 0000000..44127c0
--- /dev/null
+++ b/studio/static/doc/flask-docs/deploying/index.html
@@ -0,0 +1,150 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Deployment Options &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="next" title="mod_wsgi (Apache)" href="mod_wsgi.html" />
+ <link rel="prev" title="Deferred Request Callbacks" href="../patterns/deferredcallbacks.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="mod_wsgi.html" title="mod_wsgi (Apache)"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="../patterns/deferredcallbacks.html" title="Deferred Request Callbacks"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="deployment-options">
+<span id="deployment"></span><h1>Deployment Options<a class="headerlink" href="#deployment-options" title="Permalink to this headline">¶</a></h1>
+<p>Depending on what you have available there are multiple ways to run
+Flask applications. You can use the builtin server during development,
+but you should use a full deployment option for production applications.
+(Do not use the builtin development server in production.) Several
+options are available and documented here.</p>
+<p>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
+<tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt> application object is the actual WSGI application.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="mod_wsgi.html">mod_wsgi (Apache)</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="mod_wsgi.html#installing-mod-wsgi">Installing <cite>mod_wsgi</cite></a></li>
+<li class="toctree-l2"><a class="reference internal" href="mod_wsgi.html#creating-a-wsgi-file">Creating a <cite>.wsgi</cite> file</a></li>
+<li class="toctree-l2"><a class="reference internal" href="mod_wsgi.html#configuring-apache">Configuring Apache</a></li>
+<li class="toctree-l2"><a class="reference internal" href="mod_wsgi.html#troubleshooting">Troubleshooting</a></li>
+<li class="toctree-l2"><a class="reference internal" href="mod_wsgi.html#support-for-automatic-reloading">Support for Automatic Reloading</a></li>
+<li class="toctree-l2"><a class="reference internal" href="mod_wsgi.html#working-with-virtual-environments">Working with Virtual Environments</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="cgi.html">CGI</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="cgi.html#creating-a-cgi-file">Creating a <cite>.cgi</cite> file</a></li>
+<li class="toctree-l2"><a class="reference internal" href="cgi.html#server-setup">Server Setup</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="fastcgi.html">FastCGI</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="fastcgi.html#creating-a-fcgi-file">Creating a <cite>.fcgi</cite> file</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fastcgi.html#configuring-lighttpd">Configuring lighttpd</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fastcgi.html#configuring-nginx">Configuring nginx</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fastcgi.html#running-fastcgi-processes">Running FastCGI Processes</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fastcgi.html#debugging">Debugging</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="uwsgi.html">uWSGI</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="uwsgi.html#starting-your-app-with-uwsgi">Starting your app with uwsgi</a></li>
+<li class="toctree-l2"><a class="reference internal" href="uwsgi.html#configuring-nginx">Configuring nginx</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="others.html">Other Servers</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="others.html#tornado">Tornado</a></li>
+<li class="toctree-l2"><a class="reference internal" href="others.html#gevent">Gevent</a></li>
+<li class="toctree-l2"><a class="reference internal" href="others.html#gunicorn">Gunicorn</a></li>
+<li class="toctree-l2"><a class="reference internal" href="others.html#proxy-setups">Proxy Setups</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="../patterns/deferredcallbacks.html" title="previous chapter">Deferred Request Callbacks</a></li>
+ <li>Next: <a href="mod_wsgi.html" title="next chapter">mod_wsgi (Apache)</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/deploying/index.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/deploying/mod_wsgi.html b/studio/static/doc/flask-docs/deploying/mod_wsgi.html
new file mode 100644
index 0000000..a431756
--- /dev/null
+++ b/studio/static/doc/flask-docs/deploying/mod_wsgi.html
@@ -0,0 +1,255 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>mod_wsgi (Apache) &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Deployment Options" href="index.html" />
+ <link rel="next" title="CGI" href="cgi.html" />
+ <link rel="prev" title="Deployment Options" href="index.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="cgi.html" title="CGI"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="index.html" title="Deployment Options"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Deployment Options</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="mod-wsgi-apache">
+<span id="mod-wsgi-deployment"></span><h1>mod_wsgi (Apache)<a class="headerlink" href="#mod-wsgi-apache" title="Permalink to this headline">¶</a></h1>
+<p>If you are using the <a class="reference external" href="http://httpd.apache.org/">Apache</a> webserver, consider using <a class="reference external" href="http://code.google.com/p/modwsgi/">mod_wsgi</a>.</p>
+<div class="admonition-watch-out admonition ">
+<p class="first admonition-title">Watch Out</p>
+<p class="last">Please make sure in advance that any <tt class="docutils literal"><span class="pre">app.run()</span></tt> calls you might
+have in your application file are inside an <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span>
+<span class="pre">'__main__':</span></tt> block or moved to a separate file. Just make sure it&#8217;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.</p>
+</div>
+<div class="section" id="installing-mod-wsgi">
+<h2>Installing <cite>mod_wsgi</cite><a class="headerlink" href="#installing-mod-wsgi" title="Permalink to this headline">¶</a></h2>
+<p>If you don&#8217;t have <cite>mod_wsgi</cite> installed yet you have to either install it
+using a package manager or compile it yourself. The mod_wsgi
+<a class="reference external" href="http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide">installation instructions</a> cover source installations on UNIX systems.</p>
+<p>If you are using Ubuntu/Debian you can apt-get it and activate it as
+follows:</p>
+<div class="highlight-text"><div class="highlight"><pre># apt-get install libapache2-mod-wsgi
+</pre></div>
+</div>
+<p>On FreeBSD install <cite>mod_wsgi</cite> by compiling the <cite>www/mod_wsgi</cite> port or by
+using pkg_add:</p>
+<div class="highlight-text"><div class="highlight"><pre># pkg_add -r mod_wsgi
+</pre></div>
+</div>
+<p>If you are using pkgsrc you can install <cite>mod_wsgi</cite> by compiling the
+<cite>www/ap2-wsgi</cite> package.</p>
+<p>If you encounter segfaulting child processes after the first apache
+reload you can safely ignore them. Just restart the server.</p>
+</div>
+<div class="section" id="creating-a-wsgi-file">
+<h2>Creating a <cite>.wsgi</cite> file<a class="headerlink" href="#creating-a-wsgi-file" title="Permalink to this headline">¶</a></h2>
+<p>To run your application you need a <cite>yourapplication.wsgi</cite> file. This file
+contains the code <cite>mod_wsgi</cite> is executing on startup to get the application
+object. The object called <cite>application</cite> in that file is then used as
+application.</p>
+<p>For most applications the following file should be sufficient:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span> <span class="k">as</span> <span class="n">application</span>
+</pre></div>
+</div>
+<p>If you don&#8217;t have a factory function for application creation but a singleton
+instance you can directly import that one as <cite>application</cite>.</p>
+<p>Store that file somewhere that you will find it again (e.g.:
+<cite>/var/www/yourapplication</cite>) and make sure that <cite>yourapplication</cite> and all
+the libraries that are in use are on the python load path. If you don&#8217;t
+want to install it system wide consider using a <a class="reference external" href="http://pypi.python.org/pypi/virtualenv">virtual python</a>
+instance.</p>
+</div>
+<div class="section" id="configuring-apache">
+<h2>Configuring Apache<a class="headerlink" href="#configuring-apache" title="Permalink to this headline">¶</a></h2>
+<p>The last thing you have to do is to create an Apache configuration file
+for your application. In this example we are telling <cite>mod_wsgi</cite> to
+execute the application under a different user for security reasons:</p>
+<div class="highlight-apache"><div class="highlight"><pre><span class="nt">&lt;VirtualHost</span> <span class="s">*</span><span class="nt">&gt;</span>
+ <span class="nb">ServerName</span> example.com
+
+ <span class="nb">WSGIDaemonProcess</span> yourapplication <span class="k">user</span>=user1 <span class="k">group</span>=group1 threads=5
+ <span class="nb">WSGIScriptAlias</span> / <span class="sx">/var/www/yourapplication/yourapplication.wsgi</span>
+
+ <span class="nt">&lt;Directory</span> <span class="s">/var/www/yourapplication</span><span class="nt">&gt;</span>
+ <span class="nb">WSGIProcessGroup</span> yourapplication
+ <span class="nb">WSGIApplicationGroup</span> %{GLOBAL}
+ <span class="nb">Order</span> deny,allow
+ <span class="nb">Allow</span> from <span class="k">all</span>
+ <span class="nt">&lt;/Directory&gt;</span>
+<span class="nt">&lt;/VirtualHost&gt;</span>
+</pre></div>
+</div>
+<p>For more information consult the <a class="reference external" href="http://code.google.com/p/modwsgi/wiki/">mod_wsgi wiki</a>.</p>
+</div>
+<div class="section" id="troubleshooting">
+<h2>Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permalink to this headline">¶</a></h2>
+<p>If your application does not run, follow this guide to troubleshoot:</p>
+<dl class="docutils">
+<dt><strong>Problem:</strong> application does not run, errorlog shows SystemExit ignored</dt>
+<dd>You have a <tt class="docutils literal"><span class="pre">app.run()</span></tt> call in your application file that is not
+guarded by an <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__':</span></tt> condition. Either
+remove that <a class="reference internal" href="../api.html#flask.Flask.run" title="flask.Flask.run"><tt class="xref py py-meth docutils literal"><span class="pre">run()</span></tt></a> call from the file and move it
+into a separate <cite>run.py</cite> file or put it into such an if block.</dd>
+<dt><strong>Problem:</strong> application gives permission errors</dt>
+<dd>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
+(<tt class="docutils literal"><span class="pre">user</span></tt> and <tt class="docutils literal"><span class="pre">group</span></tt> parameter to the <cite>WSGIDaemonProcess</cite>
+directive)</dd>
+<dt><strong>Problem:</strong> application dies with an error on print</dt>
+<dd><p class="first">Keep in mind that mod_wsgi disallows doing anything with
+<a class="reference external" href="http://docs.python.org/dev/library/sys.html#sys.stdout" title="(in Python v3.3)"><tt class="xref py py-data docutils literal"><span class="pre">sys.stdout</span></tt></a> and <a class="reference external" href="http://docs.python.org/dev/library/sys.html#sys.stderr" title="(in Python v3.3)"><tt class="xref py py-data docutils literal"><span class="pre">sys.stderr</span></tt></a>. You can disable this
+protection from the config by setting the <cite>WSGIRestrictStdout</cite> to
+<tt class="docutils literal"><span class="pre">off</span></tt>:</p>
+<div class="highlight-apache"><div class="highlight"><pre><span class="nb">WSGIRestrictStdout</span> <span class="k">Off</span>
+</pre></div>
+</div>
+<p>Alternatively you can also replace the standard out in the .wsgi file
+with a different stream:</p>
+<div class="last highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">sys</span>
+<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span>
+</pre></div>
+</div>
+</dd>
+<dt><strong>Problem:</strong> accessing resources gives IO errors</dt>
+<dd><p class="first">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.</p>
+<p class="last">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.</p>
+</dd>
+</dl>
+</div>
+<div class="section" id="support-for-automatic-reloading">
+<h2>Support for Automatic Reloading<a class="headerlink" href="#support-for-automatic-reloading" title="Permalink to this headline">¶</a></h2>
+<p>To help deployment tools you can activate support for automatic
+reloading. Whenever something changes the <cite>.wsgi</cite> file, <cite>mod_wsgi</cite> will
+reload all the daemon processes for us.</p>
+<p>For that, just add the following directive to your <cite>Directory</cite> section:</p>
+<div class="highlight-apache"><div class="highlight"><pre><span class="nb">WSGIScriptReloading</span> <span class="k">On</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="working-with-virtual-environments">
+<h2>Working with Virtual Environments<a class="headerlink" href="#working-with-virtual-environments" title="Permalink to this headline">¶</a></h2>
+<p>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 <cite>.wsgi</cite> file slightly.</p>
+<p>Add the following lines to the top of your <cite>.wsgi</cite> file:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">activate_this</span> <span class="o">=</span> <span class="s">&#39;/path/to/env/bin/activate_this.py&#39;</span>
+<span class="nb">execfile</span><span class="p">(</span><span class="n">activate_this</span><span class="p">,</span> <span class="nb">dict</span><span class="p">(</span><span class="n">__file__</span><span class="o">=</span><span class="n">activate_this</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>This sets up the load paths according to the settings of the virtual
+environment. Keep in mind that the path has to be absolute.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">mod_wsgi (Apache)</a><ul>
+<li><a class="reference internal" href="#installing-mod-wsgi">Installing <cite>mod_wsgi</cite></a></li>
+<li><a class="reference internal" href="#creating-a-wsgi-file">Creating a <cite>.wsgi</cite> file</a></li>
+<li><a class="reference internal" href="#configuring-apache">Configuring Apache</a></li>
+<li><a class="reference internal" href="#troubleshooting">Troubleshooting</a></li>
+<li><a class="reference internal" href="#support-for-automatic-reloading">Support for Automatic Reloading</a></li>
+<li><a class="reference internal" href="#working-with-virtual-environments">Working with Virtual Environments</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Deployment Options</a><ul>
+ <li>Previous: <a href="index.html" title="previous chapter">Deployment Options</a></li>
+ <li>Next: <a href="cgi.html" title="next chapter">CGI</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/deploying/mod_wsgi.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/deploying/others.html b/studio/static/doc/flask-docs/deploying/others.html
new file mode 100644
index 0000000..b1e545b
--- /dev/null
+++ b/studio/static/doc/flask-docs/deploying/others.html
@@ -0,0 +1,200 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Other Servers &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Deployment Options" href="index.html" />
+ <link rel="next" title="Becoming Big" href="../becomingbig.html" />
+ <link rel="prev" title="uWSGI" href="uwsgi.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="../becomingbig.html" title="Becoming Big"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="uwsgi.html" title="uWSGI"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Deployment Options</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="other-servers">
+<span id="deploying-other-servers"></span><h1>Other Servers<a class="headerlink" href="#other-servers" title="Permalink to this headline">¶</a></h1>
+<p>There are popular servers written in Python that allow the execution of WSGI
+applications as well. These servers stand alone when they run; you can proxy
+to them from your web server.</p>
+<div class="section" id="tornado">
+<h2>Tornado<a class="headerlink" href="#tornado" title="Permalink to this headline">¶</a></h2>
+<p><a class="reference external" href="http://www.tornadoweb.org/">Tornado</a> is an open source version of the scalable, non-blocking web
+server and tools that power <a class="reference external" href="http://friendfeed.com/">FriendFeed</a>. 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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">tornado.wsgi</span> <span class="kn">import</span> <span class="n">WSGIContainer</span>
+<span class="kn">from</span> <span class="nn">tornado.httpserver</span> <span class="kn">import</span> <span class="n">HTTPServer</span>
+<span class="kn">from</span> <span class="nn">tornado.ioloop</span> <span class="kn">import</span> <span class="n">IOLoop</span>
+<span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span>
+
+<span class="n">http_server</span> <span class="o">=</span> <span class="n">HTTPServer</span><span class="p">(</span><span class="n">WSGIContainer</span><span class="p">(</span><span class="n">app</span><span class="p">))</span>
+<span class="n">http_server</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">5000</span><span class="p">)</span>
+<span class="n">IOLoop</span><span class="o">.</span><span class="n">instance</span><span class="p">()</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="gevent">
+<h2>Gevent<a class="headerlink" href="#gevent" title="Permalink to this headline">¶</a></h2>
+<p><a class="reference external" href="http://www.gevent.org/">Gevent</a> is a coroutine-based Python networking library that uses
+<a class="reference external" href="http://codespeak.net/py/0.9.2/greenlet.html">greenlet</a> to provide a high-level synchronous API on top of <a class="reference external" href="http://monkey.org/~provos/libevent/">libevent</a>
+event loop:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">gevent.wsgi</span> <span class="kn">import</span> <span class="n">WSGIServer</span>
+<span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span>
+
+<span class="n">http_server</span> <span class="o">=</span> <span class="n">WSGIServer</span><span class="p">((</span><span class="s">&#39;&#39;</span><span class="p">,</span> <span class="mi">5000</span><span class="p">),</span> <span class="n">app</span><span class="p">)</span>
+<span class="n">http_server</span><span class="o">.</span><span class="n">serve_forever</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="gunicorn">
+<h2>Gunicorn<a class="headerlink" href="#gunicorn" title="Permalink to this headline">¶</a></h2>
+<p><a class="reference external" href="http://gunicorn.org/">Gunicorn</a> &#8216;Green Unicorn&#8217; is a WSGI HTTP Server for UNIX. It&#8217;s a pre-fork
+worker model ported from Ruby&#8217;s Unicorn project. It supports both <a class="reference external" href="http://eventlet.net/">eventlet</a>
+and <a class="reference external" href="http://codespeak.net/py/0.9.2/greenlet.html">greenlet</a>. Running a Flask application on this server is quite simple:</p>
+<div class="highlight-python"><pre>gunicorn myproject:app</pre>
+</div>
+<p><a class="reference external" href="http://gunicorn.org/">Gunicorn</a> provides many command-line options &#8211; see <tt class="docutils literal"><span class="pre">gunicorn</span> <span class="pre">-h</span></tt>.
+For example, to run a Flask application with 4 worker processes (<tt class="docutils literal"><span class="pre">-w</span>
+<span class="pre">4</span></tt>) binding to localhost port 4000 (<tt class="docutils literal"><span class="pre">-b</span> <span class="pre">127.0.0.1:4000</span></tt>):</p>
+<div class="highlight-python"><pre>gunicorn -w 4 -b 127.0.0.1:4000 myproject:app</pre>
+</div>
+</div>
+<div class="section" id="proxy-setups">
+<h2>Proxy Setups<a class="headerlink" href="#proxy-setups" title="Permalink to this headline">¶</a></h2>
+<p>If you deploy your application using one of these servers 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 <cite>REMOTE_ADDR</cite> and <cite>HTTP_HOST</cite>. Werkzeug ships a fixer that
+will solve some common setups, but you might want to write your own WSGI
+middleware for specific setups.</p>
+<p>The most common setup invokes the host being set from <cite>X-Forwarded-Host</cite>
+and the remote address from <cite>X-Forwarded-For</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.contrib.fixers</span> <span class="kn">import</span> <span class="n">ProxyFix</span>
+<span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span> <span class="o">=</span> <span class="n">ProxyFix</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>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.</p>
+<p>If you want to rewrite the headers from another header, you might want to
+use a fixer like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">CustomProxyFix</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">app</span>
+
+ <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
+ <span class="n">host</span> <span class="o">=</span> <span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;HTTP_X_FHOST&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">host</span><span class="p">:</span>
+ <span class="n">environ</span><span class="p">[</span><span class="s">&#39;HTTP_HOST&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">host</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
+
+<span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span> <span class="o">=</span> <span class="n">CustomProxyFix</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Other Servers</a><ul>
+<li><a class="reference internal" href="#tornado">Tornado</a></li>
+<li><a class="reference internal" href="#gevent">Gevent</a></li>
+<li><a class="reference internal" href="#gunicorn">Gunicorn</a></li>
+<li><a class="reference internal" href="#proxy-setups">Proxy Setups</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Deployment Options</a><ul>
+ <li>Previous: <a href="uwsgi.html" title="previous chapter">uWSGI</a></li>
+ <li>Next: <a href="../becomingbig.html" title="next chapter">Becoming Big</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/deploying/others.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/deploying/uwsgi.html b/studio/static/doc/flask-docs/deploying/uwsgi.html
new file mode 100644
index 0000000..38eeed0
--- /dev/null
+++ b/studio/static/doc/flask-docs/deploying/uwsgi.html
@@ -0,0 +1,167 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>uWSGI &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Deployment Options" href="index.html" />
+ <link rel="next" title="Other Servers" href="others.html" />
+ <link rel="prev" title="FastCGI" href="fastcgi.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="others.html" title="Other Servers"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="fastcgi.html" title="FastCGI"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Deployment Options</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="uwsgi">
+<span id="deploying-uwsgi"></span><h1>uWSGI<a class="headerlink" href="#uwsgi" title="Permalink to this headline">¶</a></h1>
+<p>uWSGI is a deployment option on servers like <a class="reference external" href="http://nginx.org/">nginx</a>, <a class="reference external" href="http://www.lighttpd.net/">lighttpd</a>, and
+<a class="reference external" href="http://www.cherokee-project.com/">cherokee</a>; see <a class="reference internal" href="fastcgi.html#deploying-fastcgi"><em>FastCGI</em></a> and
+<a class="reference internal" href="others.html#deploying-other-servers"><em>Other Servers</em></a> for other options. To use your WSGI
+application with uWSGI protocol you will need a uWSGI server
+first. uWSGI is both a protocol and an application server; the
+application server can serve uWSGI, FastCGI, and HTTP protocols.</p>
+<p>The most popular uWSGI server is <a class="reference external" href="http://projects.unbit.it/uwsgi/">uwsgi</a>, which we will use for this
+guide. Make sure to have it installed to follow along.</p>
+<div class="admonition-watch-out admonition ">
+<p class="first admonition-title">Watch Out</p>
+<p class="last">Please make sure in advance that any <tt class="docutils literal"><span class="pre">app.run()</span></tt> calls you might
+have in your application file are inside an <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span>
+<span class="pre">'__main__':</span></tt> block or moved to a separate file. Just make sure it&#8217;s
+not called because this will always start a local WSGI server which
+we do not want if we deploy that application to uWSGI.</p>
+</div>
+<div class="section" id="starting-your-app-with-uwsgi">
+<h2>Starting your app with uwsgi<a class="headerlink" href="#starting-your-app-with-uwsgi" title="Permalink to this headline">¶</a></h2>
+<p><cite>uwsgi</cite> is designed to operate on WSGI callables found in python modules.</p>
+<p>Given a flask application in myapp.py, use the following command:</p>
+<div class="highlight-text"><div class="highlight"><pre>$ uwsgi -s /tmp/uwsgi.sock --module myapp --callable app
+</pre></div>
+</div>
+<p>Or, if you prefer:</p>
+<div class="highlight-text"><div class="highlight"><pre>$ uwsgi -s /tmp/uwsgi.sock -w myapp:app
+</pre></div>
+</div>
+</div>
+<div class="section" id="configuring-nginx">
+<h2>Configuring nginx<a class="headerlink" href="#configuring-nginx" title="Permalink to this headline">¶</a></h2>
+<p>A basic flask uWSGI configuration for nginx looks like this:</p>
+<div class="highlight-python"><pre>location = /yourapplication { rewrite ^ /yourapplication/; }
+location /yourapplication { try_files $uri @yourapplication; }
+location @yourapplication {
+ include uwsgi_params;
+ uwsgi_param SCRIPT_NAME /yourapplication;
+ uwsgi_modifier1 30;
+ uwsgi_pass unix:/tmp/uwsgi.sock;
+}</pre>
+</div>
+<p>This configuration binds the application to <cite>/yourapplication</cite>. If you want
+to have it in the URL root it&#8217;s a bit simpler because you don&#8217;t have to tell
+it the WSGI <cite>SCRIPT_NAME</cite> or set the uwsgi modifier to make use of it:</p>
+<div class="highlight-python"><pre>location / { try_files $uri @yourapplication; }
+location @yourapplication {
+ include uwsgi_params;
+ uwsgi_pass unix:/tmp/uwsgi.sock;
+}</pre>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">uWSGI</a><ul>
+<li><a class="reference internal" href="#starting-your-app-with-uwsgi">Starting your app with uwsgi</a></li>
+<li><a class="reference internal" href="#configuring-nginx">Configuring nginx</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Deployment Options</a><ul>
+ <li>Previous: <a href="fastcgi.html" title="previous chapter">FastCGI</a></li>
+ <li>Next: <a href="others.html" title="next chapter">Other Servers</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/deploying/uwsgi.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/design.html b/studio/static/doc/flask-docs/design.html
new file mode 100644
index 0000000..b5dd479
--- /dev/null
+++ b/studio/static/doc/flask-docs/design.html
@@ -0,0 +1,276 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Design Decisions in Flask &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="HTML/XHTML FAQ" href="htmlfaq.html" />
+ <link rel="prev" title="API" href="api.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="htmlfaq.html" title="HTML/XHTML FAQ"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="api.html" title="API"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="design-decisions-in-flask">
+<span id="design"></span><h1>Design Decisions in Flask<a class="headerlink" href="#design-decisions-in-flask" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<div class="section" id="the-explicit-application-object">
+<h2>The Explicit Application Object<a class="headerlink" href="#the-explicit-application-object" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference internal" href="api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> class. Each Flask application has
+to create an instance of this class itself and pass it the name of the
+module, but why can&#8217;t Flask do that itself?</p>
+<p>Without such an explicit application object the following code:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&#39;Hello World!&#39;</span>
+</pre></div>
+</div>
+<p>Would look like this instead:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">hypothetical_flask</span> <span class="kn">import</span> <span class="n">route</span>
+
+<span class="nd">@route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&#39;Hello World!&#39;</span>
+</pre></div>
+</div>
+<p>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&#8217;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.</p>
+<p>Another thing that becomes possible when you have an explicit object lying
+around in your code is that you can subclass the base class
+(<a class="reference internal" href="api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a>) 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.</p>
+<p>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 <cite>__name__</cite> as package name.
+Flask depends on that information to properly load resources relative
+to your module. With Python&#8217;s outstanding support for reflection it can
+then access the package to figure out where the templates and static files
+are stored (see <a class="reference internal" href="api.html#flask.Flask.open_resource" title="flask.Flask.open_resource"><tt class="xref py py-meth docutils literal"><span class="pre">open_resource()</span></tt></a>). 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.</p>
+<p>The third reason is &#8220;explicit is better than implicit&#8221;. That object is
+your WSGI application, you don&#8217;t have to remember anything else. If you
+want to apply a WSGI middleware, just wrap it and you&#8217;re done (though
+there are better ways to do that so that you do not lose the reference
+to the application object <a class="reference internal" href="api.html#flask.Flask.wsgi_app" title="flask.Flask.wsgi_app"><tt class="xref py py-meth docutils literal"><span class="pre">wsgi_app()</span></tt></a>).</p>
+<p>Furthermore this design makes it possible to use a factory function to
+create the application which is very helpful for unittesting and similar
+things (<a class="reference internal" href="patterns/appfactories.html#app-factories"><em>Application Factories</em></a>).</p>
+</div>
+<div class="section" id="the-routing-system">
+<h2>The Routing System<a class="headerlink" href="#the-routing-system" title="Permalink to this headline">¶</a></h2>
+<p>Flask uses the Werkzeug routing system which has was designed to
+automatically order routes by complexity. This means that you can declare
+routes in arbitrary order and they will still work as expected. This is a
+requirement if you want to properly implement decorator based routing
+since decorators could be fired in undefined order when the application is
+split into multiple modules.</p>
+<p>Another design decision with the Werkzeug routing system is that routes
+in Werkzeug try to ensure that there is that URLs are unique. Werkzeug
+will go quite far with that in that it will automatically redirect to a
+canonical URL if a route is ambiguous.</p>
+</div>
+<div class="section" id="one-template-engine">
+<h2>One Template Engine<a class="headerlink" href="#one-template-engine" title="Permalink to this headline">¶</a></h2>
+<p>Flask decides on one template engine: Jinja2. Why doesn&#8217;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 <em>always</em> configured will probably go away,
+the decision to bundle one template engine and use that will not.</p>
+<p>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.</p>
+<p>But that&#8217;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.</p>
+<p>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&#8217;s extensive autoescaping support. Also it provides
+ways to access macros from Jinja2 templates.</p>
+<p>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.</p>
+<p>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.</p>
+</div>
+<div class="section" id="micro-with-dependencies">
+<h2>Micro with Dependencies<a class="headerlink" href="#micro-with-dependencies" title="Permalink to this headline">¶</a></h2>
+<p>Why does Flask call itself a microframework and yet it depends on two
+libraries (namely Werkzeug and Jinja2). Why shouldn&#8217;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&#8217;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&#8217;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.</p>
+<p>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.</p>
+</div>
+<div class="section" id="thread-locals">
+<h2>Thread Locals<a class="headerlink" href="#thread-locals" title="Permalink to this headline">¶</a></h2>
+<p>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 (<a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a>). Why is that and
+isn&#8217;t that a bad idea?</p>
+<p>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.</p>
+<p>Also see the <a class="reference internal" href="becomingbig.html#becomingbig"><em>Becoming Big</em></a> section of the documentation for some
+inspiration for larger applications based on Flask.</p>
+</div>
+<div class="section" id="what-flask-is-what-flask-is-not">
+<h2>What Flask is, What Flask is Not<a class="headerlink" href="#what-flask-is-what-flask-is-not" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>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.</p>
+<p>The idea of Flask is to build a good foundation for all applications.
+Everything else is up to you or extensions.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Design Decisions in Flask</a><ul>
+<li><a class="reference internal" href="#the-explicit-application-object">The Explicit Application Object</a></li>
+<li><a class="reference internal" href="#the-routing-system">The Routing System</a></li>
+<li><a class="reference internal" href="#one-template-engine">One Template Engine</a></li>
+<li><a class="reference internal" href="#micro-with-dependencies">Micro with Dependencies</a></li>
+<li><a class="reference internal" href="#thread-locals">Thread Locals</a></li>
+<li><a class="reference internal" href="#what-flask-is-what-flask-is-not">What Flask is, What Flask is Not</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="api.html" title="previous chapter">API</a></li>
+ <li>Next: <a href="htmlfaq.html" title="next chapter">HTML/XHTML FAQ</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/design.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/errorhandling.html b/studio/static/doc/flask-docs/errorhandling.html
new file mode 100644
index 0000000..9698707
--- /dev/null
+++ b/studio/static/doc/flask-docs/errorhandling.html
@@ -0,0 +1,347 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Handling Application Errors &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Configuration Handling" href="config.html" />
+ <link rel="prev" title="Testing Flask Applications" href="testing.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="config.html" title="Configuration Handling"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="testing.html" title="Testing Flask Applications"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="handling-application-errors">
+<span id="application-errors"></span><h1>Handling Application Errors<a class="headerlink" href="#handling-application-errors" title="Permalink to this headline">¶</a></h1>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.3.</span></p>
+<p>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:</p>
+<ul class="simple">
+<li>the client terminated the request early and the application was still
+reading from the incoming data.</li>
+<li>the database server was overloaded and could not handle the query.</li>
+<li>a filesystem is full</li>
+<li>a harddrive crashed</li>
+<li>a backend server overloaded</li>
+<li>a programming error in a library you are using</li>
+<li>network connection of the server to another system failed.</li>
+</ul>
+<p>And that&#8217;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 <a class="reference internal" href="api.html#flask.Flask.logger" title="flask.Flask.logger"><tt class="xref py py-attr docutils literal"><span class="pre">logger</span></tt></a>.</p>
+<p>But there is more you can do, and we will cover some better setups to deal
+with errors.</p>
+<div class="section" id="error-mails">
+<h2>Error Mails<a class="headerlink" href="#error-mails" title="Permalink to this headline">¶</a></h2>
+<p>If the application runs in production mode (which it will do on your
+server) you won&#8217;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.</p>
+<p>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.</p>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">ADMINS</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;yourname@example.com&#39;</span><span class="p">]</span>
+<span class="k">if</span> <span class="ow">not</span> <span class="n">app</span><span class="o">.</span><span class="n">debug</span><span class="p">:</span>
+ <span class="kn">import</span> <span class="nn">logging</span>
+ <span class="kn">from</span> <span class="nn">logging.handlers</span> <span class="kn">import</span> <span class="n">SMTPHandler</span>
+ <span class="n">mail_handler</span> <span class="o">=</span> <span class="n">SMTPHandler</span><span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span>
+ <span class="s">&#39;server-error@example.com&#39;</span><span class="p">,</span>
+ <span class="n">ADMINS</span><span class="p">,</span> <span class="s">&#39;YourApplication Failed&#39;</span><span class="p">)</span>
+ <span class="n">mail_handler</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">ERROR</span><span class="p">)</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">mail_handler</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>So what just happened? We created a new
+<a class="reference external" href="http://docs.python.org/dev/library/logging.handlers.html#logging.handlers.SMTPHandler" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">SMTPHandler</span></tt></a> that will send mails with the mail
+server listening on <tt class="docutils literal"><span class="pre">127.0.0.1</span></tt> to all the <cite>ADMINS</cite> from the address
+<em>server-error&#64;example.com</em> with the subject &#8220;YourApplication Failed&#8221;. If
+your mail server requires credentials, these can also be provided. For
+that check out the documentation for the
+<a class="reference external" href="http://docs.python.org/dev/library/logging.handlers.html#logging.handlers.SMTPHandler" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">SMTPHandler</span></tt></a>.</p>
+<p>We also tell the handler to only send errors and more critical messages.
+Because we certainly don&#8217;t want to get a mail for warnings or other
+useless logs that might happen during request handling.</p>
+<p>Before you run that in production, please also look at <a class="reference internal" href="#logformat"><em>Controlling the Log Format</em></a> to
+put more information into that error mail. That will save you from a lot
+of frustration.</p>
+</div>
+<div class="section" id="logging-to-a-file">
+<h2>Logging to a File<a class="headerlink" href="#logging-to-a-file" title="Permalink to this headline">¶</a></h2>
+<p>Even if you get mails, you probably also want to log warnings. It&#8217;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&#8217;s your responsibility to warn in the
+code if something seems odd.</p>
+<p>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:</p>
+<ul class="simple">
+<li><a class="reference external" href="http://docs.python.org/dev/library/logging.handlers.html#logging.FileHandler" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">FileHandler</span></tt></a> - logs messages to a file on the
+filesystem.</li>
+<li><a class="reference external" href="http://docs.python.org/dev/library/logging.handlers.html#logging.handlers.RotatingFileHandler" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">RotatingFileHandler</span></tt></a> - logs messages to a file
+on the filesystem and will rotate after a certain number of messages.</li>
+<li><a class="reference external" href="http://docs.python.org/dev/library/logging.handlers.html#logging.handlers.NTEventLogHandler" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">NTEventLogHandler</span></tt></a> - 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.</li>
+<li><a class="reference external" href="http://docs.python.org/dev/library/logging.handlers.html#logging.handlers.SysLogHandler" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">SysLogHandler</span></tt></a> - sends logs to a UNIX
+syslog.</li>
+</ul>
+<p>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
+<cite>WARNING</cite>):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">if</span> <span class="ow">not</span> <span class="n">app</span><span class="o">.</span><span class="n">debug</span><span class="p">:</span>
+ <span class="kn">import</span> <span class="nn">logging</span>
+ <span class="kn">from</span> <span class="nn">themodule</span> <span class="kn">import</span> <span class="n">TheHandlerYouWant</span>
+ <span class="n">file_handler</span> <span class="o">=</span> <span class="n">TheHandlerYouWant</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
+ <span class="n">file_handler</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">WARNING</span><span class="p">)</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">file_handler</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="controlling-the-log-format">
+<span id="logformat"></span><h2>Controlling the Log Format<a class="headerlink" href="#controlling-the-log-format" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>A formatter can be instantiated with a format string. Note that
+tracebacks are appended to the log entry automatically. You don&#8217;t have to
+do that in the log formatter format string.</p>
+<p>Here some example setups:</p>
+<div class="section" id="email">
+<h3>Email<a class="headerlink" href="#email" title="Permalink to this headline">¶</a></h3>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">logging</span> <span class="kn">import</span> <span class="n">Formatter</span>
+<span class="n">mail_handler</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">Formatter</span><span class="p">(</span><span class="s">&#39;&#39;&#39;</span>
+<span class="s">Message type: </span><span class="si">%(levelname)s</span><span class="s"></span>
+<span class="s">Location: </span><span class="si">%(pathname)s</span><span class="s">:</span><span class="si">%(lineno)d</span><span class="s"></span>
+<span class="s">Module: </span><span class="si">%(module)s</span><span class="s"></span>
+<span class="s">Function: </span><span class="si">%(funcName)s</span><span class="s"></span>
+<span class="s">Time: </span><span class="si">%(asctime)s</span><span class="s"></span>
+
+<span class="s">Message:</span>
+
+<span class="si">%(message)s</span><span class="s"></span>
+<span class="s">&#39;&#39;&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="file-logging">
+<h3>File logging<a class="headerlink" href="#file-logging" title="Permalink to this headline">¶</a></h3>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">logging</span> <span class="kn">import</span> <span class="n">Formatter</span>
+<span class="n">file_handler</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">Formatter</span><span class="p">(</span>
+ <span class="s">&#39;</span><span class="si">%(asctime)s</span><span class="s"> </span><span class="si">%(levelname)s</span><span class="s">: </span><span class="si">%(message)s</span><span class="s"> &#39;</span>
+ <span class="s">&#39;[in </span><span class="si">%(pathname)s</span><span class="s">:</span><span class="si">%(lineno)d</span><span class="s">]&#39;</span>
+<span class="p">))</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="complex-log-formatting">
+<h3>Complex Log Formatting<a class="headerlink" href="#complex-log-formatting" title="Permalink to this headline">¶</a></h3>
+<p>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
+<a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging" title="(in Python v3.3)"><tt class="xref py py-mod docutils literal"><span class="pre">logging</span></tt></a> package for a full list.</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="26%" />
+<col width="74%" />
+</colgroup>
+<thead valign="bottom">
+<tr class="row-odd"><th class="head">Format</th>
+<th class="head">Description</th>
+</tr>
+</thead>
+<tbody valign="top">
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">%(levelname)s</span></tt></td>
+<td>Text logging level for the message
+(<tt class="docutils literal"><span class="pre">'DEBUG'</span></tt>, <tt class="docutils literal"><span class="pre">'INFO'</span></tt>, <tt class="docutils literal"><span class="pre">'WARNING'</span></tt>,
+<tt class="docutils literal"><span class="pre">'ERROR'</span></tt>, <tt class="docutils literal"><span class="pre">'CRITICAL'</span></tt>).</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">%(pathname)s</span></tt></td>
+<td>Full pathname of the source file where the
+logging call was issued (if available).</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">%(filename)s</span></tt></td>
+<td>Filename portion of pathname.</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">%(module)s</span></tt></td>
+<td>Module (name portion of filename).</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">%(funcName)s</span></tt></td>
+<td>Name of function containing the logging call.</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">%(lineno)d</span></tt></td>
+<td>Source line number where the logging call was
+issued (if available).</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">%(asctime)s</span></tt></td>
+<td>Human-readable time when the LogRecord` was
+created. By default this is of the form
+<tt class="docutils literal"><span class="pre">&quot;2003-07-08</span> <span class="pre">16:49:45,896&quot;</span></tt> (the numbers after
+the comma are millisecond portion of the time).
+This can be changed by subclassing the formatter
+and overriding the
+<a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.Formatter.formatTime" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">formatTime()</span></tt></a> method.</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">%(message)s</span></tt></td>
+<td>The logged message, computed as <tt class="docutils literal"><span class="pre">msg</span> <span class="pre">%</span> <span class="pre">args</span></tt></td>
+</tr>
+</tbody>
+</table>
+<p>If you want to further customize the formatting, you can subclass the
+formatter. The formatter has three interesting methods:</p>
+<dl class="docutils">
+<dt><a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.Formatter.format" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">format()</span></tt></a>:</dt>
+<dd>handles the actual formatting. It is passed a
+<a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.LogRecord" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">LogRecord</span></tt></a> object and has to return the formatted
+string.</dd>
+<dt><a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.Formatter.formatTime" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">formatTime()</span></tt></a>:</dt>
+<dd>called for <cite>asctime</cite> formatting. If you want a different time format
+you can override this method.</dd>
+<dt><a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.Formatter.formatException" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">formatException()</span></tt></a></dt>
+<dd>called for exception formatting. It is passed an <tt class="xref py py-attr docutils literal"><span class="pre">exc_info</span></tt>
+tuple and has to return a string. The default is usually fine, you
+don&#8217;t have to override it.</dd>
+</dl>
+<p>For more information, head over to the official documentation.</p>
+</div>
+</div>
+<div class="section" id="other-libraries">
+<h2>Other Libraries<a class="headerlink" href="#other-libraries" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging" title="(in Python v3.3)"><tt class="xref py py-mod docutils literal"><span class="pre">logging</span></tt></a> 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.</p>
+<p>Instead, I would recommend figuring out which loggers you are interested
+in, getting the loggers with the <a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.getLogger" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">getLogger()</span></tt></a> function and
+iterating over them to attach handlers:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">logging</span> <span class="kn">import</span> <span class="n">getLogger</span>
+<span class="n">loggers</span> <span class="o">=</span> <span class="p">[</span><span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="p">,</span> <span class="n">getLogger</span><span class="p">(</span><span class="s">&#39;sqlalchemy&#39;</span><span class="p">),</span>
+ <span class="n">getLogger</span><span class="p">(</span><span class="s">&#39;otherlibrary&#39;</span><span class="p">)]</span>
+<span class="k">for</span> <span class="n">logger</span> <span class="ow">in</span> <span class="n">loggers</span><span class="p">:</span>
+ <span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">mail_handler</span><span class="p">)</span>
+ <span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">file_handler</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Handling Application Errors</a><ul>
+<li><a class="reference internal" href="#error-mails">Error Mails</a></li>
+<li><a class="reference internal" href="#logging-to-a-file">Logging to a File</a></li>
+<li><a class="reference internal" href="#controlling-the-log-format">Controlling the Log Format</a><ul>
+<li><a class="reference internal" href="#email">Email</a></li>
+<li><a class="reference internal" href="#file-logging">File logging</a></li>
+<li><a class="reference internal" href="#complex-log-formatting">Complex Log Formatting</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#other-libraries">Other Libraries</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="testing.html" title="previous chapter">Testing Flask Applications</a></li>
+ <li>Next: <a href="config.html" title="next chapter">Configuration Handling</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/errorhandling.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/extensiondev.html b/studio/static/doc/flask-docs/extensiondev.html
new file mode 100644
index 0000000..05044b0
--- /dev/null
+++ b/studio/static/doc/flask-docs/extensiondev.html
@@ -0,0 +1,475 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Flask Extension Development &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Pocoo Styleguide" href="styleguide.html" />
+ <link rel="prev" title="Unicode in Flask" href="unicode.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="styleguide.html" title="Pocoo Styleguide"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="unicode.html" title="Unicode in Flask"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="flask-extension-development">
+<h1>Flask Extension Development<a class="headerlink" href="#flask-extension-development" title="Permalink to this headline">¶</a></h1>
+<p>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 <a class="reference external" href="http://flask.pocoo.org/extensions/">Flask Extension Registry</a>
+was created.</p>
+<p>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.</p>
+<div class="section" id="anatomy-of-an-extension">
+<h2>Anatomy of an Extension<a class="headerlink" href="#anatomy-of-an-extension" title="Permalink to this headline">¶</a></h2>
+<p>Extensions are all located in a package called <tt class="docutils literal"><span class="pre">flask_something</span></tt>
+where &#8220;something&#8221; is the name of the library you want to bridge. So for
+example if you plan to add support for a library named <cite>simplexml</cite> to
+Flask, you would name your extension&#8217;s package <tt class="docutils literal"><span class="pre">flask_simplexml</span></tt>.</p>
+<p>The name of the actual extension (the human readable name) however would
+be something like &#8220;Flask-SimpleXML&#8221;. Make sure to include the name
+&#8220;Flask&#8221; somewhere in that name and that you check the capitalization.
+This is how users can then register dependencies to your extension in
+their <cite>setup.py</cite> files.</p>
+<p>Flask sets up a redirect package called <tt class="xref py py-data docutils literal"><span class="pre">flask.ext</span></tt> where users
+should import the extensions from. If you for instance have a package
+called <tt class="docutils literal"><span class="pre">flask_something</span></tt> users would import it as
+<tt class="docutils literal"><span class="pre">flask.ext.something</span></tt>. This is done to transition from the old
+namespace packages. See <a class="reference internal" href="#ext-import-transition"><em>Extension Import Transition</em></a> for more details.</p>
+<p>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
+<a class="reference internal" href="patterns/appfactories.html#app-factories"><em>Application Factories</em></a> 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.</p>
+<p>Most importantly the extension must be shipped with a <cite>setup.py</cite> 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.</p>
+<p>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.</p>
+</div>
+<div class="section" id="hello-flaskext">
+<h2>&#8220;Hello Flaskext!&#8221;<a class="headerlink" href="#hello-flaskext" title="Permalink to this headline">¶</a></h2>
+<p>So let&#8217;s get started with creating such a Flask extension. The extension
+we want to create here will provide very basic support for SQLite3.</p>
+<p>First we create the following folder structure:</p>
+<div class="highlight-python"><pre>flask-sqlite3/
+ flask_sqlite3.py
+ LICENSE
+ README</pre>
+</div>
+<p>Here&#8217;s the contents of the most important files:</p>
+<div class="section" id="setup-py">
+<h3>setup.py<a class="headerlink" href="#setup-py" title="Permalink to this headline">¶</a></h3>
+<p>The next file that is absolutely required is the <cite>setup.py</cite> file which is
+used to install your Flask extension. The following contents are
+something you can work with:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="sd">&quot;&quot;&quot;</span>
+<span class="sd">Flask-SQLite3</span>
+<span class="sd">-------------</span>
+
+<span class="sd">This is the description for that library</span>
+<span class="sd">&quot;&quot;&quot;</span>
+<span class="kn">from</span> <span class="nn">setuptools</span> <span class="kn">import</span> <span class="n">setup</span>
+
+
+<span class="n">setup</span><span class="p">(</span>
+ <span class="n">name</span><span class="o">=</span><span class="s">&#39;Flask-SQLite3&#39;</span><span class="p">,</span>
+ <span class="n">version</span><span class="o">=</span><span class="s">&#39;1.0&#39;</span><span class="p">,</span>
+ <span class="n">url</span><span class="o">=</span><span class="s">&#39;http://example.com/flask-sqlite3/&#39;</span><span class="p">,</span>
+ <span class="n">license</span><span class="o">=</span><span class="s">&#39;BSD&#39;</span><span class="p">,</span>
+ <span class="n">author</span><span class="o">=</span><span class="s">&#39;Your Name&#39;</span><span class="p">,</span>
+ <span class="n">author_email</span><span class="o">=</span><span class="s">&#39;your-email@example.com&#39;</span><span class="p">,</span>
+ <span class="n">description</span><span class="o">=</span><span class="s">&#39;Very short description&#39;</span><span class="p">,</span>
+ <span class="n">long_description</span><span class="o">=</span><span class="n">__doc__</span><span class="p">,</span>
+ <span class="n">py_modules</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;flask_sqlite3&#39;</span><span class="p">],</span>
+ <span class="c"># if you would be using a package instead use packages instead</span>
+ <span class="c"># of py_modules:</span>
+ <span class="c"># packages=[&#39;flask_sqlite3&#39;],</span>
+ <span class="n">zip_safe</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
+ <span class="n">include_package_data</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">platforms</span><span class="o">=</span><span class="s">&#39;any&#39;</span><span class="p">,</span>
+ <span class="n">install_requires</span><span class="o">=</span><span class="p">[</span>
+ <span class="s">&#39;Flask&#39;</span>
+ <span class="p">],</span>
+ <span class="n">classifiers</span><span class="o">=</span><span class="p">[</span>
+ <span class="s">&#39;Environment :: Web Environment&#39;</span><span class="p">,</span>
+ <span class="s">&#39;Intended Audience :: Developers&#39;</span><span class="p">,</span>
+ <span class="s">&#39;License :: OSI Approved :: BSD License&#39;</span><span class="p">,</span>
+ <span class="s">&#39;Operating System :: OS Independent&#39;</span><span class="p">,</span>
+ <span class="s">&#39;Programming Language :: Python&#39;</span><span class="p">,</span>
+ <span class="s">&#39;Topic :: Internet :: WWW/HTTP :: Dynamic Content&#39;</span><span class="p">,</span>
+ <span class="s">&#39;Topic :: Software Development :: Libraries :: Python Modules&#39;</span>
+ <span class="p">]</span>
+<span class="p">)</span>
+</pre></div>
+</div>
+<p>That&#8217;s a lot of code but you can really just copy/paste that from existing
+extensions and adapt.</p>
+</div>
+<div class="section" id="flask-sqlite3-py">
+<h3>flask_sqlite3.py<a class="headerlink" href="#flask-sqlite3-py" title="Permalink to this headline">¶</a></h3>
+<p>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.</p>
+</div>
+</div>
+<div class="section" id="initializing-extensions">
+<h2>Initializing Extensions<a class="headerlink" href="#initializing-extensions" title="Permalink to this headline">¶</a></h2>
+<p>Many extensions will need some kind of initialization step. For example,
+consider your application is currently connecting to SQLite like the
+documentation suggests (<a class="reference internal" href="patterns/sqlite3.html#sqlite3"><em>Using SQLite 3 with Flask</em></a>) 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?</p>
+<p>Quite simple: you pass it to it.</p>
+<p>There are two recommended ways for an extension to initialize:</p>
+<dl class="docutils">
+<dt>initialization functions:</dt>
+<dd>If your extension is called <cite>helloworld</cite> you might have a function
+called <tt class="docutils literal"><span class="pre">init_helloworld(app[,</span> <span class="pre">extra_args])</span></tt> that initializes the
+extension for that application. It could attach before / after
+handlers etc.</dd>
+<dt>classes:</dt>
+<dd>Classes work mostly like initialization functions but can later be
+used to further change the behaviour. For an example look at how the
+<a class="reference external" href="http://packages.python.org/Flask-OAuth/">OAuth extension</a> works: there is an <cite>OAuth</cite> object that provides
+some helper functions like <cite>OAuth.remote_app</cite> to create a reference to
+a remote application that uses OAuth.</dd>
+</dl>
+<p>What to use depends on what you have in mind. For the SQLite 3 extension
+we will use the class based approach because it will provide users with a
+manager object that handles opening and closing database connections.</p>
+</div>
+<div class="section" id="the-extension-code">
+<h2>The Extension Code<a class="headerlink" href="#the-extension-code" title="Permalink to this headline">¶</a></h2>
+<p>Here&#8217;s the contents of the <cite>flask_sqlite3.py</cite> for copy/paste:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">absolute_import</span>
+<span class="kn">import</span> <span class="nn">sqlite3</span>
+
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">_request_ctx_stack</span>
+
+<span class="k">class</span> <span class="nc">SQLite3</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">app</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s">&#39;SQLITE3_DATABASE&#39;</span><span class="p">,</span> <span class="s">&#39;:memory:&#39;</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">teardown_request</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">teardown_request</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">before_request</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">before_request</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">connect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;SQLITE3_DATABASE&#39;</span><span class="p">])</span>
+
+ <span class="k">def</span> <span class="nf">before_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="n">ctx</span><span class="o">.</span><span class="n">sqlite3_db</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">teardown_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exception</span><span class="p">):</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="n">ctx</span><span class="o">.</span><span class="n">sqlite3_db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">get_db</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="k">if</span> <span class="n">ctx</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">ctx</span><span class="o">.</span><span class="n">sqlite3_db</span>
+</pre></div>
+</div>
+<p>So here&#8217;s what these lines of code do:</p>
+<ol class="arabic simple">
+<li>The <tt class="docutils literal"><span class="pre">__future__</span></tt> import is necessary to activate absolute imports.
+Otherwise we could not call our module <cite>sqlite3.py</cite> and import the
+top-level <cite>sqlite3</cite> module which actually implements the connection to
+SQLite.</li>
+<li>We create a class for our extension that requires a supplied <cite>app</cite> object,
+sets a configuration for the database if it&#8217;s not there
+(<a class="reference external" href="http://docs.python.org/dev/library/stdtypes.html#dict.setdefault" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">dict.setdefault()</span></tt></a>), and attaches <cite>before_request</cite> and
+<cite>teardown_request</cite> handlers.</li>
+<li>Next, we define a <cite>connect</cite> function that opens a database connection.</li>
+<li>Then we set up the request handlers we bound to the app above. Note here
+that we&#8217;re attaching our database connection to the top request context via
+<cite>_request_ctx_stack.top</cite>. Extensions should use the top context and not the
+<cite>g</cite> object to store things like database connections.</li>
+<li>Finally, we add a <cite>get_db</cite> function that simplifies access to the context&#8217;s
+database.</li>
+</ol>
+<p>So why did we decide on a class based approach here? Because using our
+extension looks something like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="kn">from</span> <span class="nn">flask_sqlite3</span> <span class="kn">import</span> <span class="n">SQLite3</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="s">&#39;the-config.cfg&#39;</span><span class="p">)</span>
+<span class="n">manager</span> <span class="o">=</span> <span class="n">SQLite3</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">manager</span><span class="o">.</span><span class="n">get_db</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>You can then use the database from views like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_all</span><span class="p">():</span>
+ <span class="n">cur</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
+ <span class="n">cur</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Opening a database connection from outside a view function is simple.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">db</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">cur</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">cur</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="adding-an-init-app-function">
+<h2>Adding an <cite>init_app</cite> Function<a class="headerlink" href="#adding-an-init-app-function" title="Permalink to this headline">¶</a></h2>
+<p>In practice, you&#8217;ll almost always want to permit users to initialize your
+extension and provide an app object after the fact. This can help avoid
+circular import problems when a user is breaking their app into multiple files.
+Our extension could add an <cite>init_app</cite> function as follows:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">SQLite3</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">app</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">app</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">init_app</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="bp">None</span>
+
+ <span class="k">def</span> <span class="nf">init_app</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">app</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">app</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s">&#39;SQLITE3_DATABASE&#39;</span><span class="p">,</span> <span class="s">&#39;:memory:&#39;</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">teardown_request</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">teardown_request</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">before_request</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">before_request</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">connect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;SQLITE3_DATABASE&#39;</span><span class="p">])</span>
+
+ <span class="k">def</span> <span class="nf">before_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="n">ctx</span><span class="o">.</span><span class="n">sqlite3_db</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">teardown_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exception</span><span class="p">):</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="n">ctx</span><span class="o">.</span><span class="n">sqlite3_db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">get_db</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="k">if</span> <span class="n">ctx</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">ctx</span><span class="o">.</span><span class="n">sqlite3_db</span>
+</pre></div>
+</div>
+<p>The user could then initialize the extension in one file:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">manager</span> <span class="o">=</span> <span class="n">SQLite3</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>and bind their app to the extension in another file:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">manager</span><span class="o">.</span><span class="n">init_app</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="end-of-request-behavior">
+<h2>End-Of-Request Behavior<a class="headerlink" href="#end-of-request-behavior" title="Permalink to this headline">¶</a></h2>
+<p>Due to the change in Flask 0.7 regarding functions that are run at the end
+of the request your extension will have to be extra careful there if it
+wants to continue to support older versions of Flask. The following
+pattern is a good way to support both:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">close_connection</span><span class="p">(</span><span class="n">response</span><span class="p">):</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">_request_ctx_stack</span><span class="o">.</span><span class="n">top</span>
+ <span class="n">ctx</span><span class="o">.</span><span class="n">sqlite3_db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">response</span>
+
+<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="s">&#39;teardown_request&#39;</span><span class="p">):</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">teardown_request</span><span class="p">(</span><span class="n">close_connection</span><span class="p">)</span>
+<span class="k">else</span><span class="p">:</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">after_request</span><span class="p">(</span><span class="n">close_connection</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Strictly speaking the above code is wrong, because teardown functions are
+passed the exception and typically don&#8217;t return anything. However because
+the return value is discarded this will just work assuming that the code
+in between does not touch the passed parameter.</p>
+</div>
+<div class="section" id="learn-from-others">
+<h2>Learn from Others<a class="headerlink" href="#learn-from-others" title="Permalink to this headline">¶</a></h2>
+<p>This documentation only touches the bare minimum for extension
+development. If you want to learn more, it&#8217;s a very good idea to check
+out existing extensions on the <a class="reference external" href="http://flask.pocoo.org/extensions/">Flask Extension Registry</a>. If you feel
+lost there is still the <a class="reference external" href="http://flask.pocoo.org/mailinglist/">mailinglist</a> and the <a class="reference external" href="http://flask.pocoo.org/community/irc/">IRC channel</a> 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.</p>
+<p>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.</p>
+<p>The best Flask extensions are extensions that share common idioms for the
+API. And this can only work if collaboration happens early.</p>
+</div>
+<div class="section" id="approved-extensions">
+<h2>Approved Extensions<a class="headerlink" href="#approved-extensions" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference external" href="http://flask.pocoo.org/extensions/">Flask
+Extension Registry</a> and marked appropriately. If you want your own
+extension to be approved you have to follow these guidelines:</p>
+<ol class="arabic simple">
+<li>An approved Flask extension must provide exactly one package or module
+named <tt class="docutils literal"><span class="pre">flask_extensionname</span></tt>. They might also reside inside a
+<tt class="docutils literal"><span class="pre">flaskext</span></tt> namespace packages though this is discouraged now.</li>
+<li>It must ship a testing suite that can either be invoked with <tt class="docutils literal"><span class="pre">make</span> <span class="pre">test</span></tt>
+or <tt class="docutils literal"><span class="pre">python</span> <span class="pre">setup.py</span> <span class="pre">test</span></tt>. For test suites invoked with <tt class="docutils literal"><span class="pre">make</span>
+<span class="pre">test</span></tt> the extension has to ensure that all dependencies for the test
+are installed automatically, in case of <tt class="docutils literal"><span class="pre">python</span> <span class="pre">setup.py</span> <span class="pre">test</span></tt>
+dependencies for tests alone can be specified in the <cite>setup.py</cite>
+file. The test suite also has to be part of the distribution.</li>
+<li>APIs of approved extensions will be checked for the following
+characteristics:<ul>
+<li>an approved extension has to support multiple applications
+running in the same Python process.</li>
+<li>it must be possible to use the factory pattern for creating
+applications.</li>
+</ul>
+</li>
+<li>The license must be BSD/MIT/WTFPL licensed.</li>
+<li>The naming scheme for official extensions is <em>Flask-ExtensionName</em> or
+<em>ExtensionName-Flask</em>.</li>
+<li>Approved extensions must define all their dependencies in the
+<cite>setup.py</cite> file unless a dependency cannot be met because it is not
+available on PyPI.</li>
+<li>The extension must have documentation that uses one of the two Flask
+themes for Sphinx documentation.</li>
+<li>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
+(<tt class="docutils literal"><span class="pre">PackageName==dev</span></tt>).</li>
+<li>The <tt class="docutils literal"><span class="pre">zip_safe</span></tt> flag in the setup script must be set to <tt class="docutils literal"><span class="pre">False</span></tt>,
+even if the extension would be safe for zipping.</li>
+<li>An extension currently has to support Python 2.5, 2.6 as well as
+Python 2.7</li>
+</ol>
+</div>
+<div class="section" id="extension-import-transition">
+<span id="ext-import-transition"></span><h2>Extension Import Transition<a class="headerlink" href="#extension-import-transition" title="Permalink to this headline">¶</a></h2>
+<p>For a while we recommended using namespace packages for Flask extensions.
+This turned out to be problematic in practice because many different
+competing namespace package systems exist and pip would automatically
+switch between different systems and this caused a lot of problems for
+users.</p>
+<p>Instead we now recommend naming packages <tt class="docutils literal"><span class="pre">flask_foo</span></tt> instead of the now
+deprecated <tt class="docutils literal"><span class="pre">flaskext.foo</span></tt>. Flask 0.8 introduces a redirect import
+system that lets uses import from <tt class="docutils literal"><span class="pre">flask.ext.foo</span></tt> and it will try
+<tt class="docutils literal"><span class="pre">flask_foo</span></tt> first and if that fails <tt class="docutils literal"><span class="pre">flaskext.foo</span></tt>.</p>
+<p>Flask extensions should urge users to import from <tt class="docutils literal"><span class="pre">flask.ext.foo</span></tt>
+instead of <tt class="docutils literal"><span class="pre">flask_foo</span></tt> or <tt class="docutils literal"><span class="pre">flaskext_foo</span></tt> so that extensions can
+transition to the new package name without affecting users.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Flask Extension Development</a><ul>
+<li><a class="reference internal" href="#anatomy-of-an-extension">Anatomy of an Extension</a></li>
+<li><a class="reference internal" href="#hello-flaskext">&#8220;Hello Flaskext!&#8221;</a><ul>
+<li><a class="reference internal" href="#setup-py">setup.py</a></li>
+<li><a class="reference internal" href="#flask-sqlite3-py">flask_sqlite3.py</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#initializing-extensions">Initializing Extensions</a></li>
+<li><a class="reference internal" href="#the-extension-code">The Extension Code</a></li>
+<li><a class="reference internal" href="#adding-an-init-app-function">Adding an <cite>init_app</cite> Function</a></li>
+<li><a class="reference internal" href="#end-of-request-behavior">End-Of-Request Behavior</a></li>
+<li><a class="reference internal" href="#learn-from-others">Learn from Others</a></li>
+<li><a class="reference internal" href="#approved-extensions">Approved Extensions</a></li>
+<li><a class="reference internal" href="#extension-import-transition">Extension Import Transition</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="unicode.html" title="previous chapter">Unicode in Flask</a></li>
+ <li>Next: <a href="styleguide.html" title="next chapter">Pocoo Styleguide</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/extensiondev.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/extensions.html b/studio/static/doc/flask-docs/extensions.html
new file mode 100644
index 0000000..d5ee4a7
--- /dev/null
+++ b/studio/static/doc/flask-docs/extensions.html
@@ -0,0 +1,153 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Flask Extensions &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Working with the Shell" href="shell.html" />
+ <link rel="prev" title="Modular Applications with Blueprints" href="blueprints.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="shell.html" title="Working with the Shell"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="blueprints.html" title="Modular Applications with Blueprints"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="flask-extensions">
+<h1>Flask Extensions<a class="headerlink" href="#flask-extensions" title="Permalink to this headline">¶</a></h1>
+<p>Flask extensions extend the functionality of Flask in various different
+ways. For instance they add support for databases and other common tasks.</p>
+<div class="section" id="finding-extensions">
+<h2>Finding Extensions<a class="headerlink" href="#finding-extensions" title="Permalink to this headline">¶</a></h2>
+<p>Flask extensions are listed on the <a class="reference external" href="http://flask.pocoo.org/extensions/">Flask Extension Registry</a> and can be
+downloaded with <tt class="docutils literal"><span class="pre">easy_install</span></tt> or <tt class="docutils literal"><span class="pre">pip</span></tt>. If you add a Flask extension
+as dependency to your <tt class="docutils literal"><span class="pre">requirements.rst</span></tt> or <tt class="docutils literal"><span class="pre">setup.py</span></tt> file they are
+usually installed with a simple command or when your application installs.</p>
+</div>
+<div class="section" id="using-extensions">
+<h2>Using Extensions<a class="headerlink" href="#using-extensions" title="Permalink to this headline">¶</a></h2>
+<p>Extensions typically have documentation that goes along that shows how to
+use it. There are no general rules in how extensions are supposed to
+behave but they are imported from common locations. If you have an
+extension called <tt class="docutils literal"><span class="pre">Flask-Foo</span></tt> or <tt class="docutils literal"><span class="pre">Foo-Flask</span></tt> it will be always
+importable from <tt class="docutils literal"><span class="pre">flask.ext.foo</span></tt>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask.ext</span> <span class="kn">import</span> <span class="n">foo</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="flask-before-0-8">
+<h2>Flask Before 0.8<a class="headerlink" href="#flask-before-0-8" title="Permalink to this headline">¶</a></h2>
+<p>If you are using Flask 0.7 or earlier the <tt class="xref py py-data docutils literal"><span class="pre">flask.ext</span></tt> package will not
+exist, instead you have to import from <tt class="docutils literal"><span class="pre">flaskext.foo</span></tt> or <tt class="docutils literal"><span class="pre">flask_foo</span></tt>
+depending on how the extension is distributed. If you want to develop an
+application that supports Flask 0.7 or earlier you should still import
+from the <tt class="xref py py-data docutils literal"><span class="pre">flask.ext</span></tt> package. We provide you with a compatibility
+module that provides this package for older versions of Flask. You can
+download it from github: <a class="reference external" href="https://github.com/mitsuhiko/flask/raw/master/scripts/flaskext_compat.py">flaskext_compat.py</a></p>
+<p>And here is how you can use it:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">flaskext_compat</span>
+<span class="n">flaskext_compat</span><span class="o">.</span><span class="n">activate</span><span class="p">()</span>
+
+<span class="kn">from</span> <span class="nn">flask.ext</span> <span class="kn">import</span> <span class="n">foo</span>
+</pre></div>
+</div>
+<p>Once the <tt class="docutils literal"><span class="pre">flaskext_compat</span></tt> module is activated the <tt class="xref py py-data docutils literal"><span class="pre">flask.ext</span></tt> will
+exist and you can start importing from there.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Flask Extensions</a><ul>
+<li><a class="reference internal" href="#finding-extensions">Finding Extensions</a></li>
+<li><a class="reference internal" href="#using-extensions">Using Extensions</a></li>
+<li><a class="reference internal" href="#flask-before-0-8">Flask Before 0.8</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="blueprints.html" title="previous chapter">Modular Applications with Blueprints</a></li>
+ <li>Next: <a href="shell.html" title="next chapter">Working with the Shell</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/extensions.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/foreword.html b/studio/static/doc/flask-docs/foreword.html
new file mode 100644
index 0000000..8cf1096
--- /dev/null
+++ b/studio/static/doc/flask-docs/foreword.html
@@ -0,0 +1,198 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Foreword &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Installation" href="installation.html" />
+ <link rel="prev" title="Welcome to Flask" href="index.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="installation.html" title="Installation"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="index.html" title="Welcome to Flask"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="foreword">
+<h1>Foreword<a class="headerlink" href="#foreword" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<div class="section" id="what-does-micro-mean">
+<h2>What does &#8220;micro&#8221; mean?<a class="headerlink" href="#what-does-micro-mean" title="Permalink to this headline">¶</a></h2>
+<p>To me, the &#8220;micro&#8221; in microframework refers not only to the simplicity and
+small size of the framework, but also the fact that it does not make much
+decisions for you. While Flask does pick a templating engine for you, we
+won&#8217;t make such decisions for your datastore or other parts.</p>
+<p>For us however the term “micro” does not mean that the whole implementation
+has to fit into a single Python file.</p>
+<p>One of the design decisions with Flask was that simple tasks should be
+simple and not take up a lot of code and yet not limit yourself. Because
+of that we took a few design choices that some people might find
+surprising or unorthodox. For example, Flask uses thread-local objects
+internally so that you don&#8217;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. In order to solve these
+problems we don&#8217;t hide the thread locals for you but instead embrace them
+and provide you with a lot of tools to make it as pleasant as possible to
+work with them.</p>
+<p>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. While this can be changed you usually don&#8217;t have to.</p>
+<p>The main reason however why Flask is called a &#8220;microframework&#8221; 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.</p>
+<p>Since Flask is based on a very solid foundation there is not a lot of code
+in Flask itself. As such it&#8217;s easy to adapt even for lage applications
+and we are making sure that you can either configure it as much as
+possible by subclassing things or by forking the entire codebase. If you
+are interested in that, check out the <a class="reference internal" href="becomingbig.html#becomingbig"><em>Becoming Big</em></a> chapter.</p>
+<p>If you are curious about the Flask design principles, head over to the
+section about <a class="reference internal" href="design.html#design"><em>Design Decisions in Flask</em></a>.</p>
+</div>
+<div class="section" id="web-development-is-dangerous">
+<h2>Web Development is Dangerous<a class="headerlink" href="#web-development-is-dangerous" title="Permalink to this headline">¶</a></h2>
+<p>I&#8217;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.</p>
+<p>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.</p>
+<p>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&#8217;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.</p>
+<p>So always keep security in mind when doing web development.</p>
+</div>
+<div class="section" id="the-status-of-python-3">
+<h2>The Status of Python 3<a class="headerlink" href="#the-status-of-python-3" title="Permalink to this headline">¶</a></h2>
+<p>Currently the Python community is in the process of improving libraries to
+support the new iteration of the Python programming language. While the
+situation is greatly improving there are still some issues that make it
+hard for us to switch over to Python 3 just now. These problems are
+partially caused by changes in the language that went unreviewed for too
+long, partially also because we have not quite worked out how the lower
+level API should change for the unicode differences in Python3.</p>
+<p>Werkzeug and Flask will be ported to Python 3 as soon as a solution for
+the changes 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. If you plan on upgrading to Python 3 in the near future we
+strongly recommend that you read <a class="reference external" href="http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/">How to write forwards compatible
+Python code</a>.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Foreword</a><ul>
+<li><a class="reference internal" href="#what-does-micro-mean">What does &#8220;micro&#8221; mean?</a></li>
+<li><a class="reference internal" href="#web-development-is-dangerous">Web Development is Dangerous</a></li>
+<li><a class="reference internal" href="#the-status-of-python-3">The Status of Python 3</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="index.html" title="previous chapter">Welcome to Flask</a></li>
+ <li>Next: <a href="installation.html" title="next chapter">Installation</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/foreword.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/genindex.html b/studio/static/doc/flask-docs/genindex.html
new file mode 100644
index 0000000..6ce9a91
--- /dev/null
+++ b/studio/static/doc/flask-docs/genindex.html
@@ -0,0 +1,1287 @@
+
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Index &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="#" title="General Index"
+ accesskey="I">index</a></li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+
+<h1 id="index">Index</h1>
+
+<div class="genindex-jumpbox">
+ <a href="#_"><strong>_</strong></a>
+ | <a href="#A"><strong>A</strong></a>
+ | <a href="#B"><strong>B</strong></a>
+ | <a href="#C"><strong>C</strong></a>
+ | <a href="#D"><strong>D</strong></a>
+ | <a href="#E"><strong>E</strong></a>
+ | <a href="#F"><strong>F</strong></a>
+ | <a href="#G"><strong>G</strong></a>
+ | <a href="#H"><strong>H</strong></a>
+ | <a href="#I"><strong>I</strong></a>
+ | <a href="#J"><strong>J</strong></a>
+ | <a href="#L"><strong>L</strong></a>
+ | <a href="#M"><strong>M</strong></a>
+ | <a href="#N"><strong>N</strong></a>
+ | <a href="#O"><strong>O</strong></a>
+ | <a href="#P"><strong>P</strong></a>
+ | <a href="#R"><strong>R</strong></a>
+ | <a href="#S"><strong>S</strong></a>
+ | <a href="#T"><strong>T</strong></a>
+ | <a href="#U"><strong>U</strong></a>
+ | <a href="#V"><strong>V</strong></a>
+ | <a href="#W"><strong>W</strong></a>
+ | <a href="#Y"><strong>Y</strong></a>
+
+</div>
+<h2 id="_">_</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask._request_ctx_stack">_request_ctx_stack (in module flask)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="A">A</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.abort">abort() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.add_url_rule">add_url_rule() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.add_url_rule">(flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.add_url_rule">(flask.blueprints.BlueprintSetupState method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Blueprint.after_app_request">after_app_request() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.after_request">after_request() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.after_request">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.after_request_funcs">after_request_funcs (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.app">app (flask.blueprints.BlueprintSetupState attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.app_context_processor">app_context_processor() (flask.Blueprint method)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Blueprint.app_errorhandler">app_errorhandler() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.app_url_defaults">app_url_defaults() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.app_url_value_preprocessor">app_url_value_preprocessor() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.args">args (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.views.View.as_view">as_view() (flask.views.View class method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.auto_find_instance_path">auto_find_instance_path() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="B">B</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.base_url">base_url (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.before_app_first_request">before_app_first_request() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.before_app_request">before_app_request() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.before_first_request">before_first_request() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.before_first_request_funcs">before_first_request_funcs (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.before_request">before_request() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.before_request">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.before_request_funcs">before_request_funcs (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint">Blueprint (class in flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.blueprint">blueprint (flask.blueprints.BlueprintSetupState attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Request.blueprint">(flask.Request attribute)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.blueprints">blueprints (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState">BlueprintSetupState (class in flask.blueprints)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="C">C</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Config">Config (class in flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.config">config (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.context_processor">context_processor() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.context_processor">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Request.cookies">cookies (flask.Request attribute)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.create_global_jinja_loader">create_global_jinja_loader() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.create_jinja_environment">create_jinja_environment() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.create_url_adapter">create_url_adapter() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.current_app">current_app (in module flask)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="D">D</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.data">data (flask.Request attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Response.data">(flask.Response attribute)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.debug">debug (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.debug_log_format">debug_log_format (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.views.View.decorators">decorators (flask.views.View attribute)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.default_config">default_config (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.dispatch_request">dispatch_request() (flask.Flask method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.views.View.dispatch_request">(flask.views.View method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.do_teardown_request">do_teardown_request() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="E">E</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.enable_modules">enable_modules (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.endpoint">endpoint (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.endpoint">endpoint() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.endpoint">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Request.environ">environ (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt>
+ environment variable
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="tutorial/setup.html#index-0">FLASKR_SETTINGS</a>
+ </dt>
+
+
+ <dt><a href="config.html#index-0">YOURAPPLICATION_SETTINGS</a>
+ </dt>
+
+ </dl></dd>
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.error_handler_spec">error_handler_spec (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.errorhandler">errorhandler() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.errorhandler">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Markup.escape">escape() (flask.Markup class method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.escape">(in module flask)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.extensions">extensions (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="F">F</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.files">files (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.first_registration">first_registration (flask.blueprints.BlueprintSetupState attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.flash">flash() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask">Flask (class in flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#module-flask">flask (module)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.flask.ext">flask.ext (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.signals.Namespace">flask.signals.Namespace (built-in class)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.testing.FlaskClient">FlaskClient (class in flask.testing)</a>
+ </dt>
+
+
+ <dt><a href="tutorial/setup.html#index-0">FLASKR_SETTINGS</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.form">form (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Config.from_envvar">from_envvar() (flask.Config method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Config.from_object">from_object() (flask.Config method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Config.from_pyfile">from_pyfile() (flask.Config method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.full_dispatch_request">full_dispatch_request() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="G">G</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.g">g (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.get_cookie_domain">get_cookie_domain() (flask.sessions.SessionInterface method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.get_cookie_httponly">get_cookie_httponly() (flask.sessions.SessionInterface method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.get_cookie_path">get_cookie_path() (flask.sessions.SessionInterface method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.get_cookie_secure">get_cookie_secure() (flask.sessions.SessionInterface method)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.get_expiration_time">get_expiration_time() (flask.sessions.SessionInterface method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.get_flashed_messages">get_flashed_messages() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.get_template_attribute">get_template_attribute() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.got_first_request">got_first_request (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.got_request_exception">got_request_exception (in module flask)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="H">H</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.handle_exception">handle_exception() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.handle_http_exception">handle_http_exception() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.handle_user_exception">handle_user_exception() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.has_request_context">has_request_context() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.has_static_folder">has_static_folder (flask.Blueprint attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.has_static_folder">(flask.Flask attribute)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Request.headers">headers (flask.Request attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Response.headers">(flask.Response attribute)</a>
+ </dt>
+
+ </dl></dd>
+ </dl></td>
+</tr></table>
+
+<h2 id="I">I</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.init_jinja_globals">init_jinja_globals() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.inject_url_defaults">inject_url_defaults() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.instance_path">instance_path (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.is_null_session">is_null_session() (flask.sessions.SessionInterface method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.is_xhr">is_xhr (flask.Request attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="J">J</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.jinja_env">jinja_env (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.jinja_loader">jinja_loader (flask.Blueprint attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.jinja_loader">(flask.Flask attribute)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.jinja_options">jinja_options (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.json">json (flask.Request attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.json">(in module flask)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.jsonify">jsonify() (in module flask)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="L">L</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.log_exception">log_exception() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.logger">logger (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.logger_name">logger_name (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="M">M</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.make_config">make_config() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.make_default_options_response">make_default_options_response() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.make_null_session">make_null_session() (flask.Flask method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.make_null_session">(flask.sessions.SessionInterface method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.make_response">make_response() (flask.Flask method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.make_response">(in module flask)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Blueprint.make_setup_state">make_setup_state() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Markup">Markup (class in flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.ctx.RequestContext.match_request">match_request() (flask.ctx.RequestContext method)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.max_content_length">max_content_length (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.method">method (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.views.View.methods">methods (flask.views.View attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.views.MethodView">MethodView (class in flask.views)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Response.mimetype">mimetype (flask.Response attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.session.modified">modified (flask.session attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionMixin.modified">(flask.sessions.SessionMixin attribute)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Request.module">module (flask.Request attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="N">N</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.name">name (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.session.new">new (flask.session attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionMixin.new">(flask.sessions.SessionMixin attribute)</a>
+ </dt>
+
+ </dl></dd>
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.null_session_class">null_session_class (flask.sessions.SessionInterface attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.NullSession">NullSession (class in flask.sessions)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="O">O</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.on_json_loading_failed">on_json_loading_failed() (flask.Request method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.open_instance_resource">open_instance_resource() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.open_resource">open_resource() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.open_resource">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.open_session">open_session() (flask.Flask method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.open_session">(flask.sessions.SessionInterface method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.options">options (flask.blueprints.BlueprintSetupState attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="P">P</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.path">path (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.session.permanent">permanent (flask.session attribute)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionMixin.permanent">(flask.sessions.SessionMixin attribute)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.permanent_session_lifetime">permanent_session_lifetime (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.ctx.RequestContext.pop">pop() (flask.ctx.RequestContext method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.preprocess_request">preprocess_request() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.preserve_context_on_exception">preserve_context_on_exception (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.process_response">process_response() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.propagate_exceptions">propagate_exceptions (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.ctx.RequestContext.push">push() (flask.ctx.RequestContext method)</a>
+ </dt>
+
+
+ <dt>
+ Python Enhancement Proposals
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="styleguide.html#index-0">PEP 8</a>
+ </dt>
+
+ </dl></dd>
+ </dl></td>
+</tr></table>
+
+<h2 id="R">R</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Blueprint.record">record() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.record_once">record_once() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.redirect">redirect() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.register">register() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.register_blueprint">register_blueprint() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.register_error_handler">register_error_handler() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.register_module">register_module() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.render_template">render_template() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.render_template_string">render_template_string() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.request">request (class in flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request">Request (class in flask)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.request_class">request_class (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.request_context">request_context() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.request_finished">request_finished (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.request_started">request_started (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.request_tearing_down">request_tearing_down (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.ctx.RequestContext">RequestContext (class in flask.ctx)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Response">Response (class in flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.response_class">response_class (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.route">route() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.route">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Request.routing_exception">routing_exception (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.run">run() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="S">S</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.safe_join">safe_join() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.save_session">save_session() (flask.Flask method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.sessions.SessionInterface.save_session">(flask.sessions.SessionInterface method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Request.script_root">script_root (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.secret_key">secret_key (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SecureCookieSessionInterface">SecureCookieSessionInterface (class in flask.sessions)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.select_jinja_autoescape">select_jinja_autoescape() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.send_file">send_file() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.send_from_directory">send_from_directory() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.send_static_file">send_static_file() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.send_static_file">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.session">session (class in flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SecureCookieSessionInterface.session_class">session_class (flask.sessions.SecureCookieSessionInterface attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.session_cookie_name">session_cookie_name (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.session_interface">session_interface (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.testing.FlaskClient.session_transaction">session_transaction() (flask.testing.FlaskClient method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SessionInterface">SessionInterface (class in flask.sessions)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.sessions.SessionMixin">SessionMixin (class in flask.sessions)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Response.set_cookie">set_cookie() (flask.Response method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.signals.Namespace.signal">signal() (flask.signals.Namespace method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.signals_available">signals_available (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Response.status_code">status_code (flask.Response attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.stream">stream (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Markup.striptags">striptags() (flask.Markup method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.subdomain">subdomain (flask.blueprints.BlueprintSetupState attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="T">T</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Blueprint.teardown_app_request">teardown_app_request() (flask.Blueprint method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.teardown_request">teardown_request() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.teardown_request">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.teardown_request_funcs">teardown_request_funcs (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.template_context_processors">template_context_processors (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.template_filter">template_filter() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.template_rendered">template_rendered (in module flask)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.test_client">test_client() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.test_client_class">test_client_class (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.test_request_context">test_request_context() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.testing">testing (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.trap_http_exception">trap_http_exception() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="U">U</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Markup.unescape">unescape() (flask.Markup method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.update_template_context">update_template_context() (flask.Flask method)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.url">url (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.url_default_functions">url_default_functions (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.url_defaults">url_defaults (flask.blueprints.BlueprintSetupState attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.url_defaults">url_defaults() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.url_defaults">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.url_for">url_for() (in module flask)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.url_map">url_map (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.blueprints.BlueprintSetupState.url_prefix">url_prefix (flask.blueprints.BlueprintSetupState attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.url_root">url_root (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Request.url_rule">url_rule (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.url_rule_class">url_rule_class (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Blueprint.url_value_preprocessor">url_value_preprocessor() (flask.Blueprint method)</a>
+ </dt>
+
+ <dd><dl>
+
+ <dt><a href="api.html#flask.Flask.url_value_preprocessor">(flask.Flask method)</a>
+ </dt>
+
+ </dl></dd>
+
+ <dt><a href="api.html#flask.Flask.url_value_preprocessors">url_value_preprocessors (flask.Flask attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.use_x_sendfile">use_x_sendfile (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="V">V</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.values">values (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.views.View">View (class in flask.views)</a>
+ </dt>
+
+ </dl></td>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Request.view_args">view_args (flask.Request attribute)</a>
+ </dt>
+
+
+ <dt><a href="api.html#flask.Flask.view_functions">view_functions (flask.Flask attribute)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="W">W</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="api.html#flask.Flask.wsgi_app">wsgi_app() (flask.Flask method)</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+<h2 id="Y">Y</h2>
+<table style="width: 100%" class="indextable genindextable"><tr>
+ <td style="width: 33%" valign="top"><dl>
+
+ <dt><a href="config.html#index-0">YOURAPPLICATION_SETTINGS</a>
+ </dt>
+
+ </dl></td>
+</tr></table>
+
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ </ul></li>
+</ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/htmlfaq.html b/studio/static/doc/flask-docs/htmlfaq.html
new file mode 100644
index 0000000..e018009
--- /dev/null
+++ b/studio/static/doc/flask-docs/htmlfaq.html
@@ -0,0 +1,357 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>HTML/XHTML FAQ &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Security Considerations" href="security.html" />
+ <link rel="prev" title="Design Decisions in Flask" href="design.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="security.html" title="Security Considerations"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="design.html" title="Design Decisions in Flask"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="html-xhtml-faq">
+<h1>HTML/XHTML FAQ<a class="headerlink" href="#html-xhtml-faq" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<div class="section" id="history-of-xhtml">
+<h2>History of XHTML<a class="headerlink" href="#history-of-xhtml" title="Permalink to this headline">¶</a></h2>
+<p>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&#8217;s lack of proper
+XHTML support. The XHTML spec states that XHTML must be served with the MIME
+type <cite>application/xhtml+xml</cite>, 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.</p>
+<p>One of the most important causes of pain is XML&#8217;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.</p>
+<p>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 (<tt class="docutils literal"><span class="pre">&lt;br&gt;</span></tt> becomes <tt class="docutils literal"><span class="pre">&lt;br/&gt;</span></tt> or <tt class="docutils literal"><span class="pre">&lt;br&gt;&lt;/br&gt;</span></tt> 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.</p>
+<p>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.</p>
+</div>
+<div class="section" id="history-of-html5">
+<h2>History of HTML5<a class="headerlink" href="#history-of-html5" title="Permalink to this headline">¶</a></h2>
+<p>Development of the HTML5 specification was started in 2004 under the name
+&#8220;Web Applications 1.0&#8221; 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.</p>
+<p>For example, in HTML4 <tt class="docutils literal"><span class="pre">&lt;title/Hello/</span></tt> theoretically parses exactly the
+same as <tt class="docutils literal"><span class="pre">&lt;title&gt;Hello&lt;/title&gt;</span></tt>. However, since people were using
+XHTML-like tags along the lines of <tt class="docutils literal"><span class="pre">&lt;link</span> <span class="pre">/&gt;</span></tt>, browser vendors implemented
+the XHTML syntax over the syntax defined by the specification.</p>
+<p>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.</p>
+</div>
+<div class="section" id="html-versus-xhtml">
+<h2>HTML versus XHTML<a class="headerlink" href="#html-versus-xhtml" title="Permalink to this headline">¶</a></h2>
+<p>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.)</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="58%" />
+<col width="14%" />
+<col width="14%" />
+<col width="14%" />
+</colgroup>
+<thead valign="bottom">
+<tr class="row-odd"><th class="head">&nbsp;</th>
+<th class="head">HTML4.01</th>
+<th class="head">XHTML1.1</th>
+<th class="head">HTML5</th>
+</tr>
+</thead>
+<tbody valign="top">
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">&lt;tag/value/</span></tt> == <tt class="docutils literal"><span class="pre">&lt;tag&gt;value&lt;/tag&gt;</span></tt></td>
+<td><img alt="Yes" src="_images/yes.png" /> <a class="footnote-reference" href="#id4" id="id1">[1]</a></td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="No" src="_images/no.png" /></td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">&lt;br/&gt;</span></tt> supported</td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /> <a class="footnote-reference" href="#id5" id="id2">[2]</a></td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">&lt;script/&gt;</span></tt> supported</td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+<td><img alt="No" src="_images/no.png" /></td>
+</tr>
+<tr class="row-odd"><td>should be served as <cite>text/html</cite></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+<td><img alt="No" src="_images/no.png" /> <a class="footnote-reference" href="#id6" id="id3">[3]</a></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+</tr>
+<tr class="row-even"><td>should be served as
+<cite>application/xhtml+xml</cite></td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+<td><img alt="No" src="_images/no.png" /></td>
+</tr>
+<tr class="row-odd"><td>strict error handling</td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+<td><img alt="No" src="_images/no.png" /></td>
+</tr>
+<tr class="row-even"><td>inline SVG</td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+</tr>
+<tr class="row-odd"><td>inline MathML</td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">&lt;video&gt;</span></tt> tag</td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">&lt;audio&gt;</span></tt> tag</td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+</tr>
+<tr class="row-even"><td>New semantic tags like <tt class="docutils literal"><span class="pre">&lt;article&gt;</span></tt></td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="No" src="_images/no.png" /></td>
+<td><img alt="Yes" src="_images/yes.png" /></td>
+</tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id4" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td>This is an obscure feature inherited from SGML. It is usually not
+supported by browsers, for reasons detailed above.</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id5" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[2]</a></td><td>This is for compatibility with server code that generates XHTML for
+tags such as <tt class="docutils literal"><span class="pre">&lt;br&gt;</span></tt>. It should not be used in new code.</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="id6" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3">[3]</a></td><td>XHTML 1.0 is the last XHTML standard that allows to be served
+as <cite>text/html</cite> for backwards compatibility reasons.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="what-does-strict-mean">
+<h2>What does &#8220;strict&#8221; mean?<a class="headerlink" href="#what-does-strict-mean" title="Permalink to this headline">¶</a></h2>
+<p>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).</p>
+<p>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:</p>
+<ul class="simple">
+<li>Wrapping the document in an <tt class="docutils literal"><span class="pre">&lt;html&gt;</span></tt> tag</li>
+<li>Wrapping header elements in <tt class="docutils literal"><span class="pre">&lt;head&gt;</span></tt> or the body elements in
+<tt class="docutils literal"><span class="pre">&lt;body&gt;</span></tt></li>
+<li>Closing the <tt class="docutils literal"><span class="pre">&lt;p&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;li&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;dt&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;dd&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;tr&gt;</span></tt>,
+<tt class="docutils literal"><span class="pre">&lt;td&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;th&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;tbody&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;thead&gt;</span></tt>, or <tt class="docutils literal"><span class="pre">&lt;tfoot&gt;</span></tt> tags.</li>
+<li>Quoting attributes, so long as they contain no whitespace or
+special characters (like <tt class="docutils literal"><span class="pre">&lt;</span></tt>, <tt class="docutils literal"><span class="pre">&gt;</span></tt>, <tt class="docutils literal"><span class="pre">'</span></tt>, or <tt class="docutils literal"><span class="pre">&quot;</span></tt>).</li>
+<li>Requiring boolean attributes to have a value.</li>
+</ul>
+<p>This means the following page in HTML5 is perfectly valid:</p>
+<div class="highlight-html"><div class="highlight"><pre><span class="cp">&lt;!doctype html&gt;</span>
+<span class="nt">&lt;title&gt;</span>Hello HTML5<span class="nt">&lt;/title&gt;</span>
+<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">header</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;h1&gt;</span>Hello HTML5<span class="nt">&lt;/h1&gt;</span>
+ <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">tagline</span><span class="nt">&gt;</span>HTML5 is awesome
+<span class="nt">&lt;/div&gt;</span>
+<span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">nav</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;li&gt;&lt;a</span> <span class="na">href=</span><span class="s">/index</span><span class="nt">&gt;</span>Index<span class="nt">&lt;/a&gt;</span>
+ <span class="nt">&lt;li&gt;&lt;a</span> <span class="na">href=</span><span class="s">/downloads</span><span class="nt">&gt;</span>Downloads<span class="nt">&lt;/a&gt;</span>
+ <span class="nt">&lt;li&gt;&lt;a</span> <span class="na">href=</span><span class="s">/about</span><span class="nt">&gt;</span>About<span class="nt">&lt;/a&gt;</span>
+<span class="nt">&lt;/ul&gt;</span>
+<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">body</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;h2&gt;</span>HTML5 is probably the future<span class="nt">&lt;/h2&gt;</span>
+ <span class="nt">&lt;p&gt;</span>
+ There might be some other things around but in terms of
+ browser vendor support, HTML5 is hard to beat.
+ <span class="nt">&lt;dl&gt;</span>
+ <span class="nt">&lt;dt&gt;</span>Key 1
+ <span class="nt">&lt;dd&gt;</span>Value 1
+ <span class="nt">&lt;dt&gt;</span>Key 2
+ <span class="nt">&lt;dd&gt;</span>Value 2
+ <span class="nt">&lt;/dl&gt;</span>
+<span class="nt">&lt;/div&gt;</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="new-technologies-in-html5">
+<h2>New technologies in HTML5<a class="headerlink" href="#new-technologies-in-html5" title="Permalink to this headline">¶</a></h2>
+<p>HTML5 adds many new features that make Web applications easier to write
+and to use.</p>
+<ul class="simple">
+<li>The <tt class="docutils literal"><span class="pre">&lt;audio&gt;</span></tt> and <tt class="docutils literal"><span class="pre">&lt;video&gt;</span></tt> tags provide a way to embed audio and
+video without complicated add-ons like QuickTime or Flash.</li>
+<li>Semantic elements like <tt class="docutils literal"><span class="pre">&lt;article&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;header&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;nav&gt;</span></tt>, and
+<tt class="docutils literal"><span class="pre">&lt;time&gt;</span></tt> that make content easier to understand.</li>
+<li>The <tt class="docutils literal"><span class="pre">&lt;canvas&gt;</span></tt> tag, which supports a powerful drawing API, reducing
+the need for server-generated images to present data graphically.</li>
+<li>New form control types like <tt class="docutils literal"><span class="pre">&lt;input</span> <span class="pre">type=&quot;date&quot;&gt;</span></tt> that allow user
+agents to make entering and validating values easier.</li>
+<li>Advanced JavaScript APIs like Web Storage, Web Workers, Web Sockets,
+geolocation, and offline applications.</li>
+</ul>
+<p>Many other features have been added, as well. A good guide to new features
+in HTML5 is Mark Pilgrim&#8217;s soon-to-be-published book, <a class="reference external" href="http://www.diveintohtml5.org/">Dive Into HTML5</a>.
+Not all of them are supported in browsers yet, however, so use caution.</p>
+</div>
+<div class="section" id="what-should-be-used">
+<h2>What should be used?<a class="headerlink" href="#what-should-be-used" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<ul class="simple">
+<li>Internet Explorer (which, sadly, currently leads in market share)
+has poor support for XHTML.</li>
+<li>Many JavaScript libraries also do not support XHTML, due to the more
+complicated namespacing API it requires.</li>
+<li>HTML5 adds several new features, including semantic tags and the
+long-awaited <tt class="docutils literal"><span class="pre">&lt;audio&gt;</span></tt> and <tt class="docutils literal"><span class="pre">&lt;video&gt;</span></tt> tags.</li>
+<li>It has the support of most browser vendors behind it.</li>
+<li>It is much easier to write, and more compact.</li>
+</ul>
+<p>For most applications, it is undoubtedly better to use HTML5 than XHTML.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">HTML/XHTML FAQ</a><ul>
+<li><a class="reference internal" href="#history-of-xhtml">History of XHTML</a></li>
+<li><a class="reference internal" href="#history-of-html5">History of HTML5</a></li>
+<li><a class="reference internal" href="#html-versus-xhtml">HTML versus XHTML</a></li>
+<li><a class="reference internal" href="#what-does-strict-mean">What does &#8220;strict&#8221; mean?</a></li>
+<li><a class="reference internal" href="#new-technologies-in-html5">New technologies in HTML5</a></li>
+<li><a class="reference internal" href="#what-should-be-used">What should be used?</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="design.html" title="previous chapter">Design Decisions in Flask</a></li>
+ <li>Next: <a href="security.html" title="next chapter">Security Considerations</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/htmlfaq.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/index.html b/studio/static/doc/flask-docs/index.html
new file mode 100644
index 0000000..dd464f1
--- /dev/null
+++ b/studio/static/doc/flask-docs/index.html
@@ -0,0 +1,426 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Welcome to Flask &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="#" />
+ <link rel="next" title="Foreword" href="foreword.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="foreword.html" title="Foreword"
+ accesskey="N">next</a> |</li>
+ <li><a href="#">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="welcome-to-flask">
+<h1>Welcome to Flask<a class="headerlink" href="#welcome-to-flask" title="Permalink to this headline">¶</a></h1>
+<img alt="Flask: web development, one drop at a time" class="floatingflask" src="_images/logo-full2.png" />
+<p>Welcome to Flask&#8217;s documentation. This documentation is divided into
+different parts. I recommend that you get started with
+<a class="reference internal" href="installation.html#installation"><em>Installation</em></a> and then head over to the <a class="reference internal" href="quickstart.html#quickstart"><em>Quickstart</em></a>.
+Besides the quickstart there is also a more detailed <a class="reference internal" href="tutorial/index.html#tutorial"><em>Tutorial</em></a> that
+shows how to create a complete (albeit small) application with Flask. If
+you&#8217;d rather dive into the internals of Flask, check out
+the <a class="reference internal" href="api.html#api"><em>API</em></a> documentation. Common patterns are described in the
+<a class="reference internal" href="patterns/index.html#patterns"><em>Patterns for Flask</em></a> section.</p>
+<p>Flask depends on two external libraries: the <a class="reference external" href="http://jinja.pocoo.org/2/">Jinja2</a> template
+engine and the <a class="reference external" href="http://werkzeug.pocoo.org/">Werkzeug</a> WSGI toolkit. These libraries are not documented
+here. If you want to dive into their documentation check out the
+following links:</p>
+<ul class="simple">
+<li><a class="reference external" href="http://jinja.pocoo.org/2/documentation/">Jinja2 Documentation</a></li>
+<li><a class="reference external" href="http://werkzeug.pocoo.org/documentation/">Werkzeug Documentation</a></li>
+</ul>
+<div class="section" id="user-s-guide">
+<h2>User&#8217;s Guide<a class="headerlink" href="#user-s-guide" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="foreword.html">Foreword</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="foreword.html#what-does-micro-mean">What does &#8220;micro&#8221; mean?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="foreword.html#web-development-is-dangerous">Web Development is Dangerous</a></li>
+<li class="toctree-l2"><a class="reference internal" href="foreword.html#the-status-of-python-3">The Status of Python 3</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#virtualenv">virtualenv</a></li>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#system-wide-installation">System Wide Installation</a></li>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#living-on-the-edge">Living on the Edge</a></li>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#easy-install-on-windows"><cite>easy_install</cite> on Windows</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#a-minimal-application">A Minimal Application</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#debug-mode">Debug Mode</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#routing">Routing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#static-files">Static Files</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#rendering-templates">Rendering Templates</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#accessing-request-data">Accessing Request Data</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#redirects-and-errors">Redirects and Errors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#about-responses">About Responses</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#sessions">Sessions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#message-flashing">Message Flashing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#logging">Logging</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#hooking-in-wsgi-middlewares">Hooking in WSGI Middlewares</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="tutorial/index.html">Tutorial</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/introduction.html">Introducing Flaskr</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/folders.html">Step 0: Creating The Folders</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/schema.html">Step 1: Database Schema</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/setup.html">Step 2: Application Setup Code</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/dbinit.html">Step 3: Creating The Database</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/dbcon.html">Step 4: Request Database Connections</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/views.html">Step 5: The View Functions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/templates.html">Step 6: The Templates</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/css.html">Step 7: Adding Style</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/testing.html">Bonus: Testing the Application</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="templating.html">Templates</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#jinja-setup">Jinja Setup</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#standard-context">Standard Context</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#standard-filters">Standard Filters</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#controlling-autoescaping">Controlling Autoescaping</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#registering-filters">Registering Filters</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#context-processors">Context Processors</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="testing.html">Testing Flask Applications</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#the-application">The Application</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#the-testing-skeleton">The Testing Skeleton</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#the-first-test">The First Test</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#logging-in-and-out">Logging In and Out</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#test-adding-messages">Test Adding Messages</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#other-testing-tricks">Other Testing Tricks</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#keeping-the-context-around">Keeping the Context Around</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#accessing-and-modifying-sessions">Accessing and Modifying Sessions</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="errorhandling.html">Handling Application Errors</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#error-mails">Error Mails</a></li>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#logging-to-a-file">Logging to a File</a></li>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#controlling-the-log-format">Controlling the Log Format</a></li>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#other-libraries">Other Libraries</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="config.html">Configuration Handling</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="config.html#configuration-basics">Configuration Basics</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#builtin-configuration-values">Builtin Configuration Values</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#configuring-from-files">Configuring from Files</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#configuration-best-practices">Configuration Best Practices</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#development-production">Development / Production</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#instance-folders">Instance Folders</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="signals.html">Signals</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#subscribing-to-signals">Subscribing to Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#creating-signals">Creating Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#sending-signals">Sending Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#decorator-based-signal-subscriptions">Decorator Based Signal Subscriptions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#core-signals">Core Signals</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="views.html">Pluggable Views</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="views.html#basic-principle">Basic Principle</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#method-hints">Method Hints</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#method-based-dispatching">Method Based Dispatching</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#decorating-views">Decorating Views</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#method-views-for-apis">Method Views for APIs</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="reqcontext.html">The Request Context</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#diving-into-context-locals">Diving into Context Locals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#how-the-context-works">How the Context Works</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#callbacks-and-errors">Callbacks and Errors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#teardown-callbacks">Teardown Callbacks</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#notes-on-proxies">Notes On Proxies</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#context-preservation-on-error">Context Preservation on Error</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="blueprints.html">Modular Applications with Blueprints</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#why-blueprints">Why Blueprints?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#the-concept-of-blueprints">The Concept of Blueprints</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#my-first-blueprint">My First Blueprint</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#registering-blueprints">Registering Blueprints</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#blueprint-resources">Blueprint Resources</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#building-urls">Building URLs</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="extensions.html">Flask Extensions</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="extensions.html#finding-extensions">Finding Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensions.html#using-extensions">Using Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensions.html#flask-before-0-8">Flask Before 0.8</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="shell.html">Working with the Shell</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="shell.html#creating-a-request-context">Creating a Request Context</a></li>
+<li class="toctree-l2"><a class="reference internal" href="shell.html#firing-before-after-request">Firing Before/After Request</a></li>
+<li class="toctree-l2"><a class="reference internal" href="shell.html#further-improving-the-shell-experience">Further Improving the Shell Experience</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="patterns/index.html">Patterns for Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="patterns/packages.html">Larger Applications</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/appfactories.html">Application Factories</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/appdispatch.html">Application Dispatching</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/urlprocessors.html">Using URL Processors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/distribute.html">Deploying with Distribute</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/fabric.html">Deploying with Fabric</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/sqlite3.html">Using SQLite 3 with Flask</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/sqlalchemy.html">SQLAlchemy in Flask</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/fileuploads.html">Uploading Files</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/caching.html">Caching</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/viewdecorators.html">View Decorators</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/wtforms.html">Form Validation with WTForms</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/templateinheritance.html">Template Inheritance</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/flashing.html">Message Flashing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/jquery.html">AJAX with jQuery</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/errorpages.html">Custom Error Pages</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/lazyloading.html">Lazily Loading Views</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/mongokit.html">MongoKit in Flask</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/favicon.html">Adding a favicon</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/streaming.html">Streaming Contents</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/deferredcallbacks.html">Deferred Request Callbacks</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="deploying/index.html">Deployment Options</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="deploying/mod_wsgi.html">mod_wsgi (Apache)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/cgi.html">CGI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/fastcgi.html">FastCGI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/uwsgi.html">uWSGI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/others.html">Other Servers</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="becomingbig.html">Becoming Big</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="becomingbig.html#why-consider-forking">Why consider Forking?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="becomingbig.html#scaling-like-a-pro">Scaling like a Pro</a></li>
+<li class="toctree-l2"><a class="reference internal" href="becomingbig.html#dialogue-with-the-community">Dialogue with the Community</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div class="section" id="api-reference">
+<h2>API Reference<a class="headerlink" href="#api-reference" title="Permalink to this headline">¶</a></h2>
+<p>If you are looking for information on a specific function, class or
+method, this part of the documentation is for you.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="api.html">API</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="api.html#application-object">Application Object</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#blueprint-objects">Blueprint Objects</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#incoming-request-data">Incoming Request Data</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#response-objects">Response Objects</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#sessions">Sessions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#session-interface">Session Interface</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#test-client">Test Client</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#application-globals">Application Globals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#useful-functions-and-classes">Useful Functions and Classes</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#message-flashing">Message Flashing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#returning-json">Returning JSON</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#template-rendering">Template Rendering</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#configuration">Configuration</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#extensions">Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#useful-internals">Useful Internals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#signals">Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#class-based-views">Class Based Views</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#url-route-registrations">URL Route Registrations</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#view-function-options">View Function Options</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div class="section" id="additional-notes">
+<h2>Additional Notes<a class="headerlink" href="#additional-notes" title="Permalink to this headline">¶</a></h2>
+<p>Design notes, legal information and changelog are here for the interested.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="design.html">Design Decisions in Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="design.html#the-explicit-application-object">The Explicit Application Object</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#the-routing-system">The Routing System</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#one-template-engine">One Template Engine</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#micro-with-dependencies">Micro with Dependencies</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#thread-locals">Thread Locals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#what-flask-is-what-flask-is-not">What Flask is, What Flask is Not</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="htmlfaq.html">HTML/XHTML FAQ</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#history-of-xhtml">History of XHTML</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#history-of-html5">History of HTML5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#html-versus-xhtml">HTML versus XHTML</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#what-does-strict-mean">What does &#8220;strict&#8221; mean?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#new-technologies-in-html5">New technologies in HTML5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#what-should-be-used">What should be used?</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="security.html">Security Considerations</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="security.html#cross-site-scripting-xss">Cross-Site Scripting (XSS)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="security.html#cross-site-request-forgery-csrf">Cross-Site Request Forgery (CSRF)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="security.html#json-security">JSON Security</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="unicode.html">Unicode in Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#automatic-conversion">Automatic Conversion</a></li>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#the-golden-rule">The Golden Rule</a></li>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#encoding-and-decoding-yourself">Encoding and Decoding Yourself</a></li>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#configuring-editors">Configuring Editors</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="extensiondev.html">Flask Extension Development</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#anatomy-of-an-extension">Anatomy of an Extension</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#hello-flaskext">&#8220;Hello Flaskext!&#8221;</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#initializing-extensions">Initializing Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#the-extension-code">The Extension Code</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#adding-an-init-app-function">Adding an <cite>init_app</cite> Function</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#end-of-request-behavior">End-Of-Request Behavior</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#learn-from-others">Learn from Others</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#approved-extensions">Approved Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#extension-import-transition">Extension Import Transition</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="styleguide.html">Pocoo Styleguide</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#general-layout">General Layout</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#expressions-and-statements">Expressions and Statements</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#naming-conventions">Naming Conventions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#docstrings">Docstrings</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#comments">Comments</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="upgrading.html">Upgrading to Newer Releases</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-8">Version 0.8</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-7">Version 0.7</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-6">Version 0.6</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-5">Version 0.5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-4">Version 0.4</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-3">Version 0.3</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="changelog.html">Flask Changelog</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-9">Version 0.9</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-8">Version 0.8</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7-3">Version 0.7.3</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7-2">Version 0.7.2</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7-1">Version 0.7.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7">Version 0.7</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-6-1">Version 0.6.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-6">Version 0.6</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-5-2">Version 0.5.2</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-5-1">Version 0.5.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-5">Version 0.5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-4">Version 0.4</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-3-1">Version 0.3.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-3">Version 0.3</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-2">Version 0.2</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-1">Version 0.1</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="license.html">License</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="license.html#authors">Authors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="license.html#general-license-definitions">General License Definitions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="license.html#flask-license">Flask License</a></li>
+<li class="toctree-l2"><a class="reference internal" href="license.html#flask-artwork-license">Flask Artwork License</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><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>
+ <li><a href="http://github.com/mitsuhiko/flask/issues">Issue Tracker</a></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/index.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/installation.html b/studio/static/doc/flask-docs/installation.html
new file mode 100644
index 0000000..bdb5661
--- /dev/null
+++ b/studio/static/doc/flask-docs/installation.html
@@ -0,0 +1,249 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Installation &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Quickstart" href="quickstart.html" />
+ <link rel="prev" title="Foreword" href="foreword.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="quickstart.html" title="Quickstart"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="foreword.html" title="Foreword"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="installation">
+<span id="id1"></span><h1>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h1>
+<p>Flask depends on two external libraries, <a class="reference external" href="http://werkzeug.pocoo.org/">Werkzeug</a> and <a class="reference external" href="http://jinja.pocoo.org/2/">Jinja2</a>.
+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.</p>
+<p>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&#8217;s look at that first.</p>
+<p>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.</p>
+<div class="section" id="virtualenv">
+<span id="id2"></span><h2>virtualenv<a class="headerlink" href="#virtualenv" title="Permalink to this headline">¶</a></h2>
+<p>Virtualenv is probably what you want to use during development, and in
+production too if you have shell access there.</p>
+<p>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&#8217;s face it; quite often
+libraries break backwards compatibility, and it&#8217;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?</p>
+<p>Virtualenv to the rescue! It basically enables multiple side-by-side
+installations of Python, one for each project. It doesn&#8217;t actually
+install separate copies of Python, but it does provide a clever way
+to keep different project environments isolated.</p>
+<p>So let&#8217;s see how virtualenv works!</p>
+<p>If you are on Mac OS X or Linux, chances are that one of the following two
+commands will work for you:</p>
+<div class="highlight-python"><pre>$ sudo easy_install virtualenv</pre>
+</div>
+<p>or even better:</p>
+<div class="highlight-python"><pre>$ sudo pip install virtualenv</pre>
+</div>
+<p>One of these will probably install virtualenv on your system. Maybe it&#8217;s
+even in your package manager. If you use Ubuntu, try:</p>
+<div class="highlight-python"><pre>$ sudo apt-get install python-virtualenv</pre>
+</div>
+<p>If you are on Windows and don&#8217;t have the <cite>easy_install</cite> command, you must
+install it first. Check the <a class="reference internal" href="#windows-easy-install"><em>easy_install on Windows</em></a> section for more
+information about how to do that. Once you have it installed, run the
+same commands as above, but without the <cite>sudo</cite> prefix.</p>
+<p>Once you have virtualenv installed, just fire up a shell and create
+your own environment. I usually create a project folder and an <cite>env</cite>
+folder within:</p>
+<div class="highlight-python"><pre>$ mkdir myproject
+$ cd myproject
+$ virtualenv env
+New python executable in env/bin/python
+Installing setuptools............done.</pre>
+</div>
+<p>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:</p>
+<div class="highlight-python"><pre>$ . env/bin/activate</pre>
+</div>
+<p>(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 <tt class="docutils literal"><span class="pre">source</span></tt>)</p>
+<p>If you are a Windows user, the following command is for you:</p>
+<div class="highlight-python"><pre>$ env\scripts\activate</pre>
+</div>
+<p>Either way, you should now be using your virtualenv (see how the prompt of
+your shell has changed to show the virtualenv).</p>
+<p>Now you can just enter the following command to get Flask activated in
+your virtualenv:</p>
+<div class="highlight-python"><pre>$ easy_install Flask</pre>
+</div>
+<p>A few seconds later you are good to go.</p>
+</div>
+<div class="section" id="system-wide-installation">
+<h2>System Wide Installation<a class="headerlink" href="#system-wide-installation" title="Permalink to this headline">¶</a></h2>
+<p>This is possible as well, but I do not recommend it. Just run
+<cite>easy_install</cite> with root rights:</p>
+<div class="highlight-python"><pre>$ sudo easy_install Flask</pre>
+</div>
+<p>(Run it in an Admin shell on Windows systems and without <cite>sudo</cite>).</p>
+</div>
+<div class="section" id="living-on-the-edge">
+<h2>Living on the Edge<a class="headerlink" href="#living-on-the-edge" title="Permalink to this headline">¶</a></h2>
+<p>If you want to work with the latest version of Flask, there are two ways: you
+can either let <cite>easy_install</cite> pull in the development version, or tell it
+to operate on a git checkout. Either way, virtualenv is recommended.</p>
+<p>Get the git checkout in a new virtualenv and run in development mode:</p>
+<div class="highlight-python"><pre>$ 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</pre>
+</div>
+<p>This will pull in the dependencies and activate the git head as the current
+version inside the virtualenv. Then you just have to <tt class="docutils literal"><span class="pre">git</span> <span class="pre">pull</span> <span class="pre">origin</span></tt>
+to get the latest version.</p>
+<p>To just get the development version without git, do this instead:</p>
+<div class="highlight-python"><pre>$ 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</pre>
+</div>
+</div>
+<div class="section" id="easy-install-on-windows">
+<span id="windows-easy-install"></span><h2><cite>easy_install</cite> on Windows<a class="headerlink" href="#easy-install-on-windows" title="Permalink to this headline">¶</a></h2>
+<p>On Windows, installation of <cite>easy_install</cite> is a little bit trickier because
+slightly different rules apply on Windows than on Unix-like systems, but
+it&#8217;s not difficult. The easiest way to do it is to download the
+<a class="reference external" href="http://peak.telecommunity.com/dist/ez_setup.py">ez_setup.py</a> file and run it. The easiest way to run the file is to
+open your downloads folder and double-click on the file.</p>
+<p>Next, add the <cite>easy_install</cite> command and other Python scripts to the
+command search path, by adding your Python installation&#8217;s Scripts folder
+to the <cite>PATH</cite> environment variable. To do that, right-click on the
+&#8220;Computer&#8221; icon on the Desktop or in the Start menu, and choose
+&#8220;Properties&#8221;. Then, on Windows Vista and Windows 7 click on &#8220;Advanced System
+settings&#8221;; on Windows XP, click on the &#8220;Advanced&#8221; tab instead. Then click
+on the &#8220;Environment variables&#8221; button and double click on the &#8220;Path&#8221;
+variable in the &#8220;System variables&#8221; section. There append the path of your
+Python interpreter&#8217;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:</p>
+<div class="highlight-python"><pre>;C:\Python26\Scripts</pre>
+</div>
+<p>Then you are done. To check that it worked, open the Command Prompt and
+execute <tt class="docutils literal"><span class="pre">easy_install</span></tt>. If you have User Account Control enabled on
+Windows Vista or Windows 7, it should prompt you for admin privileges.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Installation</a><ul>
+<li><a class="reference internal" href="#virtualenv">virtualenv</a></li>
+<li><a class="reference internal" href="#system-wide-installation">System Wide Installation</a></li>
+<li><a class="reference internal" href="#living-on-the-edge">Living on the Edge</a></li>
+<li><a class="reference internal" href="#easy-install-on-windows"><cite>easy_install</cite> on Windows</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="foreword.html" title="previous chapter">Foreword</a></li>
+ <li>Next: <a href="quickstart.html" title="next chapter">Quickstart</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/installation.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/latexindex.html b/studio/static/doc/flask-docs/latexindex.html
new file mode 100644
index 0000000..c604ff1
--- /dev/null
+++ b/studio/static/doc/flask-docs/latexindex.html
@@ -0,0 +1,406 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Flask Documentation &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="flask-documentation">
+<h1>Flask Documentation<a class="headerlink" href="#flask-documentation" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="user-s-guide">
+<h2>User&#8217;s Guide<a class="headerlink" href="#user-s-guide" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="foreword.html">Foreword</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="foreword.html#what-does-micro-mean">What does &#8220;micro&#8221; mean?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="foreword.html#web-development-is-dangerous">Web Development is Dangerous</a></li>
+<li class="toctree-l2"><a class="reference internal" href="foreword.html#the-status-of-python-3">The Status of Python 3</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#virtualenv">virtualenv</a></li>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#system-wide-installation">System Wide Installation</a></li>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#living-on-the-edge">Living on the Edge</a></li>
+<li class="toctree-l2"><a class="reference internal" href="installation.html#easy-install-on-windows"><cite>easy_install</cite> on Windows</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#a-minimal-application">A Minimal Application</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#debug-mode">Debug Mode</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#routing">Routing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#static-files">Static Files</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#rendering-templates">Rendering Templates</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#accessing-request-data">Accessing Request Data</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#redirects-and-errors">Redirects and Errors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#about-responses">About Responses</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#sessions">Sessions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#message-flashing">Message Flashing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#logging">Logging</a></li>
+<li class="toctree-l2"><a class="reference internal" href="quickstart.html#hooking-in-wsgi-middlewares">Hooking in WSGI Middlewares</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="tutorial/index.html">Tutorial</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/introduction.html">Introducing Flaskr</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/folders.html">Step 0: Creating The Folders</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/schema.html">Step 1: Database Schema</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/setup.html">Step 2: Application Setup Code</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/dbinit.html">Step 3: Creating The Database</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/dbcon.html">Step 4: Request Database Connections</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/views.html">Step 5: The View Functions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/templates.html">Step 6: The Templates</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/css.html">Step 7: Adding Style</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial/testing.html">Bonus: Testing the Application</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="templating.html">Templates</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#jinja-setup">Jinja Setup</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#standard-context">Standard Context</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#standard-filters">Standard Filters</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#controlling-autoescaping">Controlling Autoescaping</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#registering-filters">Registering Filters</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templating.html#context-processors">Context Processors</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="testing.html">Testing Flask Applications</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#the-application">The Application</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#the-testing-skeleton">The Testing Skeleton</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#the-first-test">The First Test</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#logging-in-and-out">Logging In and Out</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#test-adding-messages">Test Adding Messages</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#other-testing-tricks">Other Testing Tricks</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#keeping-the-context-around">Keeping the Context Around</a></li>
+<li class="toctree-l2"><a class="reference internal" href="testing.html#accessing-and-modifying-sessions">Accessing and Modifying Sessions</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="errorhandling.html">Handling Application Errors</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#error-mails">Error Mails</a></li>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#logging-to-a-file">Logging to a File</a></li>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#controlling-the-log-format">Controlling the Log Format</a></li>
+<li class="toctree-l2"><a class="reference internal" href="errorhandling.html#other-libraries">Other Libraries</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="config.html">Configuration Handling</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="config.html#configuration-basics">Configuration Basics</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#builtin-configuration-values">Builtin Configuration Values</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#configuring-from-files">Configuring from Files</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#configuration-best-practices">Configuration Best Practices</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#development-production">Development / Production</a></li>
+<li class="toctree-l2"><a class="reference internal" href="config.html#instance-folders">Instance Folders</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="signals.html">Signals</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#subscribing-to-signals">Subscribing to Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#creating-signals">Creating Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#sending-signals">Sending Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#decorator-based-signal-subscriptions">Decorator Based Signal Subscriptions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="signals.html#core-signals">Core Signals</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="views.html">Pluggable Views</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="views.html#basic-principle">Basic Principle</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#method-hints">Method Hints</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#method-based-dispatching">Method Based Dispatching</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#decorating-views">Decorating Views</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#method-views-for-apis">Method Views for APIs</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="reqcontext.html">The Request Context</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#diving-into-context-locals">Diving into Context Locals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#how-the-context-works">How the Context Works</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#callbacks-and-errors">Callbacks and Errors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#teardown-callbacks">Teardown Callbacks</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#notes-on-proxies">Notes On Proxies</a></li>
+<li class="toctree-l2"><a class="reference internal" href="reqcontext.html#context-preservation-on-error">Context Preservation on Error</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="blueprints.html">Modular Applications with Blueprints</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#why-blueprints">Why Blueprints?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#the-concept-of-blueprints">The Concept of Blueprints</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#my-first-blueprint">My First Blueprint</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#registering-blueprints">Registering Blueprints</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#blueprint-resources">Blueprint Resources</a></li>
+<li class="toctree-l2"><a class="reference internal" href="blueprints.html#building-urls">Building URLs</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="extensions.html">Flask Extensions</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="extensions.html#finding-extensions">Finding Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensions.html#using-extensions">Using Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensions.html#flask-before-0-8">Flask Before 0.8</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="shell.html">Working with the Shell</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="shell.html#creating-a-request-context">Creating a Request Context</a></li>
+<li class="toctree-l2"><a class="reference internal" href="shell.html#firing-before-after-request">Firing Before/After Request</a></li>
+<li class="toctree-l2"><a class="reference internal" href="shell.html#further-improving-the-shell-experience">Further Improving the Shell Experience</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="patterns/index.html">Patterns for Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="patterns/packages.html">Larger Applications</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/appfactories.html">Application Factories</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/appdispatch.html">Application Dispatching</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/urlprocessors.html">Using URL Processors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/distribute.html">Deploying with Distribute</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/fabric.html">Deploying with Fabric</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/sqlite3.html">Using SQLite 3 with Flask</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/sqlalchemy.html">SQLAlchemy in Flask</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/fileuploads.html">Uploading Files</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/caching.html">Caching</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/viewdecorators.html">View Decorators</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/wtforms.html">Form Validation with WTForms</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/templateinheritance.html">Template Inheritance</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/flashing.html">Message Flashing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/jquery.html">AJAX with jQuery</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/errorpages.html">Custom Error Pages</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/lazyloading.html">Lazily Loading Views</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/mongokit.html">MongoKit in Flask</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/favicon.html">Adding a favicon</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/streaming.html">Streaming Contents</a></li>
+<li class="toctree-l2"><a class="reference internal" href="patterns/deferredcallbacks.html">Deferred Request Callbacks</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="deploying/index.html">Deployment Options</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="deploying/mod_wsgi.html">mod_wsgi (Apache)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/cgi.html">CGI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/fastcgi.html">FastCGI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/uwsgi.html">uWSGI</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deploying/others.html">Other Servers</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="becomingbig.html">Becoming Big</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="becomingbig.html#why-consider-forking">Why consider Forking?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="becomingbig.html#scaling-like-a-pro">Scaling like a Pro</a></li>
+<li class="toctree-l2"><a class="reference internal" href="becomingbig.html#dialogue-with-the-community">Dialogue with the Community</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div class="section" id="api-reference">
+<h2>API Reference<a class="headerlink" href="#api-reference" title="Permalink to this headline">¶</a></h2>
+<p>If you are looking for information on a specific function, class or
+method, this part of the documentation is for you.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="api.html">API</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="api.html#application-object">Application Object</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#blueprint-objects">Blueprint Objects</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#incoming-request-data">Incoming Request Data</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#response-objects">Response Objects</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#sessions">Sessions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#session-interface">Session Interface</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#test-client">Test Client</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#application-globals">Application Globals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#useful-functions-and-classes">Useful Functions and Classes</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#message-flashing">Message Flashing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#returning-json">Returning JSON</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#template-rendering">Template Rendering</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#configuration">Configuration</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#extensions">Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#useful-internals">Useful Internals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#signals">Signals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#class-based-views">Class Based Views</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#url-route-registrations">URL Route Registrations</a></li>
+<li class="toctree-l2"><a class="reference internal" href="api.html#view-function-options">View Function Options</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div class="section" id="additional-notes">
+<h2>Additional Notes<a class="headerlink" href="#additional-notes" title="Permalink to this headline">¶</a></h2>
+<p>Design notes, legal information and changelog are here for the interested.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="design.html">Design Decisions in Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="design.html#the-explicit-application-object">The Explicit Application Object</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#the-routing-system">The Routing System</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#one-template-engine">One Template Engine</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#micro-with-dependencies">Micro with Dependencies</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#thread-locals">Thread Locals</a></li>
+<li class="toctree-l2"><a class="reference internal" href="design.html#what-flask-is-what-flask-is-not">What Flask is, What Flask is Not</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="htmlfaq.html">HTML/XHTML FAQ</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#history-of-xhtml">History of XHTML</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#history-of-html5">History of HTML5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#html-versus-xhtml">HTML versus XHTML</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#what-does-strict-mean">What does &#8220;strict&#8221; mean?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#new-technologies-in-html5">New technologies in HTML5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="htmlfaq.html#what-should-be-used">What should be used?</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="security.html">Security Considerations</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="security.html#cross-site-scripting-xss">Cross-Site Scripting (XSS)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="security.html#cross-site-request-forgery-csrf">Cross-Site Request Forgery (CSRF)</a></li>
+<li class="toctree-l2"><a class="reference internal" href="security.html#json-security">JSON Security</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="unicode.html">Unicode in Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#automatic-conversion">Automatic Conversion</a></li>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#the-golden-rule">The Golden Rule</a></li>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#encoding-and-decoding-yourself">Encoding and Decoding Yourself</a></li>
+<li class="toctree-l2"><a class="reference internal" href="unicode.html#configuring-editors">Configuring Editors</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="extensiondev.html">Flask Extension Development</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#anatomy-of-an-extension">Anatomy of an Extension</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#hello-flaskext">&#8220;Hello Flaskext!&#8221;</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#initializing-extensions">Initializing Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#the-extension-code">The Extension Code</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#adding-an-init-app-function">Adding an <cite>init_app</cite> Function</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#end-of-request-behavior">End-Of-Request Behavior</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#learn-from-others">Learn from Others</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#approved-extensions">Approved Extensions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="extensiondev.html#extension-import-transition">Extension Import Transition</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="styleguide.html">Pocoo Styleguide</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#general-layout">General Layout</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#expressions-and-statements">Expressions and Statements</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#naming-conventions">Naming Conventions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#docstrings">Docstrings</a></li>
+<li class="toctree-l2"><a class="reference internal" href="styleguide.html#comments">Comments</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="upgrading.html">Upgrading to Newer Releases</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-8">Version 0.8</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-7">Version 0.7</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-6">Version 0.6</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-5">Version 0.5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-4">Version 0.4</a></li>
+<li class="toctree-l2"><a class="reference internal" href="upgrading.html#version-0-3">Version 0.3</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="changelog.html">Flask Changelog</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-9">Version 0.9</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-8">Version 0.8</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7-3">Version 0.7.3</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7-2">Version 0.7.2</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7-1">Version 0.7.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-7">Version 0.7</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-6-1">Version 0.6.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-6">Version 0.6</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-5-2">Version 0.5.2</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-5-1">Version 0.5.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-5">Version 0.5</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-4">Version 0.4</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-3-1">Version 0.3.1</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-3">Version 0.3</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-2">Version 0.2</a></li>
+<li class="toctree-l2"><a class="reference internal" href="changelog.html#version-0-1">Version 0.1</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="license.html">License</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="license.html#authors">Authors</a></li>
+<li class="toctree-l2"><a class="reference internal" href="license.html#general-license-definitions">General License Definitions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="license.html#flask-license">Flask License</a></li>
+<li class="toctree-l2"><a class="reference internal" href="license.html#flask-artwork-license">Flask Artwork License</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Flask Documentation</a><ul>
+<li><a class="reference internal" href="#user-s-guide">User&#8217;s Guide</a><ul>
+</ul>
+</li>
+<li><a class="reference internal" href="#api-reference">API Reference</a><ul>
+</ul>
+</li>
+<li><a class="reference internal" href="#additional-notes">Additional Notes</a><ul>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/latexindex.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/license.html b/studio/static/doc/flask-docs/license.html
new file mode 100644
index 0000000..7f00395
--- /dev/null
+++ b/studio/static/doc/flask-docs/license.html
@@ -0,0 +1,220 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>License &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="prev" title="Flask Changelog" href="changelog.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="changelog.html" title="Flask Changelog"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="license">
+<h1>License<a class="headerlink" href="#license" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>The full license text can be found below (<a class="reference internal" href="#flask-license"><em>Flask License</em></a>). For the
+documentation and artwork different licenses apply.</p>
+<div class="section" id="authors">
+<span id="id1"></span><h2>Authors<a class="headerlink" href="#authors" title="Permalink to this headline">¶</a></h2>
+<p>Flask is written and maintained by Armin Ronacher and
+various contributors:</p>
+<div class="section" id="development-lead">
+<h3>Development Lead<a class="headerlink" href="#development-lead" title="Permalink to this headline">¶</a></h3>
+<ul class="simple">
+<li>Armin Ronacher &lt;<a class="reference external" href="mailto:armin&#46;ronacher&#37;&#52;&#48;active-4&#46;com">armin<span>&#46;</span>ronacher<span>&#64;</span>active-4<span>&#46;</span>com</a>&gt;</li>
+</ul>
+</div>
+<div class="section" id="patches-and-suggestions">
+<h3>Patches and Suggestions<a class="headerlink" href="#patches-and-suggestions" title="Permalink to this headline">¶</a></h3>
+<ul class="simple">
+<li>Adam Zapletal</li>
+<li>Ali Afshar</li>
+<li>Chris Edgemon</li>
+<li>Chris Grindstaff</li>
+<li>Christopher Grebs</li>
+<li>Florent Xicluna</li>
+<li>Georg Brandl</li>
+<li>Justin Quick</li>
+<li>Kenneth Reitz</li>
+<li>Marian Sigler</li>
+<li>Matt Campell</li>
+<li>Matthew Frazier</li>
+<li>Michael van Tellingen</li>
+<li>Ron DuPlain</li>
+<li>Sebastien Estienne</li>
+<li>Simon Sapin</li>
+<li>Stephane Wirtel</li>
+<li>Thomas Schranz</li>
+<li>Zhao Xiaohong</li>
+</ul>
+</div>
+</div>
+<div class="section" id="general-license-definitions">
+<h2>General License Definitions<a class="headerlink" href="#general-license-definitions" title="Permalink to this headline">¶</a></h2>
+<p>The following section contains the full license texts for Flask and the
+documentation.</p>
+<ul class="simple">
+<li>&#8220;AUTHORS&#8221; hereby refers to all the authors listed in the
+<a class="reference internal" href="#authors"><em>Authors</em></a> section.</li>
+<li>The &#8220;<a class="reference internal" href="#flask-license"><em>Flask License</em></a>&#8221; applies to all the sourcecode shipped as
+part of Flask (Flask itself as well as the examples and the unittests)
+as well as documentation.</li>
+<li>The &#8220;<a class="reference internal" href="#artwork-license"><em>Flask Artwork License</em></a>&#8221; applies to the project&#8217;s Horn-Logo.</li>
+</ul>
+</div>
+<div class="section" id="flask-license">
+<span id="id2"></span><h2>Flask License<a class="headerlink" href="#flask-license" title="Permalink to this headline">¶</a></h2>
+<p>Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS
+for more details.</p>
+<p>Some rights reserved.</p>
+<p>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:</p>
+<ul class="simple">
+<li>Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.</li>
+<li>Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials provided
+with the distribution.</li>
+<li>The names of the contributors may not be used to endorse or
+promote products derived from this software without specific
+prior written permission.</li>
+</ul>
+<p>THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS &#8220;AS IS&#8221; 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.</p>
+</div>
+<div class="section" id="flask-artwork-license">
+<span id="artwork-license"></span><h2>Flask Artwork License<a class="headerlink" href="#flask-artwork-license" title="Permalink to this headline">¶</a></h2>
+<p>Copyright (c) 2010 by Armin Ronacher.</p>
+<p>Some rights reserved.</p>
+<p>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.</p>
+<p>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:</p>
+<ul class="simple">
+<li>Redistributions of source code must retain the above copyright
+notice and this list of conditions.</li>
+<li>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.</li>
+</ul>
+<p>Note: we would appreciate that you make the image a link to
+<a class="reference external" href="http://flask.pocoo.org/">http://flask.pocoo.org/</a> if you use it on a web page.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">License</a><ul>
+<li><a class="reference internal" href="#authors">Authors</a><ul>
+<li><a class="reference internal" href="#development-lead">Development Lead</a></li>
+<li><a class="reference internal" href="#patches-and-suggestions">Patches and Suggestions</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#general-license-definitions">General License Definitions</a></li>
+<li><a class="reference internal" href="#flask-license">Flask License</a></li>
+<li><a class="reference internal" href="#flask-artwork-license">Flask Artwork License</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="changelog.html" title="previous chapter">Flask Changelog</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/license.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/objects.inv b/studio/static/doc/flask-docs/objects.inv
new file mode 100644
index 0000000..4f903cd
--- /dev/null
+++ b/studio/static/doc/flask-docs/objects.inv
Binary files differ
diff --git a/studio/static/doc/flask-docs/patterns/appdispatch.html b/studio/static/doc/flask-docs/patterns/appdispatch.html
new file mode 100644
index 0000000..de88a7b
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/appdispatch.html
@@ -0,0 +1,273 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Application Dispatching &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Using URL Processors" href="urlprocessors.html" />
+ <link rel="prev" title="Application Factories" href="appfactories.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="urlprocessors.html" title="Using URL Processors"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="appfactories.html" title="Application Factories"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="application-dispatching">
+<span id="app-dispatch"></span><h1>Application Dispatching<a class="headerlink" href="#application-dispatching" title="Permalink to this headline">¶</a></h1>
+<p>Application dispatching is the process of combining multiple Flask
+applications on the WSGI level. You can not only combine Flask
+applications into something larger but any WSGI application. This would
+even allow you to run a Django and a Flask application in the same
+interpreter side by side if you want. The usefulness of this depends on
+how the applications work internally.</p>
+<p>The fundamental difference from the <a class="reference internal" href="packages.html#larger-applications"><em>module approach</em></a> is that in this case you are running the same or
+different Flask applications that are entirely isolated from each other.
+They run different configurations and are dispatched on the WSGI level.</p>
+<div class="section" id="working-with-this-document">
+<h2>Working with this Document<a class="headerlink" href="#working-with-this-document" title="Permalink to this headline">¶</a></h2>
+<p>Each of the techniques and examples below results in an <tt class="docutils literal"><span class="pre">application</span></tt> object
+that can be run with any WSGI server. For production, see <a class="reference internal" href="../deploying/index.html#deployment"><em>Deployment Options</em></a>.
+For development, Werkzeug provides a builtin server for development available
+at <a class="reference external" href="http://werkzeug.pocoo.org/docs/serving/#werkzeug.serving.run_simple" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">werkzeug.serving.run_simple()</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.serving</span> <span class="kn">import</span> <span class="n">run_simple</span>
+<span class="n">run_simple</span><span class="p">(</span><span class="s">&#39;localhost&#39;</span><span class="p">,</span> <span class="mi">5000</span><span class="p">,</span> <span class="n">application</span><span class="p">,</span> <span class="n">use_reloader</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Note that <a class="reference external" href="http://werkzeug.pocoo.org/docs/serving/#werkzeug.serving.run_simple" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">run_simple</span></tt></a> is not intended for
+use in production. Use a <a class="reference internal" href="../deploying/index.html#deployment"><em>full-blown WSGI server</em></a>.</p>
+</div>
+<div class="section" id="combining-applications">
+<h2>Combining Applications<a class="headerlink" href="#combining-applications" title="Permalink to this headline">¶</a></h2>
+<p>If you have entirely separated applications and you want them to work next
+to each other in the same Python interpreter process you can take
+advantage of the <a class="reference external" href="http://werkzeug.pocoo.org/docs/middlewares/#werkzeug.wsgi.DispatcherMiddleware" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">werkzeug.wsgi.DispatcherMiddleware</span></tt></a>. The idea
+here is that each Flask application is a valid WSGI application and they
+are combined by the dispatcher middleware into a larger one that
+dispatched based on prefix.</p>
+<p>For example you could have your main application run on <cite>/</cite> and your
+backend interface on <cite>/backend</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.wsgi</span> <span class="kn">import</span> <span class="n">DispatcherMiddleware</span>
+<span class="kn">from</span> <span class="nn">frontend_app</span> <span class="kn">import</span> <span class="n">application</span> <span class="k">as</span> <span class="n">frontend</span>
+<span class="kn">from</span> <span class="nn">backend_app</span> <span class="kn">import</span> <span class="n">application</span> <span class="k">as</span> <span class="n">backend</span>
+
+<span class="n">application</span> <span class="o">=</span> <span class="n">DispatcherMiddleware</span><span class="p">(</span><span class="n">frontend</span><span class="p">,</span> <span class="p">{</span>
+ <span class="s">&#39;/backend&#39;</span><span class="p">:</span> <span class="n">backend</span>
+<span class="p">})</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="dispatch-by-subdomain">
+<h2>Dispatch by Subdomain<a class="headerlink" href="#dispatch-by-subdomain" title="Permalink to this headline">¶</a></h2>
+<p>Sometimes you might want to use multiple instances of the same application
+with different configurations. Assuming the application is created inside
+a function and you can call that function to instanciate it, that is
+really easy to implement. In order to develop your application to support
+creating new instances in functions have a look at the
+<a class="reference internal" href="appfactories.html#app-factories"><em>Application Factories</em></a> pattern.</p>
+<p>A very common example would be creating applications per subdomain. For
+instance you configure your webserver to dispatch all requests for all
+subdomains to your application and you then use the subdomain information
+to create user-specific instances. Once you have your server set up to
+listen on all subdomains you can use a very simple WSGI application to do
+the dynamic application creation.</p>
+<p>The perfect level for abstraction in that regard is the WSGI layer. You
+write your own WSGI application that looks at the request that comes and
+and delegates it to your Flask application. If that application does not
+exist yet, it is dynamically created and remembered:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">Lock</span>
+
+<span class="k">class</span> <span class="nc">SubdomainDispatcher</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">domain</span><span class="p">,</span> <span class="n">create_app</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">domain</span> <span class="o">=</span> <span class="n">domain</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">create_app</span> <span class="o">=</span> <span class="n">create_app</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">lock</span> <span class="o">=</span> <span class="n">Lock</span><span class="p">()</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">instances</span> <span class="o">=</span> <span class="p">{}</span>
+
+ <span class="k">def</span> <span class="nf">get_application</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">host</span><span class="p">):</span>
+ <span class="n">host</span> <span class="o">=</span> <span class="n">host</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">&#39;:&#39;</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
+ <span class="k">assert</span> <span class="n">host</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="p">),</span> <span class="s">&#39;Configuration error&#39;</span>
+ <span class="n">subdomain</span> <span class="o">=</span> <span class="n">host</span><span class="p">[:</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="p">)]</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">)</span>
+ <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">lock</span><span class="p">:</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">instances</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">subdomain</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">app</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">create_app</span><span class="p">(</span><span class="n">subdomain</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">instances</span><span class="p">[</span><span class="n">subdomain</span><span class="p">]</span> <span class="o">=</span> <span class="n">app</span>
+ <span class="k">return</span> <span class="n">app</span>
+
+ <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_application</span><span class="p">(</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;HTTP_HOST&#39;</span><span class="p">])</span>
+ <span class="k">return</span> <span class="n">app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This dispatcher can then be used like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">myapplication</span> <span class="kn">import</span> <span class="n">create_app</span><span class="p">,</span> <span class="n">get_user_for_subdomain</span>
+<span class="kn">from</span> <span class="nn">werkzeug.exceptions</span> <span class="kn">import</span> <span class="n">NotFound</span>
+
+<span class="k">def</span> <span class="nf">make_app</span><span class="p">(</span><span class="n">subdomain</span><span class="p">):</span>
+ <span class="n">user</span> <span class="o">=</span> <span class="n">get_user_for_subdomain</span><span class="p">(</span><span class="n">subdomain</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">user</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="c"># if there is no user for that subdomain we still have</span>
+ <span class="c"># to return a WSGI application that handles that request.</span>
+ <span class="c"># We can then just return the NotFound() exception as</span>
+ <span class="c"># application which will render a default 404 page.</span>
+ <span class="c"># You might also redirect the user to the main page then</span>
+ <span class="k">return</span> <span class="n">NotFound</span><span class="p">()</span>
+
+ <span class="c"># otherwise create the application for the specific user</span>
+ <span class="k">return</span> <span class="n">create_app</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
+
+<span class="n">application</span> <span class="o">=</span> <span class="n">SubdomainDispatcher</span><span class="p">(</span><span class="s">&#39;example.com&#39;</span><span class="p">,</span> <span class="n">make_app</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="dispatch-by-path">
+<h2>Dispatch by Path<a class="headerlink" href="#dispatch-by-path" title="Permalink to this headline">¶</a></h2>
+<p>Dispatching by a path on the URL is very similar. Instead of looking at
+the <cite>Host</cite> header to figure out the subdomain one simply looks at the
+request path up to the first slash:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">threading</span> <span class="kn">import</span> <span class="n">Lock</span>
+<span class="kn">from</span> <span class="nn">werkzeug.wsgi</span> <span class="kn">import</span> <span class="n">pop_path_info</span><span class="p">,</span> <span class="n">peek_path_info</span>
+
+<span class="k">class</span> <span class="nc">PathDispatcher</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">default_app</span><span class="p">,</span> <span class="n">create_app</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">default_app</span> <span class="o">=</span> <span class="n">default_app</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">create_app</span> <span class="o">=</span> <span class="n">create_app</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">lock</span> <span class="o">=</span> <span class="n">Lock</span><span class="p">()</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">instances</span> <span class="o">=</span> <span class="p">{}</span>
+
+ <span class="k">def</span> <span class="nf">get_application</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">prefix</span><span class="p">):</span>
+ <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">lock</span><span class="p">:</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">instances</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">prefix</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">app</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">create_app</span><span class="p">(</span><span class="n">prefix</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">app</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">instances</span><span class="p">[</span><span class="n">prefix</span><span class="p">]</span> <span class="o">=</span> <span class="n">app</span>
+ <span class="k">return</span> <span class="n">app</span>
+
+ <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_application</span><span class="p">(</span><span class="n">peek_path_info</span><span class="p">(</span><span class="n">environ</span><span class="p">))</span>
+ <span class="k">if</span> <span class="n">app</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">pop_path_info</span><span class="p">(</span><span class="n">environ</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">default_app</span>
+ <span class="k">return</span> <span class="n">app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The big difference between this and the subdomain one is that this one
+falls back to another application if the creator function returns <cite>None</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">myapplication</span> <span class="kn">import</span> <span class="n">create_app</span><span class="p">,</span> <span class="n">default_app</span><span class="p">,</span> <span class="n">get_user_for_prefix</span>
+
+<span class="k">def</span> <span class="nf">make_app</span><span class="p">(</span><span class="n">prefix</span><span class="p">):</span>
+ <span class="n">user</span> <span class="o">=</span> <span class="n">get_user_for_prefix</span><span class="p">(</span><span class="n">prefix</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">user</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">create_app</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
+
+<span class="n">application</span> <span class="o">=</span> <span class="n">PathDispatcher</span><span class="p">(</span><span class="n">default_app</span><span class="p">,</span> <span class="n">make_app</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Application Dispatching</a><ul>
+<li><a class="reference internal" href="#working-with-this-document">Working with this Document</a></li>
+<li><a class="reference internal" href="#combining-applications">Combining Applications</a></li>
+<li><a class="reference internal" href="#dispatch-by-subdomain">Dispatch by Subdomain</a></li>
+<li><a class="reference internal" href="#dispatch-by-path">Dispatch by Path</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="appfactories.html" title="previous chapter">Application Factories</a></li>
+ <li>Next: <a href="urlprocessors.html" title="next chapter">Using URL Processors</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/appdispatch.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/appfactories.html b/studio/static/doc/flask-docs/patterns/appfactories.html
new file mode 100644
index 0000000..ccd26e0
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/appfactories.html
@@ -0,0 +1,186 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Application Factories &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Application Dispatching" href="appdispatch.html" />
+ <link rel="prev" title="Larger Applications" href="packages.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="appdispatch.html" title="Application Dispatching"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="packages.html" title="Larger Applications"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="application-factories">
+<span id="app-factories"></span><h1>Application Factories<a class="headerlink" href="#application-factories" title="Permalink to this headline">¶</a></h1>
+<p>If you are already using packages and blueprints for your application
+(<a class="reference internal" href="../blueprints.html#blueprints"><em>Modular Applications with Blueprints</em></a>) there are a couple of really nice ways to further improve
+the experience. A common pattern is creating the application object when
+the blueprint is imported. But if you move the creation of this object,
+into a function, you can then create multiple instances of this and later.</p>
+<p>So why would you want to do this?</p>
+<ol class="arabic simple">
+<li>Testing. You can have instances of the application with different
+settings to test every case.</li>
+<li>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.</li>
+</ol>
+<p>So how would you then actually implement that?</p>
+<div class="section" id="basic-factories">
+<h2>Basic Factories<a class="headerlink" href="#basic-factories" title="Permalink to this headline">¶</a></h2>
+<p>The idea is to set up the application in a function. Like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">create_app</span><span class="p">(</span><span class="n">config_filename</span><span class="p">):</span>
+ <span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_pyfile</span><span class="p">(</span><span class="n">config_filename</span><span class="p">)</span>
+
+ <span class="kn">from</span> <span class="nn">yourapplication.views.admin</span> <span class="kn">import</span> <span class="n">admin</span>
+ <span class="kn">from</span> <span class="nn">yourapplication.views.frontend</span> <span class="kn">import</span> <span class="n">frontend</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">register_blueprint</span><span class="p">(</span><span class="n">admin</span><span class="p">)</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">register_blueprint</span><span class="p">(</span><span class="n">frontend</span><span class="p">)</span>
+
+ <span class="k">return</span> <span class="n">app</span>
+</pre></div>
+</div>
+<p>The downside is that you cannot use the application object in the blueprints
+at import time. You can however use it from within a request. How do you
+get access to the application with the config? Use
+<a class="reference internal" href="../api.html#flask.current_app" title="flask.current_app"><tt class="xref py py-data docutils literal"><span class="pre">current_app</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">current_app</span><span class="p">,</span> <span class="n">Blueprint</span><span class="p">,</span> <span class="n">render_template</span>
+<span class="n">admin</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s">&#39;/admin&#39;</span><span class="p">)</span>
+
+<span class="nd">@admin.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="n">current_app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;INDEX_TEMPLATE&#39;</span><span class="p">])</span>
+</pre></div>
+</div>
+<p>Here we look up the name of a template in the config.</p>
+</div>
+<div class="section" id="using-applications">
+<h2>Using Applications<a class="headerlink" href="#using-applications" title="Permalink to this headline">¶</a></h2>
+<p>So to use such an application you then have to create the application
+first. Here an example <cite>run.py</cite> file that runs such an application:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">create_app</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">create_app</span><span class="p">(</span><span class="s">&#39;/path/to/config.cfg&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="factory-improvements">
+<h2>Factory Improvements<a class="headerlink" href="#factory-improvements" title="Permalink to this headline">¶</a></h2>
+<p>The factory function from above is not very clever so far, you can improve
+it. The following changes are straightforward and possible:</p>
+<ol class="arabic simple">
+<li>make it possible to pass in configuration values for unittests so that
+you don&#8217;t have to create config files on the filesystem</li>
+<li>call a function from a blueprint 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.)</li>
+<li>Add in WSGI middlewares when the application is creating if necessary.</li>
+</ol>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Application Factories</a><ul>
+<li><a class="reference internal" href="#basic-factories">Basic Factories</a></li>
+<li><a class="reference internal" href="#using-applications">Using Applications</a></li>
+<li><a class="reference internal" href="#factory-improvements">Factory Improvements</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="packages.html" title="previous chapter">Larger Applications</a></li>
+ <li>Next: <a href="appdispatch.html" title="next chapter">Application Dispatching</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/appfactories.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/caching.html b/studio/static/doc/flask-docs/patterns/caching.html
new file mode 100644
index 0000000..ed0d3c1
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/caching.html
@@ -0,0 +1,178 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Caching &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="View Decorators" href="viewdecorators.html" />
+ <link rel="prev" title="Uploading Files" href="fileuploads.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="viewdecorators.html" title="View Decorators"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="fileuploads.html" title="Uploading Files"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="caching">
+<span id="caching-pattern"></span><h1>Caching<a class="headerlink" href="#caching" title="Permalink to this headline">¶</a></h1>
+<p>When your application runs slow, throw some caches in. Well, at least
+it&#8217;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.</p>
+<p>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.</p>
+<div class="section" id="setting-up-a-cache">
+<h2>Setting up a Cache<a class="headerlink" href="#setting-up-a-cache" title="Permalink to this headline">¶</a></h2>
+<p>You create a cache object once and keep it around, similar to how
+<a class="reference internal" href="../api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> objects are created. If you are using the
+development server you can create a
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/contrib/cache/#werkzeug.contrib.cache.SimpleCache" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">SimpleCache</span></tt></a> object, that one is a simple
+cache that keeps the item stored in the memory of the Python interpreter:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.contrib.cache</span> <span class="kn">import</span> <span class="n">SimpleCache</span>
+<span class="n">cache</span> <span class="o">=</span> <span class="n">SimpleCache</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>If you want to use memcached, make sure to have one of the memcache modules
+supported (you get them from <a class="reference external" href="http://pypi.python.org/">PyPI</a>) and a
+memcached server running somewhere. This is how you connect to such an
+memcached server then:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.contrib.cache</span> <span class="kn">import</span> <span class="n">MemcachedCache</span>
+<span class="n">cache</span> <span class="o">=</span> <span class="n">MemcachedCache</span><span class="p">([</span><span class="s">&#39;127.0.0.1:11211&#39;</span><span class="p">])</span>
+</pre></div>
+</div>
+<p>If you are using App Engine, you can connect to the App Engine memcache
+server easily:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.contrib.cache</span> <span class="kn">import</span> <span class="n">GAEMemcachedCache</span>
+<span class="n">cache</span> <span class="o">=</span> <span class="n">GAEMemcachedCache</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="using-a-cache">
+<h2>Using a Cache<a class="headerlink" href="#using-a-cache" title="Permalink to this headline">¶</a></h2>
+<p>Now how can one use such a cache? There are two very important
+operations: <a class="reference external" href="http://werkzeug.pocoo.org/docs/contrib/cache/#werkzeug.contrib.cache.BaseCache.get" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">get()</span></tt></a> and
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/contrib/cache/#werkzeug.contrib.cache.BaseCache.set" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">set()</span></tt></a>. This is how to use them:</p>
+<p>To get an item from the cache call
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/contrib/cache/#werkzeug.contrib.cache.BaseCache.get" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">get()</span></tt></a> with a string as key name.
+If something is in the cache, it is returned. Otherwise that function
+will return <cite>None</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">rv</span> <span class="o">=</span> <span class="n">cache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;my-item&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>To add items to the cache, use the <a class="reference external" href="http://werkzeug.pocoo.org/docs/contrib/cache/#werkzeug.contrib.cache.BaseCache.set" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">set()</span></tt></a>
+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.</p>
+<p>Here a full example how this looks like normally:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">get_my_item</span><span class="p">():</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">cache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;my-item&#39;</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">rv</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">calculate_value</span><span class="p">()</span>
+ <span class="n">cache</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s">&#39;my-item&#39;</span><span class="p">,</span> <span class="n">rv</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">rv</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Caching</a><ul>
+<li><a class="reference internal" href="#setting-up-a-cache">Setting up a Cache</a></li>
+<li><a class="reference internal" href="#using-a-cache">Using a Cache</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="fileuploads.html" title="previous chapter">Uploading Files</a></li>
+ <li>Next: <a href="viewdecorators.html" title="next chapter">View Decorators</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/caching.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/deferredcallbacks.html b/studio/static/doc/flask-docs/patterns/deferredcallbacks.html
new file mode 100644
index 0000000..32b6b79
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/deferredcallbacks.html
@@ -0,0 +1,180 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Deferred Request Callbacks &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Deployment Options" href="../deploying/index.html" />
+ <link rel="prev" title="Streaming Contents" href="streaming.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="../deploying/index.html" title="Deployment Options"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="streaming.html" title="Streaming Contents"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="deferred-request-callbacks">
+<span id="deferred-callbacks"></span><h1>Deferred Request Callbacks<a class="headerlink" href="#deferred-request-callbacks" title="Permalink to this headline">¶</a></h1>
+<p>One of the design principles of Flask is that response objects are created
+and passed down a chain of potential callbacks that can modify them or
+replace them. When the request handling starts, there is no response
+object yet. It is created as necessary either by a view function or by
+some other component in the system.</p>
+<p>But what happens if you want to modify the response at a point where the
+response does not exist yet? A common example for that would be a
+before-request function that wants to set a cookie on the response object.</p>
+<p>One way is to avoid the situation. Very often that is possible. For
+instance you can try to move that logic into an after-request callback
+instead. Sometimes however moving that code there is just not a very
+pleasant experience or makes code look very awkward.</p>
+<p>As an alternative possibility you can attach a bunch of callback functions
+to the <a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a> object and call then at the end of the request.
+This way you can defer code execution from anywhere in the application.</p>
+<div class="section" id="the-decorator">
+<h2>The Decorator<a class="headerlink" href="#the-decorator" title="Permalink to this headline">¶</a></h2>
+<p>The following decorator is the key. It registers a function on a list on
+the <a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a> object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">g</span>
+
+<span class="k">def</span> <span class="nf">after_this_request</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s">&#39;after_request_callbacks&#39;</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">after_request_callbacks</span> <span class="o">=</span> <span class="p">[]</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">after_request_callbacks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">f</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="calling-the-deferred">
+<h2>Calling the Deferred<a class="headerlink" href="#calling-the-deferred" title="Permalink to this headline">¶</a></h2>
+<p>Now you can use the <cite>after_this_request</cite> decorator to mark a function to
+be called at the end of the request. But we still need to call them. For
+this the following function needs to be registered as
+<a class="reference internal" href="../api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> callback:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.after_request</span>
+<span class="k">def</span> <span class="nf">call_after_request_callbacks</span><span class="p">(</span><span class="n">response</span><span class="p">):</span>
+ <span class="k">for</span> <span class="n">callback</span> <span class="ow">in</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s">&#39;after_request_callbacks&#39;</span><span class="p">,</span> <span class="p">()):</span>
+ <span class="n">response</span> <span class="o">=</span> <span class="n">callback</span><span class="p">(</span><span class="n">response</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">response</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="a-practical-example">
+<h2>A Practical Example<a class="headerlink" href="#a-practical-example" title="Permalink to this headline">¶</a></h2>
+<p>Now we can easily at any point in time register a function to be called at
+the end of this particular request. For example you can remember the
+current language of the user in a cookie in the before-request function:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+
+<span class="nd">@app.before_request</span>
+<span class="k">def</span> <span class="nf">detect_user_language</span><span class="p">():</span>
+ <span class="n">language</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">cookies</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;user_lang&#39;</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">language</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">language</span> <span class="o">=</span> <span class="n">guess_language_from_request</span><span class="p">()</span>
+ <span class="nd">@after_this_request</span>
+ <span class="k">def</span> <span class="nf">remember_language</span><span class="p">(</span><span class="n">response</span><span class="p">):</span>
+ <span class="n">response</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="s">&#39;user_lang&#39;</span><span class="p">,</span> <span class="n">language</span><span class="p">)</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">language</span> <span class="o">=</span> <span class="n">language</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Deferred Request Callbacks</a><ul>
+<li><a class="reference internal" href="#the-decorator">The Decorator</a></li>
+<li><a class="reference internal" href="#calling-the-deferred">Calling the Deferred</a></li>
+<li><a class="reference internal" href="#a-practical-example">A Practical Example</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="streaming.html" title="previous chapter">Streaming Contents</a></li>
+ <li>Next: <a href="../deploying/index.html" title="next chapter">Deployment Options</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/deferredcallbacks.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/distribute.html b/studio/static/doc/flask-docs/patterns/distribute.html
new file mode 100644
index 0000000..8987a18
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/distribute.html
@@ -0,0 +1,254 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Deploying with Distribute &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Deploying with Fabric" href="fabric.html" />
+ <link rel="prev" title="Using URL Processors" href="urlprocessors.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="fabric.html" title="Deploying with Fabric"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="urlprocessors.html" title="Using URL Processors"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="deploying-with-distribute">
+<span id="distribute-deployment"></span><h1>Deploying with Distribute<a class="headerlink" href="#deploying-with-distribute" title="Permalink to this headline">¶</a></h1>
+<p><a class="reference external" href="http://pypi.python.org/pypi/distribute">distribute</a>, 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:</p>
+<ul class="simple">
+<li><strong>support for dependencies</strong>: a library or application can declare a
+list of other libraries it depends on which will be installed
+automatically for you.</li>
+<li><strong>package registry</strong>: 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 &#8220;entry point&#8221; another package can hook into to extend the
+other package.</li>
+<li><strong>installation manager</strong>: <cite>easy_install</cite>, which comes with distribute
+can install other libraries for you. You can also use <a class="reference external" href="http://pypi.python.org/pypi/pip">pip</a> which
+sooner or later will replace <cite>easy_install</cite> which does more than just
+installing packages for you.</li>
+</ul>
+<p>Flask itself, and all the libraries you can find on the cheeseshop
+are distributed with either distribute, the older setuptools or distutils.</p>
+<p>In this case we assume your application is called
+<cite>yourapplication.py</cite> and you are not using a module, but a <a class="reference internal" href="packages.html#larger-applications"><em>package</em></a>. Distributing resources with standard modules is
+not supported by <a class="reference external" href="http://pypi.python.org/pypi/distribute">distribute</a> so we will not bother with it. If you have
+not yet converted your application into a package, head over to the
+<a class="reference internal" href="packages.html#larger-applications"><em>Larger Applications</em></a> pattern to see how this can be done.</p>
+<p>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 <a class="reference internal" href="fabric.html#fabric-deployment"><em>Deploying with Fabric</em></a> chapter.</p>
+<div class="section" id="basic-setup-script">
+<h2>Basic Setup Script<a class="headerlink" href="#basic-setup-script" title="Permalink to this headline">¶</a></h2>
+<p>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: <a class="reference external" href="http://python-distribute.org/distribute_setup.py">distribute_setup.py</a>. Just download and
+run with your Python interpreter.</p>
+<p>Standard disclaimer applies: <a class="reference internal" href="../installation.html#virtualenv"><em>you better use a virtualenv</em></a>.</p>
+<p>Your setup code always goes into a file named <cite>setup.py</cite> 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.</p>
+<p>Yes, even if you are using <cite>distribute</cite>, you are importing from a package
+called <cite>setuptools</cite>. <cite>distribute</cite> is fully backwards compatible with
+<cite>setuptools</cite>, so it also uses the same import name.</p>
+<p>A basic <cite>setup.py</cite> file for a Flask application looks like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">setuptools</span> <span class="kn">import</span> <span class="n">setup</span>
+
+<span class="n">setup</span><span class="p">(</span>
+ <span class="n">name</span><span class="o">=</span><span class="s">&#39;Your Application&#39;</span><span class="p">,</span>
+ <span class="n">version</span><span class="o">=</span><span class="s">&#39;1.0&#39;</span><span class="p">,</span>
+ <span class="n">long_description</span><span class="o">=</span><span class="n">__doc__</span><span class="p">,</span>
+ <span class="n">packages</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;yourapplication&#39;</span><span class="p">],</span>
+ <span class="n">include_package_data</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
+ <span class="n">zip_safe</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
+ <span class="n">install_requires</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;Flask&#39;</span><span class="p">]</span>
+<span class="p">)</span>
+</pre></div>
+</div>
+<p>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 <cite>find_packages</cite> function:</p>
+<div class="highlight-python"><pre>from setuptools import setup, find_packages
+
+setup(
+ ...
+ packages=find_packages()
+)</pre>
+</div>
+<p>Most parameters to the <cite>setup</cite> function should be self explanatory,
+<cite>include_package_data</cite> and <cite>zip_safe</cite> might not be.
+<cite>include_package_data</cite> tells distribute to look for a <cite>MANIFEST.in</cite> 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 <a class="reference internal" href="#distributing-resources"><em>Distributing Resources</em></a>). The <cite>zip_safe</cite> flag can be used to
+force or prevent zip Archive creation. In general you probably don&#8217;t want
+your packages to be installed as zip files because some tools do not
+support them and they make debugging a lot harder.</p>
+</div>
+<div class="section" id="distributing-resources">
+<span id="id1"></span><h2>Distributing Resources<a class="headerlink" href="#distributing-resources" title="Permalink to this headline">¶</a></h2>
+<p>If you try to install the package you just created, you will notice that
+folders like <cite>static</cite> or <cite>templates</cite> 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 <cite>MANIFEST.in</cite> file next to your
+<cite>setup.py</cite> file. This file lists all the files that should be added to
+your tarball:</p>
+<div class="highlight-python"><pre>recursive-include yourapplication/templates *
+recursive-include yourapplication/static *</pre>
+</div>
+<p>Don&#8217;t forget that even if you enlist them in your <cite>MANIFEST.in</cite> file, they
+won&#8217;t be installed for you unless you set the <cite>include_package_data</cite>
+parameter of the <cite>setup</cite> function to <cite>True</cite>!</p>
+</div>
+<div class="section" id="declaring-dependencies">
+<h2>Declaring Dependencies<a class="headerlink" href="#declaring-dependencies" title="Permalink to this headline">¶</a></h2>
+<p>Dependencies are declared in the <cite>install_requires</cite> 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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">install_requires</span><span class="o">=</span><span class="p">[</span>
+ <span class="s">&#39;Flask&gt;=0.2&#39;</span><span class="p">,</span>
+ <span class="s">&#39;SQLAlchemy&gt;=0.6&#39;</span><span class="p">,</span>
+ <span class="s">&#39;BrokenPackage&gt;=0.7,&lt;=1.0&#39;</span>
+<span class="p">]</span>
+</pre></div>
+</div>
+<p>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&#8217;t be
+because it is an internal package you don&#8217;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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">dependency_links</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;http://example.com/yourfiles&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+<p>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.</p>
+</div>
+<div class="section" id="installing-developing">
+<h2>Installing / Developing<a class="headerlink" href="#installing-developing" title="Permalink to this headline">¶</a></h2>
+<p>To install your application (ideally into a virtualenv) just run the
+<cite>setup.py</cite> script with the <cite>install</cite> parameter. It will install your
+application into the virtualenv&#8217;s site-packages folder and also download
+and install all dependencies:</p>
+<div class="highlight-python"><pre>$ python setup.py install</pre>
+</div>
+<p>If you are developing on the package and also want the requirements to be
+installed, you can use the <cite>develop</cite> command instead:</p>
+<div class="highlight-python"><pre>$ python setup.py develop</pre>
+</div>
+<p>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 <cite>install</cite> again after each change.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Deploying with Distribute</a><ul>
+<li><a class="reference internal" href="#basic-setup-script">Basic Setup Script</a></li>
+<li><a class="reference internal" href="#distributing-resources">Distributing Resources</a></li>
+<li><a class="reference internal" href="#declaring-dependencies">Declaring Dependencies</a></li>
+<li><a class="reference internal" href="#installing-developing">Installing / Developing</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="urlprocessors.html" title="previous chapter">Using URL Processors</a></li>
+ <li>Next: <a href="fabric.html" title="next chapter">Deploying with Fabric</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/distribute.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/errorpages.html b/studio/static/doc/flask-docs/patterns/errorpages.html
new file mode 100644
index 0000000..820e271
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/errorpages.html
@@ -0,0 +1,182 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Custom Error Pages &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Lazily Loading Views" href="lazyloading.html" />
+ <link rel="prev" title="AJAX with jQuery" href="jquery.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="lazyloading.html" title="Lazily Loading Views"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="jquery.html" title="AJAX with jQuery"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="custom-error-pages">
+<h1>Custom Error Pages<a class="headerlink" href="#custom-error-pages" title="Permalink to this headline">¶</a></h1>
+<p>Flask comes with a handy <a class="reference internal" href="../api.html#flask.abort" title="flask.abort"><tt class="xref py py-func docutils literal"><span class="pre">abort()</span></tt></a> 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.</p>
+<p>Depending on the error code it is less or more likely for the user to
+actually see such an error.</p>
+<div class="section" id="common-error-codes">
+<h2>Common Error Codes<a class="headerlink" href="#common-error-codes" title="Permalink to this headline">¶</a></h2>
+<p>The following error codes are some that are often displayed to the user,
+even if the application behaves correctly:</p>
+<dl class="docutils">
+<dt><em>404 Not Found</em></dt>
+<dd>The good old &#8220;chap, you made a mistake typing that URL&#8221; message. So
+common that even novices to the internet know that 404 means: damn,
+the thing I was looking for is not there. It&#8217;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.</dd>
+<dt><em>403 Forbidden</em></dt>
+<dd>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 they try to access a forbidden resource.</dd>
+<dt><em>410 Gone</em></dt>
+<dd>Did you know that there the &#8220;404 Not Found&#8221; has a brother named &#8220;410
+Gone&#8221;? 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 they were
+looking for was deleted for all eternity.</dd>
+<dt><em>500 Internal Server Error</em></dt>
+<dd>Usually happens on programming errors or if the server is overloaded.
+A terrible good idea to have a nice page there, because your
+application <em>will</em> fail sooner or later (see also:
+<a class="reference internal" href="../errorhandling.html#application-errors"><em>Handling Application Errors</em></a>).</dd>
+</dl>
+</div>
+<div class="section" id="error-handlers">
+<h2>Error Handlers<a class="headerlink" href="#error-handlers" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference external" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.HTTPException" title="(in Werkzeug v0.7)"><tt class="xref py py-exc docutils literal"><span class="pre">HTTPException</span></tt></a>, 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.</p>
+<p>An error handler is registered with the <a class="reference internal" href="../api.html#flask.Flask.errorhandler" title="flask.Flask.errorhandler"><tt class="xref py py-meth docutils literal"><span class="pre">errorhandler()</span></tt></a>
+decorator and the error code of the exception. Keep in mind that Flask
+will <em>not</em> set the error code for you, so make sure to also provide the
+HTTP status code when returning a response.</p>
+<p>Here an example implementation for a &#8220;404 Page Not Found&#8221; exception:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">render_template</span>
+
+<span class="nd">@app.errorhandler</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">page_not_found</span><span class="p">(</span><span class="n">e</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;404.html&#39;</span><span class="p">),</span> <span class="mi">404</span>
+</pre></div>
+</div>
+<p>An example template might be this:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Page Not Found<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
+ <span class="nt">&lt;h1&gt;</span>Page Not Found<span class="nt">&lt;/h1&gt;</span>
+ <span class="nt">&lt;p&gt;</span>What you were looking for is just not there.
+ <span class="nt">&lt;p&gt;&lt;a</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;index&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>go somewhere nice<span class="nt">&lt;/a&gt;</span>
+<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Custom Error Pages</a><ul>
+<li><a class="reference internal" href="#common-error-codes">Common Error Codes</a></li>
+<li><a class="reference internal" href="#error-handlers">Error Handlers</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="jquery.html" title="previous chapter">AJAX with jQuery</a></li>
+ <li>Next: <a href="lazyloading.html" title="next chapter">Lazily Loading Views</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/errorpages.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/fabric.html b/studio/static/doc/flask-docs/patterns/fabric.html
new file mode 100644
index 0000000..9949480
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/fabric.html
@@ -0,0 +1,298 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Deploying with Fabric &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Using SQLite 3 with Flask" href="sqlite3.html" />
+ <link rel="prev" title="Deploying with Distribute" href="distribute.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="sqlite3.html" title="Using SQLite 3 with Flask"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="distribute.html" title="Deploying with Distribute"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="deploying-with-fabric">
+<span id="fabric-deployment"></span><h1>Deploying with Fabric<a class="headerlink" href="#deploying-with-fabric" title="Permalink to this headline">¶</a></h1>
+<p><a class="reference external" href="http://fabfile.org/">Fabric</a> 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 (<a class="reference internal" href="packages.html#larger-applications"><em>Larger Applications</em></a>) and a good concept for
+configurations (<a class="reference internal" href="../config.html#config"><em>Configuration Handling</em></a>) it is very easy to deploy Flask
+applications to external servers.</p>
+<p>Before we get started, here a quick checklist of things we have to ensure
+upfront:</p>
+<ul class="simple">
+<li>Fabric 1.0 has to be installed locally. This tutorial assumes the
+latest version of Fabric.</li>
+<li>The application already has to be a package and requires a working
+<cite>setup.py</cite> file (<a class="reference internal" href="distribute.html#distribute-deployment"><em>Deploying with Distribute</em></a>).</li>
+<li>In the following example we are using <cite>mod_wsgi</cite> for the remote
+servers. You can of course use your own favourite server there, but
+for this example we chose Apache + <cite>mod_wsgi</cite> because it&#8217;s very easy
+to setup and has a simple way to reload applications without root
+access.</li>
+</ul>
+<div class="section" id="creating-the-first-fabfile">
+<h2>Creating the first Fabfile<a class="headerlink" href="#creating-the-first-fabfile" title="Permalink to this headline">¶</a></h2>
+<p>A fabfile is what controls what Fabric executes. It is named <cite>fabfile.py</cite>
+and executed by the <cite>fab</cite> command. All the functions defined in that file
+will show up as <cite>fab</cite> 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.</p>
+<p>This is a basic first example that has the ability to upload the current
+sourcecode to the server and install it into a pre-existing
+virtual environment:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">fabric.api</span> <span class="kn">import</span> <span class="o">*</span>
+
+<span class="c"># the user to use for the remote commands</span>
+<span class="n">env</span><span class="o">.</span><span class="n">user</span> <span class="o">=</span> <span class="s">&#39;appuser&#39;</span>
+<span class="c"># the servers where the commands are executed</span>
+<span class="n">env</span><span class="o">.</span><span class="n">hosts</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;server1.example.com&#39;</span><span class="p">,</span> <span class="s">&#39;server2.example.com&#39;</span><span class="p">]</span>
+
+<span class="k">def</span> <span class="nf">pack</span><span class="p">():</span>
+ <span class="c"># create a new source distribution as tarball</span>
+ <span class="n">local</span><span class="p">(</span><span class="s">&#39;python setup.py sdist --formats=gztar&#39;</span><span class="p">,</span> <span class="n">capture</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
+
+<span class="k">def</span> <span class="nf">deploy</span><span class="p">():</span>
+ <span class="c"># figure out the release name and version</span>
+ <span class="n">dist</span> <span class="o">=</span> <span class="n">local</span><span class="p">(</span><span class="s">&#39;python setup.py --fullname&#39;</span><span class="p">,</span> <span class="n">capture</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
+ <span class="c"># upload the source tarball to the temporary folder on the server</span>
+ <span class="n">put</span><span class="p">(</span><span class="s">&#39;dist/</span><span class="si">%s</span><span class="s">.tar.gz&#39;</span> <span class="o">%</span> <span class="n">dist</span><span class="p">,</span> <span class="s">&#39;/tmp/yourapplication.tar.gz&#39;</span><span class="p">)</span>
+ <span class="c"># create a place where we can unzip the tarball, then enter</span>
+ <span class="c"># that directory and unzip it</span>
+ <span class="n">run</span><span class="p">(</span><span class="s">&#39;mkdir /tmp/yourapplication&#39;</span><span class="p">)</span>
+ <span class="k">with</span> <span class="n">cd</span><span class="p">(</span><span class="s">&#39;/tmp/yourapplication&#39;</span><span class="p">):</span>
+ <span class="n">run</span><span class="p">(</span><span class="s">&#39;tar xzf /tmp/yourapplication.tar.gz&#39;</span><span class="p">)</span>
+ <span class="c"># now setup the package with our virtual environment&#39;s</span>
+ <span class="c"># python interpreter</span>
+ <span class="n">run</span><span class="p">(</span><span class="s">&#39;/var/www/yourapplication/env/bin/python setup.py install&#39;</span><span class="p">)</span>
+ <span class="c"># now that all is set up, delete the folder again</span>
+ <span class="n">run</span><span class="p">(</span><span class="s">&#39;rm -rf /tmp/yourapplication /tmp/yourapplication.tar.gz&#39;</span><span class="p">)</span>
+ <span class="c"># and finally touch the .wsgi file so that mod_wsgi triggers</span>
+ <span class="c"># a reload of the application</span>
+ <span class="n">run</span><span class="p">(</span><span class="s">&#39;touch /var/www/yourapplication.wsgi&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The example above is well documented and should be straightforward. Here
+a recap of the most common commands fabric provides:</p>
+<ul class="simple">
+<li><cite>run</cite> - executes a command on a remote server</li>
+<li><cite>local</cite> - executes a command on the local machine</li>
+<li><cite>put</cite> - uploads a file to the remote server</li>
+<li><cite>cd</cite> - changes the directory on the serverside. This has to be used
+in combination with the <cite>with</cite> statement.</li>
+</ul>
+</div>
+<div class="section" id="running-fabfiles">
+<h2>Running Fabfiles<a class="headerlink" href="#running-fabfiles" title="Permalink to this headline">¶</a></h2>
+<p>Now how do you execute that fabfile? You use the <cite>fab</cite> command. To
+deploy the current version of the code on the remote server you would use
+this command:</p>
+<div class="highlight-python"><pre>$ fab pack deploy</pre>
+</div>
+<p>However this requires that our server already has the
+<tt class="docutils literal"><span class="pre">/var/www/yourapplication</span></tt> folder created and
+<tt class="docutils literal"><span class="pre">/var/www/yourapplication/env</span></tt> to be a virtual environment. Furthermore
+are we not creating the configuration or <cite>.wsgi</cite> file on the server. So
+how do we bootstrap a new server into our infrastructure?</p>
+<p>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
+<cite>setup</cite> or <cite>bootstrap</cite> and then pass the servername explicitly on the
+command line:</p>
+<div class="highlight-python"><pre>$ fab -H newserver.example.com bootstrap</pre>
+</div>
+<p>To setup a new server you would roughly do these steps:</p>
+<ol class="arabic">
+<li><p class="first">Create the directory structure in <tt class="docutils literal"><span class="pre">/var/www</span></tt>:</p>
+<div class="highlight-python"><pre>$ mkdir /var/www/yourapplication
+$ cd /var/www/yourapplication
+$ virtualenv --distribute env</pre>
+</div>
+</li>
+<li><p class="first">Upload a new <cite>application.wsgi</cite> file to the server and the
+configuration file for the application (eg: <cite>application.cfg</cite>)</p>
+</li>
+<li><p class="first">Create a new Apache config for <cite>yourapplication</cite> and activate it.
+Make sure to activate watching for changes of the <cite>.wsgi</cite> file so
+that we can automatically reload the application by touching it.
+(See <a class="reference internal" href="../deploying/mod_wsgi.html#mod-wsgi-deployment"><em>mod_wsgi (Apache)</em></a> for more information)</p>
+</li>
+</ol>
+<p>So now the question is, where do the <cite>application.wsgi</cite> and
+<cite>application.cfg</cite> files come from?</p>
+</div>
+<div class="section" id="the-wsgi-file">
+<h2>The WSGI File<a class="headerlink" href="#the-wsgi-file" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
+<span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;YOURAPPLICATION_CONFIG&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;/var/www/yourapplication/application.cfg&#39;</span>
+<span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span>
+</pre></div>
+</div>
+<p>The application itself then has to initialize itself like this to look for
+the config at that environment variable:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="s">&#39;yourapplication.default_config&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_envvar</span><span class="p">(</span><span class="s">&#39;YOURAPPLICATION_CONFIG&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This approach is explained in detail in the <a class="reference internal" href="../config.html#config"><em>Configuration Handling</em></a> section of the
+documentation.</p>
+</div>
+<div class="section" id="the-configuration-file">
+<h2>The Configuration File<a class="headerlink" href="#the-configuration-file" title="Permalink to this headline">¶</a></h2>
+<p>Now as mentioned above, the application will find the correct
+configuration file by looking up the <cite>YOURAPPLICATION_CONFIG</cite> 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.</p>
+<p>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&#8217;s expected (eg: <tt class="docutils literal"><span class="pre">/var/www/yourapplication</span></tt>).</p>
+<p>Either way, in our case here we only expect one or two servers and we can
+upload them ahead of time by hand.</p>
+</div>
+<div class="section" id="first-deployment">
+<h2>First Deployment<a class="headerlink" href="#first-deployment" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<div class="highlight-python"><pre>$ fab pack deploy</pre>
+</div>
+<p>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 <cite>setup.py</cite> file we
+will automatically pull in the required libraries into our virtual
+environment.</p>
+</div>
+<div class="section" id="next-steps">
+<h2>Next Steps<a class="headerlink" href="#next-steps" title="Permalink to this headline">¶</a></h2>
+<p>From that point onwards there is so much that can be done to make
+deployment actually fun:</p>
+<ul class="simple">
+<li>Create a <cite>bootstrap</cite> command that initializes new servers. It could
+initialize a new virtual environment, setup apache appropriately etc.</li>
+<li>Put configuration files into a separate version control repository
+and symlink the active configs into place.</li>
+<li>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.</li>
+<li>hook in testing functionality so that you can deploy to an external
+server and run the testsuite.</li>
+</ul>
+<p>Working with Fabric is fun and you will notice that it&#8217;s quite magical to
+type <tt class="docutils literal"><span class="pre">fab</span> <span class="pre">deploy</span></tt> and see your application being deployed automatically
+to one or more remote servers.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Deploying with Fabric</a><ul>
+<li><a class="reference internal" href="#creating-the-first-fabfile">Creating the first Fabfile</a></li>
+<li><a class="reference internal" href="#running-fabfiles">Running Fabfiles</a></li>
+<li><a class="reference internal" href="#the-wsgi-file">The WSGI File</a></li>
+<li><a class="reference internal" href="#the-configuration-file">The Configuration File</a></li>
+<li><a class="reference internal" href="#first-deployment">First Deployment</a></li>
+<li><a class="reference internal" href="#next-steps">Next Steps</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="distribute.html" title="previous chapter">Deploying with Distribute</a></li>
+ <li>Next: <a href="sqlite3.html" title="next chapter">Using SQLite 3 with Flask</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/fabric.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/favicon.html b/studio/static/doc/flask-docs/patterns/favicon.html
new file mode 100644
index 0000000..55c7ba7
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/favicon.html
@@ -0,0 +1,162 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Adding a favicon &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Streaming Contents" href="streaming.html" />
+ <link rel="prev" title="MongoKit in Flask" href="mongokit.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="streaming.html" title="Streaming Contents"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="mongokit.html" title="MongoKit in Flask"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="adding-a-favicon">
+<h1>Adding a favicon<a class="headerlink" href="#adding-a-favicon" title="Permalink to this headline">¶</a></h1>
+<p>A &#8220;favicon&#8221; is an icon used by browsers for tabs and bookmarks. This helps
+to distinguish your website and to give it a unique brand.</p>
+<p>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
+<tt class="file docutils literal"><span class="pre">favicon.ico</span></tt>.</p>
+<p>Now, to get browsers to find your icon, the correct way is to add a link
+tag in your HTML. So, for example:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">&quot;shortcut icon&quot;</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;static&#39;</span><span class="o">,</span> <span class="nv">filename</span><span class="o">=</span><span class="s1">&#39;favicon.ico&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>
+</pre></div>
+</div>
+<p>That&#8217;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&#8217;t do that you&#8217;re out of luck. If
+however your application is the root you can simply route a redirect:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/favicon.ico&#39;</span><span class="p">,</span>
+ <span class="n">redirect_to</span><span class="o">=</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;static&#39;</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s">&#39;favicon.ico&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>If you want to save the extra redirect request you can also write a view
+using <a class="reference internal" href="../api.html#flask.send_from_directory" title="flask.send_from_directory"><tt class="xref py py-func docutils literal"><span class="pre">send_from_directory()</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">send_from_directory</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/favicon.ico&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">favicon</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">send_from_directory</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">root_path</span><span class="p">,</span> <span class="s">&#39;static&#39;</span><span class="p">),</span>
+ <span class="s">&#39;favicon.ico&#39;</span><span class="p">,</span> <span class="n">mimetype</span><span class="o">=</span><span class="s">&#39;image/vnd.microsoft.icon&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>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.</p>
+<p>The above will serve the icon via your application and if possible it&#8217;s
+better to configure your dedicated web server to serve it; refer to the
+webserver&#8217;s documentation.</p>
+<div class="section" id="see-also">
+<h2>See also<a class="headerlink" href="#see-also" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>The <a class="reference external" href="http://en.wikipedia.org/wiki/Favicon">Favicon</a> article on
+Wikipedia</li>
+</ul>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Adding a favicon</a><ul>
+<li><a class="reference internal" href="#see-also">See also</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="mongokit.html" title="previous chapter">MongoKit in Flask</a></li>
+ <li>Next: <a href="streaming.html" title="next chapter">Streaming Contents</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/favicon.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/fileuploads.html b/studio/static/doc/flask-docs/patterns/fileuploads.html
new file mode 100644
index 0000000..2941367
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/fileuploads.html
@@ -0,0 +1,283 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Uploading Files &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Caching" href="caching.html" />
+ <link rel="prev" title="SQLAlchemy in Flask" href="sqlalchemy.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="caching.html" title="Caching"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="sqlalchemy.html" title="SQLAlchemy in Flask"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="uploading-files">
+<span id="id1"></span><h1>Uploading Files<a class="headerlink" href="#uploading-files" title="Permalink to this headline">¶</a></h1>
+<p>Ah yes, the good old problem of file uploads. The basic idea of file
+uploads is actually quite simple. It basically works like this:</p>
+<ol class="arabic simple">
+<li>A <tt class="docutils literal"><span class="pre">&lt;form&gt;</span></tt> tag is marked with <tt class="docutils literal"><span class="pre">enctype=multipart/form-data</span></tt>
+and an <tt class="docutils literal"><span class="pre">&lt;input</span> <span class="pre">type=file&gt;</span></tt> is placed in that form.</li>
+<li>The application accesses the file from the <tt class="xref py py-attr docutils literal"><span class="pre">files</span></tt>
+dictionary on the request object.</li>
+<li>use the <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.save" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">save()</span></tt></a> method of the file to save
+the file permanently somewhere on the filesystem.</li>
+</ol>
+<div class="section" id="a-gentle-introduction">
+<h2>A Gentle Introduction<a class="headerlink" href="#a-gentle-introduction" title="Permalink to this headline">¶</a></h2>
+<p>Let&#8217;s start with a very basic application that uploads a file to a
+specific upload folder and displays a file to the user. Let&#8217;s look at the
+bootstrapping code for our application:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span>
+<span class="kn">from</span> <span class="nn">werkzeug</span> <span class="kn">import</span> <span class="n">secure_filename</span>
+
+<span class="n">UPLOAD_FOLDER</span> <span class="o">=</span> <span class="s">&#39;/path/to/the/uploads&#39;</span>
+<span class="n">ALLOWED_EXTENSIONS</span> <span class="o">=</span> <span class="nb">set</span><span class="p">([</span><span class="s">&#39;txt&#39;</span><span class="p">,</span> <span class="s">&#39;pdf&#39;</span><span class="p">,</span> <span class="s">&#39;png&#39;</span><span class="p">,</span> <span class="s">&#39;jpg&#39;</span><span class="p">,</span> <span class="s">&#39;jpeg&#39;</span><span class="p">,</span> <span class="s">&#39;gif&#39;</span><span class="p">])</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">UPLOAD_FOLDER</span>
+</pre></div>
+</div>
+<p>So first we need a couple of imports. Most should be straightforward, the
+<tt class="xref py py-func docutils literal"><span class="pre">werkzeug.secure_filename()</span></tt> is explained a little bit later. The
+<cite>UPLOAD_FOLDER</cite> is where we will store the uploaded files and the
+<cite>ALLOWED_EXTENSIONS</cite> is the set of allowed file extensions. Then we add a
+URL rule by hand to the application. Now usually we&#8217;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.</p>
+<p>Why do we limit the extensions that are allowed? You probably don&#8217;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
+<a class="reference internal" href="../security.html#xss"><em>Cross-Site Scripting (XSS)</em></a>). Also make sure to disallow <cite>.php</cite> files if the server
+executes them, but who has PHP installed on his server, right? :)</p>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">allowed_file</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;.&#39;</span> <span class="ow">in</span> <span class="n">filename</span> <span class="ow">and</span> \
+ <span class="n">filename</span><span class="o">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">in</span> <span class="n">ALLOWED_EXTENSIONS</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">upload_file</span><span class="p">():</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="nb">file</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">files</span><span class="p">[</span><span class="s">&#39;file&#39;</span><span class="p">]</span>
+ <span class="k">if</span> <span class="nb">file</span> <span class="ow">and</span> <span class="n">allowed_file</span><span class="p">(</span><span class="nb">file</span><span class="o">.</span><span class="n">filename</span><span class="p">):</span>
+ <span class="n">filename</span> <span class="o">=</span> <span class="n">secure_filename</span><span class="p">(</span><span class="nb">file</span><span class="o">.</span><span class="n">filename</span><span class="p">)</span>
+ <span class="nb">file</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">],</span> <span class="n">filename</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;uploaded_file&#39;</span><span class="p">,</span>
+ <span class="n">filename</span><span class="o">=</span><span class="n">filename</span><span class="p">))</span>
+ <span class="k">return</span> <span class="s">&#39;&#39;&#39;</span>
+<span class="s"> &lt;!doctype html&gt;</span>
+<span class="s"> &lt;title&gt;Upload new File&lt;/title&gt;</span>
+<span class="s"> &lt;h1&gt;Upload new File&lt;/h1&gt;</span>
+<span class="s"> &lt;form action=&quot;&quot; method=post enctype=multipart/form-data&gt;</span>
+<span class="s"> &lt;p&gt;&lt;input type=file name=file&gt;</span>
+<span class="s"> &lt;input type=submit value=Upload&gt;</span>
+<span class="s"> &lt;/form&gt;</span>
+<span class="s"> &#39;&#39;&#39;</span>
+</pre></div>
+</div>
+<p>So what does that <a class="reference external" href="http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">secure_filename()</span></tt></a> function actually do?
+Now the problem is that there is that principle called &#8220;never trust user
+input&#8221;. 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.</p>
+<div class="admonition-information-for-the-pros admonition ">
+<p class="first admonition-title">Information for the Pros</p>
+<p>So you&#8217;re interested in what that <a class="reference external" href="http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">secure_filename()</span></tt></a>
+function does and what the problem is if you&#8217;re not using it? So just
+imagine someone would send the following information as <cite>filename</cite> to
+your application:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">filename</span> <span class="o">=</span> <span class="s">&quot;../../../../home/username/.bashrc&quot;</span>
+</pre></div>
+</div>
+<p>Assuming the number of <tt class="docutils literal"><span class="pre">../</span></tt> is correct and you would join this with
+the <cite>UPLOAD_FOLDER</cite> the user might have the ability to modify a file on
+the server&#8217;s filesystem he or she should not modify. This does require some
+knowledge about how the application looks like, but trust me, hackers
+are patient :)</p>
+<p>Now let&#8217;s look how that function works:</p>
+<div class="last highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">secure_filename</span><span class="p">(</span><span class="s">&#39;../../../../home/username/.bashrc&#39;</span><span class="p">)</span>
+<span class="go">&#39;home_username_.bashrc&#39;</span>
+</pre></div>
+</div>
+</div>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">send_from_directory</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/uploads/&lt;filename&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">uploaded_file</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">send_from_directory</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">],</span>
+ <span class="n">filename</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Alternatively you can register <cite>uploaded_file</cite> as <cite>build_only</cite> rule and
+use the <a class="reference external" href="http://werkzeug.pocoo.org/docs/middlewares/#werkzeug.wsgi.SharedDataMiddleware" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">SharedDataMiddleware</span></tt></a>. This also works with
+older versions of Flask:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug</span> <span class="kn">import</span> <span class="n">SharedDataMiddleware</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/uploads/&lt;filename&gt;&#39;</span><span class="p">,</span> <span class="s">&#39;uploaded_file&#39;</span><span class="p">,</span>
+ <span class="n">build_only</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span> <span class="o">=</span> <span class="n">SharedDataMiddleware</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span><span class="p">,</span> <span class="p">{</span>
+ <span class="s">&#39;/uploads&#39;</span><span class="p">:</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;UPLOAD_FOLDER&#39;</span><span class="p">]</span>
+<span class="p">})</span>
+</pre></div>
+</div>
+<p>If you now run the application everything should work as expected.</p>
+</div>
+<div class="section" id="improving-uploads">
+<h2>Improving Uploads<a class="headerlink" href="#improving-uploads" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6.</span></p>
+<p>So how exactly does Flask handle uploads? Well it will store them in the
+webserver&#8217;s memory if the files are reasonable small otherwise in a
+temporary location (as returned by <a class="reference external" href="http://docs.python.org/dev/library/tempfile.html#tempfile.gettempdir" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">tempfile.gettempdir()</span></tt></a>). 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 <tt class="docutils literal"><span class="pre">MAX_CONTENT_LENGTH</span></tt>
+config key:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">Request</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;MAX_CONTENT_LENGTH&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="mi">16</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span>
+</pre></div>
+</div>
+<p>The code above will limited the maximum allowed payload to 16 megabytes.
+If a larger file is transmitted, Flask will raise an
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.RequestEntityTooLarge" title="(in Werkzeug v0.7)"><tt class="xref py py-exc docutils literal"><span class="pre">RequestEntityTooLarge</span></tt></a> exception.</p>
+<p>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.</p>
+</div>
+<div class="section" id="upload-progress-bars">
+<h2>Upload Progress Bars<a class="headerlink" href="#upload-progress-bars" title="Permalink to this headline">¶</a></h2>
+<p>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 it has transmitted
+already. Do you realize the irony? The client is asking for something it
+should already know.</p>
+<p>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:</p>
+<ul class="simple">
+<li><a class="reference external" href="http://www.plupload.com/">Plupload</a> - HTML5, Java, Flash</li>
+<li><a class="reference external" href="http://www.swfupload.org/">SWFUpload</a> - Flash</li>
+<li><a class="reference external" href="http://jumploader.com/">JumpLoader</a> - Java</li>
+</ul>
+</div>
+<div class="section" id="an-easier-solution">
+<h2>An Easier Solution<a class="headerlink" href="#an-easier-solution" title="Permalink to this headline">¶</a></h2>
+<p>Because the common pattern for file uploads exists almost unchanged in all
+applications dealing with uploads, there is a Flask extension called
+<a class="reference external" href="http://packages.python.org/Flask-Uploads/">Flask-Uploads</a> that implements a full fledged upload mechanism with
+white and blacklisting of extensions and more.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Uploading Files</a><ul>
+<li><a class="reference internal" href="#a-gentle-introduction">A Gentle Introduction</a></li>
+<li><a class="reference internal" href="#improving-uploads">Improving Uploads</a></li>
+<li><a class="reference internal" href="#upload-progress-bars">Upload Progress Bars</a></li>
+<li><a class="reference internal" href="#an-easier-solution">An Easier Solution</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="sqlalchemy.html" title="previous chapter">SQLAlchemy in Flask</a></li>
+ <li>Next: <a href="caching.html" title="next chapter">Caching</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/fileuploads.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/flashing.html b/studio/static/doc/flask-docs/patterns/flashing.html
new file mode 100644
index 0000000..b4f53c7
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/flashing.html
@@ -0,0 +1,221 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Message Flashing &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="AJAX with jQuery" href="jquery.html" />
+ <link rel="prev" title="Template Inheritance" href="templateinheritance.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="jquery.html" title="AJAX with jQuery"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="templateinheritance.html" title="Template Inheritance"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="message-flashing">
+<span id="message-flashing-pattern"></span><h1>Message Flashing<a class="headerlink" href="#message-flashing" title="Permalink to this headline">¶</a></h1>
+<p>Good applications and user interfaces are all about feedback. If the user
+does not get enough feedback they 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.</p>
+<div class="section" id="simple-flashing">
+<h2>Simple Flashing<a class="headerlink" href="#simple-flashing" title="Permalink to this headline">¶</a></h2>
+<p>So here is a full example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">flash</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span><span class="p">,</span> <span class="n">render_template</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;index.html&#39;</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
+ <span class="n">error</span> <span class="o">=</span> <span class="bp">None</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">]</span> <span class="o">!=</span> <span class="s">&#39;admin&#39;</span> <span class="ow">or</span> \
+ <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;password&#39;</span><span class="p">]</span> <span class="o">!=</span> <span class="s">&#39;secret&#39;</span><span class="p">:</span>
+ <span class="n">error</span> <span class="o">=</span> <span class="s">&#39;Invalid credentials&#39;</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">flash</span><span class="p">(</span><span class="s">&#39;You were successfully logged in&#39;</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;index&#39;</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;login.html&#39;</span><span class="p">,</span> <span class="n">error</span><span class="o">=</span><span class="n">error</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>And here the <tt class="docutils literal"><span class="pre">layout.html</span></tt> template which does the magic:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">&lt;!doctype html&gt;</span>
+<span class="nt">&lt;title&gt;</span>My Application<span class="nt">&lt;/title&gt;</span>
+<span class="cp">{%</span> <span class="k">with</span> <span class="nv">messages</span> <span class="o">=</span> <span class="nv">get_flashed_messages</span><span class="o">()</span> <span class="cp">%}</span>
+ <span class="cp">{%</span> <span class="k">if</span> <span class="nv">messages</span> <span class="cp">%}</span>
+ <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">flashes</span><span class="nt">&gt;</span>
+ <span class="cp">{%</span> <span class="k">for</span> <span class="nv">message</span> <span class="k">in</span> <span class="nv">messages</span> <span class="cp">%}</span>
+ <span class="nt">&lt;li&gt;</span><span class="cp">{{</span> <span class="nv">message</span> <span class="cp">}}</span><span class="nt">&lt;/li&gt;</span>
+ <span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/ul&gt;</span>
+ <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">endwith</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>And here the index.html template:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
+ <span class="nt">&lt;h1&gt;</span>Overview<span class="nt">&lt;/h1&gt;</span>
+ <span class="nt">&lt;p&gt;</span>Do you want to <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;login&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>log in?<span class="nt">&lt;/a&gt;</span>
+<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>And of course the login template:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
+ <span class="nt">&lt;h1&gt;</span>Login<span class="nt">&lt;/h1&gt;</span>
+ <span class="cp">{%</span> <span class="k">if</span> <span class="nv">error</span> <span class="cp">%}</span>
+ <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">error</span><span class="nt">&gt;&lt;strong&gt;</span>Error:<span class="nt">&lt;/strong&gt;</span> <span class="cp">{{</span> <span class="nv">error</span> <span class="cp">}}</span>
+ <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+ <span class="nt">&lt;form</span> <span class="na">action=</span><span class="s">&quot;&quot;</span> <span class="na">method=</span><span class="s">post</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dl&gt;</span>
+ <span class="nt">&lt;dt&gt;</span>Username:
+ <span class="nt">&lt;dd&gt;&lt;input</span> <span class="na">type=</span><span class="s">text</span> <span class="na">name=</span><span class="s">username</span> <span class="na">value=</span><span class="s">&quot;</span><span class="cp">{{</span>
+ <span class="nv">request.form.username</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dt&gt;</span>Password:
+ <span class="nt">&lt;dd&gt;&lt;input</span> <span class="na">type=</span><span class="s">password</span> <span class="na">name=</span><span class="s">password</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;/dl&gt;</span>
+ <span class="nt">&lt;p&gt;&lt;input</span> <span class="na">type=</span><span class="s">submit</span> <span class="na">value=</span><span class="s">Login</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;/form&gt;</span>
+<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="flashing-with-categories">
+<h2>Flashing With Categories<a class="headerlink" href="#flashing-with-categories" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.3.</span></p>
+<p>It is also possible to provide categories when flashing a message. The
+default category if nothing is provided is <tt class="docutils literal"><span class="pre">'message'</span></tt>. Alternative
+categories can be used to give the user better feedback. For example
+error messages could be displayed with a red background.</p>
+<p>To flash a message with a different category, just use the second argument
+to the <a class="reference internal" href="../api.html#flask.flash" title="flask.flash"><tt class="xref py py-func docutils literal"><span class="pre">flash()</span></tt></a> function:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">flash</span><span class="p">(</span><span class="s">u&#39;Invalid password provided&#39;</span><span class="p">,</span> <span class="s">&#39;error&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Inside the template you then have to tell the
+<a class="reference internal" href="../api.html#flask.get_flashed_messages" title="flask.get_flashed_messages"><tt class="xref py py-func docutils literal"><span class="pre">get_flashed_messages()</span></tt></a> function to also return the
+categories. The loop looks slightly different in that situation then:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">with</span> <span class="nv">messages</span> <span class="o">=</span> <span class="nv">get_flashed_messages</span><span class="o">(</span><span class="nv">with_categories</span><span class="o">=</span><span class="kp">true</span><span class="o">)</span> <span class="cp">%}</span>
+ <span class="cp">{%</span> <span class="k">if</span> <span class="nv">messages</span> <span class="cp">%}</span>
+ <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">flashes</span><span class="nt">&gt;</span>
+ <span class="cp">{%</span> <span class="k">for</span> <span class="nv">category</span><span class="o">,</span> <span class="nv">message</span> <span class="k">in</span> <span class="nv">messages</span> <span class="cp">%}</span>
+ <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">category</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span><span class="cp">{{</span> <span class="nv">message</span> <span class="cp">}}</span><span class="nt">&lt;/li&gt;</span>
+ <span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/ul&gt;</span>
+ <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">endwith</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>This is just one example of how to render these flashed messages. One
+might also use the category to add a prefix such as
+<tt class="docutils literal"><span class="pre">&lt;strong&gt;Error:&lt;/strong&gt;</span></tt> to the message.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Message Flashing</a><ul>
+<li><a class="reference internal" href="#simple-flashing">Simple Flashing</a></li>
+<li><a class="reference internal" href="#flashing-with-categories">Flashing With Categories</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="templateinheritance.html" title="previous chapter">Template Inheritance</a></li>
+ <li>Next: <a href="jquery.html" title="next chapter">AJAX with jQuery</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/flashing.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/index.html b/studio/static/doc/flask-docs/patterns/index.html
new file mode 100644
index 0000000..8190785
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/index.html
@@ -0,0 +1,240 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Patterns for Flask &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="next" title="Larger Applications" href="packages.html" />
+ <link rel="prev" title="Working with the Shell" href="../shell.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="packages.html" title="Larger Applications"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="../shell.html" title="Working with the Shell"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="patterns-for-flask">
+<span id="patterns"></span><h1>Patterns for Flask<a class="headerlink" href="#patterns-for-flask" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>There are more user contributed snippets and patterns in the <a class="reference external" href="http://flask.pocoo.org/snippets/">Flask
+Snippet Archives</a>.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="packages.html">Larger Applications</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="packages.html#simple-packages">Simple Packages</a></li>
+<li class="toctree-l2"><a class="reference internal" href="packages.html#working-with-blueprints">Working with Blueprints</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="appfactories.html">Application Factories</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="appfactories.html#basic-factories">Basic Factories</a></li>
+<li class="toctree-l2"><a class="reference internal" href="appfactories.html#using-applications">Using Applications</a></li>
+<li class="toctree-l2"><a class="reference internal" href="appfactories.html#factory-improvements">Factory Improvements</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="appdispatch.html">Application Dispatching</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="appdispatch.html#working-with-this-document">Working with this Document</a></li>
+<li class="toctree-l2"><a class="reference internal" href="appdispatch.html#combining-applications">Combining Applications</a></li>
+<li class="toctree-l2"><a class="reference internal" href="appdispatch.html#dispatch-by-subdomain">Dispatch by Subdomain</a></li>
+<li class="toctree-l2"><a class="reference internal" href="appdispatch.html#dispatch-by-path">Dispatch by Path</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="urlprocessors.html">Using URL Processors</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="urlprocessors.html#internationalized-application-urls">Internationalized Application URLs</a></li>
+<li class="toctree-l2"><a class="reference internal" href="urlprocessors.html#internationalized-blueprint-urls">Internationalized Blueprint URLs</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="distribute.html">Deploying with Distribute</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="distribute.html#basic-setup-script">Basic Setup Script</a></li>
+<li class="toctree-l2"><a class="reference internal" href="distribute.html#distributing-resources">Distributing Resources</a></li>
+<li class="toctree-l2"><a class="reference internal" href="distribute.html#declaring-dependencies">Declaring Dependencies</a></li>
+<li class="toctree-l2"><a class="reference internal" href="distribute.html#installing-developing">Installing / Developing</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="fabric.html">Deploying with Fabric</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="fabric.html#creating-the-first-fabfile">Creating the first Fabfile</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fabric.html#running-fabfiles">Running Fabfiles</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fabric.html#the-wsgi-file">The WSGI File</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fabric.html#the-configuration-file">The Configuration File</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fabric.html#first-deployment">First Deployment</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fabric.html#next-steps">Next Steps</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="sqlite3.html">Using SQLite 3 with Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="sqlite3.html#connect-on-demand">Connect on Demand</a></li>
+<li class="toctree-l2"><a class="reference internal" href="sqlite3.html#easy-querying">Easy Querying</a></li>
+<li class="toctree-l2"><a class="reference internal" href="sqlite3.html#initial-schemas">Initial Schemas</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="sqlalchemy.html">SQLAlchemy in Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="sqlalchemy.html#flask-sqlalchemy-extension">Flask-SQLAlchemy Extension</a></li>
+<li class="toctree-l2"><a class="reference internal" href="sqlalchemy.html#declarative">Declarative</a></li>
+<li class="toctree-l2"><a class="reference internal" href="sqlalchemy.html#manual-object-relational-mapping">Manual Object Relational Mapping</a></li>
+<li class="toctree-l2"><a class="reference internal" href="sqlalchemy.html#sql-abstraction-layer">SQL Abstraction Layer</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="fileuploads.html">Uploading Files</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="fileuploads.html#a-gentle-introduction">A Gentle Introduction</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fileuploads.html#improving-uploads">Improving Uploads</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fileuploads.html#upload-progress-bars">Upload Progress Bars</a></li>
+<li class="toctree-l2"><a class="reference internal" href="fileuploads.html#an-easier-solution">An Easier Solution</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="caching.html">Caching</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="caching.html#setting-up-a-cache">Setting up a Cache</a></li>
+<li class="toctree-l2"><a class="reference internal" href="caching.html#using-a-cache">Using a Cache</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="viewdecorators.html">View Decorators</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="viewdecorators.html#login-required-decorator">Login Required Decorator</a></li>
+<li class="toctree-l2"><a class="reference internal" href="viewdecorators.html#caching-decorator">Caching Decorator</a></li>
+<li class="toctree-l2"><a class="reference internal" href="viewdecorators.html#templating-decorator">Templating Decorator</a></li>
+<li class="toctree-l2"><a class="reference internal" href="viewdecorators.html#endpoint-decorator">Endpoint Decorator</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="wtforms.html">Form Validation with WTForms</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="wtforms.html#the-forms">The Forms</a></li>
+<li class="toctree-l2"><a class="reference internal" href="wtforms.html#in-the-view">In the View</a></li>
+<li class="toctree-l2"><a class="reference internal" href="wtforms.html#forms-in-templates">Forms in Templates</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="templateinheritance.html">Template Inheritance</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="templateinheritance.html#base-template">Base Template</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templateinheritance.html#child-template">Child Template</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="flashing.html">Message Flashing</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="flashing.html#simple-flashing">Simple Flashing</a></li>
+<li class="toctree-l2"><a class="reference internal" href="flashing.html#flashing-with-categories">Flashing With Categories</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="jquery.html">AJAX with jQuery</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="jquery.html#loading-jquery">Loading jQuery</a></li>
+<li class="toctree-l2"><a class="reference internal" href="jquery.html#where-is-my-site">Where is My Site?</a></li>
+<li class="toctree-l2"><a class="reference internal" href="jquery.html#json-view-functions">JSON View Functions</a></li>
+<li class="toctree-l2"><a class="reference internal" href="jquery.html#the-html">The HTML</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="errorpages.html">Custom Error Pages</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="errorpages.html#common-error-codes">Common Error Codes</a></li>
+<li class="toctree-l2"><a class="reference internal" href="errorpages.html#error-handlers">Error Handlers</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="lazyloading.html">Lazily Loading Views</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="lazyloading.html#converting-to-centralized-url-map">Converting to Centralized URL Map</a></li>
+<li class="toctree-l2"><a class="reference internal" href="lazyloading.html#loading-late">Loading Late</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="mongokit.html">MongoKit in Flask</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="mongokit.html#declarative">Declarative</a></li>
+<li class="toctree-l2"><a class="reference internal" href="mongokit.html#pymongo-compatibility-layer">PyMongo Compatibility Layer</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="favicon.html">Adding a favicon</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="favicon.html#see-also">See also</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="streaming.html">Streaming Contents</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="streaming.html#basic-usage">Basic Usage</a></li>
+<li class="toctree-l2"><a class="reference internal" href="streaming.html#streaming-from-templates">Streaming from Templates</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="deferredcallbacks.html">Deferred Request Callbacks</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="deferredcallbacks.html#the-decorator">The Decorator</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deferredcallbacks.html#calling-the-deferred">Calling the Deferred</a></li>
+<li class="toctree-l2"><a class="reference internal" href="deferredcallbacks.html#a-practical-example">A Practical Example</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="../shell.html" title="previous chapter">Working with the Shell</a></li>
+ <li>Next: <a href="packages.html" title="next chapter">Larger Applications</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/index.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/jquery.html b/studio/static/doc/flask-docs/patterns/jquery.html
new file mode 100644
index 0000000..7d03623
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/jquery.html
@@ -0,0 +1,261 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>AJAX with jQuery &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Custom Error Pages" href="errorpages.html" />
+ <link rel="prev" title="Message Flashing" href="flashing.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="errorpages.html" title="Custom Error Pages"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="flashing.html" title="Message Flashing"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="ajax-with-jquery">
+<h1>AJAX with jQuery<a class="headerlink" href="#ajax-with-jquery" title="Permalink to this headline">¶</a></h1>
+<p><a class="reference external" href="http://jquery.com/">jQuery</a> 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.</p>
+<p>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.</p>
+<p>If you have Python 2.6 JSON will work out of the box, in Python 2.5 you
+will have to install the <a class="reference external" href="http://pypi.python.org/pypi/simplejson">simplejson</a> library from PyPI.</p>
+<div class="section" id="loading-jquery">
+<h2>Loading jQuery<a class="headerlink" href="#loading-jquery" title="Permalink to this headline">¶</a></h2>
+<p>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&#8217;s loaded. Ideally
+you have a layout template that is used for all pages where you just have
+to add a script statement to the bottom of your <cite>&lt;body&gt;</cite> to load jQuery:</p>
+<div class="highlight-html"><div class="highlight"><pre><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span> <span class="na">src=</span><span class="s">&quot;{{</span>
+<span class="s"> url_for(&#39;static&#39;, filename=&#39;jquery.js&#39;) }}&quot;</span><span class="nt">&gt;&lt;/script&gt;</span>
+</pre></div>
+</div>
+<p>Another method is using Google&#8217;s <a class="reference external" href="http://code.google.com/apis/ajaxlibs/documentation/">AJAX Libraries API</a> to load jQuery:</p>
+<div class="highlight-html"><div class="highlight"><pre><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">&quot;//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js&quot;</span><span class="nt">&gt;&lt;/script&gt;</span>
+<span class="nt">&lt;script&gt;</span><span class="nb">window</span><span class="p">.</span><span class="nx">jQuery</span> <span class="o">||</span> <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;&lt;script src=&quot;{{</span>
+<span class="s1"> url_for(&#39;</span><span class="kr">static</span><span class="s1">&#39;, filename=&#39;</span><span class="nx">jquery</span><span class="p">.</span><span class="nx">js</span><span class="s1">&#39;) }}&quot;&gt;\x3C/script&gt;&#39;</span><span class="p">)</span><span class="nt">&lt;/script&gt;</span>
+</pre></div>
+</div>
+<p>In this case you have to put jQuery into your static folder as a fallback, but it will
+first try to load it directly from Google. 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.</p>
+</div>
+<div class="section" id="where-is-my-site">
+<h2>Where is My Site?<a class="headerlink" href="#where-is-my-site" title="Permalink to this headline">¶</a></h2>
+<p>Do you know where your application is? If you are developing the answer
+is quite simple: it&#8217;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 <tt class="docutils literal"><span class="pre">http://example.com/myapp</span></tt>? On
+the server side this never was a problem because we were using the handy
+<a class="reference internal" href="../api.html#flask.url_for" title="flask.url_for"><tt class="xref py py-func docutils literal"><span class="pre">url_for()</span></tt></a> 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?</p>
+<p>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:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span><span class="nt">&gt;</span>
+ <span class="nx">$SCRIPT_ROOT</span> <span class="o">=</span> <span class="cp">{{</span> <span class="nv">request.script_root</span><span class="o">|</span><span class="nf">tojson</span><span class="o">|</span><span class="nf">safe</span> <span class="cp">}}</span><span class="p">;</span>
+<span class="nt">&lt;/script&gt;</span>
+</pre></div>
+</div>
+<p>The <tt class="docutils literal"><span class="pre">|safe</span></tt> 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 <cite>script</cite> block here where different rules apply.</p>
+<div class="admonition-information-for-pros admonition ">
+<p class="first admonition-title">Information for Pros</p>
+<p class="last">In HTML the <cite>script</cite> tag is declared <cite>CDATA</cite> which means that entities
+will not be parsed. Everything until <tt class="docutils literal"><span class="pre">&lt;/script&gt;</span></tt> is handled as script.
+This also means that there must never be any <tt class="docutils literal"><span class="pre">&lt;/</span></tt> between the script
+tags. <tt class="docutils literal"><span class="pre">|tojson</span></tt> is kind enough to do the right thing here and
+escape slashes for you (<tt class="docutils literal"><span class="pre">{{</span> <span class="pre">&quot;&lt;/script&gt;&quot;|tojson|safe</span> <span class="pre">}}</span></tt> is rendered as
+<tt class="docutils literal"><span class="pre">&quot;&lt;\/script&gt;&quot;</span></tt>).</p>
+</div>
+</div>
+<div class="section" id="json-view-functions">
+<h2>JSON View Functions<a class="headerlink" href="#json-view-functions" title="Permalink to this headline">¶</a></h2>
+<p>Now let&#8217;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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">jsonify</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">request</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/_add_numbers&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">add_numbers</span><span class="p">():</span>
+ <span class="n">a</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;a&#39;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
+ <span class="n">b</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;b&#39;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">result</span><span class="o">=</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;index.html&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>As you can see I also added an <cite>index</cite> 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.</p>
+<p>Note that we are using the <a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.MultiDict.get" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">get()</span></tt></a> method here
+which will never fail. If the key is missing a default value (here <tt class="docutils literal"><span class="pre">0</span></tt>)
+is returned. Furthermore it can convert values to a specific type (like
+in our case <cite>int</cite>). This is especially handy for code that is
+triggered by a script (APIs, JavaScript etc.) because you don&#8217;t need
+special error reporting in that case.</p>
+</div>
+<div class="section" id="the-html">
+<h2>The HTML<a class="headerlink" href="#the-html" title="Permalink to this headline">¶</a></h2>
+<p>Your index.html template either has to extend a <cite>layout.html</cite> template with
+jQuery loaded and the <cite>$SCRIPT_ROOT</cite> variable set, or do that on the top.
+Here&#8217;s the HTML code needed for our little application (<cite>index.html</cite>).
+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:</p>
+<div class="highlight-html"><div class="highlight"><pre><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span><span class="nt">&gt;</span>
+ <span class="nx">$</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
+ <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;a#calculate&#39;</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="s1">&#39;click&#39;</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
+ <span class="nx">$</span><span class="p">.</span><span class="nx">getJSON</span><span class="p">(</span><span class="nx">$SCRIPT_ROOT</span> <span class="o">+</span> <span class="s1">&#39;/_add_numbers&#39;</span><span class="p">,</span> <span class="p">{</span>
+ <span class="nx">a</span><span class="o">:</span> <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;input[name=&quot;a&quot;]&#39;</span><span class="p">).</span><span class="nx">val</span><span class="p">(),</span>
+ <span class="nx">b</span><span class="o">:</span> <span class="nx">$</span><span class="p">(</span><span class="s1">&#39;input[name=&quot;b&quot;]&#39;</span><span class="p">).</span><span class="nx">val</span><span class="p">()</span>
+ <span class="p">},</span> <span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
+ <span class="nx">$</span><span class="p">(</span><span class="s2">&quot;#result&quot;</span><span class="p">).</span><span class="nx">text</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">result</span><span class="p">);</span>
+ <span class="p">});</span>
+ <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
+ <span class="p">});</span>
+ <span class="p">});</span>
+<span class="nt">&lt;/script&gt;</span>
+<span class="nt">&lt;h1&gt;</span>jQuery Example<span class="nt">&lt;/h1&gt;</span>
+<span class="nt">&lt;p&gt;&lt;input</span> <span class="na">type=</span><span class="s">text</span> <span class="na">size=</span><span class="s">5</span> <span class="na">name=</span><span class="s">a</span><span class="nt">&gt;</span> +
+ <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">text</span> <span class="na">size=</span><span class="s">5</span> <span class="na">name=</span><span class="s">b</span><span class="nt">&gt;</span> =
+ <span class="nt">&lt;span</span> <span class="na">id=</span><span class="s">result</span><span class="nt">&gt;</span>?<span class="nt">&lt;/span&gt;</span>
+<span class="nt">&lt;p&gt;&lt;a</span> <span class="na">href=</span><span class="s">#</span> <span class="na">id=</span><span class="s">calculate</span><span class="nt">&gt;</span>calculate server side<span class="nt">&lt;/a&gt;</span>
+</pre></div>
+</div>
+<p>I won&#8217;t got into detail here about how jQuery works, just a very quick
+explanation of the little bit of code above:</p>
+<ol class="arabic simple">
+<li><tt class="docutils literal"><span class="pre">$(function()</span> <span class="pre">{</span> <span class="pre">...</span> <span class="pre">})</span></tt> specifies code that should run once the
+browser is done loading the basic parts of the page.</li>
+<li><tt class="docutils literal"><span class="pre">$('selector')</span></tt> selects an element and lets you operate on it.</li>
+<li><tt class="docutils literal"><span class="pre">element.bind('event',</span> <span class="pre">func)</span></tt> specifies a function that should run
+when the user clicked on the element. If that function returns
+<cite>false</cite>, the default behaviour will not kick in (in this case, navigate
+to the <cite>#</cite> URL).</li>
+<li><tt class="docutils literal"><span class="pre">$.getJSON(url,</span> <span class="pre">data,</span> <span class="pre">func)</span></tt> sends a <cite>GET</cite> request to <cite>url</cite> and will
+send the contents of the <cite>data</cite> 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 <cite>$SCRIPT_ROOT</cite> variable here that
+we set earlier.</li>
+</ol>
+<p>If you don&#8217;t get the whole picture, download the <a class="reference external" href="http://github.com/mitsuhiko/flask/tree/master/examples/jqueryexample">sourcecode
+for this example</a>
+from github.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">AJAX with jQuery</a><ul>
+<li><a class="reference internal" href="#loading-jquery">Loading jQuery</a></li>
+<li><a class="reference internal" href="#where-is-my-site">Where is My Site?</a></li>
+<li><a class="reference internal" href="#json-view-functions">JSON View Functions</a></li>
+<li><a class="reference internal" href="#the-html">The HTML</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="flashing.html" title="previous chapter">Message Flashing</a></li>
+ <li>Next: <a href="errorpages.html" title="next chapter">Custom Error Pages</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/jquery.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/lazyloading.html b/studio/static/doc/flask-docs/patterns/lazyloading.html
new file mode 100644
index 0000000..982b86a
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/lazyloading.html
@@ -0,0 +1,214 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Lazily Loading Views &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="MongoKit in Flask" href="mongokit.html" />
+ <link rel="prev" title="Custom Error Pages" href="errorpages.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="mongokit.html" title="MongoKit in Flask"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="errorpages.html" title="Custom Error Pages"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="lazily-loading-views">
+<h1>Lazily Loading Views<a class="headerlink" href="#lazily-loading-views" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>This can be a problem if your application has to import quick. It might
+have to do that on systems like Google&#8217;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.</p>
+<p>The system that enables having a central URL map is the
+<a class="reference internal" href="../api.html#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a> function. Instead of using decorators,
+you have a file that sets up the application with all URLs.</p>
+<div class="section" id="converting-to-centralized-url-map">
+<h2>Converting to Centralized URL Map<a class="headerlink" href="#converting-to-centralized-url-map" title="Permalink to this headline">¶</a></h2>
+<p>Imagine the current application looks somewhat like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/user/&lt;username&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">user</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>Then the centralized approach you would have one file with the views
+(<cite>views.py</cite>) but without any decorator:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="k">def</span> <span class="nf">user</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>And then a file that sets up an application which maps the functions to
+URLs:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">views</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">views</span><span class="o">.</span><span class="n">index</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/user/&lt;username&gt;&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">views</span><span class="o">.</span><span class="n">user</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="loading-late">
+<h2>Loading Late<a class="headerlink" href="#loading-late" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug</span> <span class="kn">import</span> <span class="n">import_string</span><span class="p">,</span> <span class="n">cached_property</span>
+
+<span class="k">class</span> <span class="nc">LazyView</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">import_name</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">__module__</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__name__</span> <span class="o">=</span> <span class="n">import_name</span><span class="o">.</span><span class="n">rsplit</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">import_name</span> <span class="o">=</span> <span class="n">import_name</span>
+
+ <span class="nd">@cached_property</span>
+ <span class="k">def</span> <span class="nf">view</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">import_string</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">import_name</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">view</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>What&#8217;s important here is is that <cite>__module__</cite> and <cite>__name__</cite> are properly
+set. This is used by Flask internally to figure out how to name the
+URL rules in case you don&#8217;t provide a name for the rule yourself.</p>
+<p>Then you can define your central place to combine the views like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="kn">from</span> <span class="nn">yourapplication.helpers</span> <span class="kn">import</span> <span class="n">LazyView</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span>
+ <span class="n">view_func</span><span class="o">=</span><span class="n">LazyView</span><span class="p">(</span><span class="s">&#39;yourapplication.views.index&#39;</span><span class="p">))</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/user/&lt;username&gt;&#39;</span><span class="p">,</span>
+ <span class="n">view_func</span><span class="o">=</span><span class="n">LazyView</span><span class="p">(</span><span class="s">&#39;yourapplication.views.user&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>You can further optimize this in terms of amount of keystrokes needed to
+write this by having a function that calls into
+<a class="reference internal" href="../api.html#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a> by prefixing a string with the project
+name and a dot, and by wrapping <cite>view_func</cite> in a <cite>LazyView</cite> as needed:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">url</span><span class="p">(</span><span class="n">url_rule</span><span class="p">,</span> <span class="n">import_name</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">):</span>
+ <span class="n">view</span> <span class="o">=</span> <span class="n">LazyView</span><span class="p">(</span><span class="s">&#39;yourapplication.&#39;</span> <span class="o">+</span> <span class="n">import_name</span><span class="p">)</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="n">url_rule</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">view</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">)</span>
+
+<span class="n">url</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="s">&#39;views.index&#39;</span><span class="p">)</span>
+<span class="n">url</span><span class="p">(</span><span class="s">&#39;/user/&lt;username&gt;&#39;</span><span class="p">,</span> <span class="s">&#39;views.user&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>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.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Lazily Loading Views</a><ul>
+<li><a class="reference internal" href="#converting-to-centralized-url-map">Converting to Centralized URL Map</a></li>
+<li><a class="reference internal" href="#loading-late">Loading Late</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="errorpages.html" title="previous chapter">Custom Error Pages</a></li>
+ <li>Next: <a href="mongokit.html" title="next chapter">MongoKit in Flask</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/lazyloading.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/mongokit.html b/studio/static/doc/flask-docs/patterns/mongokit.html
new file mode 100644
index 0000000..fa851dc
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/mongokit.html
@@ -0,0 +1,241 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>MongoKit in Flask &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Adding a favicon" href="favicon.html" />
+ <link rel="prev" title="Lazily Loading Views" href="lazyloading.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="favicon.html" title="Adding a favicon"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="lazyloading.html" title="Lazily Loading Views"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="mongokit-in-flask">
+<h1>MongoKit in Flask<a class="headerlink" href="#mongokit-in-flask" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>This pattern requires a running MongoDB server and the MongoKit library
+installed.</p>
+<p>There are two very common ways to use MongoKit. I will outline each of them
+here:</p>
+<div class="section" id="declarative">
+<h2>Declarative<a class="headerlink" href="#declarative" title="Permalink to this headline">¶</a></h2>
+<p>The default behaviour of MongoKit is the declarative one that is based on
+common ideas from Django or the SQLAlchemy declarative extension.</p>
+<p>Here an example <cite>app.py</cite> module for your application:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="kn">from</span> <span class="nn">mongokit</span> <span class="kn">import</span> <span class="n">Connection</span><span class="p">,</span> <span class="n">Document</span>
+
+<span class="c"># configuration</span>
+<span class="n">MONGODB_HOST</span> <span class="o">=</span> <span class="s">&#39;localhost&#39;</span>
+<span class="n">MONGODB_PORT</span> <span class="o">=</span> <span class="mi">27017</span>
+
+<span class="c"># create the little application object</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="c"># connect to the database</span>
+<span class="n">connection</span> <span class="o">=</span> <span class="n">Connection</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;MONGODB_HOST&#39;</span><span class="p">],</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;MONGODB_PORT&#39;</span><span class="p">])</span>
+</pre></div>
+</div>
+<p>To define your models, just subclass the <cite>Document</cite> class that is imported
+from MongoKit. If you&#8217;ve seen the SQLAlchemy pattern you may wonder why we do
+not have a session and even do not define a <cite>init_db</cite> 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.</p>
+<p>Here is an example document (put this also into <cite>app.py</cite>, e.g.):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">max_length</span><span class="p">(</span><span class="n">length</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
+ <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">length</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">True</span>
+ <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s">&#39;</span><span class="si">%s</span><span class="s"> must be at most </span><span class="si">%s</span><span class="s"> characters long&#39;</span> <span class="o">%</span> <span class="n">length</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">validate</span>
+
+<span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">Document</span><span class="p">):</span>
+ <span class="n">structure</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s">&#39;name&#39;</span><span class="p">:</span> <span class="nb">unicode</span><span class="p">,</span>
+ <span class="s">&#39;email&#39;</span><span class="p">:</span> <span class="nb">unicode</span><span class="p">,</span>
+ <span class="p">}</span>
+ <span class="n">validators</span> <span class="o">=</span> <span class="p">{</span>
+ <span class="s">&#39;name&#39;</span><span class="p">:</span> <span class="n">max_length</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span>
+ <span class="s">&#39;email&#39;</span><span class="p">:</span> <span class="n">max_length</span><span class="p">(</span><span class="mi">120</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="n">use_dot_notation</span> <span class="o">=</span> <span class="bp">True</span>
+ <span class="k">def</span> <span class="nf">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;&lt;User </span><span class="si">%r</span><span class="s">&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
+
+<span class="c"># register the User document with our current connection</span>
+<span class="n">connection</span><span class="o">.</span><span class="n">register</span><span class="p">([</span><span class="n">User</span><span class="p">])</span>
+</pre></div>
+</div>
+<p>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 <cite>use_dot_notation</cite>. Per default MongoKit behaves like a python
+dictionary but with <cite>use_dot_notation</cite> set to <cite>True</cite> you can use your
+documents like you use models in nearly any other ORM by using dots to
+separate between attributes.</p>
+<p>You can insert entries into the database like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">yourapplication.database</span> <span class="kn">import</span> <span class="n">connection</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">yourapplication.models</span> <span class="kn">import</span> <span class="n">User</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">collection</span> <span class="o">=</span> <span class="n">connection</span><span class="p">[</span><span class="s">&#39;test&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">users</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">user</span> <span class="o">=</span> <span class="n">collection</span><span class="o">.</span><span class="n">User</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">user</span><span class="p">[</span><span class="s">&#39;name&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">u&#39;admin&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">user</span><span class="p">[</span><span class="s">&#39;email&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">u&#39;admin@localhost&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">user</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Note that MongoKit is kinda strict with used column types, you must not use a
+common <cite>str</cite> type for either <cite>name</cite> or <cite>email</cite> but unicode.</p>
+<p>Querying is simple as well:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">collection</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">find</span><span class="p">())</span>
+<span class="go">[&lt;User u&#39;admin&#39;&gt;]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">collection</span><span class="o">.</span><span class="n">User</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s">&#39;name&#39;</span><span class="p">:</span> <span class="s">u&#39;admin&#39;</span><span class="p">})</span>
+<span class="go">&lt;User u&#39;admin&#39;&gt;</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="pymongo-compatibility-layer">
+<h2>PyMongo Compatibility Layer<a class="headerlink" href="#pymongo-compatibility-layer" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">MongoKit</span> <span class="kn">import</span> <span class="n">Connection</span>
+
+<span class="n">connection</span> <span class="o">=</span> <span class="n">Connection</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>To insert data you can use the <cite>insert</cite> method. We have to get a
+collection first, this is somewhat the same as a table in the SQL world.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">collection</span> <span class="o">=</span> <span class="n">connection</span><span class="p">[</span><span class="s">&#39;test&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">users</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">user</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;name&#39;</span><span class="p">:</span> <span class="s">u&#39;admin&#39;</span><span class="p">,</span> <span class="s">&#39;email&#39;</span><span class="p">:</span> <span class="s">u&#39;admin@localhost&#39;</span><span class="p">}</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">collection</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>print list(collection.find())
+print collection.find_one({&#8216;name&#8217;: u&#8217;admin&#8217;})</p>
+<p>MongoKit will automatically commit for us.</p>
+<p>To query your database, you use the collection directly:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">collection</span><span class="o">.</span><span class="n">find</span><span class="p">())</span>
+<span class="go">[{u&#39;_id&#39;: ObjectId(&#39;4c271729e13823182f000000&#39;), u&#39;name&#39;: u&#39;admin&#39;, u&#39;email&#39;: u&#39;admin@localhost&#39;}]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s">&#39;name&#39;</span><span class="p">:</span> <span class="s">u&#39;admin&#39;</span><span class="p">})</span>
+<span class="go">{u&#39;_id&#39;: ObjectId(&#39;4c271729e13823182f000000&#39;), u&#39;name&#39;: u&#39;admin&#39;, u&#39;email&#39;: u&#39;admin@localhost&#39;}</span>
+</pre></div>
+</div>
+<p>These results are also dict-like objects:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">r</span> <span class="o">=</span> <span class="n">collection</span><span class="o">.</span><span class="n">find_one</span><span class="p">({</span><span class="s">&#39;name&#39;</span><span class="p">:</span> <span class="s">u&#39;admin&#39;</span><span class="p">})</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">r</span><span class="p">[</span><span class="s">&#39;email&#39;</span><span class="p">]</span>
+<span class="go">u&#39;admin@localhost&#39;</span>
+</pre></div>
+</div>
+<p>For more information about MongoKit, head over to the
+<a class="reference external" href="http://bytebucket.org/namlook/mongokit/">website</a>.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">MongoKit in Flask</a><ul>
+<li><a class="reference internal" href="#declarative">Declarative</a></li>
+<li><a class="reference internal" href="#pymongo-compatibility-layer">PyMongo Compatibility Layer</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="lazyloading.html" title="previous chapter">Lazily Loading Views</a></li>
+ <li>Next: <a href="favicon.html" title="next chapter">Adding a favicon</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/mongokit.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/packages.html b/studio/static/doc/flask-docs/patterns/packages.html
new file mode 100644
index 0000000..9704b14
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/packages.html
@@ -0,0 +1,219 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Larger Applications &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Application Factories" href="appfactories.html" />
+ <link rel="prev" title="Patterns for Flask" href="index.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="appfactories.html" title="Application Factories"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="index.html" title="Patterns for Flask"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="larger-applications">
+<span id="id1"></span><h1>Larger Applications<a class="headerlink" href="#larger-applications" title="Permalink to this headline">¶</a></h1>
+<p>For larger applications it&#8217;s a good idea to use a package instead of a
+module. That is quite simple. Imagine a small application looks like
+this:</p>
+<div class="highlight-python"><pre>/yourapplication
+ /yourapplication.py
+ /static
+ /style.css
+ /templates
+ layout.html
+ index.html
+ login.html
+ ...</pre>
+</div>
+<div class="section" id="simple-packages">
+<h2>Simple Packages<a class="headerlink" href="#simple-packages" title="Permalink to this headline">¶</a></h2>
+<p>To convert that into a larger one, just create a new folder
+<cite>yourapplication</cite> inside the existing one and move everything below it.
+Then rename <cite>yourapplication.py</cite> to <cite>__init__.py</cite>. (Make sure to delete
+all <cite>.pyc</cite> files first, otherwise things would most likely break)</p>
+<p>You should then end up with something like that:</p>
+<div class="highlight-python"><pre>/yourapplication
+ /yourapplication
+ /__init__.py
+ /static
+ /style.css
+ /templates
+ layout.html
+ index.html
+ login.html
+ ...</pre>
+</div>
+<p>But how do you run your application now? The naive <tt class="docutils literal"><span class="pre">python</span>
+<span class="pre">yourapplication/__init__.py</span></tt> will not work. Let&#8217;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 <cite>runserver.py</cite> next to the inner
+<cite>yourapplication</cite> folder with the following contents:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span>
+<span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>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:</p>
+<ol class="arabic simple">
+<li>the <cite>Flask</cite> application object creation has to be in the
+<cite>__init__.py</cite> file. That way each module can import it safely and the
+<cite>__name__</cite> variable will resolve to the correct package.</li>
+<li>all the view functions (the ones with a <a class="reference internal" href="../api.html#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a>
+decorator on top) have to be imported when in the <cite>__init__.py</cite> file.
+Not the object itself, but the module it is in. Import the view module
+<strong>after the application object is created</strong>.</li>
+</ol>
+<p>Here&#8217;s an example <cite>__init__.py</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="kn">import</span> <span class="nn">yourapplication.views</span>
+</pre></div>
+</div>
+<p>And this is what <cite>views.py</cite> would look like:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">app</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&#39;Hello World!&#39;</span>
+</pre></div>
+</div>
+<p>You should then end up with something like that:</p>
+<div class="highlight-python"><pre>/yourapplication
+ /runserver.py
+ /yourapplication
+ /__init__.py
+ /views.py
+ /static
+ /style.css
+ /templates
+ layout.html
+ index.html
+ login.html
+ ...</pre>
+</div>
+<div class="admonition-circular-imports admonition ">
+<p class="first admonition-title">Circular Imports</p>
+<p>Every Python programmer hates them, and yet we just added some:
+circular imports (That&#8217;s when two modules depend on each other. In this
+case <cite>views.py</cite> depends on <cite>__init__.py</cite>). 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 <cite>__init__.py</cite> and just
+ensuring the module is imported and we are doing that at the bottom of
+the file.</p>
+<p class="last">There are still some problems with that approach but if you want to use
+decorators there is no way around that. Check out the
+<a class="reference internal" href="../becomingbig.html#becomingbig"><em>Becoming Big</em></a> section for some inspiration how to deal with that.</p>
+</div>
+</div>
+<div class="section" id="working-with-blueprints">
+<span id="working-with-modules"></span><h2>Working with Blueprints<a class="headerlink" href="#working-with-blueprints" title="Permalink to this headline">¶</a></h2>
+<p>If you have larger applications it&#8217;s recommended to divide them into
+smaller groups where each group is implemented with the help of a
+blueprint. For a gentle introduction into this topic refer to the
+<a class="reference internal" href="../blueprints.html#blueprints"><em>Modular Applications with Blueprints</em></a> chapter of the documentation.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Larger Applications</a><ul>
+<li><a class="reference internal" href="#simple-packages">Simple Packages</a></li>
+<li><a class="reference internal" href="#working-with-blueprints">Working with Blueprints</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="index.html" title="previous chapter">Patterns for Flask</a></li>
+ <li>Next: <a href="appfactories.html" title="next chapter">Application Factories</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/packages.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/sqlalchemy.html b/studio/static/doc/flask-docs/patterns/sqlalchemy.html
new file mode 100644
index 0000000..8fd4363
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/sqlalchemy.html
@@ -0,0 +1,310 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>SQLAlchemy in Flask &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Uploading Files" href="fileuploads.html" />
+ <link rel="prev" title="Using SQLite 3 with Flask" href="sqlite3.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="fileuploads.html" title="Uploading Files"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="sqlite3.html" title="Using SQLite 3 with Flask"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="sqlalchemy-in-flask">
+<span id="sqlalchemy-pattern"></span><h1>SQLAlchemy in Flask<a class="headerlink" href="#sqlalchemy-in-flask" title="Permalink to this headline">¶</a></h1>
+<p>Many people prefer <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a> for database access. In this case it&#8217;s
+encouraged to use a package instead of a module for your flask application
+and drop the models into a separate module (<a class="reference internal" href="packages.html#larger-applications"><em>Larger Applications</em></a>).
+While that is not necessary, it makes a lot of sense.</p>
+<p>There are four very common ways to use SQLAlchemy. I will outline each
+of them here:</p>
+<div class="section" id="flask-sqlalchemy-extension">
+<h2>Flask-SQLAlchemy Extension<a class="headerlink" href="#flask-sqlalchemy-extension" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>You can download <a class="reference external" href="http://packages.python.org/Flask-SQLAlchemy/">Flask-SQLAlchemy</a> from <a class="reference external" href="http://pypi.python.org/pypi/Flask-SQLAlchemy">PyPI</a>.</p>
+</div>
+<div class="section" id="declarative">
+<h2>Declarative<a class="headerlink" href="#declarative" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference external" href="http://www.sqlalchemy.org/docs/orm/extensions/declarative.html">declarative</a> extension.</p>
+<p>Here the example <cite>database.py</cite> module for your application:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">create_engine</span>
+<span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">import</span> <span class="n">scoped_session</span><span class="p">,</span> <span class="n">sessionmaker</span>
+<span class="kn">from</span> <span class="nn">sqlalchemy.ext.declarative</span> <span class="kn">import</span> <span class="n">declarative_base</span>
+
+<span class="n">engine</span> <span class="o">=</span> <span class="n">create_engine</span><span class="p">(</span><span class="s">&#39;sqlite:////tmp/test.db&#39;</span><span class="p">,</span> <span class="n">convert_unicode</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="n">db_session</span> <span class="o">=</span> <span class="n">scoped_session</span><span class="p">(</span><span class="n">sessionmaker</span><span class="p">(</span><span class="n">autocommit</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
+ <span class="n">autoflush</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
+ <span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">))</span>
+<span class="n">Base</span> <span class="o">=</span> <span class="n">declarative_base</span><span class="p">()</span>
+<span class="n">Base</span><span class="o">.</span><span class="n">query</span> <span class="o">=</span> <span class="n">db_session</span><span class="o">.</span><span class="n">query_property</span><span class="p">()</span>
+
+<span class="k">def</span> <span class="nf">init_db</span><span class="p">():</span>
+ <span class="c"># import all modules here that might define models so that</span>
+ <span class="c"># they will be registered properly on the metadata. Otherwise</span>
+ <span class="c"># you will have to import them first before calling init_db()</span>
+ <span class="kn">import</span> <span class="nn">yourapplication.models</span>
+ <span class="n">Base</span><span class="o">.</span><span class="n">metadata</span><span class="o">.</span><span class="n">create_all</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>To define your models, just subclass the <cite>Base</cite> class that was created by
+the code above. If you are wondering why we don&#8217;t have to care about
+threads here (like we did in the SQLite3 example above with the
+<a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a> object): that&#8217;s because SQLAlchemy does that for us
+already with the <tt class="xref py py-class docutils literal"><span class="pre">scoped_session</span></tt>.</p>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">yourapplication.database</span> <span class="kn">import</span> <span class="n">db_session</span>
+
+<span class="nd">@app.teardown_request</span>
+<span class="k">def</span> <span class="nf">shutdown_session</span><span class="p">(</span><span class="n">exception</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="n">db_session</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Here is an example model (put this into <cite>models.py</cite>, e.g.):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">Column</span><span class="p">,</span> <span class="n">Integer</span><span class="p">,</span> <span class="n">String</span>
+<span class="kn">from</span> <span class="nn">yourapplication.database</span> <span class="kn">import</span> <span class="n">Base</span>
+
+<span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span>
+ <span class="n">__tablename__</span> <span class="o">=</span> <span class="s">&#39;users&#39;</span>
+ <span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+ <span class="n">email</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">(</span><span class="mi">120</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span>
+
+ <span class="k">def</span> <span class="nf">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;&lt;User </span><span class="si">%r</span><span class="s">&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>To create the database you can use the <cite>init_db</cite> function:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">yourapplication.database</span> <span class="kn">import</span> <span class="n">init_db</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">init_db</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>You can insert entries into the database like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">yourapplication.database</span> <span class="kn">import</span> <span class="n">db_session</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">yourapplication.models</span> <span class="kn">import</span> <span class="n">User</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">u</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="s">&#39;admin@localhost&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db_session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">u</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db_session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Querying is simple as well:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+<span class="go">[&lt;User u&#39;admin&#39;&gt;]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">&#39;admin&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
+<span class="go">&lt;User u&#39;admin&#39;&gt;</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="manual-object-relational-mapping">
+<h2>Manual Object Relational Mapping<a class="headerlink" href="#manual-object-relational-mapping" title="Permalink to this headline">¶</a></h2>
+<p>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&#8217;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.</p>
+<p>Here is an example <cite>database.py</cite> module for your application:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">create_engine</span><span class="p">,</span> <span class="n">MetaData</span>
+<span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">import</span> <span class="n">scoped_session</span><span class="p">,</span> <span class="n">sessionmaker</span>
+
+<span class="n">engine</span> <span class="o">=</span> <span class="n">create_engine</span><span class="p">(</span><span class="s">&#39;sqlite:////tmp/test.db&#39;</span><span class="p">,</span> <span class="n">convert_unicode</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="n">metadata</span> <span class="o">=</span> <span class="n">MetaData</span><span class="p">()</span>
+<span class="n">db_session</span> <span class="o">=</span> <span class="n">scoped_session</span><span class="p">(</span><span class="n">sessionmaker</span><span class="p">(</span><span class="n">autocommit</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
+ <span class="n">autoflush</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
+ <span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">))</span>
+<span class="k">def</span> <span class="nf">init_db</span><span class="p">():</span>
+ <span class="n">metadata</span><span class="o">.</span><span class="n">create_all</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>As for the declarative approach you need to close the session after
+each request. Put this into your application module:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">yourapplication.database</span> <span class="kn">import</span> <span class="n">db_session</span>
+
+<span class="nd">@app.teardown_request</span>
+<span class="k">def</span> <span class="nf">shutdown_session</span><span class="p">(</span><span class="n">exception</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="n">db_session</span><span class="o">.</span><span class="n">remove</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Here is an example table and model (put this into <cite>models.py</cite>):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">Table</span><span class="p">,</span> <span class="n">Column</span><span class="p">,</span> <span class="n">Integer</span><span class="p">,</span> <span class="n">String</span>
+<span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">import</span> <span class="n">mapper</span>
+<span class="kn">from</span> <span class="nn">yourapplication.database</span> <span class="kn">import</span> <span class="n">metadata</span><span class="p">,</span> <span class="n">db_session</span>
+
+<span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="n">query</span> <span class="o">=</span> <span class="n">db_session</span><span class="o">.</span><span class="n">query_property</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span>
+
+ <span class="k">def</span> <span class="nf">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;&lt;User </span><span class="si">%r</span><span class="s">&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
+
+<span class="n">users</span> <span class="o">=</span> <span class="n">Table</span><span class="p">(</span><span class="s">&#39;users&#39;</span><span class="p">,</span> <span class="n">metadata</span><span class="p">,</span>
+ <span class="n">Column</span><span class="p">(</span><span class="s">&#39;id&#39;</span><span class="p">,</span> <span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>
+ <span class="n">Column</span><span class="p">(</span><span class="s">&#39;name&#39;</span><span class="p">,</span> <span class="n">String</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>
+ <span class="n">Column</span><span class="p">(</span><span class="s">&#39;email&#39;</span><span class="p">,</span> <span class="n">String</span><span class="p">(</span><span class="mi">120</span><span class="p">),</span> <span class="n">unique</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="p">)</span>
+<span class="n">mapper</span><span class="p">(</span><span class="n">User</span><span class="p">,</span> <span class="n">users</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Querying and inserting works exactly the same as in the example above.</p>
+</div>
+<div class="section" id="sql-abstraction-layer">
+<h2>SQL Abstraction Layer<a class="headerlink" href="#sql-abstraction-layer" title="Permalink to this headline">¶</a></h2>
+<p>If you just want to use the database system (and SQL) abstraction layer
+you basically only need the engine:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">sqlalchemy</span> <span class="kn">import</span> <span class="n">create_engine</span><span class="p">,</span> <span class="n">MetaData</span>
+
+<span class="n">engine</span> <span class="o">=</span> <span class="n">create_engine</span><span class="p">(</span><span class="s">&#39;sqlite:////tmp/test.db&#39;</span><span class="p">,</span> <span class="n">convert_unicode</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="n">metadata</span> <span class="o">=</span> <span class="n">MetaData</span><span class="p">(</span><span class="n">bind</span><span class="o">=</span><span class="n">engine</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Then you can either declare the tables in your code like in the examples
+above, or automatically load them:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">users</span> <span class="o">=</span> <span class="n">Table</span><span class="p">(</span><span class="s">&#39;users&#39;</span><span class="p">,</span> <span class="n">metadata</span><span class="p">,</span> <span class="n">autoload</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>To insert data you can use the <cite>insert</cite> method. We have to get a
+connection first so that we can use a transaction:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">con</span> <span class="o">=</span> <span class="n">engine</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">con</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">users</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="n">email</span><span class="o">=</span><span class="s">&#39;admin@localhost&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>SQLAlchemy will automatically commit for us.</p>
+<p>To query your database, you use the engine directly or use a connection:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">users</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="n">users</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
+<span class="go">(1, u&#39;admin&#39;, u&#39;admin@localhost&#39;)</span>
+</pre></div>
+</div>
+<p>These results are also dict-like tuples:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">r</span> <span class="o">=</span> <span class="n">users</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="n">users</span><span class="o">.</span><span class="n">c</span><span class="o">.</span><span class="n">id</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">r</span><span class="p">[</span><span class="s">&#39;name&#39;</span><span class="p">]</span>
+<span class="go">u&#39;admin&#39;</span>
+</pre></div>
+</div>
+<p>You can also pass strings of SQL statements to the
+<a class="reference external" href="http://www.sqlalchemy.org/docs/core/connections.html#sqlalchemy.engine.base.Connection.execute" title="(in SQLAlchemy v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">execute()</span></tt></a> method:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">engine</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s">&#39;select * from users where id = :1&#39;</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
+<span class="go">(1, u&#39;admin&#39;, u&#39;admin@localhost&#39;)</span>
+</pre></div>
+</div>
+<p>For more information about SQLAlchemy, head over to the
+<a class="reference external" href="http://sqlalchemy.org/">website</a>.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">SQLAlchemy in Flask</a><ul>
+<li><a class="reference internal" href="#flask-sqlalchemy-extension">Flask-SQLAlchemy Extension</a></li>
+<li><a class="reference internal" href="#declarative">Declarative</a></li>
+<li><a class="reference internal" href="#manual-object-relational-mapping">Manual Object Relational Mapping</a></li>
+<li><a class="reference internal" href="#sql-abstraction-layer">SQL Abstraction Layer</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="sqlite3.html" title="previous chapter">Using SQLite 3 with Flask</a></li>
+ <li>Next: <a href="fileuploads.html" title="next chapter">Uploading Files</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/sqlalchemy.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/sqlite3.html b/studio/static/doc/flask-docs/patterns/sqlite3.html
new file mode 100644
index 0000000..8e892e3
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/sqlite3.html
@@ -0,0 +1,226 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Using SQLite 3 with Flask &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="SQLAlchemy in Flask" href="sqlalchemy.html" />
+ <link rel="prev" title="Deploying with Fabric" href="fabric.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="sqlalchemy.html" title="SQLAlchemy in Flask"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="fabric.html" title="Deploying with Fabric"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="using-sqlite-3-with-flask">
+<span id="sqlite3"></span><h1>Using SQLite 3 with Flask<a class="headerlink" href="#using-sqlite-3-with-flask" title="Permalink to this headline">¶</a></h1>
+<p>In Flask you can implement the opening of database connections at the
+beginning of the request and closing at the end with the
+<a class="reference internal" href="../api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> and <a class="reference internal" href="../api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a>
+decorators in combination with the special <a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-class docutils literal"><span class="pre">g</span></tt></a> object.</p>
+<p>So here is a simple example of how you can use SQLite 3 with Flask:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">sqlite3</span>
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">g</span>
+
+<span class="n">DATABASE</span> <span class="o">=</span> <span class="s">&#39;/path/to/database.db&#39;</span>
+
+<span class="k">def</span> <span class="nf">connect_db</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">DATABASE</span><span class="p">)</span>
+
+<span class="nd">@app.before_request</span>
+<span class="k">def</span> <span class="nf">before_request</span><span class="p">():</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span> <span class="o">=</span> <span class="n">connect_db</span><span class="p">()</span>
+
+<span class="nd">@app.teardown_request</span>
+<span class="k">def</span> <span class="nf">teardown_request</span><span class="p">(</span><span class="n">exception</span><span class="p">):</span>
+ <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s">&#39;db&#39;</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+</pre></div>
+</div>
+<div class="admonition note">
+<p class="first admonition-title">Note</p>
+<p class="last">Please keep in mind that the teardown request functions are always
+executed, even if a before-request handler failed or was never
+executed. Because of this we have to make sure here that the database
+is there before we close it.</p>
+</div>
+<div class="section" id="connect-on-demand">
+<h2>Connect on Demand<a class="headerlink" href="#connect-on-demand" title="Permalink to this headline">¶</a></h2>
+<p>The downside of this approach is that this will only work if Flask
+executed the before-request handlers for you. If you are attempting to
+use the database from a script or the interactive Python shell you would
+have to do something like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">():</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">preprocess_request</span><span class="p">()</span>
+ <span class="c"># now you can use the g.db object</span>
+</pre></div>
+</div>
+<p>In order to trigger the execution of the connection code. You won&#8217;t be
+able to drop the dependency on the request context this way, but you could
+make it so that the application connects when necessary:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">get_connection</span><span class="p">():</span>
+ <span class="n">db</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s">&#39;_db&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">db</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">db</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">_db</span> <span class="o">=</span> <span class="n">connect_db</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">db</span>
+</pre></div>
+</div>
+<p>Downside here is that you have to use <tt class="docutils literal"><span class="pre">db</span> <span class="pre">=</span> <span class="pre">get_connection()</span></tt> instead of
+just being able to use <tt class="docutils literal"><span class="pre">g.db</span></tt> directly.</p>
+</div>
+<div class="section" id="easy-querying">
+<span id="id1"></span><h2>Easy Querying<a class="headerlink" href="#easy-querying" title="Permalink to this headline">¶</a></h2>
+<p>Now in each request handling function you can access <cite>g.db</cite> to get the
+current open database connection. To simplify working with SQLite, a
+helper function can be useful:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">query_db</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(),</span> <span class="n">one</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
+ <span class="n">cur</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="p">[</span><span class="nb">dict</span><span class="p">((</span><span class="n">cur</span><span class="o">.</span><span class="n">description</span><span class="p">[</span><span class="n">idx</span><span class="p">][</span><span class="mi">0</span><span class="p">],</span> <span class="n">value</span><span class="p">)</span>
+ <span class="k">for</span> <span class="n">idx</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">row</span><span class="p">))</span> <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">cur</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()]</span>
+ <span class="k">return</span> <span class="p">(</span><span class="n">rv</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">if</span> <span class="n">rv</span> <span class="k">else</span> <span class="bp">None</span><span class="p">)</span> <span class="k">if</span> <span class="n">one</span> <span class="k">else</span> <span class="n">rv</span>
+</pre></div>
+</div>
+<p>This handy little function makes working with the database much more
+pleasant than it is by just using the raw cursor and connection objects.</p>
+<p>Here is how you can use it:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">for</span> <span class="n">user</span> <span class="ow">in</span> <span class="n">query_db</span><span class="p">(</span><span class="s">&#39;select * from users&#39;</span><span class="p">):</span>
+ <span class="k">print</span> <span class="n">user</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">],</span> <span class="s">&#39;has the id&#39;</span><span class="p">,</span> <span class="n">user</span><span class="p">[</span><span class="s">&#39;user_id&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+<p>Or if you just want a single result:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">user</span> <span class="o">=</span> <span class="n">query_db</span><span class="p">(</span><span class="s">&#39;select * from users where username = ?&#39;</span><span class="p">,</span>
+ <span class="p">[</span><span class="n">the_username</span><span class="p">],</span> <span class="n">one</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="k">if</span> <span class="n">user</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="k">print</span> <span class="s">&#39;No such user&#39;</span>
+<span class="k">else</span><span class="p">:</span>
+ <span class="k">print</span> <span class="n">the_username</span><span class="p">,</span> <span class="s">&#39;has the id&#39;</span><span class="p">,</span> <span class="n">user</span><span class="p">[</span><span class="s">&#39;user_id&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+<p>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 <a class="reference external" href="http://en.wikipedia.org/wiki/SQL_injection">SQL Injections</a>.</p>
+</div>
+<div class="section" id="initial-schemas">
+<h2>Initial Schemas<a class="headerlink" href="#initial-schemas" title="Permalink to this headline">¶</a></h2>
+<p>Relational databases need schemas, so applications often ship a
+<cite>schema.sql</cite> file that creates the database. It&#8217;s a good idea to provide
+a function that creates the database based on that schema. This function
+can do that for you:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">closing</span>
+
+<span class="k">def</span> <span class="nf">init_db</span><span class="p">():</span>
+ <span class="k">with</span> <span class="n">closing</span><span class="p">(</span><span class="n">connect_db</span><span class="p">())</span> <span class="k">as</span> <span class="n">db</span><span class="p">:</span>
+ <span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">open_resource</span><span class="p">(</span><span class="s">&#39;schema.sql&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span><span class="o">.</span><span class="n">executescript</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
+ <span class="n">db</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>You can then create such a database from the python shell:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">yourapplication</span> <span class="kn">import</span> <span class="n">init_db</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">init_db</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Using SQLite 3 with Flask</a><ul>
+<li><a class="reference internal" href="#connect-on-demand">Connect on Demand</a></li>
+<li><a class="reference internal" href="#easy-querying">Easy Querying</a></li>
+<li><a class="reference internal" href="#initial-schemas">Initial Schemas</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="fabric.html" title="previous chapter">Deploying with Fabric</a></li>
+ <li>Next: <a href="sqlalchemy.html" title="next chapter">SQLAlchemy in Flask</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/sqlite3.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/streaming.html b/studio/static/doc/flask-docs/patterns/streaming.html
new file mode 100644
index 0000000..9285ec4
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/streaming.html
@@ -0,0 +1,172 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Streaming Contents &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Deferred Request Callbacks" href="deferredcallbacks.html" />
+ <link rel="prev" title="Adding a favicon" href="favicon.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="deferredcallbacks.html" title="Deferred Request Callbacks"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="favicon.html" title="Adding a favicon"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="streaming-contents">
+<h1>Streaming Contents<a class="headerlink" href="#streaming-contents" title="Permalink to this headline">¶</a></h1>
+<p>Sometimes you want to send an enormous amount of data to the client, much
+more than you want to keep in memory. When you are generating the data on
+the fly though, how do you send that back to the client without the
+roundtrip to the filesystem?</p>
+<p>The answer is by using generators and direct responses.</p>
+<div class="section" id="basic-usage">
+<h2>Basic Usage<a class="headerlink" href="#basic-usage" title="Permalink to this headline">¶</a></h2>
+<p>This is a basic view function that generates a lot of CSV data on the fly.
+The trick is to have an inner function that uses a generator to generate
+data and to then invoke that function and pass it to a response object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Response</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/large.csv&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">generate_large_csv</span><span class="p">():</span>
+ <span class="k">def</span> <span class="nf">generate</span><span class="p">():</span>
+ <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">iter_all_rows</span><span class="p">():</span>
+ <span class="k">yield</span> <span class="s">&#39;,&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">row</span><span class="p">)</span> <span class="o">+</span> <span class="s">&#39;</span><span class="se">\n</span><span class="s">&#39;</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">generate</span><span class="p">(),</span> <span class="n">mimetype</span><span class="o">=</span><span class="s">&#39;text/csv&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Each <tt class="docutils literal"><span class="pre">yield</span></tt> expression is directly sent to the browser. Now though
+that some WSGI middlewares might break streaming, so be careful there in
+debug environments with profilers and other things you might have enabled.</p>
+</div>
+<div class="section" id="streaming-from-templates">
+<h2>Streaming from Templates<a class="headerlink" href="#streaming-from-templates" title="Permalink to this headline">¶</a></h2>
+<p>The Jinja2 template engine also supports rendering templates piece by
+piece. This functionality is not directly exposed by Flask because it is
+quite uncommon, but you can easily do it yourself:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Response</span>
+
+<span class="k">def</span> <span class="nf">stream_template</span><span class="p">(</span><span class="n">template_name</span><span class="p">,</span> <span class="o">**</span><span class="n">context</span><span class="p">):</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">update_template_context</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
+ <span class="n">t</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">jinja_env</span><span class="o">.</span><span class="n">get_template</span><span class="p">(</span><span class="n">template_name</span><span class="p">)</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">stream</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
+ <span class="n">rv</span><span class="o">.</span><span class="n">enable_buffering</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">rv</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/my-large-page.html&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">render_large_template</span><span class="p">():</span>
+ <span class="n">rows</span> <span class="o">=</span> <span class="n">iter_all_rows</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">stream_template</span><span class="p">(</span><span class="s">&#39;the_template.html&#39;</span><span class="p">,</span> <span class="n">rows</span><span class="o">=</span><span class="n">rows</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>The trick here is to get the template object from the Jinja2 environment
+on the application and to call <tt class="xref py py-meth docutils literal"><span class="pre">stream()</span></tt> instead of
+<tt class="xref py py-meth docutils literal"><span class="pre">render()</span></tt> which returns a stream object instead of a
+string. Since we&#8217;re bypassing the Flask template render functions and
+using the template object itself we have to make sure to update the render
+context ourselves by calling <a class="reference internal" href="../api.html#flask.Flask.update_template_context" title="flask.Flask.update_template_context"><tt class="xref py py-meth docutils literal"><span class="pre">update_template_context()</span></tt></a>.
+The template is then evaluated as the stream is iterated over. Since each
+time you do a yield the server will flush the content to the client you
+might want to buffer up a few items in the template which you can do with
+<tt class="docutils literal"><span class="pre">rv.enable_buffering(size)</span></tt>. <tt class="docutils literal"><span class="pre">5</span></tt> is a sane default.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Streaming Contents</a><ul>
+<li><a class="reference internal" href="#basic-usage">Basic Usage</a></li>
+<li><a class="reference internal" href="#streaming-from-templates">Streaming from Templates</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="favicon.html" title="previous chapter">Adding a favicon</a></li>
+ <li>Next: <a href="deferredcallbacks.html" title="next chapter">Deferred Request Callbacks</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/streaming.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/templateinheritance.html b/studio/static/doc/flask-docs/patterns/templateinheritance.html
new file mode 100644
index 0000000..6075905
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/templateinheritance.html
@@ -0,0 +1,173 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Template Inheritance &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Message Flashing" href="flashing.html" />
+ <link rel="prev" title="Form Validation with WTForms" href="wtforms.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="flashing.html" title="Message Flashing"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="wtforms.html" title="Form Validation with WTForms"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="template-inheritance">
+<span id="id1"></span><h1>Template Inheritance<a class="headerlink" href="#template-inheritance" title="Permalink to this headline">¶</a></h1>
+<p>The most powerful part of Jinja is template inheritance. Template inheritance
+allows you to build a base &#8220;skeleton&#8221; template that contains all the common
+elements of your site and defines <strong>blocks</strong> that child templates can override.</p>
+<p>Sounds complicated but is very basic. It&#8217;s easiest to understand it by starting
+with an example.</p>
+<div class="section" id="base-template">
+<h2>Base Template<a class="headerlink" href="#base-template" title="Permalink to this headline">¶</a></h2>
+<p>This template, which we&#8217;ll call <tt class="docutils literal"><span class="pre">layout.html</span></tt>, defines a simple HTML skeleton
+document that you might use for a simple two-column page. It&#8217;s the job of
+&#8220;child&#8221; templates to fill the empty blocks with content:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">&lt;!doctype html&gt;</span>
+<span class="nt">&lt;html&gt;</span>
+ <span class="nt">&lt;head&gt;</span>
+ <span class="cp">{%</span> <span class="k">block</span> <span class="nv">head</span> <span class="cp">%}</span>
+ <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">&quot;stylesheet&quot;</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;static&#39;</span><span class="o">,</span> <span class="nv">filename</span><span class="o">=</span><span class="s1">&#39;style.css&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;title&gt;</span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span> - My Webpage<span class="nt">&lt;/title&gt;</span>
+ <span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/head&gt;</span>
+<span class="nt">&lt;body&gt;</span>
+ <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&quot;content&quot;</span><span class="nt">&gt;</span><span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span><span class="nt">&lt;/div&gt;</span>
+ <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&quot;footer&quot;</span><span class="nt">&gt;</span>
+ <span class="cp">{%</span> <span class="k">block</span> <span class="nv">footer</span> <span class="cp">%}</span>
+ <span class="ni">&amp;copy;</span> Copyright 2010 by <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;http://domain.invalid/&quot;</span><span class="nt">&gt;</span>you<span class="nt">&lt;/a&gt;</span>.
+ <span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/div&gt;</span>
+<span class="nt">&lt;/body&gt;</span>
+</pre></div>
+</div>
+<p>In this example, the <tt class="docutils literal"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">%}</span></tt> tags define four blocks that child templates
+can fill in. All the <cite>block</cite> tag does is tell the template engine that a
+child template may override those portions of the template.</p>
+</div>
+<div class="section" id="child-template">
+<h2>Child Template<a class="headerlink" href="#child-template" title="Permalink to this headline">¶</a></h2>
+<p>A child template might look like this:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">title</span> <span class="cp">%}</span>Index<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">head</span> <span class="cp">%}</span>
+ <span class="cp">{{</span> <span class="nb">super</span><span class="o">()</span> <span class="cp">}}</span>
+ <span class="nt">&lt;style </span><span class="na">type=</span><span class="s">&quot;text/css&quot;</span><span class="nt">&gt;</span>
+ <span class="nc">.important</span> <span class="p">{</span> <span class="k">color</span><span class="o">:</span> <span class="m">#336699</span><span class="p">;</span> <span class="p">}</span>
+ <span class="nt">&lt;/style&gt;</span>
+<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">content</span> <span class="cp">%}</span>
+ <span class="nt">&lt;h1&gt;</span>Index<span class="nt">&lt;/h1&gt;</span>
+ <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">&quot;important&quot;</span><span class="nt">&gt;</span>
+ Welcome on my awesome homepage.
+<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>The <tt class="docutils literal"><span class="pre">{%</span> <span class="pre">extends</span> <span class="pre">%}</span></tt> tag is the key here. It tells the template engine that
+this template &#8220;extends&#8221; 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 <tt class="docutils literal"><span class="pre">{{</span> <span class="pre">super()</span> <span class="pre">}}</span></tt>.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Template Inheritance</a><ul>
+<li><a class="reference internal" href="#base-template">Base Template</a></li>
+<li><a class="reference internal" href="#child-template">Child Template</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="wtforms.html" title="previous chapter">Form Validation with WTForms</a></li>
+ <li>Next: <a href="flashing.html" title="next chapter">Message Flashing</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/templateinheritance.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/urlprocessors.html b/studio/static/doc/flask-docs/patterns/urlprocessors.html
new file mode 100644
index 0000000..8e50e36
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/urlprocessors.html
@@ -0,0 +1,237 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Using URL Processors &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Deploying with Distribute" href="distribute.html" />
+ <link rel="prev" title="Application Dispatching" href="appdispatch.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="distribute.html" title="Deploying with Distribute"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="appdispatch.html" title="Application Dispatching"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="using-url-processors">
+<h1>Using URL Processors<a class="headerlink" href="#using-url-processors" title="Permalink to this headline">¶</a></h1>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+<p>Flask 0.7 introduces the concept of URL processors. The idea is that you
+might have a bunch of resources with common parts in the URL that you
+don&#8217;t always explicitly want to provide. For instance you might have a
+bunch of URLs that have the language code in it but you don&#8217;t want to have
+to handle it in every single function yourself.</p>
+<p>URL processors are especially helpful when combined with blueprints. We
+will handle both application specific URL processors here as well as
+blueprint specifics.</p>
+<div class="section" id="internationalized-application-urls">
+<h2>Internationalized Application URLs<a class="headerlink" href="#internationalized-application-urls" title="Permalink to this headline">¶</a></h2>
+<p>Consider an application like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">g</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&lt;lang_code&gt;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="n">lang_code</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span> <span class="o">=</span> <span class="n">lang_code</span>
+ <span class="o">...</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&lt;lang_code&gt;/about&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">about</span><span class="p">(</span><span class="n">lang_code</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span> <span class="o">=</span> <span class="n">lang_code</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+<p>This is an awful lot of repetition as you have to handle the language code
+setting on the <a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a> object yourself in every single function.
+Sure, a decorator could be used to simplify this, but if you want to
+generate URLs from one function to another you would have to still provide
+the language code explicitly which can be annoying.</p>
+<p>For the latter, this is where <a class="reference internal" href="../api.html#flask.Flask.url_defaults" title="flask.Flask.url_defaults"><tt class="xref py py-func docutils literal"><span class="pre">url_defaults()</span></tt></a> functions
+come in. They can automatically inject values into a call for
+<a class="reference internal" href="../api.html#flask.url_for" title="flask.url_for"><tt class="xref py py-func docutils literal"><span class="pre">url_for()</span></tt></a> automatically. The code below checks if the
+language code is not yet in the dictionary of URL values and if the
+endpoint wants a value named <tt class="docutils literal"><span class="pre">'lang_code'</span></tt>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.url_defaults</span>
+<span class="k">def</span> <span class="nf">add_language_code</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
+ <span class="k">if</span> <span class="s">&#39;lang_code&#39;</span> <span class="ow">in</span> <span class="n">values</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span><span class="p">:</span>
+ <span class="k">return</span>
+ <span class="k">if</span> <span class="n">app</span><span class="o">.</span><span class="n">url_map</span><span class="o">.</span><span class="n">is_endpoint_expecting</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="s">&#39;lang_code&#39;</span><span class="p">):</span>
+ <span class="n">values</span><span class="p">[</span><span class="s">&#39;lang_code&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span>
+</pre></div>
+</div>
+<p>The method <a class="reference external" href="http://werkzeug.pocoo.org/docs/routing/#werkzeug.routing.Map.is_endpoint_expecting" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">is_endpoint_expecting()</span></tt></a> of the URL
+map can be used to figure out if it would make sense to provide a language
+code for the given endpoint.</p>
+<p>The reverse of that function are
+<a class="reference internal" href="../api.html#flask.Flask.url_value_preprocessor" title="flask.Flask.url_value_preprocessor"><tt class="xref py py-meth docutils literal"><span class="pre">url_value_preprocessor()</span></tt></a>s. They are executed right
+after the request was matched and can execute code based on the URL
+values. The idea is that they pull information out of the values
+dictionary and put it somewhere else:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.url_value_preprocessor</span>
+<span class="k">def</span> <span class="nf">pull_lang_code</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span> <span class="o">=</span> <span class="n">values</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">&#39;lang_code&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>That way you no longer have to do the <cite>lang_code</cite> assigment to
+<a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a> in every function. You can further improve that by
+writing your own decorator that prefixes URLs with the language code, but
+the more beautiful solution is using a blueprint. Once the
+<tt class="docutils literal"><span class="pre">'lang_code'</span></tt> is popped from the values dictionary and it will no longer
+be forwarded to the view function reducing the code to this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">g</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@app.url_defaults</span>
+<span class="k">def</span> <span class="nf">add_language_code</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
+ <span class="k">if</span> <span class="s">&#39;lang_code&#39;</span> <span class="ow">in</span> <span class="n">values</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span><span class="p">:</span>
+ <span class="k">return</span>
+ <span class="k">if</span> <span class="n">app</span><span class="o">.</span><span class="n">url_map</span><span class="o">.</span><span class="n">is_endpoint_expecting</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="s">&#39;lang_code&#39;</span><span class="p">):</span>
+ <span class="n">values</span><span class="p">[</span><span class="s">&#39;lang_code&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span>
+
+<span class="nd">@app.url_value_preprocessor</span>
+<span class="k">def</span> <span class="nf">pull_lang_code</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span> <span class="o">=</span> <span class="n">values</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">&#39;lang_code&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&lt;lang_code&gt;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="o">...</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&lt;lang_code&gt;/about&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">about</span><span class="p">():</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="internationalized-blueprint-urls">
+<h2>Internationalized Blueprint URLs<a class="headerlink" href="#internationalized-blueprint-urls" title="Permalink to this headline">¶</a></h2>
+<p>Because blueprints can automatically prefix all URLs with a common string
+it&#8217;s easy to automatically do that for every function. Furthermore
+blueprints can have per-blueprint URL processors which removes a whole lot
+of logic from the <a class="reference internal" href="../api.html#flask.Flask.url_defaults" title="flask.Flask.url_defaults"><tt class="xref py py-meth docutils literal"><span class="pre">url_defaults()</span></tt></a> function because it no
+longer has to check if the URL is really interested in a <tt class="docutils literal"><span class="pre">'lang_code'</span></tt>
+parameter:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Blueprint</span><span class="p">,</span> <span class="n">g</span>
+
+<span class="n">bp</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">&#39;frontend&#39;</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s">&#39;/&lt;lang_code&gt;&#39;</span><span class="p">)</span>
+
+<span class="nd">@bp.url_defaults</span>
+<span class="k">def</span> <span class="nf">add_language_code</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
+ <span class="n">values</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s">&#39;lang_code&#39;</span><span class="p">,</span> <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span><span class="p">)</span>
+
+<span class="nd">@bp.url_value_preprocessor</span>
+<span class="k">def</span> <span class="nf">pull_lang_code</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">lang_code</span> <span class="o">=</span> <span class="n">values</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">&#39;lang_code&#39;</span><span class="p">)</span>
+
+<span class="nd">@bp.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="o">...</span>
+
+<span class="nd">@bp.route</span><span class="p">(</span><span class="s">&#39;/about&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">about</span><span class="p">():</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Using URL Processors</a><ul>
+<li><a class="reference internal" href="#internationalized-application-urls">Internationalized Application URLs</a></li>
+<li><a class="reference internal" href="#internationalized-blueprint-urls">Internationalized Blueprint URLs</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="appdispatch.html" title="previous chapter">Application Dispatching</a></li>
+ <li>Next: <a href="distribute.html" title="next chapter">Deploying with Distribute</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/urlprocessors.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/viewdecorators.html b/studio/static/doc/flask-docs/patterns/viewdecorators.html
new file mode 100644
index 0000000..9a10276
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/viewdecorators.html
@@ -0,0 +1,274 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>View Decorators &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Form Validation with WTForms" href="wtforms.html" />
+ <link rel="prev" title="Caching" href="caching.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="wtforms.html" title="Form Validation with WTForms"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="caching.html" title="Caching"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="view-decorators">
+<h1>View Decorators<a class="headerlink" href="#view-decorators" title="Permalink to this headline">¶</a></h1>
+<p>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 <a class="reference internal" href="../api.html#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a>
+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, they should be redirected to the
+login page. This is a good example of a use case where a decorator is an
+excellent solution.</p>
+<div class="section" id="login-required-decorator">
+<h2>Login Required Decorator<a class="headerlink" href="#login-required-decorator" title="Permalink to this headline">¶</a></h2>
+<p>So let&#8217;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
+<cite>__name__</cite>, <cite>__module__</cite> and some other attributes of a function. This is
+often forgotten, but you don&#8217;t have to do that by hand, there is a
+function for that that is used like a decorator (<a class="reference external" href="http://docs.python.org/dev/library/functools.html#functools.wraps" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">functools.wraps()</span></tt></a>).</p>
+<p>This example assumes that the login page is called <tt class="docutils literal"><span class="pre">'login'</span></tt> and that
+the current user is stored as <cite>g.user</cite> and <cite>None</cite> if there is no-one
+logged in:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">g</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span>
+
+<span class="k">def</span> <span class="nf">login_required</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
+ <span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
+ <span class="k">def</span> <span class="nf">decorated_function</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">g</span><span class="o">.</span><span class="n">user</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;login&#39;</span><span class="p">,</span> <span class="nb">next</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">url</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">decorated_function</span>
+</pre></div>
+</div>
+<p>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 <a class="reference internal" href="../api.html#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a> decorator is the outermost:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/secret_page&#39;</span><span class="p">)</span>
+<span class="nd">@login_required</span>
+<span class="k">def</span> <span class="nf">secret_page</span><span class="p">():</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="caching-decorator">
+<h2>Caching Decorator<a class="headerlink" href="#caching-decorator" title="Permalink to this headline">¶</a></h2>
+<p>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&#8217;re
+assuming you have set up a cache like mentioned in <a class="reference internal" href="caching.html#caching-pattern"><em>Caching</em></a>.</p>
+<p>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.</p>
+<p>The decorated function will then work as follows</p>
+<ol class="arabic simple">
+<li>get the unique cache key for the current request base on the current
+path.</li>
+<li>get the value for that key from the cache. If the cache returned
+something we will return that value.</li>
+<li>otherwise the original function is called and the return value is
+stored in the cache for the timeout provided (by default 5 minutes).</li>
+</ol>
+<p>Here the code:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+
+<span class="k">def</span> <span class="nf">cached</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">5</span> <span class="o">*</span> <span class="mi">60</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="s">&#39;view/</span><span class="si">%s</span><span class="s">&#39;</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
+ <span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
+ <span class="k">def</span> <span class="nf">decorated_function</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="n">cache_key</span> <span class="o">=</span> <span class="n">key</span> <span class="o">%</span> <span class="n">request</span><span class="o">.</span><span class="n">path</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">cache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">cache_key</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">rv</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">rv</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+ <span class="n">cache</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">cache_key</span><span class="p">,</span> <span class="n">rv</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">rv</span>
+ <span class="k">return</span> <span class="n">decorated_function</span>
+ <span class="k">return</span> <span class="n">decorator</span>
+</pre></div>
+</div>
+<p>Notice that this assumes an instantiated <cite>cache</cite> object is available, see
+<a class="reference internal" href="caching.html#caching-pattern"><em>Caching</em></a> for more information.</p>
+</div>
+<div class="section" id="templating-decorator">
+<h2>Templating Decorator<a class="headerlink" href="#templating-decorator" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;index.html&#39;</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="nd">@templated</span><span class="p">(</span><span class="s">&#39;index.html&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="nd">@templated</span><span class="p">()</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>As you can see, if no template name is provided it will use the endpoint
+of the URL map with dots converted to slashes + <tt class="docutils literal"><span class="pre">'.html'</span></tt>. Otherwise
+the provided template name is used. When the decorated function returns,
+the dictionary returned is passed to the template rendering function. If
+<cite>None</cite> 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.</p>
+<p>Here the code for that decorator:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+
+<span class="k">def</span> <span class="nf">templated</span><span class="p">(</span><span class="n">template</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
+ <span class="nd">@wraps</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
+ <span class="k">def</span> <span class="nf">decorated_function</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="n">template</span>
+ <span class="k">if</span> <span class="n">template_name</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">template_name</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">endpoint</span> \
+ <span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s">&#39;.&#39;</span><span class="p">,</span> <span class="s">&#39;/&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="s">&#39;.html&#39;</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">ctx</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="n">ctx</span> <span class="o">=</span> <span class="p">{}</span>
+ <span class="k">elif</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nb">dict</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">ctx</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="n">template_name</span><span class="p">,</span> <span class="o">**</span><span class="n">ctx</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">decorated_function</span>
+ <span class="k">return</span> <span class="n">decorator</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="endpoint-decorator">
+<h2>Endpoint Decorator<a class="headerlink" href="#endpoint-decorator" title="Permalink to this headline">¶</a></h2>
+<p>When you want to use the werkzeug routing system for more flexibility you
+need to map the endpoint as defined in the <a class="reference external" href="http://werkzeug.pocoo.org/docs/routing/#werkzeug.routing.Rule" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Rule</span></tt></a>
+to a view function. This is possible with this decorator. For example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="kn">from</span> <span class="nn">werkzeug.routing</span> <span class="kn">import</span> <span class="n">Rule</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">url_map</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">Rule</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">,</span> <span class="n">endpoint</span><span class="o">=</span><span class="s">&#39;index&#39;</span><span class="p">))</span>
+
+<span class="nd">@app.endpoint</span><span class="p">(</span><span class="s">&#39;index&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">my_index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&quot;Hello world&quot;</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">View Decorators</a><ul>
+<li><a class="reference internal" href="#login-required-decorator">Login Required Decorator</a></li>
+<li><a class="reference internal" href="#caching-decorator">Caching Decorator</a></li>
+<li><a class="reference internal" href="#templating-decorator">Templating Decorator</a></li>
+<li><a class="reference internal" href="#endpoint-decorator">Endpoint Decorator</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="caching.html" title="previous chapter">Caching</a></li>
+ <li>Next: <a href="wtforms.html" title="next chapter">Form Validation with WTForms</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/viewdecorators.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/patterns/wtforms.html b/studio/static/doc/flask-docs/patterns/wtforms.html
new file mode 100644
index 0000000..f998f2e
--- /dev/null
+++ b/studio/static/doc/flask-docs/patterns/wtforms.html
@@ -0,0 +1,224 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Form Validation with WTForms &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Patterns for Flask" href="index.html" />
+ <link rel="next" title="Template Inheritance" href="templateinheritance.html" />
+ <link rel="prev" title="View Decorators" href="viewdecorators.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="templateinheritance.html" title="Template Inheritance"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="viewdecorators.html" title="View Decorators"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Patterns for Flask</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="form-validation-with-wtforms">
+<h1>Form Validation with WTForms<a class="headerlink" href="#form-validation-with-wtforms" title="Permalink to this headline">¶</a></h1>
+<p>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 <a class="reference external" href="http://wtforms.simplecodes.com/">WTForms</a> which we
+will handle here. If you find yourself in the situation of having many
+forms, you might want to give it a try.</p>
+<p>When you are working with WTForms you have to define your forms as classes
+first. I recommend breaking up the application into multiple modules
+(<a class="reference internal" href="packages.html#larger-applications"><em>Larger Applications</em></a>) for that and adding a separate module for the
+forms.</p>
+<div class="admonition-getting-most-of-wtforms-with-an-extension admonition ">
+<p class="first admonition-title">Getting most of WTForms with an Extension</p>
+<p class="last">The <a class="reference external" href="http://packages.python.org/Flask-WTF/">Flask-WTF</a> 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 <a class="reference external" href="http://pypi.python.org/pypi/Flask-WTF">PyPI</a>.</p>
+</div>
+<div class="section" id="the-forms">
+<h2>The Forms<a class="headerlink" href="#the-forms" title="Permalink to this headline">¶</a></h2>
+<p>This is an example form for a typical registration page:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">wtforms</span> <span class="kn">import</span> <span class="n">Form</span><span class="p">,</span> <span class="n">BooleanField</span><span class="p">,</span> <span class="n">TextField</span><span class="p">,</span> <span class="n">validators</span>
+
+<span class="k">class</span> <span class="nc">RegistrationForm</span><span class="p">(</span><span class="n">Form</span><span class="p">):</span>
+ <span class="n">username</span> <span class="o">=</span> <span class="n">TextField</span><span class="p">(</span><span class="s">&#39;Username&#39;</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">Length</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="mi">25</span><span class="p">)])</span>
+ <span class="n">email</span> <span class="o">=</span> <span class="n">TextField</span><span class="p">(</span><span class="s">&#39;Email Address&#39;</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">Length</span><span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span> <span class="nb">max</span><span class="o">=</span><span class="mi">35</span><span class="p">)])</span>
+ <span class="n">password</span> <span class="o">=</span> <span class="n">PasswordField</span><span class="p">(</span><span class="s">&#39;New Password&#39;</span><span class="p">,</span> <span class="p">[</span>
+ <span class="n">validators</span><span class="o">.</span><span class="n">Required</span><span class="p">(),</span>
+ <span class="n">validators</span><span class="o">.</span><span class="n">EqualTo</span><span class="p">(</span><span class="s">&#39;confirm&#39;</span><span class="p">,</span> <span class="n">message</span><span class="o">=</span><span class="s">&#39;Passwords must match&#39;</span><span class="p">)</span>
+ <span class="p">])</span>
+ <span class="n">confirm</span> <span class="o">=</span> <span class="n">PasswordField</span><span class="p">(</span><span class="s">&#39;Repeat Password&#39;</span><span class="p">)</span>
+ <span class="n">accept_tos</span> <span class="o">=</span> <span class="n">BooleanField</span><span class="p">(</span><span class="s">&#39;I accept the TOS&#39;</span><span class="p">,</span> <span class="p">[</span><span class="n">validators</span><span class="o">.</span><span class="n">Required</span><span class="p">()])</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="in-the-view">
+<h2>In the View<a class="headerlink" href="#in-the-view" title="Permalink to this headline">¶</a></h2>
+<p>In the view function, the usage of this form looks like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/register&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">register</span><span class="p">():</span>
+ <span class="n">form</span> <span class="o">=</span> <span class="n">RegistrationForm</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span> <span class="ow">and</span> <span class="n">form</span><span class="o">.</span><span class="n">validate</span><span class="p">():</span>
+ <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="n">form</span><span class="o">.</span><span class="n">username</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">form</span><span class="o">.</span><span class="n">email</span><span class="o">.</span><span class="n">data</span><span class="p">,</span>
+ <span class="n">form</span><span class="o">.</span><span class="n">password</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
+ <span class="n">db_session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
+ <span class="n">flash</span><span class="p">(</span><span class="s">&#39;Thanks for registering&#39;</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;login&#39;</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;register.html&#39;</span><span class="p">,</span> <span class="n">form</span><span class="o">=</span><span class="n">form</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Notice that we are implying that the view is using SQLAlchemy here
+(<a class="reference internal" href="sqlalchemy.html#sqlalchemy-pattern"><em>SQLAlchemy in Flask</em></a>) but this is no requirement of course. Adapt
+the code as necessary.</p>
+<p>Things to remember:</p>
+<ol class="arabic simple">
+<li>create the form from the request <tt class="xref py py-attr docutils literal"><span class="pre">form</span></tt> value if
+the data is submitted via the HTTP <cite>POST</cite> method and
+<tt class="xref py py-attr docutils literal"><span class="pre">args</span></tt> if the data is submitted as <cite>GET</cite>.</li>
+<li>to validate the data, call the <tt class="xref py py-func docutils literal"><span class="pre">validate()</span></tt>
+method which will return <cite>True</cite> if the data validates, <cite>False</cite>
+otherwise.</li>
+<li>to access individual values from the form, access <cite>form.&lt;NAME&gt;.data</cite>.</li>
+</ol>
+</div>
+<div class="section" id="forms-in-templates">
+<h2>Forms in Templates<a class="headerlink" href="#forms-in-templates" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>Here&#8217;s an example <cite>_formhelpers.html</cite> template with such a macro:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">macro</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">field</span><span class="o">)</span> <span class="cp">%}</span>
+ <span class="nt">&lt;dt&gt;</span><span class="cp">{{</span> <span class="nv">field.label</span> <span class="cp">}}</span>
+ <span class="nt">&lt;dd&gt;</span><span class="cp">{{</span> <span class="nv">field</span><span class="o">(**</span><span class="nv">kwargs</span><span class="o">)|</span><span class="nf">safe</span> <span class="cp">}}</span>
+ <span class="cp">{%</span> <span class="k">if</span> <span class="nv">field.errors</span> <span class="cp">%}</span>
+ <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">&quot;errors&quot;</span><span class="nt">&gt;</span>
+ <span class="cp">{%</span> <span class="k">for</span> <span class="nv">error</span> <span class="k">in</span> <span class="nv">field.errors</span> <span class="cp">%}</span><span class="nt">&lt;li&gt;</span><span class="cp">{{</span> <span class="nv">error</span> <span class="cp">}}{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/ul&gt;</span>
+ <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/dd&gt;</span>
+<span class="cp">{%</span> <span class="k">endmacro</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>This macro accepts a couple of keyword arguments that are forwarded to
+WTForm&#8217;s field function that renders the field for us. The keyword
+arguments will be inserted as HTML attributes. So for example you can
+call <tt class="docutils literal"><span class="pre">render_field(form.username,</span> <span class="pre">class='username')</span></tt> 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 <cite>|safe</cite> filter.</p>
+<p>Here the <cite>register.html</cite> template for the function we used above which
+takes advantage of the <cite>_formhelpers.html</cite> template:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">from</span> <span class="s2">&quot;_formhelpers.html&quot;</span> <span class="k">import</span> <span class="nv">render_field</span> <span class="cp">%}</span>
+<span class="nt">&lt;form</span> <span class="na">method=</span><span class="s">&quot;post&quot;</span> <span class="na">action=</span><span class="s">&quot;/register&quot;</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dl&gt;</span>
+ <span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.username</span><span class="o">)</span> <span class="cp">}}</span>
+ <span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.email</span><span class="o">)</span> <span class="cp">}}</span>
+ <span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.password</span><span class="o">)</span> <span class="cp">}}</span>
+ <span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.confirm</span><span class="o">)</span> <span class="cp">}}</span>
+ <span class="cp">{{</span> <span class="nv">render_field</span><span class="o">(</span><span class="nv">form.accept_tos</span><span class="o">)</span> <span class="cp">}}</span>
+ <span class="nt">&lt;/dl&gt;</span>
+ <span class="nt">&lt;p&gt;&lt;input</span> <span class="na">type=</span><span class="s">submit</span> <span class="na">value=</span><span class="s">Register</span><span class="nt">&gt;</span>
+<span class="nt">&lt;/form&gt;</span>
+</pre></div>
+</div>
+<p>For more information about WTForms, head over to the <a class="reference external" href="http://wtforms.simplecodes.com/">WTForms
+website</a>.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Form Validation with WTForms</a><ul>
+<li><a class="reference internal" href="#the-forms">The Forms</a></li>
+<li><a class="reference internal" href="#in-the-view">In the View</a></li>
+<li><a class="reference internal" href="#forms-in-templates">Forms in Templates</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Patterns for Flask</a><ul>
+ <li>Previous: <a href="viewdecorators.html" title="previous chapter">View Decorators</a></li>
+ <li>Next: <a href="templateinheritance.html" title="next chapter">Template Inheritance</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/patterns/wtforms.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/quickstart.html b/studio/static/doc/flask-docs/quickstart.html
new file mode 100644
index 0000000..4a26798
--- /dev/null
+++ b/studio/static/doc/flask-docs/quickstart.html
@@ -0,0 +1,875 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Quickstart &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Tutorial" href="tutorial/index.html" />
+ <link rel="prev" title="Installation" href="installation.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="tutorial/index.html" title="Tutorial"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="installation.html" title="Installation"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="quickstart">
+<span id="id1"></span><h1>Quickstart<a class="headerlink" href="#quickstart" title="Permalink to this headline">¶</a></h1>
+<p>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 <a class="reference internal" href="installation.html#installation"><em>Installation</em></a> section.</p>
+<div class="section" id="a-minimal-application">
+<h2>A Minimal Application<a class="headerlink" href="#a-minimal-application" title="Permalink to this headline">¶</a></h2>
+<p>A minimal Flask application looks something like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">hello_world</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&#39;Hello World!&#39;</span>
+
+<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Just save it as <cite>hello.py</cite> or something similar and run it with your
+Python interpreter. Make sure to not call your application <cite>flask.py</cite>
+because this would conflict with Flask itself.</p>
+<div class="highlight-python"><pre>$ python hello.py
+ * Running on http://127.0.0.1:5000/</pre>
+</div>
+<p>Head over to <a class="reference external" href="http://127.0.0.1:5000/">http://127.0.0.1:5000/</a>, you should
+see your hello world greeting.</p>
+<p>So what did that code do?</p>
+<ol class="arabic simple">
+<li>First we imported the <a class="reference internal" href="api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> class. An instance of this
+class will be our WSGI application. The first argument is the name of
+the application&#8217;s module. If you are using a single module (like here)
+you should use <cite>__name__</cite> because depending on if it&#8217;s started as
+application or imported as module the name will be different
+(<tt class="docutils literal"><span class="pre">'__main__'</span></tt> versus the actual import name). For more information
+on that, have a look at the <a class="reference internal" href="api.html#flask.Flask" title="flask.Flask"><tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt></a> documentation.</li>
+<li>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.</li>
+<li>Then we use the <a class="reference internal" href="api.html#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a> decorator to tell Flask
+what URL should trigger our function.</li>
+<li>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&#8217;s browser.</li>
+<li>Finally we use the <a class="reference internal" href="api.html#flask.Flask.run" title="flask.Flask.run"><tt class="xref py py-meth docutils literal"><span class="pre">run()</span></tt></a> function to run the
+local server with our application. The <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__':</span></tt>
+makes sure the server only runs if the script is executed directly from
+the Python interpreter and not used as imported module.</li>
+</ol>
+<p>To stop the server, hit control-C.</p>
+<div class="admonition-externally-visible-server admonition " id="public-server">
+<p class="first admonition-title">Externally Visible Server</p>
+<p>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 <cite>debug</cite> disabled
+or trust the users on your network, you can make the server publicly
+available.</p>
+<p>Just change the call of the <a class="reference internal" href="api.html#flask.Flask.run" title="flask.Flask.run"><tt class="xref py py-meth docutils literal"><span class="pre">run()</span></tt></a> method to look
+like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s">&#39;0.0.0.0&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p class="last">This tells your operating system to listen on a public IP.</p>
+</div>
+</div>
+<div class="section" id="debug-mode">
+<h2>Debug Mode<a class="headerlink" href="#debug-mode" title="Permalink to this headline">¶</a></h2>
+<p>The <a class="reference internal" href="api.html#flask.Flask.run" title="flask.Flask.run"><tt class="xref py py-meth docutils literal"><span class="pre">run()</span></tt></a> 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.</p>
+<p>There are two ways to enable debugging. Either set that flag on the
+application object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">debug</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Or pass it to run:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Both will have exactly the same effect.</p>
+<div class="admonition-attention admonition ">
+<p class="first admonition-title">Attention</p>
+<p class="last">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 <strong>must never be used on production machines</strong>.</p>
+</div>
+<p>Screenshot of the debugger in action:</p>
+<img alt="screenshot of debugger in action" class="screenshot align-center" src="_images/debugger5.png" />
+<div class="admonition-working-with-other-debuggers admonition ">
+<p class="first admonition-title">Working With Other Debuggers</p>
+<p class="last">Debuggers interfere with each other. If you are using another debugger
+(e.g. PyDev or IntelliJ), you may need to set <tt class="docutils literal"><span class="pre">app.debug</span> <span class="pre">=</span> <span class="pre">False</span></tt>.</p>
+</div>
+</div>
+<div class="section" id="routing">
+<h2>Routing<a class="headerlink" href="#routing" title="Permalink to this headline">¶</a></h2>
+<p>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 they will like the page and come back next time.</p>
+<p>As you have seen above, the <a class="reference internal" href="api.html#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a> decorator is used
+to bind a function to a URL. Here are some basic examples:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&#39;Index Page&#39;</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/hello&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">hello</span><span class="p">():</span>
+ <span class="k">return</span> <span class="s">&#39;Hello World&#39;</span>
+</pre></div>
+</div>
+<p>But there is more to it! You can make certain parts of the URL dynamic
+and attach multiple rules to a function.</p>
+<div class="section" id="variable-rules">
+<h3>Variable Rules<a class="headerlink" href="#variable-rules" title="Permalink to this headline">¶</a></h3>
+<p>To add variable parts to a URL you can mark these special sections as
+<tt class="docutils literal"><span class="pre">&lt;variable_name&gt;</span></tt>. Such a part is then passed as keyword argument to
+your function. Optionally a converter can be specified by specifying a
+rule with <tt class="docutils literal"><span class="pre">&lt;converter:variable_name&gt;</span></tt>. Here are some nice examples:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/user/&lt;username&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_user_profile</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
+ <span class="c"># show the user profile for that user</span>
+ <span class="k">pass</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/post/&lt;int:post_id&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_post</span><span class="p">(</span><span class="n">post_id</span><span class="p">):</span>
+ <span class="c"># show the post with the given id, the id is an integer</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>The following converters exist:</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="20%" />
+<col width="80%" />
+</colgroup>
+<tbody valign="top">
+<tr class="row-odd"><td><cite>int</cite></td>
+<td>accepts integers</td>
+</tr>
+<tr class="row-even"><td><cite>float</cite></td>
+<td>like <cite>int</cite> but for floating point values</td>
+</tr>
+<tr class="row-odd"><td><cite>path</cite></td>
+<td>like the default but also accepts slashes</td>
+</tr>
+</tbody>
+</table>
+<div class="admonition-unique-urls-redirection-behaviour admonition ">
+<p class="first admonition-title">Unique URLs / Redirection Behaviour</p>
+<p>Flask&#8217;s URL rules are based on Werkzeug&#8217;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.</p>
+<p>Take these two rules:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/projects/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">projects</span><span class="p">():</span>
+ <span class="k">pass</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/about&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">about</span><span class="p">():</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>They look rather similar, the difference is the trailing slash in the
+URL <em>definition</em>. In the first case, the canonical URL for the
+<cite>projects</cite> endpoint has a trailing slash. It&#8217;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.</p>
+<p>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.</p>
+<p class="last">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.</p>
+</div>
+</div>
+<div class="section" id="url-building">
+<span id="id2"></span><h3>URL Building<a class="headerlink" href="#url-building" title="Permalink to this headline">¶</a></h3>
+<p>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 <a class="reference internal" href="api.html#flask.url_for" title="flask.url_for"><tt class="xref py py-func docutils literal"><span class="pre">url_for()</span></tt></a>
+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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">url_for</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="gp">... </span><span class="k">def</span> <span class="nf">index</span><span class="p">():</span> <span class="k">pass</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">)</span>
+<span class="gp">... </span><span class="k">def</span> <span class="nf">login</span><span class="p">():</span> <span class="k">pass</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/user/&lt;username&gt;&#39;</span><span class="p">)</span>
+<span class="gp">... </span><span class="k">def</span> <span class="nf">profile</span><span class="p">(</span><span class="n">username</span><span class="p">):</span> <span class="k">pass</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">():</span>
+<span class="gp">... </span> <span class="k">print</span> <span class="n">url_for</span><span class="p">(</span><span class="s">&#39;index&#39;</span><span class="p">)</span>
+<span class="gp">... </span> <span class="k">print</span> <span class="n">url_for</span><span class="p">(</span><span class="s">&#39;login&#39;</span><span class="p">)</span>
+<span class="gp">... </span> <span class="k">print</span> <span class="n">url_for</span><span class="p">(</span><span class="s">&#39;login&#39;</span><span class="p">,</span> <span class="nb">next</span><span class="o">=</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="gp">... </span> <span class="k">print</span> <span class="n">url_for</span><span class="p">(</span><span class="s">&#39;profile&#39;</span><span class="p">,</span> <span class="n">username</span><span class="o">=</span><span class="s">&#39;John Doe&#39;</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="go">/</span>
+<span class="go">/login</span>
+<span class="go">/login?next=/</span>
+<span class="go">/user/John%20Doe</span>
+</pre></div>
+</div>
+<p>(This also uses the <a class="reference internal" href="api.html#flask.Flask.test_request_context" title="flask.Flask.test_request_context"><tt class="xref py py-meth docutils literal"><span class="pre">test_request_context()</span></tt></a> 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. <a class="reference internal" href="#context-locals"><em>Context Locals</em></a>).</p>
+<p>Why would you want to build URLs instead of hardcoding them in your
+templates? There are three good reasons for this:</p>
+<ol class="arabic simple">
+<li>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.</li>
+<li>URL building will handle escaping of special characters and Unicode
+data transparently for you, you don&#8217;t have to deal with that.</li>
+<li>If your application is placed outside the URL root (so say in
+<tt class="docutils literal"><span class="pre">/myapplication</span></tt> instead of <tt class="docutils literal"><span class="pre">/</span></tt>), <a class="reference internal" href="api.html#flask.url_for" title="flask.url_for"><tt class="xref py py-func docutils literal"><span class="pre">url_for()</span></tt></a> will
+handle that properly for you.</li>
+</ol>
+</div>
+<div class="section" id="http-methods">
+<h3>HTTP Methods<a class="headerlink" href="#http-methods" title="Permalink to this headline">¶</a></h3>
+<p>HTTP (the protocol web applications are speaking) knows different methods
+to access URLs. By default a route only answers to <cite>GET</cite> requests, but
+that can be changed by providing the <cite>methods</cite> argument to the
+<a class="reference internal" href="api.html#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-meth docutils literal"><span class="pre">route()</span></tt></a> decorator. Here are some examples:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="n">do_the_login</span><span class="p">()</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">show_the_login_form</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>If <cite>GET</cite> is present, <cite>HEAD</cite> will be added automatically for you. You
+don&#8217;t have to deal with that. It will also make sure that <cite>HEAD</cite> requests
+are handled like the <a class="reference external" href="http://www.ietf.org/rfc/rfc2068.txt">HTTP RFC</a> (the document describing the HTTP
+protocol) demands, so you can completely ignore that part of the HTTP
+specification. Likewise as of Flask 0.6, <cite>OPTIONS</cite> is implemented for you
+as well automatically.</p>
+<p>You have no idea what an HTTP method is? Worry not, here is a quick
+introduction to HTTP methods and why they matter:</p>
+<p>The HTTP method (also often called &#8220;the verb&#8221;) tells the server what the
+clients wants to <em>do</em> with the requested page. The following methods are
+very common:</p>
+<dl class="docutils">
+<dt><cite>GET</cite></dt>
+<dd>The browser tells the server to just <em>get</em> the information stored on
+that page and send it. This is probably the most common method.</dd>
+<dt><cite>HEAD</cite></dt>
+<dd>The browser tells the server to get the information, but it is only
+interested in the <em>headers</em>, not the content of the page. An
+application is supposed to handle that as if a <cite>GET</cite> request was
+received but to not deliver the actual content. In Flask you don&#8217;t
+have to deal with that at all, the underlying Werkzeug library handles
+that for you.</dd>
+<dt><cite>POST</cite></dt>
+<dd>The browser tells the server that it wants to <em>post</em> 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.</dd>
+<dt><cite>PUT</cite></dt>
+<dd>Similar to <cite>POST</cite> 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 <cite>POST</cite> that would not be possible because it must only
+be triggered once.</dd>
+<dt><cite>DELETE</cite></dt>
+<dd>Remove the information at the given location.</dd>
+<dt><cite>OPTIONS</cite></dt>
+<dd>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.</dd>
+</dl>
+<p>Now the interesting part is that in HTML4 and XHTML1, the only methods a
+form can submit to the server are <cite>GET</cite> and <cite>POST</cite>. 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.</p>
+</div>
+</div>
+<div class="section" id="static-files">
+<h2>Static Files<a class="headerlink" href="#static-files" title="Permalink to this headline">¶</a></h2>
+<p>Dynamic web applications need static files as well. That&#8217;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 <cite>static</cite> in your package or next to
+your module and it will be available at <cite>/static</cite> on the application.</p>
+<p>To generate URLs to that part of the URL, use the special <tt class="docutils literal"><span class="pre">'static'</span></tt> URL
+name:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;static&#39;</span><span class="p">,</span> <span class="n">filename</span><span class="o">=</span><span class="s">&#39;style.css&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The file has to be stored on the filesystem as <tt class="docutils literal"><span class="pre">static/style.css</span></tt>.</p>
+</div>
+<div class="section" id="rendering-templates">
+<h2>Rendering Templates<a class="headerlink" href="#rendering-templates" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference external" href="http://jinja.pocoo.org/2/">Jinja2</a> template engine for you automatically.</p>
+<p>To render a template you can use the <a class="reference internal" href="api.html#flask.render_template" title="flask.render_template"><tt class="xref py py-func docutils literal"><span class="pre">render_template()</span></tt></a>
+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&#8217;s a simple example of how to render a template:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">render_template</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/hello/&#39;</span><span class="p">)</span>
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/hello/&lt;name&gt;&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;hello.html&#39;</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="n">name</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Flask will look for templates in the <cite>templates</cite> folder. So if your
+application is a module, that folder is next to that module, if it&#8217;s a
+package it&#8217;s actually inside your package:</p>
+<p><strong>Case 1</strong>: a module:</p>
+<div class="highlight-python"><pre>/application.py
+/templates
+ /hello.html</pre>
+</div>
+<p><strong>Case 2</strong>: a package:</p>
+<div class="highlight-python"><pre>/application
+ /__init__.py
+ /templates
+ /hello.html</pre>
+</div>
+<p>For templates you can use the full power of Jinja2 templates. Head over
+to the the official <a class="reference external" href="http://jinja.pocoo.org/2/documentation/templates">Jinja2 Template Documentation</a> for more information.</p>
+<p>Here is an example template:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">&lt;!doctype html&gt;</span>
+<span class="nt">&lt;title&gt;</span>Hello from Flask<span class="nt">&lt;/title&gt;</span>
+<span class="cp">{%</span> <span class="k">if</span> <span class="nv">name</span> <span class="cp">%}</span>
+ <span class="nt">&lt;h1&gt;</span>Hello <span class="cp">{{</span> <span class="nv">name</span> <span class="cp">}}</span>!<span class="nt">&lt;/h1&gt;</span>
+<span class="cp">{%</span> <span class="k">else</span> <span class="cp">%}</span>
+ <span class="nt">&lt;h1&gt;</span>Hello World!<span class="nt">&lt;/h1&gt;</span>
+<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>Inside templates you also have access to the <a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a>,
+<a class="reference internal" href="api.html#flask.session" title="flask.session"><tt class="xref py py-class docutils literal"><span class="pre">session</span></tt></a> and <a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-class docutils literal"><span class="pre">g</span></tt></a> <a class="footnote-reference" href="#id4" id="id3">[1]</a> objects
+as well as the <a class="reference internal" href="api.html#flask.get_flashed_messages" title="flask.get_flashed_messages"><tt class="xref py py-func docutils literal"><span class="pre">get_flashed_messages()</span></tt></a> function.</p>
+<p>Templates are especially useful if inheritance is used. If you want to
+know how that works, head over to the <a class="reference internal" href="patterns/templateinheritance.html#template-inheritance"><em>Template Inheritance</em></a> pattern
+documentation. Basically template inheritance makes it possible to keep
+certain elements on each page (like header, navigation and footer).</p>
+<p>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
+<tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt> class or by using the <tt class="docutils literal"><span class="pre">|safe</span></tt> filter in the
+template. Head over to the Jinja 2 documentation for more examples.</p>
+<p>Here is a basic introduction to how the <tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt> class works:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Markup</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="p">(</span><span class="s">&#39;&lt;strong&gt;Hello </span><span class="si">%s</span><span class="s">!&lt;/strong&gt;&#39;</span><span class="p">)</span> <span class="o">%</span> <span class="s">&#39;&lt;blink&gt;hacker&lt;/blink&gt;&#39;</span>
+<span class="go">Markup(u&#39;&lt;strong&gt;Hello &amp;lt;blink&amp;gt;hacker&amp;lt;/blink&amp;gt;!&lt;/strong&gt;&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="s">&#39;&lt;blink&gt;hacker&lt;/blink&gt;&#39;</span><span class="p">)</span>
+<span class="go">Markup(u&#39;&amp;lt;blink&amp;gt;hacker&amp;lt;/blink&amp;gt;&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">Markup</span><span class="p">(</span><span class="s">&#39;&lt;em&gt;Marked up&lt;/em&gt; &amp;raquo; HTML&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">striptags</span><span class="p">()</span>
+<span class="go">u&#39;Marked up \xbb HTML&#39;</span>
+</pre></div>
+</div>
+<p class="versionchanged">
+<span class="versionmodified">Changed in version 0.5.</span></p>
+<table class="docutils footnote" frame="void" id="id4" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3">[1]</a></td><td>Unsure what that <a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-class docutils literal"><span class="pre">g</span></tt></a> object is? It&#8217;s something in which
+you can store information for your own needs, check the documentation of
+that object (<a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-class docutils literal"><span class="pre">g</span></tt></a>) and the <a class="reference internal" href="patterns/sqlite3.html#sqlite3"><em>Using SQLite 3 with Flask</em></a> for more
+information.</td></tr>
+</tbody>
+</table>
+</div>
+<div class="section" id="accessing-request-data">
+<h2>Accessing Request Data<a class="headerlink" href="#accessing-request-data" title="Permalink to this headline">¶</a></h2>
+<p>For web applications it&#8217;s crucial to react to the data a client sent to
+the server. In Flask this information is provided by the global
+<a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a> 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:</p>
+<div class="section" id="context-locals">
+<span id="id5"></span><h3>Context Locals<a class="headerlink" href="#context-locals" title="Permalink to this headline">¶</a></h3>
+<div class="admonition-insider-information admonition ">
+<p class="first admonition-title">Insider Information</p>
+<p class="last">If you want to understand how that works and how you can implement
+tests with context locals, read this section, otherwise just skip it.</p>
+</div>
+<p>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.</p>
+<p>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.</p>
+<p>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 <a class="reference internal" href="api.html#flask.Flask.test_request_context" title="flask.Flask.test_request_context"><tt class="xref py py-meth docutils literal"><span class="pre">test_request_context()</span></tt></a>
+context manager. In combination with the <cite>with</cite> statement it will bind a
+test request so that you can interact with it. Here is an example:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+
+<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">(</span><span class="s">&#39;/hello&#39;</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s">&#39;POST&#39;</span><span class="p">):</span>
+ <span class="c"># now you can do something with the request until the</span>
+ <span class="c"># end of the with block, such as basic assertions:</span>
+ <span class="k">assert</span> <span class="n">request</span><span class="o">.</span><span class="n">path</span> <span class="o">==</span> <span class="s">&#39;/hello&#39;</span>
+ <span class="k">assert</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span>
+</pre></div>
+</div>
+<p>The other possibility is passing a whole WSGI environment to the
+<a class="reference internal" href="api.html#flask.Flask.request_context" title="flask.Flask.request_context"><tt class="xref py py-meth docutils literal"><span class="pre">request_context()</span></tt></a> method:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+
+<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">request_context</span><span class="p">(</span><span class="n">environ</span><span class="p">):</span>
+ <span class="k">assert</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="the-request-object">
+<h3>The Request Object<a class="headerlink" href="#the-request-object" title="Permalink to this headline">¶</a></h3>
+<p>The request object is documented in the API section and we will not cover
+it here in detail (see <a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a>). Here is a broad overview of
+some of the most common operations. First of all you have to import it from
+the <cite>flask</cite> module:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+</pre></div>
+</div>
+<p>The current request method is available by using the
+<tt class="xref py py-attr docutils literal"><span class="pre">method</span></tt> attribute. To access form data (data
+transmitted in a <cite>POST</cite> or <cite>PUT</cite> request) you can use the
+<tt class="xref py py-attr docutils literal"><span class="pre">form</span></tt> attribute. Here is a full example of the two
+attributes mentioned above:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;POST&#39;</span><span class="p">,</span> <span class="s">&#39;GET&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
+ <span class="n">error</span> <span class="o">=</span> <span class="bp">None</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="k">if</span> <span class="n">valid_login</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">],</span>
+ <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;password&#39;</span><span class="p">]):</span>
+ <span class="k">return</span> <span class="n">log_the_user_in</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">])</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">error</span> <span class="o">=</span> <span class="s">&#39;Invalid username/password&#39;</span>
+ <span class="c"># this is executed if the request method was GET or the</span>
+ <span class="c"># credentials were invalid</span>
+</pre></div>
+</div>
+<p>What happens if the key does not exist in the <cite>form</cite> attribute? In that
+case a special <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#KeyError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">KeyError</span></tt></a> is raised. You can catch it like a
+standard <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#KeyError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">KeyError</span></tt></a> but if you don&#8217;t do that, a HTTP 400 Bad Request
+error page is shown instead. So for many situations you don&#8217;t have to
+deal with that problem.</p>
+<p>To access parameters submitted in the URL (<tt class="docutils literal"><span class="pre">?key=value</span></tt>) you can use the
+<tt class="xref py py-attr docutils literal"><span class="pre">args</span></tt> attribute:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">searchword</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;q&#39;</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>We recommend accessing URL parameters with <cite>get</cite> or by catching the
+<cite>KeyError</cite> because users might change the URL and presenting them a 400
+bad request page in that case is not user friendly.</p>
+<p>For a full list of methods and attributes of the request object, head over
+to the <a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a> documentation.</p>
+</div>
+<div class="section" id="file-uploads">
+<h3>File Uploads<a class="headerlink" href="#file-uploads" title="Permalink to this headline">¶</a></h3>
+<p>You can handle uploaded files with Flask easily. Just make sure not to
+forget to set the <tt class="docutils literal"><span class="pre">enctype=&quot;multipart/form-data&quot;</span></tt> attribute on your HTML
+form, otherwise the browser will not transmit your files at all.</p>
+<p>Uploaded files are stored in memory or at a temporary location on the
+filesystem. You can access those files by looking at the
+<tt class="xref py py-attr docutils literal"><span class="pre">files</span></tt> attribute on the request object. Each
+uploaded file is stored in that dictionary. It behaves just like a
+standard Python <tt class="xref py py-class docutils literal"><span class="pre">file</span></tt> object, but it also has a
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.save" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">save()</span></tt></a> method that allows you to store that
+file on the filesystem of the server. Here is a simple example showing how
+that works:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/upload&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">upload_file</span><span class="p">():</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="n">f</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">files</span><span class="p">[</span><span class="s">&#39;the_file&#39;</span><span class="p">]</span>
+ <span class="n">f</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s">&#39;/var/www/uploads/uploaded_file.txt&#39;</span><span class="p">)</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+<p>If you want to know how the file was named on the client before it was
+uploaded to your application, you can access the
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.filename" title="(in Werkzeug v0.7)"><tt class="xref py py-attr docutils literal"><span class="pre">filename</span></tt></a> 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 <a class="reference external" href="http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename" title="(in Werkzeug v0.7)"><tt class="xref py py-func docutils literal"><span class="pre">secure_filename()</span></tt></a> function that
+Werkzeug provides for you:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+<span class="kn">from</span> <span class="nn">werkzeug</span> <span class="kn">import</span> <span class="n">secure_filename</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/upload&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">upload_file</span><span class="p">():</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="n">f</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">files</span><span class="p">[</span><span class="s">&#39;the_file&#39;</span><span class="p">]</span>
+ <span class="n">f</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s">&#39;/var/www/uploads/&#39;</span> <span class="o">+</span> <span class="n">secure_filename</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">filename</span><span class="p">))</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+<p>For some better examples, checkout the <a class="reference internal" href="patterns/fileuploads.html#uploading-files"><em>Uploading Files</em></a> pattern.</p>
+</div>
+<div class="section" id="cookies">
+<h3>Cookies<a class="headerlink" href="#cookies" title="Permalink to this headline">¶</a></h3>
+<p>To access cookies you can use the <a class="reference internal" href="api.html#flask.Request.cookies" title="flask.Request.cookies"><tt class="xref py py-attr docutils literal"><span class="pre">cookies</span></tt></a>
+attribute. To set cookies you can use the
+<a class="reference internal" href="api.html#flask.Response.set_cookie" title="flask.Response.set_cookie"><tt class="xref py py-attr docutils literal"><span class="pre">set_cookie</span></tt></a> method of response objects. The
+<a class="reference internal" href="api.html#flask.Request.cookies" title="flask.Request.cookies"><tt class="xref py py-attr docutils literal"><span class="pre">cookies</span></tt></a> attribute of request objects 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
+<a class="reference internal" href="#sessions"><em>Sessions</em></a> in Flask that add some security on top of cookies for you.</p>
+<p>Reading cookies:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="n">username</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">cookies</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;username&#39;</span><span class="p">)</span>
+ <span class="c"># use cookies.get(key) instead of cookies[key] to not get a</span>
+ <span class="c"># KeyError if the cookie is missing.</span>
+</pre></div>
+</div>
+<p>Storing cookies:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">make_response</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="n">resp</span> <span class="o">=</span> <span class="n">make_response</span><span class="p">(</span><span class="n">render_template</span><span class="p">(</span><span class="o">...</span><span class="p">))</span>
+ <span class="n">resp</span><span class="o">.</span><span class="n">set_cookie</span><span class="p">(</span><span class="s">&#39;username&#39;</span><span class="p">,</span> <span class="s">&#39;the username&#39;</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">resp</span>
+</pre></div>
+</div>
+<p>Note that cookies are set on response objects. Since you normally you
+just return strings from the view functions Flask will convert them into
+response objects for you. If you explicitly want to do that you can use
+the <a class="reference internal" href="api.html#flask.make_response" title="flask.make_response"><tt class="xref py py-meth docutils literal"><span class="pre">make_response()</span></tt></a> function and then modify it.</p>
+<p>Sometimes you might want to set a cookie at a point where the response
+object does not exist yet. This is possible by utilizing the
+<a class="reference internal" href="patterns/deferredcallbacks.html#deferred-callbacks"><em>Deferred Request Callbacks</em></a> pattern.</p>
+<p>For this also see <a class="reference internal" href="#about-responses"><em>About Responses</em></a>.</p>
+</div>
+</div>
+<div class="section" id="redirects-and-errors">
+<h2>Redirects and Errors<a class="headerlink" href="#redirects-and-errors" title="Permalink to this headline">¶</a></h2>
+<p>To redirect a user to somewhere else you can use the
+<a class="reference internal" href="api.html#flask.redirect" title="flask.redirect"><tt class="xref py py-func docutils literal"><span class="pre">redirect()</span></tt></a> function. To abort a request early with an error
+code use the <a class="reference internal" href="api.html#flask.abort" title="flask.abort"><tt class="xref py py-func docutils literal"><span class="pre">abort()</span></tt></a> function. Here an example how this works:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">abort</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;login&#39;</span><span class="p">))</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
+ <span class="n">abort</span><span class="p">(</span><span class="mi">401</span><span class="p">)</span>
+ <span class="n">this_is_never_executed</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>This is a rather pointless example because a user will be redirected from
+the index to a page they cannot access (401 means access denied) but it
+shows how that works.</p>
+<p>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
+<a class="reference internal" href="api.html#flask.Flask.errorhandler" title="flask.Flask.errorhandler"><tt class="xref py py-meth docutils literal"><span class="pre">errorhandler()</span></tt></a> decorator:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">render_template</span>
+
+<span class="nd">@app.errorhandler</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">page_not_found</span><span class="p">(</span><span class="n">error</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;page_not_found.html&#39;</span><span class="p">),</span> <span class="mi">404</span>
+</pre></div>
+</div>
+<p>Note the <tt class="docutils literal"><span class="pre">404</span></tt> after the <a class="reference internal" href="api.html#flask.render_template" title="flask.render_template"><tt class="xref py py-func docutils literal"><span class="pre">render_template()</span></tt></a> 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.</p>
+</div>
+<div class="section" id="about-responses">
+<span id="id6"></span><h2>About Responses<a class="headerlink" href="#about-responses" title="Permalink to this headline">¶</a></h2>
+<p>The return value from a view function is automatically converted into a
+response object for you. If the return value is a string it&#8217;s converted
+into a response object with the string as response body, an <tt class="docutils literal"><span class="pre">200</span> <span class="pre">OK</span></tt>
+error code and a <tt class="docutils literal"><span class="pre">text/html</span></tt> mimetype. The logic that Flask applies to
+converting return values into response objects is as follows:</p>
+<ol class="arabic simple">
+<li>If a response object of the correct type is returned it&#8217;s directly
+returned from the view.</li>
+<li>If it&#8217;s a string, a response object is created with that data and the
+default parameters.</li>
+<li>If a tuple is returned the response object is created by passing the
+tuple as arguments to the response object&#8217;s constructor.</li>
+<li>If neither of that works, Flask will assume the return value is a
+valid WSGI application and converts that into a response object.</li>
+</ol>
+<p>If you want to get hold of the resulting response object inside the view
+you can use the <a class="reference internal" href="api.html#flask.make_response" title="flask.make_response"><tt class="xref py py-func docutils literal"><span class="pre">make_response()</span></tt></a> function.</p>
+<p>Imagine you have a view like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">not_found</span><span class="p">(</span><span class="n">error</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;error.html&#39;</span><span class="p">),</span> <span class="mi">404</span>
+</pre></div>
+</div>
+<p>You just need to wrap the return expression with
+<a class="reference internal" href="api.html#flask.make_response" title="flask.make_response"><tt class="xref py py-func docutils literal"><span class="pre">make_response()</span></tt></a> and get the result object to modify it, then
+return it:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">not_found</span><span class="p">(</span><span class="n">error</span><span class="p">):</span>
+ <span class="n">resp</span> <span class="o">=</span> <span class="n">make_response</span><span class="p">(</span><span class="n">render_template</span><span class="p">(</span><span class="s">&#39;error.html&#39;</span><span class="p">),</span> <span class="mi">404</span><span class="p">)</span>
+ <span class="n">resp</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">&#39;X-Something&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;A value&#39;</span>
+ <span class="k">return</span> <span class="n">resp</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="sessions">
+<span id="id7"></span><h2>Sessions<a class="headerlink" href="#sessions" title="Permalink to this headline">¶</a></h2>
+<p>Besides the request object there is also a second object called
+<a class="reference internal" href="api.html#flask.session" title="flask.session"><tt class="xref py py-class docutils literal"><span class="pre">session</span></tt></a> 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 they know the secret key used for signing.</p>
+<p>In order to use sessions you have to set a secret key. Here is how
+sessions work:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">session</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span><span class="p">,</span> <span class="n">escape</span><span class="p">,</span> <span class="n">request</span>
+
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">index</span><span class="p">():</span>
+ <span class="k">if</span> <span class="s">&#39;username&#39;</span> <span class="ow">in</span> <span class="n">session</span><span class="p">:</span>
+ <span class="k">return</span> <span class="s">&#39;Logged in as </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="n">escape</span><span class="p">(</span><span class="n">session</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">])</span>
+ <span class="k">return</span> <span class="s">&#39;You are not logged in&#39;</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="n">session</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">]</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;index&#39;</span><span class="p">))</span>
+ <span class="k">return</span> <span class="s">&#39;&#39;&#39;</span>
+<span class="s"> &lt;form action=&quot;&quot; method=&quot;post&quot;&gt;</span>
+<span class="s"> &lt;p&gt;&lt;input type=text name=username&gt;</span>
+<span class="s"> &lt;p&gt;&lt;input type=submit value=Login&gt;</span>
+<span class="s"> &lt;/form&gt;</span>
+<span class="s"> &#39;&#39;&#39;</span>
+
+<span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/logout&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">logout</span><span class="p">():</span>
+ <span class="c"># remove the username from the session if its there</span>
+ <span class="n">session</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">&#39;username&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;index&#39;</span><span class="p">))</span>
+
+<span class="c"># set the secret key. keep this really secret:</span>
+<span class="n">app</span><span class="o">.</span><span class="n">secret_key</span> <span class="o">=</span> <span class="s">&#39;A0Zr98j/3yX R~XHH!jmN]LWX/,?RT&#39;</span>
+</pre></div>
+</div>
+<p>The here mentioned <a class="reference internal" href="api.html#flask.escape" title="flask.escape"><tt class="xref py py-func docutils literal"><span class="pre">escape()</span></tt></a> does escaping for you if you are
+not using the template engine (like in this example).</p>
+<div class="admonition-how-to-generate-good-secret-keys admonition ">
+<p class="first admonition-title">How to generate good secret keys</p>
+<p>The problem with random is that it&#8217;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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">os</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">os</span><span class="o">.</span><span class="n">urandom</span><span class="p">(</span><span class="mi">24</span><span class="p">)</span>
+<span class="go">&#39;\xfd{H\xe5&lt;\x95\xf9\xe3\x96.5\xd1\x01O&lt;!\xd5\xa2\xa0\x9fR&quot;\xa1\xa8&#39;</span>
+</pre></div>
+</div>
+<p class="last">Just take that thing and copy/paste it into your code and you&#8217;re done.</p>
+</div>
+</div>
+<div class="section" id="message-flashing">
+<h2>Message Flashing<a class="headerlink" href="#message-flashing" title="Permalink to this headline">¶</a></h2>
+<p>Good applications and user interfaces are all about feedback. If the user
+does not get enough feedback they 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.</p>
+<p>To flash a message use the <a class="reference internal" href="api.html#flask.flash" title="flask.flash"><tt class="xref py py-func docutils literal"><span class="pre">flash()</span></tt></a> method, to get hold of the
+messages you can use <a class="reference internal" href="api.html#flask.get_flashed_messages" title="flask.get_flashed_messages"><tt class="xref py py-func docutils literal"><span class="pre">get_flashed_messages()</span></tt></a> which is also
+available in the templates. Check out the <a class="reference internal" href="patterns/flashing.html#message-flashing-pattern"><em>Message Flashing</em></a>
+for a full example.</p>
+</div>
+<div class="section" id="logging">
+<h2>Logging<a class="headerlink" href="#logging" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.3.</span></p>
+<p>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&#8217;s obviously
+malformed. This might be caused by a user tempering with the data, or the
+client code failing. Most of the time, it&#8217;s okay to reply with <tt class="docutils literal"><span class="pre">400</span> <span class="pre">Bad</span>
+<span class="pre">Request</span></tt> in that situation, but sometimes that won&#8217;t do and the code has
+to continue working.</p>
+<p>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.</p>
+<p>Here are some example log calls:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&#39;A value for debugging&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s">&#39;A warning occurred (</span><span class="si">%d</span><span class="s"> apples)&#39;</span><span class="p">,</span> <span class="mi">42</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s">&#39;An error occurred&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The attached <a class="reference internal" href="api.html#flask.Flask.logger" title="flask.Flask.logger"><tt class="xref py py-attr docutils literal"><span class="pre">logger</span></tt></a> is a standard logging
+<a class="reference external" href="http://docs.python.org/dev/library/logging.html#logging.Logger" title="(in Python v3.3)"><tt class="xref py py-class docutils literal"><span class="pre">Logger</span></tt></a>, so head over to the official <a class="reference external" href="http://docs.python.org/library/logging.html">logging
+documentation</a> for more
+information.</p>
+</div>
+<div class="section" id="hooking-in-wsgi-middlewares">
+<h2>Hooking in WSGI Middlewares<a class="headerlink" href="#hooking-in-wsgi-middlewares" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">werkzeug.contrib.fixers</span> <span class="kn">import</span> <span class="n">LighttpdCGIRootFix</span>
+<span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span> <span class="o">=</span> <span class="n">LighttpdCGIRootFix</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">wsgi_app</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Quickstart</a><ul>
+<li><a class="reference internal" href="#a-minimal-application">A Minimal Application</a></li>
+<li><a class="reference internal" href="#debug-mode">Debug Mode</a></li>
+<li><a class="reference internal" href="#routing">Routing</a><ul>
+<li><a class="reference internal" href="#variable-rules">Variable Rules</a></li>
+<li><a class="reference internal" href="#url-building">URL Building</a></li>
+<li><a class="reference internal" href="#http-methods">HTTP Methods</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#static-files">Static Files</a></li>
+<li><a class="reference internal" href="#rendering-templates">Rendering Templates</a></li>
+<li><a class="reference internal" href="#accessing-request-data">Accessing Request Data</a><ul>
+<li><a class="reference internal" href="#context-locals">Context Locals</a></li>
+<li><a class="reference internal" href="#the-request-object">The Request Object</a></li>
+<li><a class="reference internal" href="#file-uploads">File Uploads</a></li>
+<li><a class="reference internal" href="#cookies">Cookies</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#redirects-and-errors">Redirects and Errors</a></li>
+<li><a class="reference internal" href="#about-responses">About Responses</a></li>
+<li><a class="reference internal" href="#sessions">Sessions</a></li>
+<li><a class="reference internal" href="#message-flashing">Message Flashing</a></li>
+<li><a class="reference internal" href="#logging">Logging</a></li>
+<li><a class="reference internal" href="#hooking-in-wsgi-middlewares">Hooking in WSGI Middlewares</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="installation.html" title="previous chapter">Installation</a></li>
+ <li>Next: <a href="tutorial/index.html" title="next chapter">Tutorial</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/quickstart.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/reqcontext.html b/studio/static/doc/flask-docs/reqcontext.html
new file mode 100644
index 0000000..a250254
--- /dev/null
+++ b/studio/static/doc/flask-docs/reqcontext.html
@@ -0,0 +1,330 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>The Request Context &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Modular Applications with Blueprints" href="blueprints.html" />
+ <link rel="prev" title="Pluggable Views" href="views.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="blueprints.html" title="Modular Applications with Blueprints"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="views.html" title="Pluggable Views"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="the-request-context">
+<span id="request-context"></span><h1>The Request Context<a class="headerlink" href="#the-request-context" title="Permalink to this headline">¶</a></h1>
+<p>This document describes the behavior in Flask 0.7 which is mostly in line
+with the old behavior but has some small, subtle differences.</p>
+<p>One of the design ideas behind Flask is that there are two different
+“states” in which code is executed. The application setup state in which
+the application implicitly is on the module level. It starts when the
+<tt class="xref py py-class docutils literal"><span class="pre">Flask</span></tt> object is instantiated, and it implicitly ends when the
+first request comes in. While the application is in this state a few
+assumptions are true:</p>
+<ul class="simple">
+<li>the programmer can modify the application object safely.</li>
+<li>no request handling happened so far</li>
+<li>you have to have a reference to the application object in order to
+modify it, there is no magic proxy that can give you a reference to
+the application object you&#8217;re currently creating or modifying.</li>
+</ul>
+<p>On the contrast, during request handling, a couple of other rules exist:</p>
+<ul class="simple">
+<li>while a request is active, the context local objects
+(<a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-data docutils literal"><span class="pre">flask.request</span></tt></a> and others) point to the current request.</li>
+<li>any code can get hold of these objects at any time.</li>
+</ul>
+<p>The magic that makes this works is internally referred in Flask as the
+“request context”.</p>
+<div class="section" id="diving-into-context-locals">
+<h2>Diving into Context Locals<a class="headerlink" href="#diving-into-context-locals" title="Permalink to this headline">¶</a></h2>
+<p>Say you have a utility function that returns the URL the user should be
+redirected to. Imagine it would always redirect to the URL&#8217;s <tt class="docutils literal"><span class="pre">next</span></tt>
+parameter or the HTTP referrer or the index page:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request</span><span class="p">,</span> <span class="n">url_for</span>
+
+<span class="k">def</span> <span class="nf">redirect_url</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;next&#39;</span><span class="p">)</span> <span class="ow">or</span> \
+ <span class="n">request</span><span class="o">.</span><span class="n">referrer</span> <span class="ow">or</span> \
+ <span class="n">url_for</span><span class="p">(</span><span class="s">&#39;index&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">redirect_url</span><span class="p">()</span>
+<span class="gt">Traceback (most recent call last):</span>
+ File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n">&lt;module&gt;</span>
+<span class="gr">AttributeError</span>: <span class="n">&#39;NoneType&#39; object has no attribute &#39;request&#39;</span>
+</pre></div>
+</div>
+<p>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 <a class="reference internal" href="api.html#flask.Flask.test_request_context" title="flask.Flask.test_request_context"><tt class="xref py py-attr docutils literal"><span class="pre">test_request_context</span></tt></a> method can create
+us a <a class="reference internal" href="api.html#flask.ctx.RequestContext" title="flask.ctx.RequestContext"><tt class="xref py py-class docutils literal"><span class="pre">RequestContext</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">(</span><span class="s">&#39;/?next=http://example.com/&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This context can be used in two ways. Either with the <cite>with</cite> statement
+or by calling the <a class="reference internal" href="api.html#flask.ctx.RequestContext.push" title="flask.ctx.RequestContext.push"><tt class="xref py py-meth docutils literal"><span class="pre">push()</span></tt></a> and
+<a class="reference internal" href="api.html#flask.ctx.RequestContext.pop" title="flask.ctx.RequestContext.pop"><tt class="xref py py-meth docutils literal"><span class="pre">pop()</span></tt></a> methods:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">push</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>From that point onwards you can work with the request object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">redirect_url</span><span class="p">()</span>
+<span class="go">u&#39;http://example.com/&#39;</span>
+</pre></div>
+</div>
+<p>Until you call <cite>pop</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Because the request context is internally maintained as a stack you can
+push and pop multiple times. This is very handy to implement things like
+internal redirects.</p>
+<p>For more information of how to utilize the request context from the
+interactive Python shell, head over to the <a class="reference internal" href="shell.html#shell"><em>Working with the Shell</em></a> chapter.</p>
+</div>
+<div class="section" id="how-the-context-works">
+<h2>How the Context Works<a class="headerlink" href="#how-the-context-works" title="Permalink to this headline">¶</a></h2>
+<p>If you look into how the Flask WSGI application internally works, you will
+find a piece of code that looks very much like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">wsgi_app</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">):</span>
+ <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">request_context</span><span class="p">(</span><span class="n">environ</span><span class="p">):</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">full_dispatch_request</span><span class="p">()</span>
+ <span class="k">except</span> <span class="ne">Exception</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
+ <span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">make_response</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">handle_exception</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">response</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The method <tt class="xref py py-meth docutils literal"><span class="pre">request_context()</span></tt> returns a new
+<a class="reference internal" href="api.html#flask.ctx.RequestContext" title="flask.ctx.RequestContext"><tt class="xref py py-class docutils literal"><span class="pre">RequestContext</span></tt></a> object and uses it in combination with
+the <cite>with</cite> statement to bind the context. Everything that is called from
+the same thread from this point onwards until the end of the <cite>with</cite>
+statement will have access to the request globals (<a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-data docutils literal"><span class="pre">flask.request</span></tt></a>
+and others).</p>
+<p>The request context internally works like a stack: The topmost level on
+the stack is the current active request.
+<a class="reference internal" href="api.html#flask.ctx.RequestContext.push" title="flask.ctx.RequestContext.push"><tt class="xref py py-meth docutils literal"><span class="pre">push()</span></tt></a> adds the context to the stack on
+the very top, <a class="reference internal" href="api.html#flask.ctx.RequestContext.pop" title="flask.ctx.RequestContext.pop"><tt class="xref py py-meth docutils literal"><span class="pre">pop()</span></tt></a> removes it from the
+stack again. On popping the application&#8217;s
+<a class="reference internal" href="api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-func docutils literal"><span class="pre">teardown_request()</span></tt></a> functions are also executed.</p>
+</div>
+<div class="section" id="callbacks-and-errors">
+<span id="id1"></span><h2>Callbacks and Errors<a class="headerlink" href="#callbacks-and-errors" title="Permalink to this headline">¶</a></h2>
+<p>What happens if an error occurs in Flask during request processing? This
+particular behavior changed in 0.7 because we wanted to make it easier to
+understand what is actually happening. The new behavior is quite simple:</p>
+<ol class="arabic simple">
+<li>Before each request, <a class="reference internal" href="api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> functions are
+executed. If one of these functions return a response, the other
+functions are no longer called. In any case however the return value
+is treated as a replacement for the view&#8217;s return value.</li>
+<li>If the <a class="reference internal" href="api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> functions did not return a
+response, the regular request handling kicks in and the view function
+that was matched has the chance to return a response.</li>
+<li>The return value of the view is then converted into an actual response
+object and handed over to the <a class="reference internal" href="api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a>
+functions which have the chance to replace it or modify it in place.</li>
+<li>At the end of the request the <a class="reference internal" href="api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a>
+functions are executed. This always happens, even in case of an
+unhandled exception down the road or if a before-request handler was
+not executed yet or at all (for example in test environments sometimes
+you might want to not execute before-request callbacks).</li>
+</ol>
+<p>Now what happens on errors? In production mode if an exception is not
+caught, the 500 internal server handler is called. In development mode
+however the exception is not further processed and bubbles up to the WSGI
+server. That way things like the interactive debugger can provide helpful
+debug information.</p>
+<p>An important change in 0.7 is that the internal server error is now no
+longer post processed by the after request callbacks and after request
+callbacks are no longer guaranteed to be executed. This way the internal
+dispatching code looks cleaner and is easier to customize and understand.</p>
+<p>The new teardown functions are supposed to be used as a replacement for
+things that absolutely need to happen at the end of request.</p>
+</div>
+<div class="section" id="teardown-callbacks">
+<h2>Teardown Callbacks<a class="headerlink" href="#teardown-callbacks" title="Permalink to this headline">¶</a></h2>
+<p>The teardown callbacks are special callbacks in that they are executed at
+at different point. Strictly speaking they are independent of the actual
+request handling as they are bound to the lifecycle of the
+<a class="reference internal" href="api.html#flask.ctx.RequestContext" title="flask.ctx.RequestContext"><tt class="xref py py-class docutils literal"><span class="pre">RequestContext</span></tt></a> object. When the request context is
+popped, the <a class="reference internal" href="api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> functions are called.</p>
+<p>This is important to know if the life of the request context is prolonged
+by using the test client in a with statement or when using the request
+context from the command line:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span> <span class="k">as</span> <span class="n">client</span><span class="p">:</span>
+ <span class="n">resp</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/foo&#39;</span><span class="p">)</span>
+ <span class="c"># the teardown functions are still not called at that point</span>
+ <span class="c"># even though the response ended and you have the response</span>
+ <span class="c"># object in your hand</span>
+
+<span class="c"># only when the code reaches this point the teardown functions</span>
+<span class="c"># are called. Alternatively the same thing happens if another</span>
+<span class="c"># request was triggered from the test client</span>
+</pre></div>
+</div>
+<p>It&#8217;s easy to see the behavior from the command line:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nd">@app.teardown_request</span>
+<span class="gp">... </span><span class="k">def</span> <span class="nf">teardown_request</span><span class="p">(</span><span class="n">exception</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
+<span class="gp">... </span> <span class="k">print</span> <span class="s">&#39;this runs after request&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">push</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+<span class="go">this runs after request</span>
+<span class="go">&gt;&gt;&gt;</span>
+</pre></div>
+</div>
+<p>Keep in mind that teardown callbacks are always executed, even if
+before-request callbacks were not executed yet but an exception happened.
+Certain parts of the test system might also temporarily create a request
+context without calling the before-request handlers. Make sure to write
+your teardown-request handlers in a way that they will never fail.</p>
+</div>
+<div class="section" id="notes-on-proxies">
+<span id="id2"></span><h2>Notes On Proxies<a class="headerlink" href="#notes-on-proxies" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>Most of the time you don&#8217;t have to care about that, but there are some
+exceptions where it is good to know that this object is an actual proxy:</p>
+<ul class="simple">
+<li>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 <cite>_get_current_object</cite> below).</li>
+<li>if the object reference is important (so for example for sending
+<a class="reference internal" href="signals.html#signals"><em>Signals</em></a>)</li>
+</ul>
+<p>If you need to get access to the underlying object that is proxied, you
+can use the <a class="reference external" href="http://werkzeug.pocoo.org/docs/local/#werkzeug.local.LocalProxy._get_current_object" title="(in Werkzeug v0.7)"><tt class="xref py py-meth docutils literal"><span class="pre">_get_current_object()</span></tt></a> method:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">current_app</span><span class="o">.</span><span class="n">_get_current_object</span><span class="p">()</span>
+<span class="n">my_signal</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="context-preservation-on-error">
+<h2>Context Preservation on Error<a class="headerlink" href="#context-preservation-on-error" title="Permalink to this headline">¶</a></h2>
+<p>If an error occurs or not, at the end of the request the request context
+is popped and all data associated with it is destroyed. During
+development however that can be problematic as you might want to have the
+information around for a longer time in case an exception occurred. In
+Flask 0.6 and earlier in debug mode, if an exception occurred, the
+request context was not popped so that the interactive debugger can still
+provide you with important information.</p>
+<p>Starting with Flask 0.7 you have finer control over that behavior by
+setting the <tt class="docutils literal"><span class="pre">PRESERVE_CONTEXT_ON_EXCEPTION</span></tt> configuration variable. By
+default it&#8217;s linked to the setting of <tt class="docutils literal"><span class="pre">DEBUG</span></tt>. If the application is in
+debug mode the context is preserved, in production mode it&#8217;s not.</p>
+<p>Do not force activate <tt class="docutils literal"><span class="pre">PRESERVE_CONTEXT_ON_EXCEPTION</span></tt> in production mode
+as it will cause your application to leak memory on exceptions. However
+it can be useful during development to get the same error preserving
+behavior as in development mode when attempting to debug an error that
+only occurs under production settings.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">The Request Context</a><ul>
+<li><a class="reference internal" href="#diving-into-context-locals">Diving into Context Locals</a></li>
+<li><a class="reference internal" href="#how-the-context-works">How the Context Works</a></li>
+<li><a class="reference internal" href="#callbacks-and-errors">Callbacks and Errors</a></li>
+<li><a class="reference internal" href="#teardown-callbacks">Teardown Callbacks</a></li>
+<li><a class="reference internal" href="#notes-on-proxies">Notes On Proxies</a></li>
+<li><a class="reference internal" href="#context-preservation-on-error">Context Preservation on Error</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="views.html" title="previous chapter">Pluggable Views</a></li>
+ <li>Next: <a href="blueprints.html" title="next chapter">Modular Applications with Blueprints</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/reqcontext.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/search.html b/studio/static/doc/flask-docs/search.html
new file mode 100644
index 0000000..4294a7c
--- /dev/null
+++ b/studio/static/doc/flask-docs/search.html
@@ -0,0 +1,101 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Search &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <script type="text/javascript" src="_static/searchtools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <script type="text/javascript">
+ jQuery(function() { Search.loadIndex("searchindex.js"); });
+ </script>
+
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <h1 id="search-documentation">Search</h1>
+ <div id="fallback" class="admonition warning">
+ <script type="text/javascript">$('#fallback').hide();</script>
+ <p>
+ Please activate JavaScript to enable the search
+ functionality.
+ </p>
+ </div>
+ <p>
+ From here you can search these documents. Enter your search
+ words into the box below and click "search". Note that the search
+ function will automatically search for all of the words. Pages
+ containing fewer words won't appear in the result list.
+ </p>
+ <form action="" method="get">
+ <input type="text" name="q" value="" />
+ <input type="submit" value="search" />
+ <span id="search-progress" style="padding-left: 10px"></span>
+ </form>
+
+ <div id="search-results">
+
+ </div>
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ </ul></li>
+</ul>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/searchindex.js b/studio/static/doc/flask-docs/searchindex.js
new file mode 100644
index 0000000..66630ba
--- /dev/null
+++ b/studio/static/doc/flask-docs/searchindex.js
@@ -0,0 +1 @@
+Search.setIndex({objects:{"":{flask:[3,0,1]},"flask.Response":{headers:[3,3,1],status_code:[3,3,1],data:[3,3,1],mimetype:[3,3,1],set_cookie:[3,2,1]},"flask.sessions.SessionMixin":{"new":[3,3,1],permanent:[3,3,1],modified:[3,3,1]},flask:{render_template_string:[3,1,1],Flask:[3,4,1],escape:[3,1,1],request_tearing_down:[3,5,1],send_from_directory:[3,1,1],abort:[3,1,1],get_flashed_messages:[3,1,1],Config:[3,4,1],render_template:[3,1,1],redirect:[3,1,1],flash:[3,1,1],current_app:[3,5,1],json:[3,5,1],Response:[3,4,1],request_started:[3,5,1],Blueprint:[3,4,1],signals_available:[3,5,1],get_template_attribute:[3,1,1],"_request_ctx_stack":[3,5,1],Markup:[3,4,1],Request:[3,4,1],safe_join:[3,1,1],template_rendered:[3,5,1],got_request_exception:[3,5,1],url_for:[3,1,1],g:[3,5,1],make_response:[3,1,1],jsonify:[3,1,1],request:[3,4,1],session:[3,4,1],request_finished:[3,5,1],send_file:[3,1,1],has_request_context:[3,1,1]},"flask.flask":{ext:[3,5,1]},"flask.Request":{view_args:[3,3,1],url_root:[3,3,1],stream:[3,3,1],cookies:[3,3,1],module:[3,3,1],files:[3,3,1],base_url:[3,3,1],json:[3,3,1],values:[3,3,1],method:[3,3,1],blueprint:[3,3,1],form:[3,3,1],args:[3,3,1],url_rule:[3,3,1],path:[3,3,1],data:[3,3,1],max_content_length:[3,3,1],endpoint:[3,3,1],url:[3,3,1],on_json_loading_failed:[3,2,1],headers:[3,3,1],environ:[3,3,1],script_root:[3,3,1],is_xhr:[3,3,1],routing_exception:[3,3,1]},"flask.views":{MethodView:[3,4,1],View:[3,4,1]},"flask.sessions.SecureCookieSessionInterface":{session_class:[3,3,1]},"flask.sessions":{SessionMixin:[3,4,1],SecureCookieSessionInterface:[3,4,1],SessionInterface:[3,4,1],NullSession:[3,4,1]},"flask.views.View":{dispatch_request:[3,2,1],decorators:[3,3,1],methods:[3,3,1],as_view:[3,6,1]},"flask.sessions.SessionInterface":{get_cookie_httponly:[3,2,1],save_session:[3,2,1],get_cookie_path:[3,2,1],open_session:[3,2,1],get_expiration_time:[3,2,1],get_cookie_secure:[3,2,1],get_cookie_domain:[3,2,1],null_session_class:[3,3,1],make_null_session:[3,2,1],is_null_session:[3,2,1]},"flask.blueprints.BlueprintSetupState":{blueprint:[3,3,1],url_prefix:[3,3,1],first_registration:[3,3,1],app:[3,3,1],url_defaults:[3,3,1],subdomain:[3,3,1],add_url_rule:[3,2,1],options:[3,3,1]},"flask.signals.Namespace":{signal:[3,2,1]},"flask.ctx":{RequestContext:[3,4,1]},"flask.Flask":{inject_url_defaults:[3,2,1],url_map:[3,3,1],error_handler_spec:[3,3,1],testing:[3,3,1],enable_modules:[3,3,1],jinja_loader:[3,3,1],propagate_exceptions:[3,3,1],before_request_funcs:[3,3,1],open_session:[3,2,1],preprocess_request:[3,2,1],before_request:[3,2,1],logger_name:[3,3,1],register_error_handler:[3,2,1],wsgi_app:[3,2,1],after_request_funcs:[3,3,1],test_request_context:[3,2,1],url_value_preprocessor:[3,2,1],make_config:[3,2,1],create_jinja_environment:[3,2,1],url_defaults:[3,2,1],debug:[3,3,1],handle_http_exception:[3,2,1],send_static_file:[3,2,1],permanent_session_lifetime:[3,3,1],before_first_request_funcs:[3,3,1],select_jinja_autoescape:[3,2,1],save_session:[3,2,1],request_class:[3,3,1],instance_path:[3,3,1],test_client:[3,2,1],response_class:[3,3,1],handle_user_exception:[3,2,1],teardown_request_funcs:[3,3,1],logger:[3,3,1],url_value_preprocessors:[3,3,1],config:[3,3,1],session_interface:[3,3,1],trap_http_exception:[3,2,1],use_x_sendfile:[3,3,1],jinja_options:[3,3,1],process_response:[3,2,1],run:[3,2,1],context_processor:[3,2,1],before_first_request:[3,2,1],do_teardown_request:[3,2,1],log_exception:[3,2,1],errorhandler:[3,2,1],create_global_jinja_loader:[3,2,1],debug_log_format:[3,3,1],full_dispatch_request:[3,2,1],default_config:[3,3,1],template_context_processors:[3,3,1],preserve_context_on_exception:[3,3,1],create_url_adapter:[3,2,1],template_filter:[3,2,1],make_null_session:[3,2,1],url_rule_class:[3,3,1],init_jinja_globals:[3,2,1],auto_find_instance_path:[3,2,1],name:[3,3,1],blueprints:[3,3,1],endpoint:[3,2,1],register_blueprint:[3,2,1],make_default_options_response:[3,2,1],update_template_context:[3,2,1],dispatch_request:[3,2,1],make_response:[3,2,1],route:[3,2,1],open_instance_resource:[3,2,1],handle_exception:[3,2,1],session_cookie_name:[3,3,1],after_request:[3,2,1],view_functions:[3,3,1],got_first_request:[3,3,1],extensions:[3,3,1],secret_key:[3,3,1],url_default_functions:[3,3,1],request_context:[3,2,1],has_static_folder:[3,3,1],open_resource:[3,2,1],jinja_env:[3,3,1],add_url_rule:[3,2,1],test_client_class:[3,3,1],teardown_request:[3,2,1],register_module:[3,2,1]},"flask.Markup":{striptags:[3,2,1],unescape:[3,2,1],escape:[3,6,1]},"flask.testing":{FlaskClient:[3,4,1]},"flask.session":{"new":[3,3,1],permanent:[3,3,1],modified:[3,3,1]},"flask.Blueprint":{after_app_request:[3,2,1],record_once:[3,2,1],before_request:[3,2,1],url_value_preprocessor:[3,2,1],url_defaults:[3,2,1],before_app_request:[3,2,1],send_static_file:[3,2,1],app_context_processor:[3,2,1],teardown_app_request:[3,2,1],jinja_loader:[3,3,1],errorhandler:[3,2,1],app_url_defaults:[3,2,1],context_processor:[3,2,1],app_url_value_preprocessor:[3,2,1],app_errorhandler:[3,2,1],make_setup_state:[3,2,1],endpoint:[3,2,1],before_app_first_request:[3,2,1],route:[3,2,1],register:[3,2,1],record:[3,2,1],has_static_folder:[3,3,1],open_resource:[3,2,1],add_url_rule:[3,2,1],after_request:[3,2,1],teardown_request:[3,2,1]},"flask.signals":{Namespace:[3,4,1]},"flask.Config":{from_envvar:[3,2,1],from_object:[3,2,1],from_pyfile:[3,2,1]},"flask.blueprints":{BlueprintSetupState:[3,4,1]},"flask.ctx.RequestContext":{match_request:[3,2,1],push:[3,2,1],pop:[3,2,1]},"flask.testing.FlaskClient":{session_transaction:[3,2,1]}},terms:{besid:[49,15,60,7,1],get_object:20,untrust:3,rakija:51,four:[41,63,42,32],fcgi:[52,39],prefix:[47,48,49,51,3,53,56,41,26,8,44,30,31],database_uri:31,ronach:50,upsid:[21,63],session_cookie_nam:[3,31],hate:[1,43,44],gae:51,forget:[1,55,51],subdomaindispatch:48,whatwg:5,under:[50,3,21,5,40,6,31],spec:5,merchant:50,everi:[47,17,43,31,21,56,3,7,8,28,10,12],risk:[45,1],mysessioninterfac:3,my_list:41,url_default:[56,3],upstream:17,log_respons:23,appar:5,look:[1,4,42,6,7,11,12,13,14,15,21,23,24,26,27,28,29,30,64,35,18,20,38,39,43,44,45,46,47,48,49,51,52,55,3,62],httpwriter:41,upload:[64,15,38,1,36,51,3,28,45,2],correct:[64,35,1,51,60,39,40,3,55,43,28,0,10],solid:[16,36],vector:45,get_templ:59,typecheck:3,listview:20,cache_kei:53,"29th":51,schnap:51,naiv:43,direct:[40,3,7,59,50],horn:50,xf9:1,second:[47,17,35,1,4,23,3,60,8,44,28,49,31,32],provide_automatic_opt:3,show_entri:[33,61,32],even:[1,60,5,3,61,14,17,24,23,19,57,27,31,35,36,48,47,41,49,50,51,55,6,45],dialogu:[15,38,17],msysgit:21,hide:36,eventlet:22,ruthless:5,lighti:39,rerais:3,launch:3,aacbe2:16,view_arg:3,"new":[9,1,60,5,6,8,11,13,14,15,17,21,23,27,28,31,32,33,35,36,20,43,64,38,48,49,51,3],ever:1,xhtml1:[1,5],metadata:63,direct_passthrough:3,behavior:[14,15,38,51,3,60,21,6,7,45,31],do_teardown_request:3,xfd:1,here:[9,1,4,42,57,7,8,10,12,13,14,15,24,21,23,19,26,27,28,30,31,32,33,60,35,18,38,41,43,44,64,47,48,51,52,53,55,56,3,58,59,61,45,63],met:[14,50],tornado:[52,22],debugg:[3,6,31,1,18],download_fil:3,path:[47,48,0,1,2,51,3,31,53,60,21,39,49,40,57,7,26,28,25,10,12],interpret:[64,48,35,1,4,39,55,8,49,31],errorlog:40,precis:10,datetim:[3,31],mitsuhiko:49,permit:[14,50],generali:21,contextmanag:23,golden:[15,38,8],schemaless:19,bashrc:28,unix:[35,21,39,22,40,3,8,49,62],txt:[28,1],register_api:20,close_connect:14,create_global_jinja_load:3,lwx:1,describ:[15,6,1],would:[1,3,4,57,7,11,12,13,14,17,23,26,28,29,30,34,35,18,20,43,64,47,48,50,51,53,56,6,60,61,45],uwsgi_modifier1:62,harddriv:35,call:[9,1,2,4,42,6,7,8,10,11,12,13,14,16,18,19,20,21,23,24,25,26,27,28,29,30,31,34,35,36,37,39,40,43,45,64,47,48,51,53,55,56,3,58,59,60,62,63],python26:49,recommend:[14,15,47,27,35,1,36,41,3,21,23,34,8,49,43,31,63,13],care:[14,3,6,59,8,45,63],type:[64,41,35,1,24,3,5,19,6,61,26,27,44,28,63,45,42,13],tell:[35,1,18,55,60,21,39,61,40,3,7,42,8,44,49,10,62,27],fastcgi:[15,62,38,39,52],url_adapt:3,unescap:3,relat:[9,2,36,57,8,10,63],url_rul:[30,3],notic:[64,1,50,20,55,53,60,21,5,23,3,26,27,30],warn:[35,1,36,51,21,3],static_fold:[47,3],jmn:1,pkg_resourc:[],herebi:50,unpack:51,must:[14,27,49,1,50,37,3,21,5,19,41,26,8,10,31,42,13],join:[28,31,3,0,59],restor:21,setup:[13,2,60,3,14,15,21,22,25,31,33,34,35,18,37,64,38,49,51,52,55,6,58],work:[1,2,3,60,5,6,7,8,11,13,14,15,16,17,18,21,22,23,25,26,27,28,30,31,32,34,36,20,38,39,40,43,64,47,48,49,51,52,53,54,55,57,61,45,63],backend_app:48,wors:7,socket:[39,5],root:[64,0,1,51,60,39,3,7,26,49,62,31,32],overrid:[47,17,35,18,51,21,42,3,31],defer:[15,38,1,2,3,29],vodka:3,give:[14,60,0,1,20,3,51,27,5,40,6,7,39,8,44,10,13],autodetect:[31,51],smtp:35,indic:[3,50],cautiou:13,caution:[3,5],unavail:[3,34],want:[0,1,3,4,6,7,8,9,10,12,13,14,15,17,18,20,21,22,23,19,25,27,28,29,31,32,33,34,35,36,37,39,40,43,44,64,47,48,49,50,51,53,55,56,57,58,59,60,62,63],attract:36,read_fil:8,enc:8,end:[14,15,38,1,2,51,3,60,21,5,6,7,39,43,44,29,45,57,63,13],"20doe":1,ordinari:[45,3],off:[40,7],classifi:14,proxyfix:22,mochikit:3,answer:[1,36,5,24,3,26,59],config:[14,64,47,60,13,18,40,31,51,21,39,19,3,25,28,12,32],updat:[20,53,21,3,31,59],faq:[15,38,5],mongokit:[15,38,2,19],application_root:[3,31,51],timedelta:[3,31],after:[1,4,3,11,12,14,15,21,23,28,29,30,34,35,40,43,38,41,51,55,56,6,60,45,63],befor:[1,3,60,5,6,10,11,12,13,14,15,16,21,23,26,28,29,30,31,64,34,35,36,46,38,57,58,63],wrong:[14,40,1],beauti:[56,1],demonstr:60,allowed_fil:28,handl:[9,1,3,60,5,57,7,14,15,21,22,23,24,26,27,28,29,31,35,36,64,38,48,51,56,6,63],attempt:[3,6,57,5],third:[14,7],classmethod:3,bootstrap:[28,64],credenti:[35,60,9,1,44],greet:1,think:[1,36],get_friend:45,scoped_sess:63,"18th":51,environ:[14,64,48,49,1,18,51,3,52,21,39,22,40,6,25,11,31,59],enter:[64,49,5],exclus:8,first:[0,1,2,4,42,6,7,8,10,12,14,15,20,21,23,19,25,26,27,28,30,31,34,18,37,38,39,40,41,43,45,64,47,48,49,51,53,55,3,60,62,63],order:[48,9,1,36,20,3,51,21,39,22,23,40,6,7,26,60,57,32],origin:[47,17,49,53,60,3],behav:[14,1,19,21,24,3,58,30],frontend:[3,56,48,12],feedback:[51,17,1,44],softwar:[14,50,21,36,8],consent:50,over:[1,5,3,7,8,13,15,18,21,23,19,27,31,35,36,40,51,55,6,59,61,45,63],fall:[23,30,48,3],becaus:[9,1,3,60,5,6,7,11,13,14,17,18,21,22,23,24,25,26,28,31,32,34,35,36,39,40,45,64,47,49,51,53,55,56,57,59,62,63],jpeg:28,veri:[1,4,5,6,7,8,10,12,13,14,17,24,21,23,19,26,27,28,29,31,60,35,36,20,39,42,64,47,48,3,45,63],privileg:[40,49],affect:[14,3,21],jumpload:28,dosomethingwith:[3,13],flexibl:[53,3,63,20],streamlin:21,gentl:[28,43,2],fit:[50,36],fix:[21,51],christoph:50,fetchal:[57,32],"_extern":3,persist:39,easier:[34,2,21,5,6,55,27,28,31],allow_subdomain_redirect:3,"__definesetter__":45,them:[1,2,4,5,57,10,11,13,24,21,22,23,19,27,28,29,31,32,34,35,36,39,40,43,64,48,51,55,3,45,63],thei:[9,1,2,4,5,6,7,8,11,13,14,17,21,22,23,24,26,32,40,41,44,64,47,48,51,53,55,56,3,58,45,63],safe:[14,1,3,60,21,23,40,6,61,26,27,43,13],"break":[14,17,49,1,3,60,21,39,43,41,27,45,59],jinja2:[46,15,47,17,49,1,36,51,27,3,7,8,61,45,59,13],interrupt:50,blink:[3,1],app_url_default:3,choic:36,changelog:[15,38,51],bonu:[15,16,54,38,33],create_jinja_load:[21,51],localstack:3,timeout:[3,53,4,51],each:[51,48,49,1,37,3,53,20,55,19,6,7,59,43,60,45,11,57,63],debug:[15,38,35,1,18,51,3,52,21,39,23,6,55,43,31,59],went:[1,36,26],request_start:[23,3],side:[14,48,35,1,18,3,41,7,26,27,28,49,45],mean:[1,5,3,61,8,13,15,17,24,21,22,19,26,30,34,36,20,38,47,49,50,7],enorm:59,appengin:51,logo:50,unbound:[21,51],network:[35,1,22],goe:[14,41,53,55,58,30],crucial:[14,1],content:[14,15,38,1,2,37,5,43,3,59,26,8,45,31,42],rewrit:[62,25,39,22],laid:41,lowercase_with_underscor:41,adapt:[14,36,51,20,21,3,27],got:[23,24,3,26],"9em":16,listconvert:3,make_app:48,situat:[34,35,1,36,5,3,27,44,29,31],shareddatamiddlewar:28,free:[9,13],standard:[15,38,41,0,1,51,5,23,40,3,7,55,27,49,13],post_id:[3,1],build_onli:28,special_exception_handl:3,reconfigur:31,traceback:[35,51,6,39,3,31],preprocess_request:[3,34,57,60],filter:[15,47,41,1,38,3,7,27,61,63,13],bookmark:0,ish:9,isn:7,auto_find_instance_path:3,subtl:6,confus:[5,51],user_lang:29,render:[15,47,48,49,1,50,20,53,51,21,38,42,23,3,7,26,27,44,32,59,13],db_fd:60,independ:[14,6,31,60,3],hook:[64,15,38,17,1,20,55,3,45,12],instruct:[15,38,40],alreadi:[64,1,36,53,60,23,3,7,26,8,28,61,12,63,27],primari:37,badrequest:[31,3,21],trap_bad_request_key_error:[],easili:[14,64,47,34,1,18,20,4,51,23,7,27,60,29,45,31,59],top:[14,47,41,9,1,3,5,22,43,40,6,7,26,8,45,31,32],sometim:[47,48,1,36,3,60,19,6,29,59,13],downsid:[47,17,12,57,10,31,30,63],necessarili:[10,3],too:[41,49,36,19,3,7,8],similarli:47,debug_log_format:3,logged_in:[61,32],john:1,listen:[48,35,1,51,22,23,3,32],consol:[34,51],namespac:[14,23,3,5],tool:[64,36,21,55,22,40,26,31],lighttpd:[52,62,1,39],took:36,task:[58,7,36,22],cdata:26,template_fold:[47,3],happili:28,target:[3,9],keyword:[47,41,1,23,3,27],provid:[1,3,4,5,6,7,10,11,13,14,17,21,22,23,24,30,31,60,35,36,20,44,64,47,48,49,50,51,53,55,56,57,58],tree:36,project:[14,50,47,17,49,1,36,21,22,41,30,31],matter:1,minut:[53,4],fashion:20,ran:60,modern:[31,1,36],mind:[14,34,17,1,36,20,3,40,60,55,22,23,24,6,7,53,45,41,30,31,57],inject_us:13,raw:57,increment:37,"__main__":[1,18,60,39,40,25,62],seen:[19,1],seem:[60,35,39],incompat:[21,5,51],wsgiscriptalia:40,wsgicontain:22,latter:[56,60],url_value_processor:3,instance_path:[3,31],transmit:[28,45,3,1,8],anatomi:[14,15,38],simplifi:[14,47,51,54,21,23,56,57,26],shall:[3,50],usernam:[47,41,1,18,3,60,13,57,61,27,44,28,30,45,32],object:[1,2,3,4,6,7,10,11,12,13,14,15,18,21,22,23,19,26,28,29,30,31,34,35,36,20,38,40,41,43,47,48,51,52,53,56,57,60,59,61,45,63],what:[1,4,5,6,7,8,13,14,15,17,18,21,23,24,26,28,29,30,31,34,35,36,20,38,40,43,64,47,49,52,55,3,60,45],regular:[47,41,20,3,51,21,23,6,60,8,31],mouth:1,letter:[41,31,8],bsd:[14,50],coin:1,unorthodox:36,prematur:39,tradit:[3,7],simplic:36,don:[1,60,6,7,8,11,12,14,17,18,21,26,28,30,32,34,35,36,20,39,40,45,49,51,53,55,56,3,62,63],with_categori:[3,44],dom:[26,5],doc:[3,39],flow:21,doe:[1,4,5,3,7,8,11,13,14,15,17,19,26,27,28,29,31,34,36,20,38,39,40,42,43,44,64,47,48,49,50,51,53,55,60,45,63],dummi:3,bracket:3,request_context:[3,6,1],left:[16,3],passwordfield:27,came:[3,1],introspect:[3,31,60],tequila:60,random:[23,3,60,1],syntax:[13,51,5,61,7,45],template_filt:[3,13],facto:0,query_properti:63,involv:[17,35,60,51],absolut:[14,47,41,3,21,40,6,8,31],exactli:[14,64,1,53,5,23,3,28,31,63],layout:[15,38,41,1,33,42,24,3,61,26,43,44],menu:49,run_simpl:[3,48],configur:[0,1,2,60,5,6,7,8,9,12,13,14,15,36,21,19,25,31,32,35,18,39,40,45,64,38,48,51,52,3,62,63],apach:[64,15,38,1,52,39,40,25],theme:[14,51],busi:50,init_helloworld:14,"__call__":[3,30,48,22],folder:[9,1,3,7,10,14,15,16,21,26,28,31,33,18,37,38,40,43,46,47,49,51,55,61,64],nonetyp:6,likewis:[3,31,1],watch:[64,40,62,25,39],obscur:5,report:[60,35,26,51],reconstruct:3,parachut:3,bar:[28,41,2,3],emb:[8,5],dispatch_request:[3,20],sacrific:[],excel:[23,53],method:[1,4,6,61,8,10,15,17,21,23,19,25,26,27,28,31,32,34,35,18,20,44,38,41,49,51,56,3,60,63],twice:[17,21,1],bad:[17,1,51,3,54,41,7,43,31],customproxyfix:22,told:[45,21],first_registr:3,respond:[47,32],victim:45,"0x104f965d0":[],serve_forev:22,result:[34,1,51,3,53,4,5,19,48,57,26,63],respons:[14,15,38,34,35,1,51,3,60,21,23,24,6,29,45,11,59],fail:[14,35,1,3,60,21,24,6,57,26,31],import_str:30,fluffi:45,best:[14,15,38,17,55,19,31],subject:35,awar:[40,3],said:[5,20],hopefulli:36,databas:[9,2,60,57,7,8,10,11,14,15,18,19,20,21,24,28,32,33,34,35,36,37,46,38,51,3,58,63],wikipedia:[45,0],figur:[64,48,35,1,36,39,23,56,3,7,30],outstand:7,response_class:[3,34,60],sysloghandl:35,awai:7,irc:[14,17],approach:[14,64,48,36,60,57,43,30,31,63],attribut:[41,1,20,3,31,53,60,5,23,19,6,61,27,45,12,13],inabl:[13,51],accord:40,never:[41,35,1,18,3,23,40,6,7,26,28,30,11,57,45],extend:[47,17,55,21,42,24,58,26,44,61],extens:[14,15,47,17,13,2,51,3,21,38,55,23,19,36,41,7,27,28,58,31,63],html4:[3,1,5],preprocessor:3,wtf:27,upfront:[14,64,30,34],functool:53,subfold:[3,21],"_cider":3,accident:5,expos:[47,51,20,60,3,7,59],gettempdir:28,howev:[0,1,60,5,6,7,8,9,11,12,13,14,17,18,21,23,29,30,31,34,36,20,39,64,47,50,3,45],trap_http_except:[3,31],against:[41,36,60,3,7,32],und:8,logic:[47,41,1,51,56,3,29,11],login:[33,1,2,53,60,27,61,43,44,32],browser:[34,0,1,18,5,3,26,27,45,31,59],do_something_with:3,col:61,serversid:64,con:63,compromis:36,asctim:35,url_root:3,kwd:3,ez_setup:49,loader:[3,21,51],exemplari:50,shortcut:[31,3,0],diff:21,guid:[14,15,38,39,40,5,62],assum:[14,64,48,49,1,20,55,53,21,5,3,39,8,28],summar:5,wsgi_app:[6,1,22,3,7,28],speak:[14,6,1],liabil:50,chrome:51,after_request_callback:29,three:[41,35,1,50,53,60,3,7,13],been:[11,21,5],wsgiprocessgroup:40,trigger:[64,34,6,1,51,3,60,21,57,26,45],unsubscrib:23,interest:[15,38,17,35,1,36,53,21,56,3,28,32,31,13],basic:[9,1,2,4,42,3,61,8,12,14,15,21,24,26,28,31,34,35,20,38,39,44,45,64,47,49,50,54,55,59,62,63],"__doc__":[14,55],hesit:17,quickli:[47,49,3,26,27,31,63],life:6,xbb:[3,1],declare_namespac:[],suppress:3,worker:[5,22],uncommon:[45,59],ani:[1,60,5,6,7,14,17,21,23,19,25,26,27,29,30,31,34,35,18,39,40,45,47,48,49,50,51,3,61,62],cheroke:[62,39],child:[40,61,42,2],upload_fold:[28,3],init_db:[60,19,57,10,11,63],ugli:5,blazingli:19,gnu:39,servic:[45,50,22],properti:[3,47,41,49],can:[0,1,3,4,5,6,7,8,9,10,11,12,13,14,17,18,24,21,22,23,19,25,26,27,28,29,30,31,32,33,34,35,36,20,39,40,41,42,43,44,45,64,47,48,49,50,51,52,53,55,56,57,58,59,60,61,62,63],calcul:[3,53,4,26,39],neat:[53,32],publicli:[1,18],as_view:[3,20],aid:[14,3,51],appus:64,after_request:[14,51,3,60,21,6,29,11],spawn:1,opt:[21,13,51],view_funct:3,sessionmixin:3,systemexit:40,open_sess:3,toolkit:[15,49],remember_languag:29,kwarg:[30,3,53,27],sever:[52,47,5],mako:7,file_handl:35,filestorag:3,incorrectli:51,perform:[17,13,54,3,60,19,6,25],suggest:[14,50],make:[9,1,3,4,5,6,7,8,11,12,13,14,17,24,21,23,19,25,26,27,28,29,31,34,35,36,20,39,40,43,44,45,64,47,49,50,51,55,56,57,60,59,61,62,63],format:[64,15,38,41,35,3,53,57,26,8,0,45,32],"j\u00e4germeist":51,init_app:[14,15,38],complex:[17,35,18,36,53,60,21,55,3,7,8,45],split:[48,23,3,7,30,63],add_language_cod:56,complet:[15,35,1,4,10,31],mongodb_port:19,pick:[23,40,3,35,36],create_engin:63,hand:[14,64,34,13,20,53,51,21,39,19,6,7,27,28,31,32],codebas:[17,36],rais:[1,20,51,21,19,3,28,11,31],unicorn:22,kid:5,kept:[3,41],scenario:55,thu:[14,13],min:27,inherit:[15,38,1,2,5,6,7,42,61,31],contact:17,thi:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,53,55,56,57,58,59,60,61,62,63,64],endif:[61,1,27,44],programm:[6,43,5],everyth:[33,16,41,35,54,3,60,6,7,26,43,28,45,32],setformatt:35,unchang:[28,53,3],protocol:[62,7,1,8],just:[1,60,57,7,8,10,14,16,17,18,19,20,21,24,25,26,28,29,30,31,64,34,35,36,37,39,40,41,43,44,45,46,47,48,49,51,52,55,3,61,62,63],pathdispatch:48,human:[14,35],nowadai:8,yet:[14,48,18,1,36,3,60,5,40,56,6,7,55,43,49,29],languag:[14,29,56,7,36],previous:[24,21,51],xmlhttprequest:3,requestcontext:[3,34,6],easi:[36,64,17,1,2,37,54,21,5,56,6,7,26,27,48,57,32],interfer:1,had:[28,17,21],get_template_attribut:[3,51],lang_cod:56,spread:21,els:[14,33,16,17,35,1,36,20,53,41,56,48,7,8,44,61,57,32],save:[0,1,36,39,23,19,3,8,28,35],explanatori:55,session_cookie_secur:[3,31],sanit:9,applic:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,43,44,45,46,47,48,49,51,52,53,54,55,56,57,58,59,60,62,63,64],assig:56,quicktim:5,mayb:[49,36],preserv:[15,38,6,51],disposit:[45,3],background:[15,16,38,44],shadow:41,add_entri:[61,32],daemon:40,to_python:3,specif:[1,5,3,7,8,13,15,21,22,23,26,28,30,31,34,38,48,49,50,51,53,56,45],arbitrari:[41,1,51,21,3,7,45],manual:[39,63,21,1,2],instanci:[3,48,20],install_requir:[14,55],sqlite3_databas:14,unnecessari:21,teardown_request:[14,34,51,3,60,21,6,57,11,63],underli:[17,6,1,36,21,3],www:[14,64,1,39,40,3],right:[16,35,50,23,56,3,26,28,49,30,11,45],old:[14,0,1,51,4,21,24,6,8,28],deal:[17,35,1,43,3,8,28,31],negat:41,"_db":57,intern:[15,38,48,1,36,51,3,21,55,24,6,8,30,31,45],flatten:3,pain:[3,5],indirect:50,successfulli:[23,3,44,32],transmiss:1,autocommit:63,total:[45,8],ico:0,bottom:[43,16,26,18],get_current_us:3,swfupload:28,jinja_load:3,suffici:[40,3],unit:[3,21],detect_user_languag:29,condit:[40,3,21,50,51],foo:[14,41,6,60,21,3,58,31],localhost:[48,22,19,3,26,45,31,63],core:[15,38,35,36,23,7],plu:3,sensibl:3,bold:16,insecur:36,import_nam:[30,3],about_pag:20,popular:[39,64,9,1,5,22,26,62,31],promot:50,repositori:[64,49],post:[1,20,3,60,21,6,61,27,44,28,45,32],"super":42,chapter:[34,6,36,55,3,43],obj:[45,3],slightli:[40,49,44],surround:3,simul:60,memcachedcach:4,commit:[21,19,57,10,63,32],curiou:[7,36],response_wrapp:3,"float":[3,1],this_is_a_very_long:41,bound:[14,13,3,60,21,23,6],two:[1,4,42,3,7,13,14,15,17,20,22,19,25,26,60,37,43,64,47,41,49,6,45],down:[64,34,3,60,21,23,6,29,11],inject_url_default:3,formerli:[7,55],wrap:[1,53,5,3,7,30,13],bool:3,storag:5,git:[49,21,51],fabric:[64,15,38,2,55,31],wai:[0,1,3,4,5,6,7,8,9,12,13,14,17,18,21,23,19,25,28,29,31,34,36,20,43,44,64,47,49,50,51,52,53,56,57,58,60,45,63],frustrat:[17,35,13],support:[0,1,4,5,3,7,8,9,10,14,17,20,21,22,23,25,26,31,33,36,37,40,47,48,49,51,52,55,58,59],happi:17,avail:[14,46,47,34,35,1,18,51,3,52,53,60,5,23,48,7,55,61,11,31,13],width:16,reli:[23,21,39],editor:[15,38,8],lage:36,fork:[15,38,17,1,36,22],showus:20,head:[15,47,35,1,36,51,3,55,18,5,19,6,42,27,49,31,63,13],creation:[48,51,55,40,43,12],form:[15,38,50,35,1,36,20,60,5,3,7,26,27,44,28,61,45,2,32],sendfil:[3,31],forg:[28,1,22],test_empty_db:60,back:[64,17,1,3,53,21,23,24,34,26,48,30,59,32],"31st":51,"0x104f6ad10":[],sqlalchemi:[15,38,9,8,2,55,19,3,27,35,63],iri_to_uri:3,"true":[14,64,48,1,18,41,3,60,55,23,19,6,57,8,44,28,43,32,31,63,27],preview:51,bugfix:51,maximum:[28,19,41,55],until:[34,1,36,51,3,6,26],fundament:48,temper:1,featur:[33,41,51,53,21,5,19,3,7,55,28],semicolon:49,patchfil:21,hardcod:[3,31,1,26],"abstract":[14,48,2,36,7,63],uri:[62,39,8],calvado:51,exist:[1,60,5,3,11,13,14,17,21,23,24,28,29,31,36,37,43,64,48,49,6,58,45],ship:[14,57,55,50,22],face:[49,45,35],check:[1,60,3,61,10,14,15,18,21,28,32,33,35,36,39,43,64,47,41,49,56,6],inde:60,vista:49,register_modul:3,author_email:14,formatexcept:35,tip:36,refactor:[51,21,20],sysv:39,test:[14,15,16,34,1,33,3,31,51,21,38,23,19,6,7,60,54,12,63,64],webob:7,unlimit:28,runtim:[31,3,21,5,51],notif:23,intend:[14,47,48,13,60,23,3],relas:51,notepad:8,session_interfac:3,scale:[15,38,17],intent:[47,20],consid:[14,15,47,17,9,1,20,60,38,5,40,56,3],sql:[2,37,3,19,57,7,10,63,32],entrust:36,receiv:[45,3,1,51],faster:[28,26,5],furthermor:[64,17,1,50,56,7,26],anywher:[29,45,36],getjson:26,pathnam:[3,35],time:[1,4,5,6,7,10,12,14,16,21,22,23,29,31,34,35,36,20,64,47,41,49,51,53,3,60,45,59],push:[34,6,51,3,45,31],backward:[49,51,21,55,3,5],my_dict:41,concept:[14,15,47,13,36,21,38,56,7,45,31,64],make_null_sess:3,ron:50,skip:[3,21,1],global:[15,38,17,1,3,21,23,40,6,26,13],focus:[15,38],invent:53,osi:14,subclass:[17,35,36,20,51,19,3,7,28,31,63],"3em":16,"_perman":3,millisecond:35,decid:[14,17,1,51,3,7,26],hold:[3,6,31,1],depend:[1,2,60,57,7,13,14,15,17,21,24,31,34,36,38,39,40,41,43,64,47,48,49,51,52,55,3,58],flask:[0,1,2,3,4,5,6,7,8,11,12,13,14,15,17,18,24,21,22,23,19,25,26,27,28,29,30,31,32,33,34,35,36,20,38,39,41,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64],render_field:27,flash:[15,16,18,1,2,51,21,38,5,3,61,27,44,28,32],before_first_request:[3,51],rightmost:47,decis:[15,38,7,36],isinst:[53,41],sourc:[64,33,47,35,50,36,60,21,22,40,3,8,49,11,31],a_kei:60,string:[1,4,3,7,8,13,20,23,26,27,30,32,60,35,18,37,41,53,56,57,63,59],"_request_ctx_stack":[14,3],x9fr:1,context_processor:[3,13],artwork:[15,38,50],word:[41,60],exact:[17,39],cool:3,administr:60,level:[14,47,48,35,36,41,60,22,6,45,11],tear:[23,34,3],"12th":51,leftov:51,brother:24,get_my_item:4,item:[41,4,55,23,3,61,59],greenlet:[17,7,22],cooki:[1,51,60,3,8,29,45,31],div:[61,42,5],defaultx:60,attachment_filenam:3,declarative_bas:63,prevent:[45,21,55],xhh:1,slower:1,htm:[21,13,51],a0zr98j:1,sign:[3,41,9,1,32],cost:3,group1:40,run:[1,2,4,6,7,12,13,14,17,21,22,19,25,26,28,31,34,35,18,39,40,43,45,64,48,49,51,52,55,3,60,62],port:[36,22,40,3,26,31],ioloop:22,appear:[23,7,5],has_static_fold:3,repli:1,favour:[24,7,8],current:[1,2,5,3,7,11,13,14,21,23,19,57,29,30,34,36,39,64,47,49,51,53,6],sinc:[1,36,20,21,5,3,7,39,31,59],blueprint_nam:47,teardown_request_func:3,redirect:[14,15,47,48,0,1,18,51,3,53,60,38,6,7,27,44,28,32],deriv:50,gener:[1,60,5,3,13,15,21,26,27,28,31,34,20,38,43,47,41,50,51,53,55,56,58,59,45,63],"0x104e3d590":[],hairi:31,slow:4,modif:[3,50,54],send_fil:[3,21,51],address:[35,8,18,22,27,45],skeleton:[15,38,60,61,42],along:[18,5,39,58,55,62,11],"0x104bb5650":[],box:[33,10,35,26],max_content_length:[28,3,31,51],model_sav:23,make_respons:[3,6,1,51],rethink:21,bot:36,errorhandl:[24,3,21,1],"__repr__":[19,63],user_view:20,overriden:3,weird:45,commonli:[3,26,55],ourselv:59,novic:24,semant:[21,5],regardless:[3,51],wiki_pag:3,modul:[1,4,6,7,11,14,17,21,19,27,30,31,34,35,18,60,39,40,41,43,46,47,48,51,55,3,58,62,63],prefer:[62,7,63,8],fake:[3,34,6,7],instal:[1,2,60,3,10,13,14,15,21,23,19,26,28,31,39,40,64,38,49,51,52,55,58,62],url_default_funct:3,mobil:1,codenam:51,market:5,memori:[14,1,3,4,6,28,31,59],long_descript:[14,55],call_after_request_callback:29,current_app:[23,6,12,3],record_onc:3,everybodi:[34,17,55],necesari:3,live:[15,38,3,31,49],handler:[14,35,2,51,3,31,21,57,23,24,6,25,30,12,45],msg:[3,35],black:[24,1],checkout:[14,49,1],unsaf:3,configmodul:31,incident:50,peopl:[14,17,1,36,53,21,5,24,7,45,63],claus:50,prototyp:3,effort:63,easiest:[34,49,1,4,42,31],content_typ:3,fly:[3,13,59],graphic:5,judg:1,uniqu:[17,0,1,53,23,3,7,63],cat:45,zhao:50,whatev:[3,50],umlaut:8,purpos:[50,17,36,8],problemat:[14,6,22],underestim:36,favourit:[64,60],stream:[15,38,2,40,3,7,59],backslash:[41,51],levelnam:[3,35],topic:[14,45,43],critic:35,abort:[47,1,18,51,5,23,24,3,28,32],immutabledict:3,"2px":16,unfortun:[31,53,21,45,36],occur:[1,51,3,23,6,11,32],methodview:[3,20],alwai:[14,41,0,36,3,53,39,57,23,40,56,6,7,55,8,28,25,62,31,45,58],peek_path_info:48,multipl:[14,47,48,35,1,20,3,31,4,21,43,6,7,27,52,49,12,63],filename_or_fp:3,myapplic:[3,48,31,1],charset:[3,8,51],write:[39,48,0,27,36,20,3,21,5,22,23,56,6,7,26,8,49,35,30,31,32],anyon:[55,50],xhtml:[15,38,13,51,21,5,3],testingconfig:31,tbodi:5,that_returns_an_object_with_an_attribut:41,map:[2,51,53,20,56,3,30,63],product:[15,38,48,35,1,18,50,52,21,6,49,31],proc:39,max:[39,27],dive:[15,38,6,5],namespace_packag:[],relev:[21,3,0],mac:49,xzf:64,mixin:[3,41],is_xhr:3,date:[3,49,5,51],implicit_sequence_convers:3,underscor:41,data:[9,1,60,5,3,8,15,17,21,23,19,26,27,28,31,35,36,39,38,50,51,55,6,59,45,63],grow:11,man:39,practic:[14,15,38,2,29,31],"6th":51,goal:[36,5],quot:[45,41,60,5],explicit:[15,38,0,13,21,7,31],inform:[1,2,6,7,10,11,13,15,21,19,25,26,27,28,32,35,18,20,39,40,41,64,38,48,49,51,53,55,56,3,45,63],"switch":[14,36,18,51,60,39,31],combin:[64,17,1,2,3,44,56,6,57,60,48,30,45],callabl:[62,3,7],untest:60,tfoot:5,talk:8,securecookiesessioninterfac:3,shelltool:34,fafafa:16,still:[1,4,5,3,7,13,14,21,29,30,33,34,35,36,60,43,48,53,55,56,6,58,45],pointer:3,process_respons:[3,34,60],dynam:[14,48,1,26],entiti:[3,13,26],group:[40,11,43,5],thank:[64,3,7,27],how:[0,1,3,4,5,6,7,10,11,12,13,14,15,21,23,19,26,27,28,30,31,32,60,35,36,20,38,39,43,44,64,47,48,49,54,52,53,55,57,58,59,45,63],endautoescap:13,instantli:60,platform:14,window:[15,38,35,51,21,3,26,49,45,31],pythonpath:40,mail:[15,38,35,51],main:[46,34,36,20,60,3,48,63],get_cookie_httponli:3,non:[41,5,22,40,3,8,31],env:[64,40,49],encod:[15,38,41,3,26,8],supersed:5,initi:[14,15,47,34,49,18,2,3,60,38,57,10,11,64],half:27,recap:64,now:[0,1,3,4,6,7,10,11,14,16,18,21,23,26,27,28,29,32,60,35,36,43,64,49,51,53,54,57,61,45,59],introduct:[1,2,21,43,28,13],term:[30,17,36,5],searchpath:[47,21],name:[9,1,4,5,3,7,0,10,12,13,14,15,17,19,20,21,23,24,26,27,28,30,31,60,35,37,38,39,44,64,47,41,49,50,51,53,55,56,61,45,63],opera:5,perspect:20,drop:[46,34,35,18,37,51,57,26,31,63],separ:[64,47,17,35,18,40,3,39,41,19,48,25,26,27,49,62,31,63],license_fil:41,x13:31,attributeerror:[6,21,51],compil:40,domain:[3,31,48,0,42],replac:[41,49,13,20,3,55,53,21,5,40,6,61,26,29],individu:[60,3,31,27,20],continu:[14,46,16,41,9,1,18,37,51,21,55,3,61,10,11,32],ensur:[14,64,1,60,21,43,19,3,7,26,8,61,45,11],contrib:[4,1,22],redistribut:50,handle_http_except:3,year:[41,26],happen:[14,35,1,36,3,60,21,23,24,6,7,8,29,25,45],turbogear:53,secretview:3,hacker:[28,45,3,1],shown:[60,1,32],accomplish:[30,13],"3rd":45,space:[41,49],profit:[21,50],yourappl:[14,64,47,35,40,3,31,39,22,19,57,25,55,43,30,12,62,63],as_attach:3,item_count:41,profil:[45,1,59],acccess:3,internet:[14,24,5],ironi:28,app_errorhandl:3,uwsgi:[15,62,38,39,52],integr:[17,13,51,21,22,23,19,3],unfriendli:64,earlier:[1,51,21,55,6,58,26,10,31],state:[3,45,6,5],runtimeerror:[3,21,51],million:45,xhtml5:5,ajax:[15,38,26,2],orm:[19,63,8],mime:5,theori:50,org:50,"byte":[45,34,31,8],fastcgi_split_path_info:39,render_templ:[47,1,18,20,53,24,3,61,26,27,44,12,32],enable_buff:59,reusabl:[17,7],"0x1047d3190":[],setdefault:[14,56],geoloc:5,userapi:20,synchron:22,refus:5,recov:5,thing:[9,1,2,4,5,6,7,8,11,13,14,17,21,23,24,26,27,28,30,31,34,36,39,40,43,64,53,3,60,45,59],get_db:14,place:[1,3,10,11,12,13,14,21,26,28,30,31,64,34,35,18,20,46,41,51,6,45],guess_language_from_request:29,okai:1,principl:[15,38,36,20,28,29],imposs:[35,1],lambda:41,oper:[14,47,34,49,1,51,3,4,41,7,26,62,31],info:[3,35],directli:[46,59,60,9,1,18,40,3,51,21,23,19,57,7,26,28,25,10,31,63],template_nam:[53,3,59,20],carri:13,onc:[14,47,48,9,1,51,4,21,39,56,3,58,26,60,49,35,31],arrai:45,yourself:[15,38,34,1,36,60,21,39,40,56,3,8,30,31,45,59,27],fast:19,open:[14,47,41,18,2,36,3,60,22,57,8,49,10,11,31],angular:3,size:[16,9,36,61,26,28,59],given:[47,17,1,18,60,5,56,3,26,62,13],silent:[3,31,18,51],convent:[15,38,41,55,36],gif:28,teardown:[14,15,38,51,3,60,21,23,6,57],after_this_request:29,caught:[3,6,21],traction:5,max_length:19,test_client:[23,6,21,60,3],"0x104e4c890":[],white:[28,24,16,1],conveni:23,friend:45,includ:[14,41,50,51,55,21,39,3,5,62,45],hub:51,especi:[14,1,20,23,56,7,26],copi:[14,17,49,1,42,25,55],undoubtedli:5,specifi:[14,47,9,1,18,20,51,5,23,3,26,8,28,0,32],decorated_funct:53,blacklist:28,github:[58,49,26],mostli:[14,15,38,6],holder:[3,50],than:[47,17,49,1,36,51,3,53,21,5,19,57,7,55,8,31,59],png:[28,50],serv:[48,0,1,21,5,3,28,62],wide:[15,38,49,51,40,3,7,26],scriptalia:25,were:[34,1,51,3,4,21,5,23,24,6,7,26,60,44,45,32],posit:[3,8],seri:49,pre:[64,3,21,60,22],blueprint:[15,47,2,51,12,21,38,56,3,43,31],sai:[47,1,4,55,23,6,43,45],productionconfig:31,san:16,nicer:[28,3,27],pro:[28,15,38,17,26],argument:[47,41,1,3,4,23,57,26,27,44,11,13],properli:[64,47,1,51,5,3,7,8,30,45,63,13],deliv:1,notimplementederror:20,django:[19,48,31,63,20],engin:[15,38,1,36,51,4,5,3,7,42,59,25,30,45,63,13],techniqu:48,destroi:[47,6],jinja_opt:3,note:[14,15,38,48,35,1,50,3,60,21,39,61,19,6,57,26,27,49,32,31,13],altogeth:13,ideal:[47,1,55,22,3,26,45,31],maintain:[6,7,50],take:[17,1,36,20,3,4,21,27,48,7,8,45,11,13],advis:[3,50,43],green:22,wonder:[19,11,1,63],noth:[33,17,24,3,44,32],channel:[14,17],connect_via:23,infinit:51,begin:[15,38,17,2,51,3,60,57],sure:[1,3,4,57,8,13,14,21,23,24,25,28,31,32,35,36,39,40,43,45,64,47,49,51,55,56,6,59,62,63],importantli:[14,35,1],normal:[47,34,1,51,4,3],track:[3,60],toplevel:[45,3],micro:[15,38,7,36],pair:[23,3],homepag:42,icon:[49,0],renam:[17,43],textarea:61,rescu:49,adopt:5,raquo:[3,1],create_al:63,pattern:[14,15,47,48,1,2,31,53,21,38,55,19,3,27,28,12],registrationform:27,shortnam:21,salt:41,gracefulli:23,show:[51,15,47,60,9,1,33,40,20,21,5,19,3,58,26,49,45,32,64,13],error_handler_spec:3,rendit:7,my_index:53,developmentconfig:31,bright:7,concurr:[45,17,1],permiss:[40,35,50],hack:7,help:[14,47,0,1,36,20,3,60,23,40,56,6,7,8,43,45,32,31,13],xml:[13,51,21,5,3,7,26,61],onli:[9,1,3,60,6,7,8,10,11,13,14,17,21,28,30,31,32,33,34,35,36,37,39,43,44,64,47,48,49,51,53,55,57,61,45,63],explicitli:[64,1,20,51,21,39,56,3,55,10,31,45,13],transact:[10,3,60,63],activ:[14,50,34,18,1,36,51,3,60,21,40,6,58,49,31,64],enough:[47,9,1,36,20,4,39,3,26,44,2,32],dict:[14,13,40,3,53,60,19,57,61,26,31,63,32],zip_saf:[14,55],hello_world:1,class_kwarg:3,offici:[14,35,1,63,13],startswith:3,nearli:[19,7,1],variou:[36,50,51,21,55,58,31],get:[0,1,2,3,4,6,61,10,11,12,14,15,17,18,21,22,23,19,26,27,28,29,31,32,33,34,35,36,20,40,44,46,47,48,49,51,53,57,60,59,45,63,64],soon:[17,36,5],cannot:[14,47,49,1,20,12,55,23,3,61,10,31,45],url_for:[47,0,1,18,3,53,21,42,24,56,6,61,26,27,44,28,32,13],requir:[0,13,2,60,5,3,7,8,10,14,21,19,27,28,31,34,35,36,20,40,64,41,51,53,55,58,63],mapper:[19,63,36],yield:[23,59],stupid:45,adminx:60,where:[1,2,3,57,7,11,14,17,21,23,25,26,28,29,31,64,34,35,36,20,40,43,46,47,51,53,55,56,6,45,63],wiki:[40,3,1],"__module__":[30,53],from_envvar:[64,3,31,18,51],testcas:60,concern:36,"0x104e3a650":[],test_request_context:[34,6,1,60,3,57],detect:[3,21,51],foreword:[15,38,36],review:[14,21],enumer:[57,21],label:27,getattr:[29,57],teardown_app_request:3,between:[14,48,49,1,41,3,51,21,19,6,26,31],"import":[0,1,3,4,5,6,7,10,12,13,14,15,17,18,24,21,22,23,19,25,26,27,28,29,30,31,34,35,36,20,38,39,40,42,43,44,64,47,48,51,53,55,56,57,58,59,60,45,63],across:[47,39],shutdown_sess:63,plupload:28,assumpt:[3,17,6,8],parent:42,screen:[3,39],"_get_current_object":[23,6],come:[1,60,5,6,7,8,11,13,21,23,24,31,33,34,18,20,48,64,41,55,56,45],vimrc:8,similar:[64,48,1,20,3,4,39,23,41,25,26,8,60,7,63],afshar:50,contract:50,emit:[23,3,21,51],uploaded_fil:[28,1],audienc:14,tutori:[46,15,38,64,33,60,11],improv:[15,38,17,2,36,51,3,31,60,21,5,56,34,28,12],test_client_class:3,among:5,undocu:21,color:[16,42],overview:[3,1,5,44],unittest:[14,34,1,50,51,31,54,21,23,3,7,60,12],inspir:[7,43,20],equalto:27,pop:[34,1,51,3,56,6,31,32],exploit:36,colon:41,from_form_data:20,supervisor:39,poll:28,damag:50,better:[17,0,1,20,49,51,21,5,40,3,7,26,60,44,28,35,55,31],sess:60,coupl:[17,35,13,3,31,21,5,23,19,6,27,28,45,12],resort:17,invers:21,mark:[14,1,36,3,5,24,57,61,8,28,29,11,32,13],"0x104f1cb90":[],addit:[15,38,53,60,21,3,63,32],valueerror:21,thead:5,thousand:22,rubi:[7,22],enable_modul:3,those:[17,9,1,42,3,7,35,31],stream_templ:59,sound:[53,42],mount:[47,0,39],include_package_data:[14,55],trick:[15,38,34,20,60,30,45,59,32],invok:[14,1,51,22,23,3,59],connect_db:[10,11,57,18],got_request_except:[23,3],schranz:50,margin:16,advantag:[48,20,21,55,23,40,3,7,26,27],stdout:40,canon:[3,7,1],show_al:14,worri:1,endswith:48,sqlite3_db:14,myapp:[62,31,26],"return":[0,1,3,4,6,7,8,11,12,13,14,15,24,21,22,23,19,26,27,28,29,30,31,32,34,35,18,20,38,41,43,44,47,48,51,53,56,57,60,59,63],ascii:[41,8],"__init__":[14,48,1,20,3,22,41,43,30,31,63],yourfil:55,httpserver:22,develop:[1,2,4,5,6,7,8,14,15,17,36,21,23,26,28,31,33,60,18,38,48,49,50,51,52,55,3,58,45],author:[14,15,38,41,50],same:[0,1,60,5,6,7,8,12,13,14,18,21,23,19,26,30,35,36,20,39,41,47,48,49,51,53,55,3,61,45,63],binari:[41,50,8],html:[9,1,2,60,5,3,61,8,0,13,15,21,23,24,26,27,28,32,33,36,20,38,42,43,44,47,51,53,45,59],pad:16,subdomain:[47,48,2,51,3,31],frazier:50,finish:[49,54],webserv:[17,0,1,3,39,40,48,7,28,25,12],screenshot:[9,1],nest:[3,41],someon:[28,45,18],unhandl:[3,6],baseconvert:3,http_host:[48,22],mani:[14,17,49,1,36,5,22,41,7,27,28,31,63],extern:[64,15,49,1,18,51,3,45],thehandleryouw:35,permanent_session_lifetim:[3,31],appropri:[14,64,60,21,8],moder:14,fastcgi_path_info:39,megabyt:28,macro:[3,7,13,27],markup:[1,5,3,61,45,13],rotatingfilehandl:35,this_is_never_execut:1,without:[14,50,47,49,1,18,51,3,60,21,5,19,6,7,55,8,30,31,45,59,64],xb4:31,promis:35,model:[20,22,23,19,3,63],grappa:51,roughli:[64,3],ccc:16,create_url_adapt:[3,51],send_from_directori:[28,3,0],wiki_fold:3,execut:[1,3,60,6,10,11,14,21,22,23,25,28,29,31,32,34,18,20,39,40,64,47,49,51,56,57,45,63],when:[9,1,3,4,5,6,7,8,10,12,14,18,21,22,23,24,26,27,29,31,32,34,35,36,20,42,43,44,47,51,53,56,57,58,60,45,59],resp:[6,21,1,60],xiaohong:50,rest:[3,20],genshi:[7,5],touch:[14,64,31],speed:4,versu:[15,38,1,5,63],display_nam:41,hint:[15,38,11,20],except:[14,47,48,35,61,19,3,51,21,41,23,24,6,57,8,28,60,10,11,31,63],littl:[49,18,36,51,3,53,60,19,57,26,27,28,45,31,63],desktop:49,blog:9,from_pyfil:[14,31,3,12,51],vulner:[36,32],earli:[14,35,1,23,24,31],around:[15,38,34,35,1,36,60,3,4,21,5,6,7,39,43,50],read:[14,47,34,35,1,36,3,53,21,5,57,55,8,28,10,31,27],outermost:53,"0x104bef610":[],dispatch:[15,47,48,2,20,3,51,21,38,6],generate_large_csv:59,amp:3,mod:40,silverlight:28,whitespac:[3,41,5],has_request_context:[3,51],integ:[3,31,1,63,37],server:[0,1,4,5,6,7,8,10,15,17,18,24,21,22,19,25,26,28,31,35,36,39,40,45,64,38,48,49,51,52,55,3,62,59],either:[0,1,3,8,13,14,17,21,19,25,26,29,31,36,20,40,64,47,49,51,55,6,45,63],yourconfig:3,manag:[14,47,49,1,39,23,40,55,27,45,31],fulfil:3,my_file_object:21,friendfe:22,wsgiapplicationgroup:40,use_reload:[3,48],url_prefix:[47,3,12,56],dispatchermiddlewar:48,is_null_sess:3,brokenpackag:55,definit:[15,38,3,1,50],achiev:[28,17,51],legal:[15,38],x3c:26,inject:[13,3,53,21,56,57,45,32],searchword:1,unbeliev:61,complic:[42,5],refer:[14,15,38,0,50,36,3,6,7,43,31],home_username_:28,power:[17,1,42,22,5,10],gztar:64,inspect:[3,31,60],broken:60,found:[36,50,1,18,55,24,3,10,31,62],"__name__":[1,60,3,7,12,14,19,25,26,28,30,31,18,39,40,43,64,47,53,56,6,62],referr:6,"throw":4,valid_login:1,comparison:[33,41,7],central:[47,2,51,3,7,30],greatli:[47,21,36],total_second:[],microframework:[14,17,7,36],stand:22,act:3,luck:0,processor:[15,38,13,2,51,21,56,3],road:6,lazyview:30,convert_unicod:63,effici:3,status_cod:[23,3],surviv:3,show_us:[3,20],runawai:51,the_templ:59,strip:[64,3],your:[0,1,4,42,6,7,8,9,10,11,12,13,14,17,18,24,21,22,23,19,25,26,27,28,30,31,32,34,35,36,60,39,40,43,45,64,47,48,49,51,52,53,55,56,3,58,61,62,63],certainli:35,log:[15,38,9,1,2,51,53,60,21,39,61,3,7,44,35,45,32],grindstaff:50,use_debugg:3,strict:[15,38,50,5,19],interfac:[15,38,48,49,1,51,60,21,5,3,7,8,44,45],low:60,lot:[14,17,9,2,36,20,3,21,55,56,6,59,28,35,31,63],instance_root:31,strictli:[14,6,5],machin:[64,1],nteventloghandl:35,ago:[28,26],tupl:[41,35,1,3,63,32],bundl:7,regard:[14,17,48],cryptograph:[3,1],longer:[17,1,41,3,51,21,39,56,6,7,60],pull:[64,56,3,49,55],tripl:41,mathml:5,possibl:[0,1,60,3,61,10,12,13,14,18,21,29,31,36,44,49,50,51,53,55,57,7,45],"default":[1,60,3,61,8,13,21,19,26,28,31,32,35,18,20,39,44,47,48,49,51,53,55,6,59],start_respons:[3,6,48,22],add_numb:26,preconfigur:[1,36],embed:45,elegantli:11,mongodb_host:19,formattim:35,gone:[24,21],creat:[9,1,2,3,4,5,6,7,10,12,14,15,16,17,20,23,19,25,26,27,29,31,33,34,35,18,37,38,39,40,43,46,47,48,49,51,52,53,55,57,60,45,63,64],certain:[35,1,2,51,3,53,60,23,6,7,45,31],yourapplication_set:[3,31],strongli:[3,21,36],uppercase_with_underscor:41,file:[0,1,2,3,60,5,6,7,8,10,12,14,15,36,21,25,26,28,30,31,64,35,18,37,38,39,40,43,45,46,47,41,49,50,51,52,55,57,58,62],ocurr:3,fill:[45,3,42,36],again:[64,2,3,60,39,40,6,7,55,8,45,32],logger_nam:[3,31],googl:[30,25,26,51],idiom:14,valid:[15,38,48,1,36,51,60,5,19,3,27,28,45,2,31],class_arg:3,ignor:[34,1,51,40,3,45,11],you:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,18,24,21,22,23,19,25,26,27,28,29,30,31,32,33,34,35,36,20,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64],poor:5,registri:[14,3,58,55],brandl:50,sequenc:3,docstr:[15,38,41],testproject:47,register_error_handl:[3,21],reduc:[56,5],assert:[48,1,51,60,23,3],unbind:3,directori:[46,64,0,51,21,39,40,3,7,55,8],connected_to:23,bindaddress:39,tricki:3,adam:50,potenti:29,escap:[1,51,3,61,26,27,45,13],degrad:23,represent:[3,13],all:[9,1,60,5,6,7,0,11,13,14,17,18,21,23,24,25,26,28,30,31,32,35,36,20,40,41,42,43,44,64,47,48,49,50,51,55,56,3,61,45,63],dist:64,consider:[15,38,45,13],ali:50,flask_simplexml:14,lack:[21,5],month:3,rebind:41,scalar:41,runserv:43,higher:49,abil:[64,13,51,21,3,28,31],follow:[9,1,60,5,3,7,10,11,12,13,14,15,21,23,24,27,28,29,31,64,35,18,37,39,40,43,45,46,41,49,50,53,61,62,63],blueprintnam:21,edg:[15,38,49],extra:[14,7,3,0],chain:29,script_nam:[62,39],articl:[45,0,5],init:39,program:[14,24,7,35,36],hasattr:[14,29,57,21,3],rstrip:48,neglig:50,introduc:[14,15,47,9,33,20,21,38,56,51,31],"case":[1,2,60,5,6,7,8,10,12,13,14,17,21,23,24,26,30,31,34,18,39,41,43,64,47,48,51,53,55,3,63],liter:[45,8],straightforward:[64,53,5,3,28,12],fals:[14,64,41,1,3,21,55,57,26,27,31,63,13],subcommand:64,minitwit:60,offlin:5,util:[47,6,21,1],"1px":16,verb:[1,20],mechan:28,failur:3,fab:64,otherlibrari:35,list:[14,16,41,35,1,50,20,3,51,55,23,19,57,58,26,27,29,31],helloworld:14,collect:[19,47],sane:59,stderr:[40,3],small:[15,41,35,18,36,20,21,6,26,43,28,31,32],preserve_context_on_except:[3,6,31],secret_kei:[31,3,21,1,18],default_config:[64,3],ass:49,unreview:36,pk_type:20,past:[14,17,1,21,3,7],syslog:35,zero:[49,35],design:[14,15,38,17,36,51,60,6,7,27,29,62,31],pass:[1,60,57,7,11,12,13,14,21,23,24,27,29,30,31,32,35,36,20,39,64,41,51,53,3,59,61,63],further:[14,15,38,34,35,51,3,53,56,6,30,12],gunicorn:[52,22],mymiddlewar:3,sub:25,section:[64,15,50,18,1,36,54,21,40,7,43,49,13],abl:[64,17,18,51,3,21,23,57,7,28,45,31],brief:41,overload:[24,35],forc:[3,31,6,7,55],delet:[64,60,1,20,51,24,3,7,43,45,32],version:[14,15,38,17,49,64,50,51,31,21,39,22,23,3,58,26,28,55,12],primary_kei:63,use_evalex:3,row:[61,32,57,59,37],"public":[1,51],contrast:[23,6],full:[33,48,35,1,50,51,52,4,19,3,60,44,28,31],themselv:[14,35,3,31],unmodifi:3,behaviour:[14,1,51,5,19,3,7,26,13],modular:[15,47,38,3,43,12],shouldn:7,simon:50,goodby:41,strong:[61,3,60,1,44],modifi:[15,38,17,1,50,40,3,31,51,21,23,19,6,60,28,29,62,11,12,45],newserv:64,valu:[1,3,4,5,6,7,11,12,13,14,15,21,22,23,19,26,27,28,31,32,60,18,20,44,38,41,49,51,53,56,57,61,45],search:[49,1],rendertemplateview:20,sender:23,ahead:[64,7,51],memcach:4,prior:50,amount:[28,30,17,53,59],social:45,action:[1,23,61,27,44,28],pop_path_info:48,x0f5:31,server_nam:[3,31,51],via:[14,46,47,0,21,3,27,31],packagenam:14,after_request_func:3,primit:26,transit:[14,15,38,21],"0x105024550":[],vim:8,deprec:[14,21,3,7,51],href:[0,42,24,3,61,26,44,5,45],famili:16,suddenli:[30,17,1],"0x10355cb50":[],handle_error:21,select:[63,57,26,8,32],helper:[14,34,13,51,3,23,57,27,10,30],internation:[56,2],liber:14,regist:[14,15,47,34,13,36,24,20,38,55,19,3,27,28,29,51,31,45,63],libev:22,taken:21,md5:41,send_static_fil:3,toggl:31,more:[9,1,2,3,60,5,6,7,10,13,14,15,17,18,24,21,23,19,25,26,27,28,31,32,34,35,36,20,39,40,64,47,41,49,50,51,53,55,56,57,59,45,63],guesss:21,precompil:41,templatenotfound:47,desir:1,signals_avail:3,mozilla:5,myproject:[49,22],flag:[14,1,18,51,60,21,55,3,31],stick:50,broke:51,particular:[6,1,50,3,29,45],known:[3,55,5],sourcecod:[64,33,3,26,50],cach:[15,38,2,51,53,4,3,26],none:[14,16,48,1,20,3,53,4,41,56,6,57,44,29,11,31,63,32],endpoint:[47,1,2,51,53,20,21,56,3],outlin:[21,19,3,7,10,45,63],dev:[14,49,31],histori:[15,38,5],remain:30,learn:[14,15,38,17,33],before_app_first_request:3,def:[0,1,3,4,6,7,8,10,11,12,13,14,24,21,22,23,19,26,27,28,29,30,32,60,18,20,41,43,44,64,47,48,53,56,57,59,63],someobject:3,factori:[14,15,38,48,2,60,40,7,12],prompt:49,scan:21,duplain:50,registr:[15,38,51,20,21,3,27],share:[14,47,3,55,6,61,5],save_sess:3,accept:[1,51,23,3,26,27,28],compani:55,minimum:[14,55,8],unreli:[21,3,7,51],explor:5,"3yx":1,cours:[64,17,0,1,20,8,44,45,12,27],newlin:8,awkward:29,secur:[15,38,18,1,36,51,22,40,3,28,45,32,31,13],rather:[15,47,3,1,19],anoth:[14,47,48,35,1,51,3,55,21,42,22,23,56,6,7,26,45],snippet:2,reject:31,csv:59,simpl:[1,2,3,4,42,6,14,22,19,26,28,30,33,60,35,36,20,41,43,44,64,47,48,53,57,58,63],css:[46,16,1,47,42,3,61,43,45],unabl:21,resourc:[15,47,34,2,40,51,38,55,24,56,3,7,10],flip:[31,51],reflect:[3,17,7],url_value_preprocessor:[56,3],embrac:36,datastor:36,unlink:60,associ:[47,6],list_templ:51,"short":[14,23,64,28],footer:[1,42],roundtrip:59,render_large_templ:59,ambigu:7,caus:[14,50,17,1,36,51,3,21,5,23,40,6,7,28,45,13],callback:[15,38,17,1,2,51,3,21,6,29,34,45],rotat:35,max_ag:3,hypertext:5,kitten:45,autoload:63,through:[47,3,31,1,51],paramet:[14,47,41,1,20,3,55,51,21,39,40,56,6,26,32,31,13],style:[15,16,1,33,47,42,3,61,43,38],overhead:5,appl:[3,1,5],view_func:[30,3,20],late:[28,30,1,2],segfault:40,"0x105037bd0":[],bypass:59,might:[9,1,5,6,11,14,21,22,23,24,25,27,28,30,31,32,34,35,36,20,39,40,41,42,44,45,47,48,51,55,56,3,59,62,63],alter:[7,51],epol:22,finer:[6,51],good:[9,1,3,4,5,57,7,10,11,14,21,23,24,28,31,32,60,35,18,20,43,44,64,41,49,50,53,6,45],"4c271729e13823182f000000":19,timestamp:3,fidl:3,"_get_current_us":3,framework:[17,35,36,23,3,7,45],rsplit:[28,30],complain:[3,18,51],"_helper":13,document:[0,1,2,60,5,6,7,8,13,14,15,24,21,19,25,26,28,31,34,35,36,41,42,43,64,38,48,50,51,52,54,3,58,45,63],troubleshoot:[40,10,52],unlik:[49,5],authent:[45,3,36,2],did:[14,35,1,51,3,60,21,24,6,43,10,63],token:45,http_server:22,undertak:7,fulli:[55,51],unicod:[15,38,41,1,36,19,3,7,8,27],ifi:[],after_app_request:3,use_x_sendfil:[3,31],weight:16,hard:[14,36,18,60,5,39,27,45,1],idea:[1,3,4,57,7,10,11,12,13,14,17,18,24,19,26,28,34,35,36,39,43,48,54,53,56,6],procedur:1,realli:[14,48,0,1,36,39,5,23,56,26,8,44,12,53],iter:[35,36,3,7,61,59],event:[35,26,50,22],hostnam:3,"try":[14,47,41,49,13,3,60,55,23,24,6,7,26,27,29],flaskext:[14,15,38,3,58],upload_fil:[28,1],publish:5,payload:28,bubbl:[31,6,21],log_template_rend:23,etag:[3,21,51],lineno:[3,35],print:[41,1,40,23,19,6,57,31],variable_nam:[3,1],url_map:[53,3,56],get_sess:3,name_r:41,instance_relative_config:[3,31],proxi:[15,38,17,1,3,52,60,21,22,23,6],advanc:[33,49,39,40,25,5,62],select_jinja_autoescap:[3,21],guess:[35,18,51,21,3,0,45,31],pleasant:[29,34,57,36,8],quick:[64,1,50,5,3,7,26,43,30,13],reason:[34,1,36,3,5,23,40,6,7,55,43,28],base:[1,2,4,5,57,7,8,14,15,17,21,22,23,19,31,60,36,20,42,38,48,49,51,53,56,3,45,63],put:[0,1,4,3,7,8,11,13,17,20,19,25,26,31,64,34,35,37,40,46,41,51,56,61,63],basi:[3,5,51],thread:[15,38,17,1,36,3,40,6,7,48,11,63],capabl:1,propagate_except:[3,31,51],perman:[28,24,3,31,51],lifetim:[3,31],assign:[3,21],feed:33,pleas:[41,35,1,3,21,39,22,40,57,25,55,62,31,13],major:[64,17,1,60,5,25,8,7],notifi:[23,32],default_set:31,feel:[14,9],exchang:26,number:[64,17,35,1,3,41,26,8,28,31],wsgiscriptreload:40,done:[14,64,34,49,1,55,3,7,26,31,32],least:[49,24,4,31,26],blank:41,miss:[1,51,21,5,23,3,61,26,28],fanci:[24,8],differ:[9,1,60,6,7,11,12,13,14,15,17,21,23,24,26,31,35,36,20,39,40,41,44,64,47,48,49,50,52,3,58,63],php:28,script:[14,15,38,18,1,2,3,55,39,21,5,57,36,26,28,49,10,11,31,45,13],get_cookie_secur:3,interact:[34,1,18,3,21,6,57],construct:[47,55,3,5,45,11],stori:28,underlin:41,order_bi:41,expand:[3,17,27],statement:[64,15,38,34,1,41,3,51,21,6,57,26,60,10,63,32],cfg:[14,31,3,12,64],scheme:[14,21],store:[14,64,17,35,1,36,60,3,53,4,40,34,7,8,28,10,11,31,45,32],schema:[46,15,38,18,2,33,3,37,19,57,10],option:[15,47,48,1,51,52,38,5,22,23,3,39,30,62,13],behind:[11,6,1,5,22],checklist:[64,43],pymongo:[19,2],selector:26,part:[14,15,47,50,1,36,20,3,51,21,38,42,23,56,6,57,26,8,60,45,31],pars:[3,26,5],consult:[28,35,25,40],myclass:41,notfound:[3,48],kind:[14,17,1,36,21,24,3,26,30,31,45],whenev:[49,13,20,23,40,3,7,8],remot:[14,64,51,22],remov:[60,1,51,3,4,21,40,56,6,63,32],riski:31,jqueri:[15,38,3,26,2],bridg:[14,7],str:[19,3,8],consumpt:3,pull_lang_cod:56,when_template_rend:23,cleaner:[6,18,5],comput:[64,49,35,1],deleg:48,favicon:[15,38,0,2],packag:[1,2,60,3,7,10,12,14,17,21,31,64,35,40,43,46,47,41,49,51,55,58,63],uwsgi_param:62,expir:3,mod_rewrit:25,dedic:0,"null":[3,37],imagin:[1,3,53,6,43,28,30,12,45],built:[3,8,51],equival:[61,3,7],remote_addr:[3,22],self:[14,48,20,3,60,5,22,23,19,6,55,41,30,63],blindli:22,client:[15,38,35,1,18,51,3,60,21,22,23,6,26,28,11,59],also:[0,1,2,4,5,6,7,8,9,10,13,14,15,17,18,24,21,23,19,25,26,28,31,34,35,36,20,39,40,44,64,47,48,51,55,3,60,59,61,45,63],before_first_request_func:3,useless:[45,35],on_json_loading_fail:3,brace:41,"35em":16,to_url:3,vnd:0,distribut:[14,15,38,2,50,55,3,58,31,64],unsur:1,setuptool:[14,49,55],previou:[21,51],reach:[6,60],safe_join:[3,51],react:[1,5],most:[0,1,2,5,6,7,8,14,17,24,21,22,23,19,27,28,31,35,36,20,39,40,42,43,64,49,55,3,62,63],plai:[34,8,51],path_info:39,flask_extensionnam:14,enpoint:[],lazili:[15,30,38,2],filesystem:[47,35,1,60,3,8,28,12,59],mymodel:41,cover:[35,1,36,40,3,8],ext:[14,3,58,63,51],exp:41,script_root:[3,26],enctyp:[28,1],usual:[64,47,35,1,36,51,49,5,22,24,3,7,26,8,44,28,25,30,31,45,58],microsoft:0,awesom:[42,5],session_transact:[3,60,51],wsgi:[64,15,47,48,49,1,2,3,52,38,39,22,40,6,7,25,62,12,59],somewhat:[19,30],flaskr_set:18,session:[15,38,34,1,18,51,60,21,23,19,3,7,61,32,31,63,13],estienn:50,font:16,fine:[35,43],find:[64,15,47,17,0,36,2,40,3,21,38,39,19,6,58,55,27,61,30,11,31],nosql:9,copyright:[41,50,42],paranoid:60,solut:[18,36,53,60,39,56,28,2,31,1],templat:[1,2,5,3,7,8,12,13,15,21,23,24,26,27,32,33,36,20,38,42,43,44,46,47,49,51,53,55,61,45,59],factor:[47,17],wsgidaemonprocess:40,yml:51,nullsess:3,remedi:45,xss:[15,38,13,36,28,45],hit:1,ifself:47,albeit:[15,45],sapin:50,"__file__":40,express:[15,38,41,1,50,45,59],with_:3,app_url_value_preprocessor:3,absolute_import:14,myvari:13,liabl:50,cgi:[15,38,25,52],restart:[40,31,1,39],use_cooki:3,rfc:1,common:[0,1,2,42,3,7,8,12,14,15,24,22,23,19,28,29,31,34,36,39,64,47,48,51,53,56,58,63],xe5:1,wrote:[18,5],set:[1,2,4,5,6,7,8,12,14,24,22,23,19,26,28,29,30,31,32,60,35,18,20,39,40,41,64,47,48,49,51,53,55,56,3,62],aspect:36,dump:3,creator:48,cleverli:41,startup:[40,43],mutabl:3,request_tearing_down:[23,3],see:[0,1,2,60,6,7,13,14,24,21,22,19,26,27,28,31,32,35,18,20,39,41,64,47,48,49,50,51,53,54,55,3,62],bare:[14,45,5],arg:[35,1,3,53,60,6,57,26,27,30],close:[14,41,2,20,3,60,21,5,23,57,10,11,45,63],session_cookie_domain:[3,31],glue:17,someth:[1,60,3,7,8,14,24,23,19,25,26,28,4,35,39,40,41,43,46,48,53,57],topmost:6,yoda:41,reus:61,subscript:[23,15,38],experi:[15,38,34,1,31,21,3,8,28,29,12],"15th":51,altern:[20,3,21,55,40,6,44,28,29,31],latin:8,greb:50,javascript:[46,1,5,3,26,28,45,13],isol:[48,49],sigler:50,disallow:[28,24,40],superuser_requir:3,lowercas:[3,8,20],len:[23,19,48],solv:[45,49,36,22],config_filenam:12,popul:[3,31],disclaim:[55,50],last:[17,41,3,51,21,39,40,6,5,28],register_blueprint:[47,3,12],alon:[14,51,26,22],strict_slash:[],context:[14,15,38,17,49,1,20,3,51,21,57,23,6,7,60,34,31,45,59,13],forgotten:53,pdf:[28,51],oldarrai:45,whole:[47,1,36,51,21,56,3,26,31],load:[39,15,38,18,2,20,51,21,5,40,3,7,26,8,30,31,45,63],markdown:13,simpli:[48,0,5],point:[64,47,17,1,20,3,60,55,6,25,8,29,34,31],instanti:[35,47,6,7,53],open_resourc:[3,10,57,7,47],unrealist:5,header:[48,1,51,3,60,39,22,41,61,5,8,45],param:[],shutdown:34,suppli:[14,51],mistak:24,not_found:[3,1],backend:[3,4,35,60,48],add_etag:[3,21],java:28,devic:1,due:[14,17,51,20,21,5,3,45],empti:[53,3,49,42,51],accessor:3,app_context_processor:3,secret:[3,51,31,1,44],make_setup_st:3,damn:24,xab:31,session_class:3,"5em":16,error_handl:21,execfil:40,fire:[15,38,34,49,18,60,7],static_url_path:3,imag:[45,0,50,5],consequenti:50,append:[41,35,1,23,3,49,29],understand:[1,21,42,6,7,5],func:[3,26],demand:[57,21,1,2],repetit:[14,56],wsgiserv:[39,22],convers:[15,38,8],xa8:1,georgia:16,conception:51,pilgrim:5,xa0:1,xa1:1,xa2:1,xa3:31,durat:[10,3],formatt:35,"while":[14,47,35,36,3,53,60,21,5,6,7,28,45,11,63],unifi:21,match:[6,1,55,56,3,27],abov:[0,1,60,5,3,61,12,13,14,23,19,26,27,28,31,32,35,18,64,49,50,54,45,63],error:[1,2,60,5,3,61,10,15,16,21,24,26,27,31,32,35,18,39,40,44,38,48,51,6,45],fun:[64,1,27],loop:[44,61,22],pack:64,propag:[31,51],pound:41,readi:64,ctx:[14,34,6,53,3],readm:14,jpg:28,itself:[1,4,3,7,13,14,17,21,23,26,31,34,35,36,20,43,64,49,50,51,55,60,45,59],x8d:31,coroutin:22,addhandl:35,decor:[15,47,1,2,20,3,53,51,21,38,23,24,56,57,7,43,29,30,11,13],restructur:43,minim:[15,38,7,1],belong:3,decod:[15,38,3,8],sqlite3:[14,9,18,60,57,10,63],lenient:5,mod_wsgi:[64,15,38,52,40],conflict:[49,1],bom:8,appreci:50,optim:[30,25],painless:[21,8],wherea:21,alert:[45,35],moment:[28,11,20],temporari:[64,1,60,39,23,3,28],user:[9,1,2,3,60,5,6,61,13,14,15,17,18,24,21,19,26,27,28,29,30,31,32,33,34,35,36,20,38,39,40,41,44,46,47,48,49,51,53,57,45,63,64],"0x104bfa750":[],chang:[1,60,5,3,8,10,12,14,17,21,23,28,31,35,36,40,64,47,49,51,55,6,45],recent:[63,6,7,55,39],subpackag:55,lower:[47,35,36,51],sha1:41,lib:[31,26],discourag:[14,3,21],older:[14,64,55,58,39,28],find_packag:55,entri:[33,16,17,9,37,60,55,19,61,35,63,32],parenthes:41,expens:53,endfor:[3,61,27,44],the_fil:1,blurprint:3,explan:[1,26],from:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,18,24,21,22,23,19,25,26,27,28,29,30,31,32,34,35,36,20,38,39,40,43,44,64,47,48,49,50,51,53,55,56,57,58,59,60,61,45,63],morsel:3,zaplet:50,collabor:14,mysql:31,love:34,question:[64,0,36,20,3,5,57,7,26,32],move:[18,20,21,39,40,3,25,26,43,29,62,12,45],make_config:3,cur:[14,57,32],code_or_except:3,restructuredtext:41,instance_rel:3,theoret:[45,5],eager:[21,1],armin:50,input:[14,1,5,61,26,27,44,28],desc:[41,32],init_jinja_glob:[3,51],bin:[64,40,25,49,39],vendor:5,transpar:1,big:[15,38,17,36,3,23,48,7,43,45],f0d6d6:16,forbidden:24,nginx:[52,62,39],reitz:50,page_not_found:[24,3,1],bit:[34,49,20,53,39,26,43,28,62,45,63],characterist:14,httpexcept:[24,3],onmouseov:45,template_context_processor:3,marian:50,signal:[15,38,17,51,3,23,6],threadsaf:[1,36],show_user_profil:1,resolv:[3,41,43],httponli:[3,31],manifest:55,intellij:1,gretel:8,api:[14,15,38,1,36,20,5,22,23,3,26,45,64],eee:16,flup:39,jsonifi:[45,3,26],encount:[40,5],before_request_func:3,often:[14,49,1,20,3,53,5,24,57,39,29,31],visibl:[1,18],some:[0,1,4,5,6,7,8,9,10,11,14,15,16,17,24,21,22,23,19,28,29,31,34,35,36,43,38,41,50,51,53,55,3,60,45,59],styleguid:[15,38,41],understood:41,sampl:35,"28th":51,surpris:[3,7,36],virtualenv:[14,15,38,49,64,55,31],instan:51,though:[14,47,1,60,6,7,59],pep:41,per:[48,51,21,19,56,3],libapache2:40,substitut:50,larg:[47,17,36,21,7,8,45,59],slash:[48,1,53,3,26,13],smtphandler:35,reproduc:50,requestentitytoolarg:28,patient:28,sadli:5,funcnam:35,step:[14,15,16,9,18,2,37,33,20,46,55,3,61,38,10,11,64,32],log_request:23,logrecord:35,output:[3,60],dependency_link:55,flaskclient:3,idx:57,materi:50,license_nam:41,simple_pag:47,exc_info:[3,35],block:[1,61,40,60,39,22,23,24,3,7,26,44,25,10,62,42,13],plan:[14,36],"__future__":[14,10],real:[41,3,22,23,34,30],accept_to:27,visit:45,within:[47,17,49,1,36,51,3,11,12,13],"_add_numb":26,keystrok:30,contributor:50,occour:3,occupi:31,inclus:3,span:[3,41,26],spam:36,fledg:28,sock:[62,39],stylesheet:[16,61,42],submit:[1,21,61,27,44,28,45],custom:[15,38,35,1,2,51,3,21,23,24,6,45,13],pocoo:[15,38,41,50],suit:[14,60,9],forward:[13,36,39,22,23,56,3,27,31],xpath:7,doctyp:[1,42,61,5,44,28],fishi:1,server1:64,server2:64,navig:[1,26],serif:16,link:[14,15,47,0,50,36,51,3,55,21,5,24,6,61,26,45,42],translat:1,newer:[15,38,21],http_x_fhost:22,line:[14,64,41,35,18,3,21,5,22,40,6,39,8,11],populate_request:3,utf:[3,41,8],consist:[41,9,1,37,51,21,3,31],caller:20,pkg_add:40,function_cal:41,"export":[31,45,3,21],booleanfield:27,curv:17,constant:41,user1:40,flask_sqlite3:14,capit:14,doesn:[49,7],repres:[3,31],chap:24,septemb:51,guarante:[11,6,3],buffer:[3,59,8],titl:[9,1,37,60,5,24,61,42,44,28,32],xb0:31,invalid:[1,60,21,42,5,44,32],actuali:3,declar:[41,2,20,55,19,3,7,26,45,63],nav:5,librari:[1,4,5,3,7,8,14,15,17,22,23,19,26,27,28,35,36,40,64,38,49,51,55],nice:[14,1,31,53,21,24,28,12],draw:5,clean:[21,51],mongodb:19,svn:21,land:7,mimetyp:[0,1,51,21,3,45,59],svg:[50,5],confirm:27,virtualhost:40,dot:[47,41,49,53,21,19,3,30,31],leak:[3,6],far:[9,36,60,61,6,7,8,35,30,12],xbf:31,hello:[14,15,38,41,1,53,60,5,43,3,7,8],cached_properti:30,pluggabl:[15,47,51,20,38,3,7],code:[1,2,3,60,5,6,7,8,11,13,14,15,17,18,19,20,21,23,24,26,27,28,29,30,31,33,34,35,36,37,38,39,40,64,47,41,50,51,53,55,56,57,61,45,63],partial:[13,36],x95:1,x96:1,queri:[41,35,1,2,20,3,55,21,5,19,57,26,63,32],urandom:1,"0x103dc8090":3,ridicul:26,compact:5,procur:50,elsewher:23,friendli:1,send:[15,38,35,1,51,3,60,23,24,6,26,8,28,45,59],becam:26,aris:[21,50],sent:[1,23,3,26,45,11,59],unzip:64,distribute_setup:55,mous:45,x9c:31,facebook:45,implicitli:[3,6,31],userview:20,tri:[35,51,21,5,3,8,45],magic:[11,64,6,44],button:49,scalabl:22,michael:50,unquot:[45,5],allowed_extens:28,yourapplication_mod:31,freed:7,rack:7,databaseerror:3,x01o:1,malici:[36,22],impli:[50,27],smaller:43,fortun:45,readabl:[14,3,35],natur:17,focu:18,video:5,instnac:[],download:[14,49,51,55,21,5,3,58,26,63],before_request:[14,51,3,60,23,6,57,29,11],fullnam:64,py_modul:14,odd:[35,51],click:[49,26],get_user_for_prefix:48,compat:[49,2,36,51,21,5,19,3,58,55],index:[47,41,1,3,53,21,5,23,24,56,6,7,26,43,44,30,12,42],handle_except:[3,6],compar:[45,41],access:[1,60,3,7,12,13,14,15,6,23,24,27,28,31,18,38,40,44,64,47,41,49,57,61,63],importerror:[3,39],secret_pag:53,get_user_for_subdomain:48,led:51,chose:[64,31,9],fabfil:[64,2],bodi:[16,1,5,23,24,3,61,26,44,42],logout:[33,60,61,1,32],ubuntu:[40,49],striptag:[3,1],becom:[15,47,17,35,1,36,60,38,5,43,3,7,27,31],implicit:[21,45,7,20],convert:[1,2,20,3,53,55,40,6,26,43,30,32,13],activate_thi:40,mailinglist:[14,17],pretti:[14,53,3,60,1],metanav:[16,61],jinja_env:[3,13,59],larger:[64,15,47,17,9,43,2,60,21,38,5,48,7,55,27,28,11,63],technolog:[15,38,36,5],later:[14,46,47,35,18,31,21,55,24,3,26,28,49,10,12],log_except:[23,3],file_wrapp:3,counterapi:3,query_db:57,typic:[14,58,27],explain:[28,64,49,1,20],chanc:[33,49,2,36,3,6,35],cee5f5:16,florent:50,danger:[28,15,38,45,36],appli:[49,1,50,20,55,53,21,39,3,7,26],app:[0,1,3,4,6,7,10,11,12,13,14,24,21,22,23,19,25,26,27,28,29,30,31,32,34,35,18,20,39,40,43,44,64,47,48,51,52,53,56,57,60,59,62,63],foundat:[7,36],base_url:3,apt:[40,49],expect:[14,64,17,54,21,5,3,7,28],"boolean":5,session_cookie_httponli:[3,31],pw_hash:41,"377ba8":16,kenneth:50,zip:[14,21,55,51],commun:[15,38,17,39,36],doubl:[3,45,17,49,41],upgrad:[15,38,21,36,51],next:[1,2,3,10,11,14,21,19,28,30,31,32,18,41,43,44,64,48,49,53,55,6],websit:[14,17,0,27,19,5,24,61,26,8,45,63],few:[14,59,18,27,36,3,21,5,22,24,6,7,26,8,49,45,63],usr:[31,25,39],postprocess:[3,21],flask_someth:14,null_session_class:3,simpler:[62,39,51],start:[1,60,5,6,7,10,14,15,17,18,21,22,23,25,28,29,31,32,33,34,36,20,39,40,42,45,46,49,52,3,58,61,62,63,64],sort:[35,21,7],clever:[49,45,12,36],rich:13,src:[45,26],ap2:40,whiski:51,trail:[3,41,1],agent:5,cheeseshop:55,starter:18,executescript:[10,57],account:[49,7],redirect_to:0,alia:[3,39],cumbersom:1,annoi:56,obvious:[64,47,7,1,13],meet:7,control:[64,15,38,35,1,40,3,51,21,5,24,6,49,45,31,13],sqlite:[14,15,38,1,2,37,33,57,10,31,63,32],malform:[1,51],quickstart:[15,38,3,1],objectid:19,process:[14,3,17,49,36,40,55,52,51,5,22,23,19,6,7,39,27,48,11,12],lock:48,sdist:64,sudo:49,unguess:3,high:[45,2,22],tag:[0,13,51,42,3,5,28,45,26],test_messag:60,login_requir:53,tarbal:[64,55],cygwin:21,lighttpdcgirootfix:[1,39],unus:51,surfac:7,my_macro:13,gaememcachedcach:4,georg:50,subdirectori:36,instead:[1,4,5,57,7,14,21,23,24,29,30,31,60,35,36,20,40,41,43,47,48,49,51,55,3,58,59,45,63],close_db_connect:23,chri:50,static_path:3,follow_redirect:60,flaskrtestcas:60,overridden:3,singular:10,stop:[3,1],trap_bad_request_error:[3,31],await:5,attent:[1,36],pydev:1,flaskext_compat:58,alloc:7,"_preserve_context":3,bind:[14,47,1,51,3,39,22,6,7,26,62,31,63],"0x104efdc50":[],get_appl:48,correspond:[47,49,1],element:[1,42,5,27,45,26],issu:[17,35,36,51,21,22,23,34,7,45],allow:[9,1,60,5,3,61,10,11,18,22,23,28,31,34,36,40,48,42,41,51,53,55,45,63],get_cookie_domain:3,fallback:[3,26],elif:[53,32],insight:14,test_login_logout:60,comma:35,my_sign:[23,6],securecooki:3,perfect:[51,34,31,26,48],chosen:51,whether:[3,50,51],get_flashed_messag:[1,51,3,61,44,13],therefor:1,pixel:0,template_rend:[23,3],crash:35,greater:31,python:[1,3,4,6,7,8,10,11,13,14,15,17,18,21,22,19,25,26,27,31,33,34,35,36,38,39,40,43,46,47,48,49,51,53,55,57,60,62,64],db_session:[63,27],extra_arg:14,innermost:53,dai:[19,3],tellingen:50,mention:[64,1,20,53,55,3,45,31],get_expiration_tim:3,overkil:64,front:51,unregist:47,environbuild:3,somewher:[14,1,40,4,39,24,56,28],auto:[16,3],update_template_context:[3,59],autoflush:63,get_connect:57,trap:[3,51],truth:[3,60],"2em":16,reserv:[41,50],circular:[14,43],from_object:[64,19,3,31,18],"0x104dd1c10":[],intellig:[9,1],chunk:[28,21],sessionmak:63,filehandl:35,item_valu:41,"static":[46,15,16,0,1,36,51,55,61,21,47,39,3,7,26,43,38,42],our:[14,46,9,1,36,60,18,19,26,28,45,11,64],patch:[45,17,21,50,41],x8b83:31,special:[1,50,3,5,19,6,57,26,8,61,11,31,13],out:[9,1,60,3,7,8,0,10,13,14,15,17,18,21,23,25,26,27,28,30,31,32,33,35,36,39,40,41,43,45,64,38,48,50,51,56,61,62],variabl:[64,41,35,1,18,51,3,21,39,57,23,6,7,26,43,49,31,13],matt:50,reload:[64,1,52,40,3,31],before_app_request:3,session_cookie_path:[3,31],identifi:60,trickier:49,categori:[3,51,21,2,44],thoma:50,rel:[47,0,1,21,5,3,7,39,61,31,42],request_finish:[23,3],leverag:13,plural:10,clone:49,red:44,shut:11,umbrella:5,insid:[14,46,48,49,1,18,51,39,40,3,25,26,43,44,7,62,13],standalon:18,create_jinja_environ:[3,21,51],dictionari:[1,53,21,23,19,56,3,28,32,31,13],releas:[14,15,38,64,51,21],likelihood:36,afterward:[11,31],etern:24,captured_templ:23,indent:41,unwant:3,could:[14,64,47,34,35,1,3,31,44,21,57,56,6,7,26,48,45,12],ask:[28,3,1,32],keep:[1,3,4,6,10,13,14,15,17,18,22,23,24,30,31,34,35,36,20,40,38,41,49,53,55,57,60,45,59],length:[19,41,31,27],simplexml:14,outsid:[14,1,51,60,3,31],retain:50,endors:50,blown:48,qualiti:[64,21],scene:[11,6],sessioninterfac:3,mai:[0,1,50,51,21,5,19,7,42],overwrit:1,owner:50,prioriti:47,forgeri:[15,38,45],"long":[41,50,36,5,19,3,8,28,45,31],iter_all_row:59,unknown:[3,60,1],licens:[14,15,38,17,50,41],perfectli:[35,5],mkdir:[64,49],system:[1,42,6,7,8,10,13,14,15,17,21,29,30,31,35,18,20,39,40,44,38,49,51,53,55,3,63],messag:[15,38,35,1,2,51,60,21,18,5,24,3,61,27,44,32],attach:[14,35,1,20,51,21,3,29,45,31],attack:[45,57,36],request_class:3,stephan:50,termin:35,"final":[14,64,9,1,18,23,3,61,49,10,31],wizard:[],shell:[15,38,34,49,1,18,3,6,57,10,31],wtfpl:14,ming32:21,real_nam:41,juli:51,shallow:3,"1em":16,rst:58,textfield:27,nobodi:[14,35],secure_filenam:[28,1],got_first_request:3,bother:55,emac:8,structur:[14,64,19,3,45,31],charact:[41,1,5,19,3,61,8,13],probe:36,try_fil:[62,39],sens:[35,1,20,39,56,6,9,11,63,13],disband:5,seriou:49,extensionnam:[14,3],other:[1,60,5,6,7,8,10,11,14,15,19,20,21,22,23,24,25,26,29,30,31,32,34,35,36,37,38,39,43,45,47,48,49,50,52,53,55,3,58,62,59],steep:17,counter:[3,45,17,21],terribl:24,find_on:19,have:[1,3,4,5,6,7,8,10,11,12,13,14,17,18,24,21,23,19,25,26,27,28,30,31,32,33,34,35,36,20,39,40,41,43,44,45,64,47,48,49,51,52,53,54,55,56,57,58,59,60,61,62,63],tabl:[34,37,5,19,10,63],need:[0,1,3,60,5,6,7,8,10,11,13,14,21,22,19,25,26,28,29,30,31,32,33,34,18,37,39,40,46,41,49,53,57,62,63],turn:14,border:16,show_the_login_form:1,get_annoying_nam:41,"0x104da5ad0":[],rout:[14,15,47,0,1,20,53,38,43,56,3,7,26,27,44,28,30,12,59,32],roul:3,which:[1,4,5,6,7,8,10,12,14,15,17,18,21,22,23,25,26,27,28,30,31,33,34,35,36,20,38,39,40,41,42,44,45,64,47,48,49,51,55,56,3,60,61,62,59],mit:14,singl:[51,41,1,36,37,3,20,21,40,56,57,7,45,11,31],uppercas:[3,41,31,18],fastcgi_param:39,unless:[14,1,36,51,55,23,3,61,10,45,13],freebsd:40,clash:41,deploy:[64,15,38,48,49,2,52,39,40,25,55,62,31],endmacro:[3,27],who:28,campel:50,deploi:[64,15,38,35,2,39,22,40,25,55,62,31],create_app:[48,12],pyc:[43,51],segment:41,why:[14,15,47,17,35,1,36,31,38,5,19,3,7,39,28,45,12,63],urg:[14,21],url:[1,2,6,7,8,14,15,24,25,26,28,30,20,38,39,45,47,48,51,53,55,56,3,61,62],setlevel:35,request:[0,1,2,3,60,6,7,10,11,12,13,14,15,18,21,23,24,26,27,28,29,30,31,32,33,34,35,36,20,38,44,47,48,51,53,56,57,61,45,63],wtform:[15,38,27,2],enlist:[14,55],pipe:10,deni:[40,1],determin:[7,5],"_id":19,rakia:51,fact:[14,47,35,36,20,21,7],dbm:19,simplecach:4,text:[33,16,41,9,1,50,37,59,63,60,5,3,61,26,8,44,35,45,32,42,13],get_template_nam:20,connect:[9,1,2,4,3,7,10,11,14,15,21,22,23,19,32,33,34,35,18,64,38,51,57,60,63],bring:17,xd5:1,xd1:1,fear:55,cache_timeout:3,trivial:22,anywai:[55,35,13,8],setter:45,the_usernam:57,uwsgi_pass:62,locat:[14,64,47,35,1,51,55,21,39,40,3,58,26,28,10,31,62,42],should:[0,1,4,5,6,7,8,14,15,17,18,21,23,26,28,31,60,35,36,40,43,64,38,41,49,51,52,53,55,3,58,61,45],prolong:6,won:[17,35,1,36,3,21,55,57,7,26,45],suppos:[6,58,1,5,51],"5px":16,"__tablename__":63,werkzeug:[15,17,49,1,36,51,3,53,4,22,48,7,8,28,60,30,31],local:[64,15,38,17,1,36,51,3,60,21,39,40,6,7,25,62,31],themodul:35,ocur:3,contribut:[17,2],beat:5,approv:[14,15,38,41],myview:[3,20],pypi:[14,4,55,26,27,63],bear:11,autom:[60,21,55,36,54],csrf:[15,38,45],enabl:[49,1,18,51,21,39,3,61,10,31,30,59,13],organ:11,upper:41,fixer:[1,22],hypothetical_flask:7,stuff:[45,34,1],she:[28,32],contain:[47,41,35,1,50,55,60,42,40,3,61,5],"__html__":3,view:[0,1,2,60,6,11,12,14,15,24,26,27,29,30,32,33,18,20,38,43,47,51,53,56,3,59],shortcom:17,knowledg:[28,3,31],displai:[35,1,5,24,3,61,44,28],temporarili:[23,6,60,13,51],multipart:[28,1],stack:[6,51,21,3,7,31],syntact:60,statu:[15,38,1,36,60,24,3,31],correctli:24,mainli:3,tend:[60,39],favor:3,written:[64,45,50,22],progress:[28,2],neither:1,bunch:[29,56],email:[14,35,19,3,27,45,63],hash:41,mail_handl:35,blinker:[23,3,51],kei:[51,41,1,18,37,53,4,5,61,3,60,26,28,29,32,31,42,13],open_instance_resourc:[3,31],calculate_valu:4,tempfil:[28,60],job:[3,42],entir:[45,48,60,36],rare:51,"0x103937fd0":[],disconnect:23,thumb:8,admin:[47,35,18,60,21,19,3,44,49,45,12,63],joke:36,equal:[45,41],wsgiref:25,etc:[14,64,34,50,51,3,12,41,26,31],instanc:[1,3,7,12,14,15,22,23,24,29,31,20,38,40,41,47,48,51,53,56,6,58],uncaught:24,wirtel:50,html5:[28,15,38,5],comment:[15,38,41,33],endwith:44,guidelin:14,edgemon:50,arriv:[23,26],chmod:39,distinguish:0,make_default_options_respons:[3,51],googleapi:26,quit:[14,64,49,1,2,37,3,5,22,36,6,7,26,43,28,59],divid:[15,43],tort:50,addition:[47,13,60,21,23,3],obstacl:17,compon:[29,47,3,51],json:[15,38,13,2,51,21,3,26,45],write_fil:8,treat:[3,31,6,7,5],immedi:[3,34,41],"h\u00e4nsel":8,infrastructur:[64,7],yournam:35,autoescap:[15,38,13,51,21,3,7,61],both:[14,41,49,1,22,56,3,62,31,13],protect:[40,45,41,36,5],deliber:36,togeth:[17,11,26,63],present:[1,50,5,13,3,7,45,32],delimit:49,plain:[24,6],align:[16,41],rate_limit:20,xicluna:50,cursor:[14,10,57,32],defin:[14,64,17,1,18,20,3,53,51,5,19,34,39,27,45,63,10,30,42],sebastien:50,contextlib:[23,10,57],endblock:[24,61,42,44],layer:[47,48,2,36,19,3,7,63],customiz:[51,20],tar:64,almost:[14,28],"__import__":[],site:[15,38,13,36,55,53,42,40,26,28,45,2,31],middlewar:[15,38,48,1,39,22,3,7,12,59],archiv:[55,2],textfil:45,lightweight:26,prose:[15,38],incom:[15,38,35,51,22,3,28,31],revis:1,tojson:[3,13,26],scienc:7,handle_user_except:3,denial:45,let:[14,46,9,18,20,53,60,21,39,25,26,43,28,49,31,32],welcom:[15,42],parti:[14,45],cross:[15,38,13,36,51,3,28,45,31],member:41,matthew:50,flaskr:[46,15,38,60,9,18,33,37,61,10],gui:53,flaskr_test:60,multidict:3,infer:47,difficult:49,http:[14,46,60,49,1,50,20,3,55,51,42,22,24,6,61,26,8,62,31,45,27],redirect_url:6,fastcgi_script_nam:39,"8em":16,effect:1,handi:[34,1,18,24,6,57,26,12],logfil:35,canva:5,build:[15,47,1,38,42,3,7,32],decoupl:23,distutil:55,tighter:3,default_app:48,com:[14,64,48,35,50,3,55,40,6,26,49,45],well:[0,1,4,5,3,7,8,13,14,17,18,24,21,22,23,19,28,31,32,64,60,35,36,20,46,47,41,49,50,51,56,61,63],concis:[],cgihandl:25,set_cooki:[29,3,1],exampl:[0,1,2,3,4,5,6,7,8,10,11,12,13,14,17,24,21,22,23,19,25,26,27,28,29,31,32,33,60,35,36,20,39,40,41,42,43,44,64,48,50,51,53,54,55,57,45,63],command:[64,34,49,18,21,55,22,6,58,10,31,62],setq:8,choos:[49,18],undefin:[23,7],outgrow:30,audio:5,loss:50,draconian:5,latest:[64,49,5],unari:41,xee:31,newest:[9,32],"27th":51,less:[24,17,9,60],"0x1027bcf50":[],heavili:35,simultan:22,web:[0,1,2,5,3,7,8,9,14,15,17,22,26,28,31,36,20,38,39,47,49,50,53,45],jinja:[15,38,1,21,5,3,7,26,8,61,45,42,13],field:27,onward:[64,34,6],makefil:64,add:[9,1,3,4,5,6,61,0,10,12,14,16,17,18,21,26,27,28,31,32,33,60,36,40,43,44,64,49,51,53,54,55,57,58,63],cleanup:3,compet:14,logger:[35,1,51,21,23,3,31],kick:[6,51,23,3,26,49],jython:51,use_dot_not:19,webpag:42,piec:[45,6,21,59],is_endpoint_expect:56,camelcas:41,realiz:[28,17],know:[14,64,1,36,3,60,55,23,24,6,7,26,8,28,45,11,31],xe3:1,password:[41,1,18,60,61,27,44,32],recurs:[45,55],mkstemp:60,python3:36,python2:31,insert:[19,63,13,27,32],resid:14,like:[1,3,4,5,6,7,8,12,13,14,15,17,24,21,22,23,19,25,26,27,28,30,31,32,34,35,36,20,38,39,41,42,43,45,64,47,48,49,53,55,56,57,60,62,63],lost:[14,24,3,1],incred:17,securecookiesess:3,microblog:33,anyth:[14,36,60,21,40,7,8,45],necessari:[14,47,41,13,3,31,39,60,21,5,6,57,26,27,29,10,12,45,63],lose:[3,7,5],soft:41,page:[9,1,2,60,5,6,61,13,15,16,24,26,27,32,35,18,20,47,42,38,48,50,51,53,55,3,45,59],stdin:6,revers:[9,1,51,21,56,3,13],captur:[64,45],flask_foo:[14,58],linux:[49,3,31],flaskext_foo:14,gevent:[52,22],with_stat:10,flush:59,proper:[34,51,3,21,5,40,41,7],home:28,peter:60,transport:26,tmp:[64,18,39,10,62,63],trust:[28,9,1,22],pkgsrc:40,lead:[35,13,50,21,5,3],broad:1,avoid:[14,41,0,21,3,29,45],leav:[10,60,0,18,36],blueprintsetupst:3,mode:[15,38,35,1,18,51,3,21,23,6,49,31],preload:31,encourag:[23,21,63],acronym:41,autoincr:37,usag:[34,2,3,27,31,59],wsgirestrictstdout:40,symlink:[64,40],host:[64,48,1,51,22,3,31],full_dispatch_request:[3,6],nutshel:3,obei:5,offset:41,user_id:[57,20],panel:8,about:[1,5,6,7,8,13,14,15,17,21,23,19,26,27,28,31,32,35,36,20,44,38,49,51,52,56,3,63],actual:[1,4,5,3,7,12,14,24,28,30,31,32,60,35,18,20,43,64,47,49,52,53,55,6],justin:50,world:[1,8,19,3,7,43,31,53],testsuit:[64,21],column:[19,41,63,42,32],fastcgi_pass:39,harder:[21,31,7,55],remote_app:14,lifecycl:6,constructor:[47,1,21,3,45,31],discard:14,frontend_app:48,disabl:[1,18,60,21,39,40,3,31,13],own:[14,64,48,49,1,51,3,53,21,22,23,56,41,7,45,31,13],do_the_login:1,inlin:[41,5],builtin:[15,38,48,35,51,52,21,23,41,45,31],easy_instal:[15,38,49,21,55,58],automat:[1,4,3,7,8,13,14,15,20,21,19,34,35,37,40,64,38,51,52,53,55,56,60,45,63],warranti:50,guard:40,index_templ:12,pointless:1,"_formhelp":27,merg:13,combinedmultidict:3,w3c:5,van:50,val:26,pictur:26,much:[14,64,17,35,36,20,3,5,57,6,7,28,41,31,59],inner:[41,59,43],"var":[64,1,39,40,3,45,31],"function":[1,2,3,4,6,7,10,11,12,13,14,15,17,24,21,23,19,26,27,28,29,30,31,32,33,34,35,36,20,38,40,41,43,44,64,47,48,51,53,55,56,57,58,59,60,61,45,63],simplejson:[3,26],brand:0,subscrib:[23,15,38,3],add_url_rul:[0,51,20,3,28,30],keyerror:1,gain:43,uninstal:31,oauth:14,eas:21,highest:32,bug:[21,1,39,51],taglin:5,made:[24,31,21],wise:18,tab:[49,41,0,8],yourapplication_config:64,reverse_filt:13,sgml:5,troubl:[7,36],asynchron:7,record:[47,35,1,23,3,44],below:[48,1,50,3,56,6,43,10,11],limit:[17,50,36,51,3,41,7,8,28,31],otherwis:[14,48,1,50,3,53,4,43,41,60,27,28,45,11,63,32],problem:[14,35,1,36,40,51,21,18,39,43,19,3,7,26,8,28,49,30,31,45,13],"catch":[45,3,21,1,60],evalu:[45,3,7,59,42],collis:21,"int":[3,20,1,26],descript:[14,41,35,1,20,24,57],dure:[35,1,36,51,3,52,60,23,6,49,31],filenam:[47,0,1,51,55,21,42,40,3,61,26,8,28,35,31],url_rule_class:3,implement:[14,47,17,1,36,19,3,53,20,21,51,5,57,24,6,7,43,48,28,12],ini:18,pip:[14,58,49,55],probabl:[64,35,1,36,54,55,53,60,21,5,40,3,7,26,8,44,28,49],charpoint:8,nonetheless:26,ecmascript:45,boot:39,detail:[14,15,41,1,50,39,21,5,3,7,26,45,31,64],virtual:[64,40,52,51],routing_except:3,functionc:[],book:5,lookup:[3,55],futur:[1,36,54,51,21,5,3],rememb:[14,48,1,52,53,39,43,3,7,27,28,29],varieti:[49,8],log_the_user_in:1,user_api:20,will_not_be_escap:13,servernam:[64,40],repeat:27,star:34,sooner:[24,35,55],"class":[1,60,5,3,7,14,15,17,21,22,23,19,27,30,31,20,41,42,44,38,48,51,61,63],june:51,render_template_str:3,kinda:[19,45],throughout:17,singleton:[40,41],debian:40,serial:[45,3],stai:[1,36],match_request:3,multithread:3,sphinx:[14,41],get_cookie_path:3,reliabl:[28,51],root_path:[31,47,3,0],rule:[15,47,41,49,1,20,3,53,51,38,5,6,58,26,8,28,30,45],portion:[35,42],show_post:[3,1],decemb:51,getlogg:35},objtypes:{"0":"py:module","1":"py:function","2":"py:method","3":"py:attribute","4":"py:class","5":"py:data","6":"py:classmethod"},titles:["Adding a favicon","Quickstart","Patterns for Flask","API","Caching","HTML/XHTML FAQ","The Request Context","Design Decisions in Flask","Unicode in Flask","Introducing Flaskr","Step 3: Creating The Database","Step 4: Request Database Connections","Application Factories","Templates","Flask Extension Development","Welcome to Flask","Step 7: Adding Style","Becoming Big","Step 2: Application Setup Code","MongoKit in Flask","Pluggable Views","Upgrading to Newer Releases","Other Servers","Signals","Custom Error Pages","CGI","AJAX with jQuery","Form Validation with WTForms","Uploading Files","Deferred Request Callbacks","Lazily Loading Views","Configuration Handling","Step 5: The View Functions","Tutorial","Working with the Shell","Handling Application Errors","Foreword","Step 1: Database Schema","Flask Documentation","FastCGI","mod_wsgi (Apache)","Pocoo Styleguide","Template Inheritance","Larger Applications","Message Flashing","Security Considerations","Step 0: Creating The Folders","Modular Applications with Blueprints","Application Dispatching","Installation","License","Flask Changelog","Deployment Options","View Decorators","Bonus: Testing the Application","Deploying with Distribute","Using URL Processors","Using SQLite 3 with Flask","Flask Extensions","Streaming Contents","Testing Flask Applications","Step 6: The Templates","uWSGI","SQLAlchemy in Flask","Deploying with Fabric"],objnames:{"0":"Python module","1":"Python function","2":"Python method","3":"Python attribute","4":"Python class","5":"Python data","6":"Python class method"},filenames:["patterns/favicon","quickstart","patterns/index","api","patterns/caching","htmlfaq","reqcontext","design","unicode","tutorial/introduction","tutorial/dbinit","tutorial/dbcon","patterns/appfactories","templating","extensiondev","index","tutorial/css","becomingbig","tutorial/setup","patterns/mongokit","views","upgrading","deploying/others","signals","patterns/errorpages","deploying/cgi","patterns/jquery","patterns/wtforms","patterns/fileuploads","patterns/deferredcallbacks","patterns/lazyloading","config","tutorial/views","tutorial/index","shell","errorhandling","foreword","tutorial/schema","latexindex","deploying/fastcgi","deploying/mod_wsgi","styleguide","patterns/templateinheritance","patterns/packages","patterns/flashing","security","tutorial/folders","blueprints","patterns/appdispatch","installation","license","changelog","deploying/index","patterns/viewdecorators","tutorial/testing","patterns/distribute","patterns/urlprocessors","patterns/sqlite3","extensions","patterns/streaming","testing","tutorial/templates","deploying/uwsgi","patterns/sqlalchemy","patterns/fabric"]}) \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/security.html b/studio/static/doc/flask-docs/security.html
new file mode 100644
index 0000000..f118cd2
--- /dev/null
+++ b/studio/static/doc/flask-docs/security.html
@@ -0,0 +1,260 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Security Considerations &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Unicode in Flask" href="unicode.html" />
+ <link rel="prev" title="HTML/XHTML FAQ" href="htmlfaq.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="unicode.html" title="Unicode in Flask"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="htmlfaq.html" title="HTML/XHTML FAQ"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="security-considerations">
+<h1>Security Considerations<a class="headerlink" href="#security-considerations" title="Permalink to this headline">¶</a></h1>
+<p>Web applications usually face all kinds of security problems and it&#8217;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.</p>
+<div class="section" id="cross-site-scripting-xss">
+<span id="xss"></span><h2>Cross-Site Scripting (XSS)<a class="headerlink" href="#cross-site-scripting-xss" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference external" href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-Site Scripting</a>.</p>
+<p>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:</p>
+<ul class="simple">
+<li>generating HTML without the help of Jinja2</li>
+<li>calling <a class="reference internal" href="api.html#flask.Markup" title="flask.Markup"><tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt></a> on data submitted by users</li>
+<li>sending out HTML from uploaded files, never do that, use the
+<cite>Content-Disposition: attachment</cite> header to prevent that problem.</li>
+<li>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.</li>
+</ul>
+<p>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:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">href</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>the text<span class="nt">&lt;/a&gt;</span>
+</pre></div>
+</div>
+<p>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:</p>
+<div class="highlight-html"><div class="highlight"><pre>onmouseover=alert(document.cookie)
+</pre></div>
+</div>
+<p>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.</p>
+</div>
+<div class="section" id="cross-site-request-forgery-csrf">
+<h2>Cross-Site Request Forgery (CSRF)<a class="headerlink" href="#cross-site-request-forgery-csrf" title="Permalink to this headline">¶</a></h2>
+<p>Another big problem is CSRF. This is a very complex topic and I won&#8217;t
+outline it here in detail just mention what it is and how to theoretically
+prevent it.</p>
+<p>If your authentication information is stored in cookies, you have implicit
+state management. The state of &#8220;being logged in&#8221; 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&#8217;t keep that in mind, some people might be able to trick your
+application&#8217;s users with social engineering to do stupid things without
+them knowing.</p>
+<p>Say you have a specific URL that, when you sent <cite>POST</cite> requests to will
+delete a user&#8217;s profile (say <cite>http://example.com/user/delete</cite>). If an
+attacker now creates a page that sends a post request to that page with
+some JavaScript they just has to trick some users to load that page and
+their profiles will end up being deleted.</p>
+<p>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.</p>
+<p>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 <strong>and</strong> 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.</p>
+<p>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.</p>
+</div>
+<div class="section" id="json-security">
+<span id="id1"></span><h2>JSON Security<a class="headerlink" href="#json-security" title="Permalink to this headline">¶</a></h2>
+<div class="admonition-ecmascript-5-changes admonition ">
+<p class="first admonition-title">ECMAScript 5 Changes</p>
+<p class="last">Starting with ECMAScript 5 the behavior of literals changed. Now they
+are not constructed with the constructor of <tt class="docutils literal"><span class="pre">Array</span></tt> and others, but
+with the builtin constructor of <tt class="docutils literal"><span class="pre">Array</span></tt> which closes this particular
+attack vector.</p>
+</div>
+<p>JSON itself is a high-level serialization format, so there is barely
+anything that could cause security problems, right? You can&#8217;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&#8217;s side.</p>
+<p>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
+<a class="reference internal" href="api.html#flask.jsonify" title="flask.jsonify"><tt class="xref py py-func docutils literal"><span class="pre">jsonify()</span></tt></a> so you are still at risk when using other ways to
+generate JSON.</p>
+<p>So what is the issue and how to avoid it? The problem are arrays at
+top-level in JSON. Imagine you send the following data out in a JSON
+request. Say that&#8217;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:</p>
+<div class="highlight-javascript"><div class="highlight"><pre><span class="p">[</span>
+ <span class="p">{</span><span class="s2">&quot;username&quot;</span><span class="o">:</span> <span class="s2">&quot;admin&quot;</span><span class="p">,</span>
+ <span class="s2">&quot;email&quot;</span><span class="o">:</span> <span class="s2">&quot;admin@localhost&quot;</span><span class="p">}</span>
+<span class="p">]</span>
+</pre></div>
+</div>
+<p>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 <cite>GET</cite> requests to a certain URL,
+say the URL for that request is
+<tt class="docutils literal"><span class="pre">http://example.com/api/get_friends.json</span></tt>.</p>
+<p>So now what happens if a clever hacker is embedding this to his website
+and social engineers a victim to visiting his site:</p>
+<div class="highlight-html"><div class="highlight"><pre><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span><span class="nt">&gt;</span>
+<span class="kd">var</span> <span class="nx">captured</span> <span class="o">=</span> <span class="p">[];</span>
+<span class="kd">var</span> <span class="nx">oldArray</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">;</span>
+<span class="kd">function</span> <span class="nb">Array</span><span class="p">()</span> <span class="p">{</span>
+ <span class="kd">var</span> <span class="nx">obj</span> <span class="o">=</span> <span class="k">this</span><span class="p">,</span> <span class="nx">id</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">capture</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
+ <span class="nx">obj</span><span class="p">.</span><span class="nx">__defineSetter__</span><span class="p">(</span><span class="nx">id</span><span class="o">++</span><span class="p">,</span> <span class="nx">capture</span><span class="p">);</span>
+ <span class="k">if</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span>
+ <span class="nx">captured</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
+ <span class="p">};</span>
+ <span class="nx">capture</span><span class="p">();</span>
+<span class="p">}</span>
+<span class="nt">&lt;/script&gt;</span>
+<span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span>
+ <span class="na">src=</span><span class="s">http://example.com/api/get_friends.json</span><span class="nt">&gt;&lt;/script&gt;</span>
+<span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span><span class="nt">&gt;</span>
+<span class="nb">Array</span> <span class="o">=</span> <span class="nx">oldArray</span><span class="p">;</span>
+<span class="c1">// now we have all the data in the captured array.</span>
+<span class="nt">&lt;/script&gt;</span>
+</pre></div>
+</div>
+<p>If you know a bit of JavaScript internals you might know that it&#8217;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 <tt class="docutils literal"><span class="pre">application/json</span></tt>
+mimetype if <tt class="docutils literal"><span class="pre">text/javascript</span></tt> is defined as content type in the script
+tag and evaluate that as JavaScript. Because top-level 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 <cite>captured</cite> array.</p>
+<p>Because it is a syntax error in JavaScript to have an object literal
+(<tt class="docutils literal"><span class="pre">{...}</span></tt>) 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
+<a class="reference internal" href="api.html#flask.jsonify" title="flask.jsonify"><tt class="xref py py-func docutils literal"><span class="pre">jsonify()</span></tt></a>. Make sure to do the same when using an ordinary
+JSON generate function.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Security Considerations</a><ul>
+<li><a class="reference internal" href="#cross-site-scripting-xss">Cross-Site Scripting (XSS)</a></li>
+<li><a class="reference internal" href="#cross-site-request-forgery-csrf">Cross-Site Request Forgery (CSRF)</a></li>
+<li><a class="reference internal" href="#json-security">JSON Security</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="htmlfaq.html" title="previous chapter">HTML/XHTML FAQ</a></li>
+ <li>Next: <a href="unicode.html" title="next chapter">Unicode in Flask</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/security.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/shell.html b/studio/static/doc/flask-docs/shell.html
new file mode 100644
index 0000000..758ba0f
--- /dev/null
+++ b/studio/static/doc/flask-docs/shell.html
@@ -0,0 +1,194 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Working with the Shell &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Patterns for Flask" href="patterns/index.html" />
+ <link rel="prev" title="Flask Extensions" href="extensions.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="patterns/index.html" title="Patterns for Flask"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="extensions.html" title="Flask Extensions"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="working-with-the-shell">
+<span id="shell"></span><h1>Working with the Shell<a class="headerlink" href="#working-with-the-shell" title="Permalink to this headline">¶</a></h1>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.3.</span></p>
+<p>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.</p>
+<p>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&#8217;re not triggering a request like a browser does which
+means that <a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a>, <a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-data docutils literal"><span class="pre">request</span></tt></a> and others are not
+available. But the code you want to test might depend on them, so what
+can you do?</p>
+<p>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.</p>
+<p>Generally it&#8217;s recommended that you read the <a class="reference internal" href="reqcontext.html#request-context"><em>The Request Context</em></a>
+chapter of the documentation first.</p>
+<div class="section" id="creating-a-request-context">
+<h2>Creating a Request Context<a class="headerlink" href="#creating-a-request-context" title="Permalink to this headline">¶</a></h2>
+<p>The easiest way to create a proper request context from the shell is by
+using the <a class="reference internal" href="api.html#flask.Flask.test_request_context" title="flask.Flask.test_request_context"><tt class="xref py py-attr docutils literal"><span class="pre">test_request_context</span></tt></a> method which creates
+us a <a class="reference internal" href="api.html#flask.ctx.RequestContext" title="flask.ctx.RequestContext"><tt class="xref py py-class docutils literal"><span class="pre">RequestContext</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Normally you would use the <cite>with</cite> statement to make this request object
+active, but in the shell it&#8217;s easier to use the
+<a class="reference internal" href="api.html#flask.ctx.RequestContext.push" title="flask.ctx.RequestContext.push"><tt class="xref py py-meth docutils literal"><span class="pre">push()</span></tt></a> and
+<a class="reference internal" href="api.html#flask.ctx.RequestContext.pop" title="flask.ctx.RequestContext.pop"><tt class="xref py py-meth docutils literal"><span class="pre">pop()</span></tt></a> methods by hand:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">push</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>From that point onwards you can work with the request object until you
+call <cite>pop</cite>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="firing-before-after-request">
+<h2>Firing Before/After Request<a class="headerlink" href="#firing-before-after-request" title="Permalink to this headline">¶</a></h2>
+<p>By just creating a request context, you still don&#8217;t have run the code that
+is normally run before a request. This might result in your database
+being unavailable if you are connecting to the database in a
+before-request callback or the current user not being stored on the
+<a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a> object etc.</p>
+<p>This however can easily be done yourself. Just call
+<a class="reference internal" href="api.html#flask.Flask.preprocess_request" title="flask.Flask.preprocess_request"><tt class="xref py py-meth docutils literal"><span class="pre">preprocess_request()</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">push</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">app</span><span class="o">.</span><span class="n">preprocess_request</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Keep in mind that the <a class="reference internal" href="api.html#flask.Flask.preprocess_request" title="flask.Flask.preprocess_request"><tt class="xref py py-meth docutils literal"><span class="pre">preprocess_request()</span></tt></a> function
+might return a response object, in that case just ignore it.</p>
+<p>To shutdown a request, you need to trick a bit before the after request
+functions (triggered by <a class="reference internal" href="api.html#flask.Flask.process_response" title="flask.Flask.process_response"><tt class="xref py py-meth docutils literal"><span class="pre">process_response()</span></tt></a>) operate on
+a response object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">app</span><span class="o">.</span><span class="n">process_response</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">response_class</span><span class="p">())</span>
+<span class="go">&lt;Response 0 bytes [200 OK]&gt;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">ctx</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>The functions registered as <a class="reference internal" href="api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> are
+automatically called when the context is popped. So this is the perfect
+place to automatically tear down resources that were needed by the request
+context (such as database connections).</p>
+</div>
+<div class="section" id="further-improving-the-shell-experience">
+<h2>Further Improving the Shell Experience<a class="headerlink" href="#further-improving-the-shell-experience" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>Just put them into a module (like <cite>shelltools</cite> and import from there):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">shelltools</span> <span class="kn">import</span> <span class="o">*</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Working with the Shell</a><ul>
+<li><a class="reference internal" href="#creating-a-request-context">Creating a Request Context</a></li>
+<li><a class="reference internal" href="#firing-before-after-request">Firing Before/After Request</a></li>
+<li><a class="reference internal" href="#further-improving-the-shell-experience">Further Improving the Shell Experience</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="extensions.html" title="previous chapter">Flask Extensions</a></li>
+ <li>Next: <a href="patterns/index.html" title="next chapter">Patterns for Flask</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/shell.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/signals.html b/studio/static/doc/flask-docs/signals.html
new file mode 100644
index 0000000..77250d5
--- /dev/null
+++ b/studio/static/doc/flask-docs/signals.html
@@ -0,0 +1,358 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Signals &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Pluggable Views" href="views.html" />
+ <link rel="prev" title="Configuration Handling" href="config.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="views.html" title="Pluggable Views"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="config.html" title="Configuration Handling"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="signals">
+<span id="id1"></span><h1>Signals<a class="headerlink" href="#signals" title="Permalink to this headline">¶</a></h1>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6.</span></p>
+<p>Starting with Flask 0.6, there is integrated support for signalling in
+Flask. This support is provided by the excellent <a class="reference external" href="http://pypi.python.org/pypi/blinker">blinker</a> library and
+will gracefully fall back if it is not available.</p>
+<p>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.</p>
+<p>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: <a class="reference internal" href="api.html#flask.request_started" title="flask.request_started"><tt class="xref py py-data docutils literal"><span class="pre">request_started</span></tt></a> is very similar
+to <a class="reference internal" href="api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a>). There are however difference in
+how they work. The core <a class="reference internal" href="api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> 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.</p>
+<p>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.</p>
+<div class="section" id="subscribing-to-signals">
+<h2>Subscribing to Signals<a class="headerlink" href="#subscribing-to-signals" title="Permalink to this headline">¶</a></h2>
+<p>To subscribe to a signal, you can use the
+<a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connect" title="(in Blinker v1.1)"><tt class="xref py py-meth docutils literal"><span class="pre">connect()</span></tt></a> 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 <a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.disconnect" title="(in Blinker v1.1)"><tt class="xref py py-meth docutils literal"><span class="pre">disconnect()</span></tt></a> method.</p>
+<p>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.</p>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">template_rendered</span>
+<span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">contextmanager</span>
+
+<span class="nd">@contextmanager</span>
+<span class="k">def</span> <span class="nf">captured_templates</span><span class="p">(</span><span class="n">app</span><span class="p">):</span>
+ <span class="n">recorded</span> <span class="o">=</span> <span class="p">[]</span>
+ <span class="k">def</span> <span class="nf">record</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">template</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
+ <span class="n">recorded</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">template</span><span class="p">,</span> <span class="n">context</span><span class="p">))</span>
+ <span class="n">template_rendered</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">record</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="k">yield</span> <span class="n">recorded</span>
+ <span class="k">finally</span><span class="p">:</span>
+ <span class="n">template_rendered</span><span class="o">.</span><span class="n">disconnect</span><span class="p">(</span><span class="n">record</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This can now easily be paired with a test client:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">captured_templates</span><span class="p">(</span><span class="n">app</span><span class="p">)</span> <span class="k">as</span> <span class="n">templates</span><span class="p">:</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="n">rv</span><span class="o">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
+ <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">templates</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span>
+ <span class="n">template</span><span class="p">,</span> <span class="n">context</span> <span class="o">=</span> <span class="n">templates</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
+ <span class="k">assert</span> <span class="n">template</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">&#39;index.html&#39;</span>
+ <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">context</span><span class="p">[</span><span class="s">&#39;items&#39;</span><span class="p">])</span> <span class="o">==</span> <span class="mi">10</span>
+</pre></div>
+</div>
+<p>All the template rendering in the code issued by the application <cite>app</cite>
+in the body of the <cite>with</cite> block will now be recorded in the <cite>templates</cite>
+variable. Whenever a template is rendered, the template object as well as
+context are appended to it.</p>
+<p>Additionally there is a convenient helper method
+(<a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connected_to" title="(in Blinker v1.1)"><tt class="xref py py-meth docutils literal"><span class="pre">connected_to()</span></tt></a>). 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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">template_rendered</span>
+
+<span class="k">def</span> <span class="nf">captured_templates</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">recorded</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">record</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">template</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
+ <span class="n">recorded</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">template</span><span class="p">,</span> <span class="n">context</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">template_rendered</span><span class="o">.</span><span class="n">connected_to</span><span class="p">(</span><span class="n">record</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The example above would then look like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">templates</span> <span class="o">=</span> <span class="p">[]</span>
+<span class="k">with</span> <span class="n">captured_templates</span><span class="p">(</span><span class="n">app</span><span class="p">,</span> <span class="n">templates</span><span class="p">):</span>
+ <span class="o">...</span>
+ <span class="n">template</span><span class="p">,</span> <span class="n">context</span> <span class="o">=</span> <span class="n">templates</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
+</pre></div>
+</div>
+<div class="admonition-blinker-api-changes admonition ">
+<p class="first admonition-title">Blinker API Changes</p>
+<p class="last">The <a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.connected_to" title="(in Blinker v1.1)"><tt class="xref py py-meth docutils literal"><span class="pre">connected_to()</span></tt></a> method arrived in Blinker
+with version 1.1.</p>
+</div>
+</div>
+<div class="section" id="creating-signals">
+<h2>Creating Signals<a class="headerlink" href="#creating-signals" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Namespace" title="(in Blinker v1.1)"><tt class="xref py py-class docutils literal"><span class="pre">Namespace</span></tt></a>.. This is what is recommended
+most of the time:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">blinker</span> <span class="kn">import</span> <span class="n">Namespace</span>
+<span class="n">my_signals</span> <span class="o">=</span> <span class="n">Namespace</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Now you can create new signals like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">model_saved</span> <span class="o">=</span> <span class="n">my_signals</span><span class="o">.</span><span class="n">signal</span><span class="p">(</span><span class="s">&#39;model-saved&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The name for the signal here makes it unique and also simplifies
+debugging. You can access the name of the signal with the
+<a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.NamedSignal.name" title="(in Blinker v1.1)"><tt class="xref py py-attr docutils literal"><span class="pre">name</span></tt></a> attribute.</p>
+<div class="admonition-for-extension-developers admonition ">
+<p class="first admonition-title">For Extension Developers</p>
+<p class="last">If you are writing a Flask extension and you want to gracefully degrade for
+missing blinker installations, you can do so by using the
+<a class="reference internal" href="api.html#flask.signals.Namespace" title="flask.signals.Namespace"><tt class="xref py py-class docutils literal"><span class="pre">flask.signals.Namespace</span></tt></a> class.</p>
+</div>
+</div>
+<div class="section" id="sending-signals">
+<h2>Sending Signals<a class="headerlink" href="#sending-signals" title="Permalink to this headline">¶</a></h2>
+<p>If you want to emit a signal, you can do so by calling the
+<a class="reference external" href="http://discorporate.us/projects/Blinker/docs/1.1/api.html#blinker.base.Signal.send" title="(in Blinker v1.1)"><tt class="xref py py-meth docutils literal"><span class="pre">send()</span></tt></a> method. It accepts a sender as first
+argument and optionally some keyword arguments that are forwarded to the
+signal subscribers:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Model</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="nf">save</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">model_saved</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Try to always pick a good sender. If you have a class that is emitting a
+signal, pass <cite>self</cite> as sender. If you emitting a signal from a random
+function, you can pass <tt class="docutils literal"><span class="pre">current_app._get_current_object()</span></tt> as sender.</p>
+<div class="admonition-passing-proxies-as-senders admonition ">
+<p class="first admonition-title">Passing Proxies as Senders</p>
+<p class="last">Never pass <a class="reference internal" href="api.html#flask.current_app" title="flask.current_app"><tt class="xref py py-data docutils literal"><span class="pre">current_app</span></tt></a> as sender to a signal. Use
+<tt class="docutils literal"><span class="pre">current_app._get_current_object()</span></tt> instead. The reason for this is
+that <a class="reference internal" href="api.html#flask.current_app" title="flask.current_app"><tt class="xref py py-data docutils literal"><span class="pre">current_app</span></tt></a> is a proxy and not the real application
+object.</p>
+</div>
+</div>
+<div class="section" id="decorator-based-signal-subscriptions">
+<h2>Decorator Based Signal Subscriptions<a class="headerlink" href="#decorator-based-signal-subscriptions" title="Permalink to this headline">¶</a></h2>
+<p>With Blinker 1.1 you can also easily subscribe to signals by using the new
+<tt class="xref py py-meth docutils literal"><span class="pre">connect_via()</span></tt> decorator:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">template_rendered</span>
+
+<span class="nd">@template_rendered.connect_via</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">when_template_rendered</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">template</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
+ <span class="k">print</span> <span class="s">&#39;Template </span><span class="si">%s</span><span class="s"> is rendered with </span><span class="si">%s</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">template</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">context</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="core-signals">
+<h2>Core Signals<a class="headerlink" href="#core-signals" title="Permalink to this headline">¶</a></h2>
+<p>The following signals exist in Flask:</p>
+<dl class="data">
+<dt>
+<tt class="descclassname">flask.</tt><tt class="descname">template_rendered</tt></dt>
+<dd><p>This signal is sent when a template was successfully rendered. The
+signal is invoked with the instance of the template as <cite>template</cite>
+and the context as dictionary (named <cite>context</cite>).</p>
+<p>Example subscriber:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">log_template_renders</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">template</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
+ <span class="n">sender</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&#39;Rendering template &quot;</span><span class="si">%s</span><span class="s">&quot; with context </span><span class="si">%s</span><span class="s">&#39;</span><span class="p">,</span>
+ <span class="n">template</span><span class="o">.</span><span class="n">name</span> <span class="ow">or</span> <span class="s">&#39;string template&#39;</span><span class="p">,</span>
+ <span class="n">context</span><span class="p">)</span>
+
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">template_rendered</span>
+<span class="n">template_rendered</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">log_template_renders</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="data">
+<dt>
+<tt class="descclassname">flask.</tt><tt class="descname">request_started</tt></dt>
+<dd><p>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 <a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a>.</p>
+<p>Example subscriber:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">log_request</span><span class="p">(</span><span class="n">sender</span><span class="p">):</span>
+ <span class="n">sender</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&#39;Request context is set up&#39;</span><span class="p">)</span>
+
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request_started</span>
+<span class="n">request_started</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">log_request</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="data">
+<dt>
+<tt class="descclassname">flask.</tt><tt class="descname">request_finished</tt></dt>
+<dd><p>This signal is sent right before the response is sent to the client.
+It is passed the response to be sent named <cite>response</cite>.</p>
+<p>Example subscriber:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">log_response</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">response</span><span class="p">):</span>
+ <span class="n">sender</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&#39;Request context is about to close down. &#39;</span>
+ <span class="s">&#39;Response: </span><span class="si">%s</span><span class="s">&#39;</span><span class="p">,</span> <span class="n">response</span><span class="p">)</span>
+
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request_finished</span>
+<span class="n">request_finished</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">log_response</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="data">
+<dt>
+<tt class="descclassname">flask.</tt><tt class="descname">got_request_exception</tt></dt>
+<dd><p>This signal is sent when an exception happens during request processing.
+It is sent <em>before</em> 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 <cite>exception</cite>.</p>
+<p>Example subscriber:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">log_exception</span><span class="p">(</span><span class="n">sender</span><span class="p">,</span> <span class="n">exception</span><span class="p">):</span>
+ <span class="n">sender</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s">&#39;Got exception during processing: </span><span class="si">%s</span><span class="s">&#39;</span><span class="p">,</span> <span class="n">exception</span><span class="p">)</span>
+
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">got_request_exception</span>
+<span class="n">got_request_exception</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">log_exception</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</dd></dl>
+
+<dl class="data">
+<dt>
+<tt class="descclassname">flask.</tt><tt class="descname">request_tearing_down</tt></dt>
+<dd><p>This signal is sent when the request is tearing down. This is always
+called, even if an exception is caused. Currently functions listening
+to this signal are called after the regular teardown handlers, but this
+is not something you can rely on.</p>
+<p>Example subscriber:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">close_db_connection</span><span class="p">(</span><span class="n">sender</span><span class="p">):</span>
+ <span class="n">session</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">request_tearing_down</span>
+<span class="n">request_tearing_down</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">close_db_connection</span><span class="p">,</span> <span class="n">app</span><span class="p">)</span>
+</pre></div>
+</div>
+</dd></dl>
+
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Signals</a><ul>
+<li><a class="reference internal" href="#subscribing-to-signals">Subscribing to Signals</a></li>
+<li><a class="reference internal" href="#creating-signals">Creating Signals</a></li>
+<li><a class="reference internal" href="#sending-signals">Sending Signals</a></li>
+<li><a class="reference internal" href="#decorator-based-signal-subscriptions">Decorator Based Signal Subscriptions</a></li>
+<li><a class="reference internal" href="#core-signals">Core Signals</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="config.html" title="previous chapter">Configuration Handling</a></li>
+ <li>Next: <a href="views.html" title="next chapter">Pluggable Views</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/signals.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/styleguide.html b/studio/static/doc/flask-docs/styleguide.html
new file mode 100644
index 0000000..f45955d
--- /dev/null
+++ b/studio/static/doc/flask-docs/styleguide.html
@@ -0,0 +1,321 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Pocoo Styleguide &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Upgrading to Newer Releases" href="upgrading.html" />
+ <link rel="prev" title="Flask Extension Development" href="extensiondev.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="upgrading.html" title="Upgrading to Newer Releases"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="extensiondev.html" title="Flask Extension Development"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="pocoo-styleguide">
+<h1>Pocoo Styleguide<a class="headerlink" href="#pocoo-styleguide" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>In general the Pocoo Styleguide closely follows <span class="target" id="index-0"></span><a class="pep reference external" href="http://www.python.org/dev/peps/pep-0008"><strong>PEP 8</strong></a> with some small
+differences and extensions.</p>
+<div class="section" id="general-layout">
+<h2>General Layout<a class="headerlink" href="#general-layout" title="Permalink to this headline">¶</a></h2>
+<dl class="docutils">
+<dt>Indentation:</dt>
+<dd>4 real spaces. No tabs, no exceptions.</dd>
+<dt>Maximum line length:</dt>
+<dd>79 characters with a soft limit for 84 if absolutely necessary. Try
+to avoid too nested code by cleverly placing <cite>break</cite>, <cite>continue</cite> and
+<cite>return</cite> statements.</dd>
+<dt>Continuing long statements:</dt>
+<dd><p class="first">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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">this_is_a_very_long</span><span class="p">(</span><span class="n">function_call</span><span class="p">,</span> <span class="s">&#39;with many parameters&#39;</span><span class="p">)</span> \
+ <span class="o">.</span><span class="n">that_returns_an_object_with_an_attribute</span>
+
+<span class="n">MyModel</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">MyModel</span><span class="o">.</span><span class="n">scalar</span> <span class="o">&gt;</span> <span class="mi">120</span><span class="p">)</span> \
+ <span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="n">MyModel</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">desc</span><span class="p">())</span> \
+ <span class="o">.</span><span class="n">limit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>If you break in a statement with parentheses or braces, align to the
+braces:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">this_is_a_very_long</span><span class="p">(</span><span class="n">function_call</span><span class="p">,</span> <span class="s">&#39;with many parameters&#39;</span><span class="p">,</span>
+ <span class="mi">23</span><span class="p">,</span> <span class="mi">42</span><span class="p">,</span> <span class="s">&#39;and even more&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>For lists or tuples with many items, break immediately after the
+opening brace:</p>
+<div class="last highlight-python"><div class="highlight"><pre><span class="n">items</span> <span class="o">=</span> <span class="p">[</span>
+ <span class="s">&#39;this is the first&#39;</span><span class="p">,</span> <span class="s">&#39;set of items&#39;</span><span class="p">,</span> <span class="s">&#39;with more items&#39;</span><span class="p">,</span>
+ <span class="s">&#39;to come in this line&#39;</span><span class="p">,</span> <span class="s">&#39;like this&#39;</span>
+<span class="p">]</span>
+</pre></div>
+</div>
+</dd>
+<dt>Blank lines:</dt>
+<dd><p class="first">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:</p>
+<div class="last highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">hello</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
+ <span class="k">print</span> <span class="s">&#39;Hello </span><span class="si">%s</span><span class="s">!&#39;</span> <span class="o">%</span> <span class="n">name</span>
+
+
+<span class="k">def</span> <span class="nf">goodbye</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
+ <span class="k">print</span> <span class="s">&#39;See you </span><span class="si">%s</span><span class="s">.&#39;</span> <span class="o">%</span> <span class="n">name</span>
+
+
+<span class="k">class</span> <span class="nc">MyClass</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="sd">&quot;&quot;&quot;This is a simple docstring&quot;&quot;&quot;</span>
+
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
+
+ <span class="k">def</span> <span class="nf">get_annoying_name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="o">+</span> <span class="s">&#39;!!!!111&#39;</span>
+</pre></div>
+</div>
+</dd>
+</dl>
+</div>
+<div class="section" id="expressions-and-statements">
+<h2>Expressions and Statements<a class="headerlink" href="#expressions-and-statements" title="Permalink to this headline">¶</a></h2>
+<dl class="docutils">
+<dt>General whitespace rules:</dt>
+<dd><ul class="first simple">
+<li>No whitespace for unary operators that are not words
+(e.g.: <tt class="docutils literal"><span class="pre">-</span></tt>, <tt class="docutils literal"><span class="pre">~</span></tt> etc.) as well on the inner side of parentheses.</li>
+<li>Whitespace is placed between binary operators.</li>
+</ul>
+<p>Good:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">exp</span> <span class="o">=</span> <span class="o">-</span><span class="mf">1.05</span>
+<span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">item_value</span> <span class="o">/</span> <span class="n">item_count</span><span class="p">)</span> <span class="o">*</span> <span class="n">offset</span> <span class="o">/</span> <span class="n">exp</span>
+<span class="n">value</span> <span class="o">=</span> <span class="n">my_list</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
+<span class="n">value</span> <span class="o">=</span> <span class="n">my_dict</span><span class="p">[</span><span class="s">&#39;key&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+<p>Bad:</p>
+<div class="last highlight-python"><div class="highlight"><pre><span class="n">exp</span> <span class="o">=</span> <span class="o">-</span> <span class="mf">1.05</span>
+<span class="n">value</span> <span class="o">=</span> <span class="p">(</span> <span class="n">item_value</span> <span class="o">/</span> <span class="n">item_count</span> <span class="p">)</span> <span class="o">*</span> <span class="n">offset</span> <span class="o">/</span> <span class="n">exp</span>
+<span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">item_value</span><span class="o">/</span><span class="n">item_count</span><span class="p">)</span><span class="o">*</span><span class="n">offset</span><span class="o">/</span><span class="n">exp</span>
+<span class="n">value</span><span class="o">=</span><span class="p">(</span> <span class="n">item_value</span><span class="o">/</span><span class="n">item_count</span> <span class="p">)</span> <span class="o">*</span> <span class="n">offset</span><span class="o">/</span><span class="n">exp</span>
+<span class="n">value</span> <span class="o">=</span> <span class="n">my_list</span><span class="p">[</span> <span class="n">index</span> <span class="p">]</span>
+<span class="n">value</span> <span class="o">=</span> <span class="n">my_dict</span> <span class="p">[</span><span class="s">&#39;key&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+</dd>
+<dt>Yoda statements are a no-go:</dt>
+<dd><p class="first">Never compare constant with variable, always variable with constant:</p>
+<p>Good:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">if</span> <span class="n">method</span> <span class="o">==</span> <span class="s">&#39;md5&#39;</span><span class="p">:</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>Bad:</p>
+<div class="last highlight-python"><div class="highlight"><pre><span class="k">if</span> <span class="s">&#39;md5&#39;</span> <span class="o">==</span> <span class="n">method</span><span class="p">:</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+</dd>
+<dt>Comparisons:</dt>
+<dd><ul class="first last simple">
+<li>against arbitrary types: <tt class="docutils literal"><span class="pre">==</span></tt> and <tt class="docutils literal"><span class="pre">!=</span></tt></li>
+<li>against singletons with <tt class="docutils literal"><span class="pre">is</span></tt> and <tt class="docutils literal"><span class="pre">is</span> <span class="pre">not</span></tt> (eg: <tt class="docutils literal"><span class="pre">foo</span> <span class="pre">is</span> <span class="pre">not</span>
+<span class="pre">None</span></tt>)</li>
+<li>never compare something with <cite>True</cite> or <cite>False</cite> (for example never
+do <tt class="docutils literal"><span class="pre">foo</span> <span class="pre">==</span> <span class="pre">False</span></tt>, do <tt class="docutils literal"><span class="pre">not</span> <span class="pre">foo</span></tt> instead)</li>
+</ul>
+</dd>
+<dt>Negated containment checks:</dt>
+<dd>use <tt class="docutils literal"><span class="pre">foo</span> <span class="pre">not</span> <span class="pre">in</span> <span class="pre">bar</span></tt> instead of <tt class="docutils literal"><span class="pre">not</span> <span class="pre">foo</span> <span class="pre">in</span> <span class="pre">bar</span></tt></dd>
+<dt>Instance checks:</dt>
+<dd><tt class="docutils literal"><span class="pre">isinstance(a,</span> <span class="pre">C)</span></tt> instead of <tt class="docutils literal"><span class="pre">type(A)</span> <span class="pre">is</span> <span class="pre">C</span></tt>, but try to avoid
+instance checks in general. Check for features.</dd>
+</dl>
+</div>
+<div class="section" id="naming-conventions">
+<h2>Naming Conventions<a class="headerlink" href="#naming-conventions" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>Class names: <tt class="docutils literal"><span class="pre">CamelCase</span></tt>, with acronyms kept uppercase (<tt class="docutils literal"><span class="pre">HTTPWriter</span></tt>
+and not <tt class="docutils literal"><span class="pre">HttpWriter</span></tt>)</li>
+<li>Variable names: <tt class="docutils literal"><span class="pre">lowercase_with_underscores</span></tt></li>
+<li>Method and function names: <tt class="docutils literal"><span class="pre">lowercase_with_underscores</span></tt></li>
+<li>Constants: <tt class="docutils literal"><span class="pre">UPPERCASE_WITH_UNDERSCORES</span></tt></li>
+<li>precompiled regular expressions: <tt class="docutils literal"><span class="pre">name_re</span></tt></li>
+</ul>
+<p>Protected members are prefixed with a single underscore. Double
+underscores are reserved for mixin classes.</p>
+<p>On classes with keywords, trailing underscores are appended. Clashes with
+builtins are allowed and <strong>must not</strong> 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.</p>
+<dl class="docutils">
+<dt>Function and method arguments:</dt>
+<dd><ul class="first last simple">
+<li>class methods: <tt class="docutils literal"><span class="pre">cls</span></tt> as first parameter</li>
+<li>instance methods: <tt class="docutils literal"><span class="pre">self</span></tt> as first parameter</li>
+<li>lambdas for properties might have the first parameter replaced
+with <tt class="docutils literal"><span class="pre">x</span></tt> like in <tt class="docutils literal"><span class="pre">display_name</span> <span class="pre">=</span> <span class="pre">property(lambda</span> <span class="pre">x:</span> <span class="pre">x.real_name</span>
+<span class="pre">or</span> <span class="pre">x.username)</span></tt></li>
+</ul>
+</dd>
+</dl>
+</div>
+<div class="section" id="docstrings">
+<h2>Docstrings<a class="headerlink" href="#docstrings" title="Permalink to this headline">¶</a></h2>
+<dl class="docutils">
+<dt>Docstring conventions:</dt>
+<dd><p class="first">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&#8217;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:</p>
+<div class="last highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">foo</span><span class="p">():</span>
+ <span class="sd">&quot;&quot;&quot;This is a simple docstring&quot;&quot;&quot;</span>
+
+
+<span class="k">def</span> <span class="nf">bar</span><span class="p">():</span>
+ <span class="sd">&quot;&quot;&quot;This is a longer docstring with so much information in there</span>
+<span class="sd"> that it spans three lines. In this case the closing triple quote</span>
+<span class="sd"> is on its own line.</span>
+<span class="sd"> &quot;&quot;&quot;</span>
+</pre></div>
+</div>
+</dd>
+<dt>Module header:</dt>
+<dd><p class="first">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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># -*- coding: utf-8 -*-</span>
+<span class="sd">&quot;&quot;&quot;</span>
+<span class="sd"> package.module</span>
+<span class="sd"> ~~~~~~~~~~~~~~</span>
+
+<span class="sd"> A brief description goes here.</span>
+
+<span class="sd"> :copyright: (c) YEAR by AUTHOR.</span>
+<span class="sd"> :license: LICENSE_NAME, see LICENSE_FILE for more details.</span>
+<span class="sd">&quot;&quot;&quot;</span>
+</pre></div>
+</div>
+<p class="last">Please keep in mind that proper copyrights and license files are a
+requirement for approved Flask extensions.</p>
+</dd>
+</dl>
+</div>
+<div class="section" id="comments">
+<h2>Comments<a class="headerlink" href="#comments" title="Permalink to this headline">¶</a></h2>
+<p>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 (<tt class="docutils literal"><span class="pre">#</span></tt>):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="c">#: the name of the user as unicode string</span>
+ <span class="n">name</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">)</span>
+ <span class="c">#: the sha1 hash of the password + inline salt</span>
+ <span class="n">pw_hash</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">String</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Pocoo Styleguide</a><ul>
+<li><a class="reference internal" href="#general-layout">General Layout</a></li>
+<li><a class="reference internal" href="#expressions-and-statements">Expressions and Statements</a></li>
+<li><a class="reference internal" href="#naming-conventions">Naming Conventions</a></li>
+<li><a class="reference internal" href="#docstrings">Docstrings</a></li>
+<li><a class="reference internal" href="#comments">Comments</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="extensiondev.html" title="previous chapter">Flask Extension Development</a></li>
+ <li>Next: <a href="upgrading.html" title="next chapter">Upgrading to Newer Releases</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/styleguide.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/templating.html b/studio/static/doc/flask-docs/templating.html
new file mode 100644
index 0000000..4aedfbe
--- /dev/null
+++ b/studio/static/doc/flask-docs/templating.html
@@ -0,0 +1,293 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Templates &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Testing Flask Applications" href="testing.html" />
+ <link rel="prev" title="Bonus: Testing the Application" href="tutorial/testing.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="testing.html" title="Testing Flask Applications"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="tutorial/testing.html" title="Bonus: Testing the Application"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="templates">
+<h1>Templates<a class="headerlink" href="#templates" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>This section only gives a very quick introduction into how Jinja2
+is integrated into Flask. If you want information on the template
+engine&#8217;s syntax itself, head over to the official <a class="reference external" href="http://jinja.pocoo.org/2/documentation/templates">Jinja2 Template
+Documentation</a> for
+more information.</p>
+<div class="section" id="jinja-setup">
+<h2>Jinja Setup<a class="headerlink" href="#jinja-setup" title="Permalink to this headline">¶</a></h2>
+<p>Unless customized, Jinja2 is configured by Flask as follows:</p>
+<ul class="simple">
+<li>autoescaping is enabled for all templates ending in <tt class="docutils literal"><span class="pre">.html</span></tt>,
+<tt class="docutils literal"><span class="pre">.htm</span></tt>, <tt class="docutils literal"><span class="pre">.xml</span></tt> as well as <tt class="docutils literal"><span class="pre">.xhtml</span></tt></li>
+<li>a template has the ability to opt in/out autoescaping with the
+<tt class="docutils literal"><span class="pre">{%</span> <span class="pre">autoescape</span> <span class="pre">%}</span></tt> tag.</li>
+<li>Flask inserts a couple of global functions and helpers into the
+Jinja2 context, additionally to the values that are present by
+default.</li>
+</ul>
+</div>
+<div class="section" id="standard-context">
+<h2>Standard Context<a class="headerlink" href="#standard-context" title="Permalink to this headline">¶</a></h2>
+<p>The following global variables are available within Jinja2 templates
+by default:</p>
+<dl class="data">
+<dt>
+<tt class="descname">config</tt></dt>
+<dd><p>The current configuration object (<tt class="xref py py-data docutils literal"><span class="pre">flask.config</span></tt>)</p>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.6.</span></p>
+</dd></dl>
+
+<dl class="data">
+<dt>
+<tt class="descname">request</tt></dt>
+<dd><p>The current request object (<a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">flask.request</span></tt></a>)</p>
+</dd></dl>
+
+<dl class="data">
+<dt>
+<tt class="descname">session</tt></dt>
+<dd><p>The current session object (<a class="reference internal" href="api.html#flask.session" title="flask.session"><tt class="xref py py-class docutils literal"><span class="pre">flask.session</span></tt></a>)</p>
+</dd></dl>
+
+<dl class="data">
+<dt>
+<tt class="descname">g</tt></dt>
+<dd><p>The request-bound object for global variables (<a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">flask.g</span></tt></a>)</p>
+</dd></dl>
+
+<dl class="function">
+<dt>
+<tt class="descname">url_for</tt><big>(</big><big>)</big></dt>
+<dd><p>The <a class="reference internal" href="api.html#flask.url_for" title="flask.url_for"><tt class="xref py py-func docutils literal"><span class="pre">flask.url_for()</span></tt></a> function.</p>
+</dd></dl>
+
+<dl class="function">
+<dt>
+<tt class="descname">get_flashed_messages</tt><big>(</big><big>)</big></dt>
+<dd><p>The <a class="reference internal" href="api.html#flask.get_flashed_messages" title="flask.get_flashed_messages"><tt class="xref py py-func docutils literal"><span class="pre">flask.get_flashed_messages()</span></tt></a> function.</p>
+</dd></dl>
+
+<div class="admonition-the-jinja-context-behaviour admonition ">
+<p class="first admonition-title">The Jinja Context Behaviour</p>
+<p>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.</p>
+<p>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:</p>
+<ol class="arabic simple">
+<li>you explicitly pass the request to the macro as parameter, or
+the attribute of the request object you are interested in.</li>
+<li>you import the macro &#8220;with context&#8221;.</li>
+</ol>
+<p>Importing with context looks like this:</p>
+<div class="last highlight-jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">from</span> <span class="s1">&#39;_helpers.html&#39;</span> <span class="k">import</span> <span class="nv">my_macro</span> <span class="k">with context</span> <span class="cp">%}</span><span class="x"></span>
+</pre></div>
+</div>
+</div>
+</div>
+<div class="section" id="standard-filters">
+<h2>Standard Filters<a class="headerlink" href="#standard-filters" title="Permalink to this headline">¶</a></h2>
+<p>These filters are available in Jinja2 additionally to the filters provided
+by Jinja2 itself:</p>
+<dl class="function">
+<dt>
+<tt class="descname">tojson</tt><big>(</big><big>)</big></dt>
+<dd><p>This function converts the given object into JSON representation. This
+is for example very helpful if you try to generate JavaScript on the
+fly.</p>
+<p>Note that inside <cite>script</cite> tags no escaping must take place, so make
+sure to disable escaping with <tt class="docutils literal"><span class="pre">|safe</span></tt> if you intend to use it inside
+<cite>script</cite> tags:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">text/javascript</span><span class="nt">&gt;</span>
+ <span class="nx">doSomethingWith</span><span class="p">(</span><span class="cp">{{</span> <span class="nv">user.username</span><span class="o">|</span><span class="nf">tojson</span><span class="o">|</span><span class="nf">safe</span> <span class="cp">}}</span><span class="p">);</span>
+<span class="nt">&lt;/script&gt;</span>
+</pre></div>
+</div>
+<p>That the <tt class="docutils literal"><span class="pre">|tojson</span></tt> filter escapes forward slashes properly for you.</p>
+</dd></dl>
+
+</div>
+<div class="section" id="controlling-autoescaping">
+<h2>Controlling Autoescaping<a class="headerlink" href="#controlling-autoescaping" title="Permalink to this headline">¶</a></h2>
+<p>Autoescaping is the concept of automatically escaping special characters
+of you. Special characters in the sense of HTML (or XML, and thus XHTML)
+are <tt class="docutils literal"><span class="pre">&amp;</span></tt>, <tt class="docutils literal"><span class="pre">&gt;</span></tt>, <tt class="docutils literal"><span class="pre">&lt;</span></tt>, <tt class="docutils literal"><span class="pre">&quot;</span></tt> as well as <tt class="docutils literal"><span class="pre">'</span></tt>. Because these characters
+carry specific meanings in documents on their own you have to replace them
+by so called &#8220;entities&#8221; 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
+<a class="reference internal" href="security.html#xss"><em>Cross-Site Scripting (XSS)</em></a>)</p>
+<p>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.</p>
+<p>There are three ways to accomplish that:</p>
+<ul class="simple">
+<li>In the Python code, wrap the HTML string in a <a class="reference internal" href="api.html#flask.Markup" title="flask.Markup"><tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt></a>
+object before passing it to the template. This is in general the
+recommended way.</li>
+<li>Inside the template, use the <tt class="docutils literal"><span class="pre">|safe</span></tt> filter to explicitly mark a
+string as safe HTML (<tt class="docutils literal"><span class="pre">{{</span> <span class="pre">myvariable|safe</span> <span class="pre">}}</span></tt>)</li>
+<li>Temporarily disable the autoescape system altogether.</li>
+</ul>
+<p>To disable the autoescape system in templates, you can use the <tt class="docutils literal"><span class="pre">{%</span>
+<span class="pre">autoescape</span> <span class="pre">%}</span></tt> block:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">autoescape</span> <span class="kp">false</span> <span class="cp">%}</span>
+ <span class="nt">&lt;p&gt;</span>autoescaping is disabled here
+ <span class="nt">&lt;p&gt;</span><span class="cp">{{</span> <span class="nv">will_not_be_escaped</span> <span class="cp">}}</span>
+<span class="cp">{%</span> <span class="k">endautoescape</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>Whenever you do this, please be very cautious about the variables you are
+using in this block.</p>
+</div>
+<div class="section" id="registering-filters">
+<h2>Registering Filters<a class="headerlink" href="#registering-filters" title="Permalink to this headline">¶</a></h2>
+<p>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
+<a class="reference internal" href="api.html#flask.Flask.jinja_env" title="flask.Flask.jinja_env"><tt class="xref py py-attr docutils literal"><span class="pre">jinja_env</span></tt></a> of the application or use the
+<a class="reference internal" href="api.html#flask.Flask.template_filter" title="flask.Flask.template_filter"><tt class="xref py py-meth docutils literal"><span class="pre">template_filter()</span></tt></a> decorator.</p>
+<p>The two following examples work the same and both reverse an object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.template_filter</span><span class="p">(</span><span class="s">&#39;reverse&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">reverse_filter</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">s</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
+
+<span class="k">def</span> <span class="nf">reverse_filter</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">s</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
+<span class="n">app</span><span class="o">.</span><span class="n">jinja_env</span><span class="o">.</span><span class="n">filters</span><span class="p">[</span><span class="s">&#39;reverse&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">reverse_filter</span>
+</pre></div>
+</div>
+<p>In case of the decorator the argument is optional if you want to use the
+function name as name of the filter.</p>
+</div>
+<div class="section" id="context-processors">
+<h2>Context Processors<a class="headerlink" href="#context-processors" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.context_processor</span>
+<span class="k">def</span> <span class="nf">inject_user</span><span class="p">():</span>
+ <span class="k">return</span> <span class="nb">dict</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="n">g</span><span class="o">.</span><span class="n">user</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The context processor above makes a variable called <cite>user</cite> available in
+the template with the value of <cite>g.user</cite>. This example is not very
+interesting because <cite>g</cite> is available in templates anyways, but it gives an
+idea how this works.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Templates</a><ul>
+<li><a class="reference internal" href="#jinja-setup">Jinja Setup</a></li>
+<li><a class="reference internal" href="#standard-context">Standard Context</a></li>
+<li><a class="reference internal" href="#standard-filters">Standard Filters</a></li>
+<li><a class="reference internal" href="#controlling-autoescaping">Controlling Autoescaping</a></li>
+<li><a class="reference internal" href="#registering-filters">Registering Filters</a></li>
+<li><a class="reference internal" href="#context-processors">Context Processors</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="tutorial/testing.html" title="previous chapter">Bonus: Testing the Application</a></li>
+ <li>Next: <a href="testing.html" title="next chapter">Testing Flask Applications</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/templating.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/testing.html b/studio/static/doc/flask-docs/testing.html
new file mode 100644
index 0000000..2f93142
--- /dev/null
+++ b/studio/static/doc/flask-docs/testing.html
@@ -0,0 +1,391 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Testing Flask Applications &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Handling Application Errors" href="errorhandling.html" />
+ <link rel="prev" title="Templates" href="templating.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="errorhandling.html" title="Handling Application Errors"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="templating.html" title="Templates"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="testing-flask-applications">
+<span id="testing"></span><h1>Testing Flask Applications<a class="headerlink" href="#testing-flask-applications" title="Permalink to this headline">¶</a></h1>
+<blockquote>
+<div><strong>Something that is untested is broken.</strong></div></blockquote>
+<p>The origin of this quote is unknown and while it is not entirely correct, it is also
+not 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 make changes and instantly know if anything breaks.</p>
+<p>Flask provides a way to test your application by exposing the Werkzeug
+test <a class="reference external" href="http://werkzeug.pocoo.org/docs/test/#werkzeug.test.Client" title="(in Werkzeug v0.7)"><tt class="xref py py-class docutils literal"><span class="pre">Client</span></tt></a> and handling the context locals for you.
+You can then use that with your favourite testing solution. In this documentation
+we will use the <a class="reference external" href="http://docs.python.org/dev/library/unittest.html#unittest" title="(in Python v3.3)"><tt class="xref py py-mod docutils literal"><span class="pre">unittest</span></tt></a> package that comes pre-installed with Python.</p>
+<div class="section" id="the-application">
+<h2>The Application<a class="headerlink" href="#the-application" title="Permalink to this headline">¶</a></h2>
+<p>First, we need an application to test; we will use the application from
+the <a class="reference internal" href="tutorial/index.html#tutorial"><em>Tutorial</em></a>. If you don&#8217;t have that application yet, get the
+sources from <a class="reference external" href="http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/">the examples</a>.</p>
+</div>
+<div class="section" id="the-testing-skeleton">
+<h2>The Testing Skeleton<a class="headerlink" href="#the-testing-skeleton" title="Permalink to this headline">¶</a></h2>
+<p>In order to test the application, we add a second module
+(<cite>flaskr_tests.py</cite>) and create a unittest skeleton there:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
+<span class="kn">import</span> <span class="nn">flaskr</span>
+<span class="kn">import</span> <span class="nn">unittest</span>
+<span class="kn">import</span> <span class="nn">tempfile</span>
+
+<span class="k">class</span> <span class="nc">FlaskrTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">db_fd</span><span class="p">,</span> <span class="n">flaskr</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;DATABASE&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">tempfile</span><span class="o">.</span><span class="n">mkstemp</span><span class="p">()</span>
+ <span class="n">flaskr</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;TESTING&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">flaskr</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span>
+ <span class="n">flaskr</span><span class="o">.</span><span class="n">init_db</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">tearDown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">os</span><span class="o">.</span><span class="n">close</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db_fd</span><span class="p">)</span>
+ <span class="n">os</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">flaskr</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;DATABASE&#39;</span><span class="p">])</span>
+
+<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
+ <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>The code in the <a class="reference external" href="http://docs.python.org/dev/library/unittest.html#unittest.TestCase.setUp" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">setUp()</span></tt></a> method creates a new test
+client and initializes a new database. This function is called before
+each individual test function is run. To delete the database after the
+test, we close the file and remove it from the filesystem in the
+<a class="reference external" href="http://docs.python.org/dev/library/unittest.html#unittest.TestCase.tearDown" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">tearDown()</span></tt></a> method. Additionally during setup the
+<tt class="docutils literal"><span class="pre">TESTING</span></tt> config flag is activated. What it does is disabling the error
+catching during request handling so that you get better error reports when
+performing test requests against the application.</p>
+<p>This test client will 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.</p>
+<p>Because SQLite3 is filesystem-based we can easily use the tempfile module
+to create a temporary database and initialize it. The
+<a class="reference external" href="http://docs.python.org/dev/library/tempfile.html#tempfile.mkstemp" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">mkstemp()</span></tt></a> 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 <cite>db_fd</cite> around so that we can use
+the <a class="reference external" href="http://docs.python.org/dev/library/os.html#os.close" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">os.close()</span></tt></a> function to close the file.</p>
+<p>If we now run the test suite, we should see the following output:</p>
+<div class="highlight-python"><pre>$ python flaskr_tests.py
+
+----------------------------------------------------------------------
+Ran 0 tests in 0.000s
+
+OK</pre>
+</div>
+<p>Even though it did not run any actual tests, we already know that our flaskr
+application is syntactically valid, otherwise the import would have died
+with an exception.</p>
+</div>
+<div class="section" id="the-first-test">
+<h2>The First Test<a class="headerlink" href="#the-first-test" title="Permalink to this headline">¶</a></h2>
+<p>Now it&#8217;s time to start testing the functionality of the application.
+Let&#8217;s check that the application shows &#8220;No entries here so far&#8221; if we
+access the root of the application (<tt class="docutils literal"><span class="pre">/</span></tt>). To do this, we add a new
+test method to our class, like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">FlaskrTestCase</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">db_fd</span><span class="p">,</span> <span class="n">flaskr</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;DATABASE&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">tempfile</span><span class="o">.</span><span class="n">mkstemp</span><span class="p">()</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">app</span> <span class="o">=</span> <span class="n">flaskr</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span>
+ <span class="n">flaskr</span><span class="o">.</span><span class="n">init_db</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">tearDown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">os</span><span class="o">.</span><span class="n">close</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">db_fd</span><span class="p">)</span>
+ <span class="n">os</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">flaskr</span><span class="o">.</span><span class="n">DATABASE</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">test_empty_db</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="s">&#39;No entries here so far&#39;</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+</pre></div>
+</div>
+<p>Notice that our test functions begin with the word <cite>test</cite>; this allows
+<a class="reference external" href="http://docs.python.org/dev/library/unittest.html#unittest" title="(in Python v3.3)"><tt class="xref py py-mod docutils literal"><span class="pre">unittest</span></tt></a> to automatically identify the method as a test to run.</p>
+<p>By using <cite>self.app.get</cite> we can send an HTTP <cite>GET</cite> request to the application with
+the given path. The return value will be a <a class="reference internal" href="api.html#flask.Flask.response_class" title="flask.Flask.response_class"><tt class="xref py py-class docutils literal"><span class="pre">response_class</span></tt></a> object.
+We can now use the <a class="reference external" href="http://werkzeug.pocoo.org/docs/wrappers/#werkzeug.wrappers.BaseResponse.data" title="(in Werkzeug v0.7)"><tt class="xref py py-attr docutils literal"><span class="pre">data</span></tt></a> attribute to inspect
+the return value (as string) from the application. In this case, we ensure that
+<tt class="docutils literal"><span class="pre">'No</span> <span class="pre">entries</span> <span class="pre">here</span> <span class="pre">so</span> <span class="pre">far'</span></tt> is part of the output.</p>
+<p>Run it again and you should see one passing test:</p>
+<div class="highlight-python"><pre>$ python flaskr_tests.py
+.
+----------------------------------------------------------------------
+Ran 1 test in 0.034s
+
+OK</pre>
+</div>
+</div>
+<div class="section" id="logging-in-and-out">
+<h2>Logging In and Out<a class="headerlink" href="#logging-in-and-out" title="Permalink to this headline">¶</a></h2>
+<p>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 and out
+of the application. To do this, we fire some requests to the login and logout
+pages with the required form data (username and password). And because the
+login and logout pages redirect, we tell the client to <cite>follow_redirects</cite>.</p>
+<p>Add the following two methods to your <cite>FlaskrTestCase</cite> class:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">login</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span>
+ <span class="n">username</span><span class="o">=</span><span class="n">username</span><span class="p">,</span>
+ <span class="n">password</span><span class="o">=</span><span class="n">password</span>
+ <span class="p">),</span> <span class="n">follow_redirects</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+
+<span class="k">def</span> <span class="nf">logout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/logout&#39;</span><span class="p">,</span> <span class="n">follow_redirects</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Now we can easily test that logging in and out works and that it fails with
+invalid credentials. Add this new test to the class:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">test_login_logout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="s">&#39;default&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="s">&#39;You were logged in&#39;</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">logout</span><span class="p">()</span>
+ <span class="k">assert</span> <span class="s">&#39;You were logged out&#39;</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="s">&#39;adminx&#39;</span><span class="p">,</span> <span class="s">&#39;default&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="s">&#39;Invalid username&#39;</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="s">&#39;defaultx&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="s">&#39;Invalid password&#39;</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="test-adding-messages">
+<h2>Test Adding Messages<a class="headerlink" href="#test-adding-messages" title="Permalink to this headline">¶</a></h2>
+<p>We should also test that adding messages works. Add a new test method
+like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">test_messages</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="s">&#39;admin&#39;</span><span class="p">,</span> <span class="s">&#39;default&#39;</span><span class="p">)</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s">&#39;/add&#39;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="nb">dict</span><span class="p">(</span>
+ <span class="n">title</span><span class="o">=</span><span class="s">&#39;&lt;Hello&gt;&#39;</span><span class="p">,</span>
+ <span class="n">text</span><span class="o">=</span><span class="s">&#39;&lt;strong&gt;HTML&lt;/strong&gt; allowed here&#39;</span>
+ <span class="p">),</span> <span class="n">follow_redirects</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="s">&#39;No entries here so far&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+ <span class="k">assert</span> <span class="s">&#39;&amp;lt;Hello&amp;gt;&#39;</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+ <span class="k">assert</span> <span class="s">&#39;&lt;strong&gt;HTML&lt;/strong&gt; allowed here&#39;</span> <span class="ow">in</span> <span class="n">rv</span><span class="o">.</span><span class="n">data</span>
+</pre></div>
+</div>
+<p>Here we check that HTML is allowed in the text but not in the title,
+which is the intended behavior.</p>
+<p>Running that should now give us three passing tests:</p>
+<div class="highlight-python"><pre>$ python flaskr_tests.py
+...
+----------------------------------------------------------------------
+Ran 3 tests in 0.332s
+
+OK</pre>
+</div>
+<p>For more complex tests with headers and status codes, check out the
+<a class="reference external" href="http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/">MiniTwit Example</a> from the sources which contains a larger test
+suite.</p>
+</div>
+<div class="section" id="other-testing-tricks">
+<h2>Other Testing Tricks<a class="headerlink" href="#other-testing-tricks" title="Permalink to this headline">¶</a></h2>
+<p>Besides using the test client as shown above, there is also the
+<a class="reference internal" href="api.html#flask.Flask.test_request_context" title="flask.Flask.test_request_context"><tt class="xref py py-meth docutils literal"><span class="pre">test_request_context()</span></tt></a> method that can be used
+in combination with the <cite>with</cite> statement to activate a request context
+temporarily. With this you can access the <a class="reference internal" href="api.html#flask.request" title="flask.request"><tt class="xref py py-class docutils literal"><span class="pre">request</span></tt></a>,
+<a class="reference internal" href="api.html#flask.g" title="flask.g"><tt class="xref py py-class docutils literal"><span class="pre">g</span></tt></a> and <a class="reference internal" href="api.html#flask.session" title="flask.session"><tt class="xref py py-class docutils literal"><span class="pre">session</span></tt></a> objects like in view
+functions. Here is a full example that demonstrates this approach:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">flask</span><span class="o">.</span><span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">(</span><span class="s">&#39;/?name=Peter&#39;</span><span class="p">):</span>
+ <span class="k">assert</span> <span class="n">flask</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">path</span> <span class="o">==</span> <span class="s">&#39;/&#39;</span>
+ <span class="k">assert</span> <span class="n">flask</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="s">&#39;name&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s">&#39;Peter&#39;</span>
+</pre></div>
+</div>
+<p>All the other objects that are context bound can be used in the same
+way.</p>
+<p>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 <a class="reference internal" href="patterns/appfactories.html#app-factories"><em>Application Factories</em></a>).</p>
+<p>Note however that if you are using a test request context, the
+<a class="reference internal" href="api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> functions are not automatically called
+same for <a class="reference internal" href="api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> functions. However
+<a class="reference internal" href="api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> functions are indeed executed when
+the test request context leaves the <cite>with</cite> block. If you do want the
+<a class="reference internal" href="api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> functions to be called as well, you
+need to call <a class="reference internal" href="api.html#flask.Flask.preprocess_request" title="flask.Flask.preprocess_request"><tt class="xref py py-meth docutils literal"><span class="pre">preprocess_request()</span></tt></a> yourself:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">flask</span><span class="o">.</span><span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">(</span><span class="s">&#39;/?name=Peter&#39;</span><span class="p">):</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">preprocess_request</span><span class="p">()</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+<p>This can be necessary to open database connections or something similar
+depending on how your application was designed.</p>
+<p>If you want to call the <a class="reference internal" href="api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> functions you
+need to call into <a class="reference internal" href="api.html#flask.Flask.process_response" title="flask.Flask.process_response"><tt class="xref py py-meth docutils literal"><span class="pre">process_response()</span></tt></a> which however
+requires that you pass it a response object:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">flask</span><span class="o">.</span><span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_request_context</span><span class="p">(</span><span class="s">&#39;/?name=Peter&#39;</span><span class="p">):</span>
+ <span class="n">resp</span> <span class="o">=</span> <span class="n">Response</span><span class="p">(</span><span class="s">&#39;...&#39;</span><span class="p">)</span>
+ <span class="n">resp</span> <span class="o">=</span> <span class="n">app</span><span class="o">.</span><span class="n">process_response</span><span class="p">(</span><span class="n">resp</span><span class="p">)</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+<p>This in general is less useful because at that point you can directly
+start using the test client.</p>
+</div>
+<div class="section" id="keeping-the-context-around">
+<h2>Keeping the Context Around<a class="headerlink" href="#keeping-the-context-around" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.4.</span></p>
+<p>Sometimes it is helpful to trigger a regular request but still keep the
+context around for a little longer so that additional introspection can
+happen. With Flask 0.4 this is possible by using the
+<a class="reference internal" href="api.html#flask.Flask.test_client" title="flask.Flask.test_client"><tt class="xref py py-meth docutils literal"><span class="pre">test_client()</span></tt></a> with a <cite>with</cite> block:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span> <span class="o">=</span> <span class="n">flask</span><span class="o">.</span><span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+
+<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span> <span class="k">as</span> <span class="n">c</span><span class="p">:</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/?tequila=42&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="n">request</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="s">&#39;tequila&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s">&#39;42&#39;</span>
+</pre></div>
+</div>
+<p>If you were to use just the <a class="reference internal" href="api.html#flask.Flask.test_client" title="flask.Flask.test_client"><tt class="xref py py-meth docutils literal"><span class="pre">test_client()</span></tt></a> without
+the <cite>with</cite> block, the <cite>assert</cite> would fail with an error because <cite>request</cite>
+is no longer available (because you are trying to use it outside of the actual request).
+However, keep in mind that any <a class="reference internal" href="api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> functions
+are already called at this point so your database connection and
+everything involved is probably already closed down.</p>
+</div>
+<div class="section" id="accessing-and-modifying-sessions">
+<h2>Accessing and Modifying Sessions<a class="headerlink" href="#accessing-and-modifying-sessions" title="Permalink to this headline">¶</a></h2>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.8.</span></p>
+<p>Sometimes it can be very helpful to access or modify the sessions from the
+test client. Generally there are two ways for this. If you just want to
+ensure that a session has certain keys set to certain values you can just
+keep the context around and access <a class="reference internal" href="api.html#flask.session" title="flask.session"><tt class="xref py py-data docutils literal"><span class="pre">flask.session</span></tt></a>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span> <span class="k">as</span> <span class="n">c</span><span class="p">:</span>
+ <span class="n">rv</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+ <span class="k">assert</span> <span class="n">flask</span><span class="o">.</span><span class="n">session</span><span class="p">[</span><span class="s">&#39;foo&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="mi">42</span>
+</pre></div>
+</div>
+<p>This however does not make it possible to also modify the session or to
+access the session before a request was fired. Starting with Flask 0.8 we
+provide a so called “session transaction” which simulates the appropriate
+calls to open a session in the context of the test client and to modify
+it. At the end of the transaction the session is stored. This works
+independently of the session backend used:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span> <span class="k">as</span> <span class="n">c</span><span class="p">:</span>
+ <span class="k">with</span> <span class="n">c</span><span class="o">.</span><span class="n">session_transaction</span><span class="p">()</span> <span class="k">as</span> <span class="n">sess</span><span class="p">:</span>
+ <span class="n">sess</span><span class="p">[</span><span class="s">&#39;a_key&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;a value&#39;</span>
+
+ <span class="c"># once this is reached the session was stored</span>
+</pre></div>
+</div>
+<p>Note that in this case you have to use the <tt class="docutils literal"><span class="pre">sess</span></tt> object instead of the
+<a class="reference internal" href="api.html#flask.session" title="flask.session"><tt class="xref py py-data docutils literal"><span class="pre">flask.session</span></tt></a> proxy. The object however itself will provide the
+same interface.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Testing Flask Applications</a><ul>
+<li><a class="reference internal" href="#the-application">The Application</a></li>
+<li><a class="reference internal" href="#the-testing-skeleton">The Testing Skeleton</a></li>
+<li><a class="reference internal" href="#the-first-test">The First Test</a></li>
+<li><a class="reference internal" href="#logging-in-and-out">Logging In and Out</a></li>
+<li><a class="reference internal" href="#test-adding-messages">Test Adding Messages</a></li>
+<li><a class="reference internal" href="#other-testing-tricks">Other Testing Tricks</a></li>
+<li><a class="reference internal" href="#keeping-the-context-around">Keeping the Context Around</a></li>
+<li><a class="reference internal" href="#accessing-and-modifying-sessions">Accessing and Modifying Sessions</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="templating.html" title="previous chapter">Templates</a></li>
+ <li>Next: <a href="errorhandling.html" title="next chapter">Handling Application Errors</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/testing.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/css.html b/studio/static/doc/flask-docs/tutorial/css.html
new file mode 100644
index 0000000..e226e94
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/css.html
@@ -0,0 +1,132 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 7: Adding Style &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Bonus: Testing the Application" href="testing.html" />
+ <link rel="prev" title="Step 6: The Templates" href="templates.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="testing.html" title="Bonus: Testing the Application"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="templates.html" title="Step 6: The Templates"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-7-adding-style">
+<span id="tutorial-css"></span><h1>Step 7: Adding Style<a class="headerlink" href="#step-7-adding-style" title="Permalink to this headline">¶</a></h1>
+<p>Now that everything else works, it&#8217;s time to add some style to the
+application. Just create a stylesheet called <cite>style.css</cite> in the <cite>static</cite>
+folder we created before:</p>
+<div class="highlight-css"><div class="highlight"><pre><span class="nt">body</span> <span class="p">{</span> <span class="k">font-family</span><span class="o">:</span> <span class="k">sans-serif</span><span class="p">;</span> <span class="k">background</span><span class="o">:</span> <span class="m">#eee</span><span class="p">;</span> <span class="p">}</span>
+<span class="nt">a</span><span class="o">,</span> <span class="nt">h1</span><span class="o">,</span> <span class="nt">h2</span> <span class="p">{</span> <span class="k">color</span><span class="o">:</span> <span class="m">#377BA8</span><span class="p">;</span> <span class="p">}</span>
+<span class="nt">h1</span><span class="o">,</span> <span class="nt">h2</span> <span class="p">{</span> <span class="k">font-family</span><span class="o">:</span> <span class="s1">&#39;Georgia&#39;</span><span class="o">,</span> <span class="k">serif</span><span class="p">;</span> <span class="k">margin</span><span class="o">:</span> <span class="m">0</span><span class="p">;</span> <span class="p">}</span>
+<span class="nt">h1</span> <span class="p">{</span> <span class="k">border-bottom</span><span class="o">:</span> <span class="m">2px</span> <span class="k">solid</span> <span class="m">#eee</span><span class="p">;</span> <span class="p">}</span>
+<span class="nt">h2</span> <span class="p">{</span> <span class="k">font-size</span><span class="o">:</span> <span class="m">1.2em</span><span class="p">;</span> <span class="p">}</span>
+
+<span class="nc">.page</span> <span class="p">{</span> <span class="k">margin</span><span class="o">:</span> <span class="m">2em</span> <span class="k">auto</span><span class="p">;</span> <span class="k">width</span><span class="o">:</span> <span class="m">35em</span><span class="p">;</span> <span class="k">border</span><span class="o">:</span> <span class="m">5px</span> <span class="k">solid</span> <span class="m">#ccc</span><span class="p">;</span>
+ <span class="k">padding</span><span class="o">:</span> <span class="m">0.8em</span><span class="p">;</span> <span class="k">background</span><span class="o">:</span> <span class="nb">white</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.entries</span> <span class="p">{</span> <span class="k">list-style</span><span class="o">:</span> <span class="k">none</span><span class="p">;</span> <span class="k">margin</span><span class="o">:</span> <span class="m">0</span><span class="p">;</span> <span class="k">padding</span><span class="o">:</span> <span class="m">0</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.entries</span> <span class="nt">li</span> <span class="p">{</span> <span class="k">margin</span><span class="o">:</span> <span class="m">0.8em</span> <span class="m">1.2em</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.entries</span> <span class="nt">li</span> <span class="nt">h2</span> <span class="p">{</span> <span class="k">margin-left</span><span class="o">:</span> <span class="m">-1em</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.add-entry</span> <span class="p">{</span> <span class="k">font-size</span><span class="o">:</span> <span class="m">0.9em</span><span class="p">;</span> <span class="k">border-bottom</span><span class="o">:</span> <span class="m">1px</span> <span class="k">solid</span> <span class="m">#ccc</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.add-entry</span> <span class="nt">dl</span> <span class="p">{</span> <span class="k">font-weight</span><span class="o">:</span> <span class="k">bold</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.metanav</span> <span class="p">{</span> <span class="k">text-align</span><span class="o">:</span> <span class="k">right</span><span class="p">;</span> <span class="k">font-size</span><span class="o">:</span> <span class="m">0.8em</span><span class="p">;</span> <span class="k">padding</span><span class="o">:</span> <span class="m">0.3em</span><span class="p">;</span>
+ <span class="k">margin-bottom</span><span class="o">:</span> <span class="m">1em</span><span class="p">;</span> <span class="k">background</span><span class="o">:</span> <span class="m">#fafafa</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.flash</span> <span class="p">{</span> <span class="k">background</span><span class="o">:</span> <span class="m">#CEE5F5</span><span class="p">;</span> <span class="k">padding</span><span class="o">:</span> <span class="m">0.5em</span><span class="p">;</span>
+ <span class="k">border</span><span class="o">:</span> <span class="m">1px</span> <span class="k">solid</span> <span class="m">#AACBE2</span><span class="p">;</span> <span class="p">}</span>
+<span class="nc">.error</span> <span class="p">{</span> <span class="k">background</span><span class="o">:</span> <span class="m">#F0D6D6</span><span class="p">;</span> <span class="k">padding</span><span class="o">:</span> <span class="m">0.5em</span><span class="p">;</span> <span class="p">}</span>
+</pre></div>
+</div>
+<p>Continue with <a class="reference internal" href="testing.html#tutorial-testing"><em>Bonus: Testing the Application</em></a>.</p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="templates.html" title="previous chapter">Step 6: The Templates</a></li>
+ <li>Next: <a href="testing.html" title="next chapter">Bonus: Testing the Application</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/css.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/dbcon.html b/studio/static/doc/flask-docs/tutorial/dbcon.html
new file mode 100644
index 0000000..91ed599
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/dbcon.html
@@ -0,0 +1,154 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 4: Request Database Connections &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 5: The View Functions" href="views.html" />
+ <link rel="prev" title="Step 3: Creating The Database" href="dbinit.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="views.html" title="Step 5: The View Functions"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="dbinit.html" title="Step 3: Creating The Database"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-4-request-database-connections">
+<span id="tutorial-dbcon"></span><h1>Step 4: Request Database Connections<a class="headerlink" href="#step-4-request-database-connections" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>Flask allows us to do that with the <a class="reference internal" href="../api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a>,
+<a class="reference internal" href="../api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> and <a class="reference internal" href="../api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a>
+decorators:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.before_request</span>
+<span class="k">def</span> <span class="nf">before_request</span><span class="p">():</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span> <span class="o">=</span> <span class="n">connect_db</span><span class="p">()</span>
+
+<span class="nd">@app.teardown_request</span>
+<span class="k">def</span> <span class="nf">teardown_request</span><span class="p">(</span><span class="n">exception</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>Functions marked with <a class="reference internal" href="../api.html#flask.Flask.before_request" title="flask.Flask.before_request"><tt class="xref py py-meth docutils literal"><span class="pre">before_request()</span></tt></a> are called before
+a request and passed no arguments. Functions marked with
+<a class="reference internal" href="../api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a> 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. They are however not guaranteed
+to be executed if an exception is raised, this is where functions marked with
+<a class="reference internal" href="../api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> come in. They get called after the
+response has been constructed. They are not allowed to modify the request, and
+their return values are ignored. If an exception occurred while the request was
+being processed, it is passed to each function; otherwise, <cite>None</cite> is passed in.</p>
+<p>We store our current database connection on the special <a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a>
+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 <a class="reference internal" href="../api.html#flask.g" title="flask.g"><tt class="xref py py-data docutils literal"><span class="pre">g</span></tt></a> object does some magic behind
+the scenes to ensure it does the right thing.</p>
+<p>Continue to <a class="reference internal" href="views.html#tutorial-views"><em>Step 5: The View Functions</em></a>.</p>
+<div class="admonition hint">
+<p class="first admonition-title">Hint</p>
+<p>Where do I put this code?</p>
+<p>If you&#8217;ve been following along in this tutorial, you might be wondering
+where to put the code from this step and the next. A logical place is to
+group these module-level functions together, and put your new
+<tt class="docutils literal"><span class="pre">before_request</span></tt> and <tt class="docutils literal"><span class="pre">teardown_request</span></tt> functions below your existing
+<tt class="docutils literal"><span class="pre">init_db</span></tt> function (following the tutorial line-by-line).</p>
+<p class="last">If you need a moment to find your bearings, take a look at how the <a class="reference external" href="http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/">example
+source</a> is organized. In Flask, you can put all of your application code
+into a single Python module. You don&#8217;t have to, and if your app <a class="reference internal" href="../patterns/packages.html#larger-applications"><em>grows
+larger</em></a>, it&#8217;s a good idea not to.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="dbinit.html" title="previous chapter">Step 3: Creating The Database</a></li>
+ <li>Next: <a href="views.html" title="next chapter">Step 5: The View Functions</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/dbcon.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/dbinit.html b/studio/static/doc/flask-docs/tutorial/dbinit.html
new file mode 100644
index 0000000..413914e
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/dbinit.html
@@ -0,0 +1,166 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 3: Creating The Database &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 4: Request Database Connections" href="dbcon.html" />
+ <link rel="prev" title="Step 2: Application Setup Code" href="setup.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="dbcon.html" title="Step 4: Request Database Connections"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="setup.html" title="Step 2: Application Setup Code"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-3-creating-the-database">
+<span id="tutorial-dbinit"></span><h1>Step 3: Creating The Database<a class="headerlink" href="#step-3-creating-the-database" title="Permalink to this headline">¶</a></h1>
+<p>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&#8217;s important to create
+that schema.</p>
+<p>Such a schema can be created by piping the <cite>schema.sql</cite> file into the
+<cite>sqlite3</cite> command as follows:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">sqlite3</span> <span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">flaskr</span><span class="o">.</span><span class="n">db</span> <span class="o">&lt;</span> <span class="n">schema</span><span class="o">.</span><span class="n">sql</span>
+</pre></div>
+</div>
+<p>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&#8217;s a good idea to add a function that initializes the database
+for you to the application.</p>
+<p>If you want to do that, you first have to import the
+<a class="reference external" href="http://docs.python.org/dev/library/contextlib.html#contextlib.closing" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">contextlib.closing()</span></tt></a> function from the contextlib package. If you
+want to use Python 2.5 it&#8217;s also necessary to enable the <cite>with</cite> statement
+first (<cite>__future__</cite> imports must be the very first import):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">with_statement</span>
+<span class="kn">from</span> <span class="nn">contextlib</span> <span class="kn">import</span> <span class="n">closing</span>
+</pre></div>
+</div>
+<p>Next we can create a function called <cite>init_db</cite> that initializes the
+database. For this we can use the <cite>connect_db</cite> function we defined
+earlier. Just add that function below the <cite>connect_db</cite> function:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">init_db</span><span class="p">():</span>
+ <span class="k">with</span> <span class="n">closing</span><span class="p">(</span><span class="n">connect_db</span><span class="p">())</span> <span class="k">as</span> <span class="n">db</span><span class="p">:</span>
+ <span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">open_resource</span><span class="p">(</span><span class="s">&#39;schema.sql&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span><span class="o">.</span><span class="n">executescript</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
+ <span class="n">db</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>The <a class="reference external" href="http://docs.python.org/dev/library/contextlib.html#contextlib.closing" title="(in Python v3.3)"><tt class="xref py py-func docutils literal"><span class="pre">closing()</span></tt></a> helper function allows us to keep a
+connection open for the duration of the <cite>with</cite> block. The
+<a class="reference internal" href="../api.html#flask.Flask.open_resource" title="flask.Flask.open_resource"><tt class="xref py py-func docutils literal"><span class="pre">open_resource()</span></tt></a> method of the application object
+supports that functionality out of the box, so it can be used in the
+<cite>with</cite> block directly. This function opens a file from the resource
+location (your <cite>flaskr</cite> folder) and allows you to read from it. We are
+using this here to execute a script on the database connection.</p>
+<p>When we connect to a database we get a connection object (here called
+<cite>db</cite>) 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.</p>
+<p>Now it is possible to create a database by starting up a Python shell and
+importing and calling that function:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">flaskr</span> <span class="kn">import</span> <span class="n">init_db</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">init_db</span><span class="p">()</span>
+</pre></div>
+</div>
+<div class="admonition-troubleshooting admonition ">
+<p class="first admonition-title">Troubleshooting</p>
+<p class="last">If you get an exception later that a table cannot be found check that
+you did call the <cite>init_db</cite> function and that your table names are
+correct (singular vs. plural for example).</p>
+</div>
+<p>Continue with <a class="reference internal" href="dbcon.html#tutorial-dbcon"><em>Step 4: Request Database Connections</em></a></p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="setup.html" title="previous chapter">Step 2: Application Setup Code</a></li>
+ <li>Next: <a href="dbcon.html" title="next chapter">Step 4: Request Database Connections</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/dbinit.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/folders.html b/studio/static/doc/flask-docs/tutorial/folders.html
new file mode 100644
index 0000000..a711ab9
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/folders.html
@@ -0,0 +1,122 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 0: Creating The Folders &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 1: Database Schema" href="schema.html" />
+ <link rel="prev" title="Introducing Flaskr" href="introduction.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="schema.html" title="Step 1: Database Schema"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="introduction.html" title="Introducing Flaskr"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-0-creating-the-folders">
+<span id="tutorial-folders"></span><h1>Step 0: Creating The Folders<a class="headerlink" href="#step-0-creating-the-folders" title="Permalink to this headline">¶</a></h1>
+<p>Before we get started, let&#8217;s create the folders needed for this
+application:</p>
+<div class="highlight-python"><pre>/flaskr
+ /static
+ /templates</pre>
+</div>
+<p>The <cite>flaskr</cite> 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 <cite>static</cite> folder are available to users of the application via <cite>HTTP</cite>.
+This is the place where css and javascript files go. Inside the
+<cite>templates</cite> folder Flask will look for <a class="reference external" href="http://jinja.pocoo.org/2/">Jinja2</a> templates. The
+templates you create later in the tutorial will go in this directory.</p>
+<p>Continue with <a class="reference internal" href="schema.html#tutorial-schema"><em>Step 1: Database Schema</em></a>.</p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="introduction.html" title="previous chapter">Introducing Flaskr</a></li>
+ <li>Next: <a href="schema.html" title="next chapter">Step 1: Database Schema</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/folders.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/index.html b/studio/static/doc/flask-docs/tutorial/index.html
new file mode 100644
index 0000000..cc010cb
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/index.html
@@ -0,0 +1,137 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Tutorial &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="next" title="Introducing Flaskr" href="introduction.html" />
+ <link rel="prev" title="Quickstart" href="../quickstart.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="introduction.html" title="Introducing Flaskr"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="../quickstart.html" title="Quickstart"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="tutorial">
+<span id="id1"></span><h1>Tutorial<a class="headerlink" href="#tutorial" title="Permalink to this headline">¶</a></h1>
+<p>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.</p>
+<p>If you want the full sourcecode in advance or for comparison, check out
+the <a class="reference external" href="http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/">example source</a>.</p>
+<div class="toctree-wrapper compound">
+<ul>
+<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introducing Flaskr</a></li>
+<li class="toctree-l1"><a class="reference internal" href="folders.html">Step 0: Creating The Folders</a></li>
+<li class="toctree-l1"><a class="reference internal" href="schema.html">Step 1: Database Schema</a></li>
+<li class="toctree-l1"><a class="reference internal" href="setup.html">Step 2: Application Setup Code</a></li>
+<li class="toctree-l1"><a class="reference internal" href="dbinit.html">Step 3: Creating The Database</a></li>
+<li class="toctree-l1"><a class="reference internal" href="dbcon.html">Step 4: Request Database Connections</a></li>
+<li class="toctree-l1"><a class="reference internal" href="views.html">Step 5: The View Functions</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="views.html#show-entries">Show Entries</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#add-new-entry">Add New Entry</a></li>
+<li class="toctree-l2"><a class="reference internal" href="views.html#login-and-logout">Login and Logout</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="templates.html">Step 6: The Templates</a><ul>
+<li class="toctree-l2"><a class="reference internal" href="templates.html#layout-html">layout.html</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templates.html#show-entries-html">show_entries.html</a></li>
+<li class="toctree-l2"><a class="reference internal" href="templates.html#login-html">login.html</a></li>
+</ul>
+</li>
+<li class="toctree-l1"><a class="reference internal" href="css.html">Step 7: Adding Style</a></li>
+<li class="toctree-l1"><a class="reference internal" href="testing.html">Bonus: Testing the Application</a></li>
+</ul>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="../quickstart.html" title="previous chapter">Quickstart</a></li>
+ <li>Next: <a href="introduction.html" title="next chapter">Introducing Flaskr</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/index.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/introduction.html b/studio/static/doc/flask-docs/tutorial/introduction.html
new file mode 100644
index 0000000..3598713
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/introduction.html
@@ -0,0 +1,128 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Introducing Flaskr &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 0: Creating The Folders" href="folders.html" />
+ <link rel="prev" title="Tutorial" href="index.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="folders.html" title="Step 0: Creating The Folders"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="index.html" title="Tutorial"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="introducing-flaskr">
+<span id="tutorial-introduction"></span><h1>Introducing Flaskr<a class="headerlink" href="#introducing-flaskr" title="Permalink to this headline">¶</a></h1>
+<p>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:</p>
+<ol class="arabic simple">
+<li>let the user sign in and out with credentials specified in the
+configuration. Only one user is supported.</li>
+<li>when the user is logged in they 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.</li>
+<li>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.</li>
+</ol>
+<p>We will be using SQLite3 directly for that application because it&#8217;s good
+enough for an application of that size. For larger applications however
+it makes a lot of sense to use <a class="reference external" href="http://www.sqlalchemy.org/">SQLAlchemy</a> 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.</p>
+<p>Here a screenshot from the final application:</p>
+<img alt="screenshot of the final application" class="screenshot align-center" src="../_images/flaskr.png" />
+<p>Continue with <a class="reference internal" href="folders.html#tutorial-folders"><em>Step 0: Creating The Folders</em></a>.</p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="index.html" title="previous chapter">Tutorial</a></li>
+ <li>Next: <a href="folders.html" title="next chapter">Step 0: Creating The Folders</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/introduction.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/schema.html b/studio/static/doc/flask-docs/tutorial/schema.html
new file mode 100644
index 0000000..9794054
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/schema.html
@@ -0,0 +1,125 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 1: Database Schema &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 2: Application Setup Code" href="setup.html" />
+ <link rel="prev" title="Step 0: Creating The Folders" href="folders.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="setup.html" title="Step 2: Application Setup Code"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="folders.html" title="Step 0: Creating The Folders"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-1-database-schema">
+<span id="tutorial-schema"></span><h1>Step 1: Database Schema<a class="headerlink" href="#step-1-database-schema" title="Permalink to this headline">¶</a></h1>
+<p>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 <cite>schema.sql</cite> in
+the just created <cite>flaskr</cite> folder:</p>
+<div class="highlight-sql"><div class="highlight"><pre><span class="k">drop</span> <span class="k">table</span> <span class="n">if</span> <span class="k">exists</span> <span class="n">entries</span><span class="p">;</span>
+<span class="k">create</span> <span class="k">table</span> <span class="n">entries</span> <span class="p">(</span>
+ <span class="n">id</span> <span class="nb">integer</span> <span class="k">primary</span> <span class="k">key</span> <span class="n">autoincrement</span><span class="p">,</span>
+ <span class="n">title</span> <span class="n">string</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
+ <span class="nb">text</span> <span class="n">string</span> <span class="k">not</span> <span class="k">null</span>
+<span class="p">);</span>
+</pre></div>
+</div>
+<p>This schema consists of a single table called <cite>entries</cite> and each row in
+this table has an <cite>id</cite>, a <cite>title</cite> and a <cite>text</cite>. The <cite>id</cite> is an
+automatically incrementing integer and a primary key, the other two are
+strings that must not be null.</p>
+<p>Continue with <a class="reference internal" href="setup.html#tutorial-setup"><em>Step 2: Application Setup Code</em></a>.</p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="folders.html" title="previous chapter">Step 0: Creating The Folders</a></li>
+ <li>Next: <a href="setup.html" title="next chapter">Step 2: Application Setup Code</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/schema.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/setup.html b/studio/static/doc/flask-docs/tutorial/setup.html
new file mode 100644
index 0000000..142281d
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/setup.html
@@ -0,0 +1,183 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 2: Application Setup Code &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 3: Creating The Database" href="dbinit.html" />
+ <link rel="prev" title="Step 1: Database Schema" href="schema.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="dbinit.html" title="Step 3: Creating The Database"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="schema.html" title="Step 1: Database Schema"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-2-application-setup-code">
+<span id="tutorial-setup"></span><h1>Step 2: Application Setup Code<a class="headerlink" href="#step-2-application-setup-code" title="Permalink to this headline">¶</a></h1>
+<p>Now that we have the schema in place we can create the application module.
+Let&#8217;s call it <cite>flaskr.py</cite> inside the <cite>flaskr</cite> folder. For starters we
+will add the imports we will need as well as the config section. For
+small applications it&#8217;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 <cite>.ini</cite> or <cite>.py</cite> file and load that or import
+the values from there.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># all the imports</span>
+<span class="kn">import</span> <span class="nn">sqlite3</span>
+<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">session</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span><span class="p">,</span> \
+ <span class="n">abort</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">flash</span>
+
+<span class="c"># configuration</span>
+<span class="n">DATABASE</span> <span class="o">=</span> <span class="s">&#39;/tmp/flaskr.db&#39;</span>
+<span class="n">DEBUG</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="n">SECRET_KEY</span> <span class="o">=</span> <span class="s">&#39;development key&#39;</span>
+<span class="n">USERNAME</span> <span class="o">=</span> <span class="s">&#39;admin&#39;</span>
+<span class="n">PASSWORD</span> <span class="o">=</span> <span class="s">&#39;default&#39;</span>
+</pre></div>
+</div>
+<p>Next we can create our actual application and initialize it with the
+config from the same file:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># create our little application :)</span>
+<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_object</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
+</pre></div>
+</div>
+<p><a class="reference internal" href="../api.html#flask.Config.from_object" title="flask.Config.from_object"><tt class="xref py py-meth docutils literal"><span class="pre">from_object()</span></tt></a> will look at the given object (if it&#8217;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.</p>
+<p>It is also a good idea to be able to load a configuration from a
+configurable file. This is what <a class="reference internal" href="../api.html#flask.Config.from_envvar" title="flask.Config.from_envvar"><tt class="xref py py-meth docutils literal"><span class="pre">from_envvar()</span></tt></a> can
+do:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">from_envvar</span><span class="p">(</span><span class="s">&#39;FLASKR_SETTINGS&#39;</span><span class="p">,</span> <span class="n">silent</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>That way someone can set an environment variable called
+<span class="target" id="index-0"></span><tt class="xref std std-envvar docutils literal"><span class="pre">FLASKR_SETTINGS</span></tt> 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.</p>
+<p>The <cite>secret_key</cite> 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!</p>
+<p>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.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">connect_db</span><span class="p">():</span>
+ <span class="k">return</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;DATABASE&#39;</span><span class="p">])</span>
+</pre></div>
+</div>
+<p>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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>With that out of the way you should be able to start up the application
+without problems. Do this with the following command:</p>
+<div class="highlight-python"><pre>python flaskr.py</pre>
+</div>
+<p>You will see a message telling you that server has started along with
+the address at which you can access it.</p>
+<p>When you head over to the server in your browser you will get an 404
+page not found error because we don&#8217;t have any views yet. But we will
+focus on that a little later. First we should get the database working.</p>
+<div class="admonition-externally-visible-server admonition ">
+<p class="first admonition-title">Externally Visible Server</p>
+<p class="last">Want your server to be publicly available? Check out the
+<a class="reference internal" href="../quickstart.html#public-server"><em>externally visible server</em></a> section for more
+information.</p>
+</div>
+<p>Continue with <a class="reference internal" href="dbinit.html#tutorial-dbinit"><em>Step 3: Creating The Database</em></a>.</p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="schema.html" title="previous chapter">Step 1: Database Schema</a></li>
+ <li>Next: <a href="dbinit.html" title="next chapter">Step 3: Creating The Database</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/setup.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/templates.html b/studio/static/doc/flask-docs/tutorial/templates.html
new file mode 100644
index 0000000..9c99a35
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/templates.html
@@ -0,0 +1,212 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 6: The Templates &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 7: Adding Style" href="css.html" />
+ <link rel="prev" title="Step 5: The View Functions" href="views.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="css.html" title="Step 7: Adding Style"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="views.html" title="Step 5: The View Functions"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-6-the-templates">
+<span id="tutorial-templates"></span><h1>Step 6: The Templates<a class="headerlink" href="#step-6-the-templates" title="Permalink to this headline">¶</a></h1>
+<p>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 <a class="reference external" href="http://jinja.pocoo.org/2/documentation/templates">Jinja2</a> syntax and have autoescaping enabled by
+default. This means that unless you mark a value in the code with
+<a class="reference internal" href="../api.html#flask.Markup" title="flask.Markup"><tt class="xref py py-class docutils literal"><span class="pre">Markup</span></tt></a> or with the <tt class="docutils literal"><span class="pre">|safe</span></tt> filter in the template,
+Jinja2 will ensure that special characters such as <tt class="docutils literal"><span class="pre">&lt;</span></tt> or <tt class="docutils literal"><span class="pre">&gt;</span></tt> are
+escaped with their XML equivalents.</p>
+<p>We are also using template inheritance which makes it possible to reuse
+the layout of the website in all pages.</p>
+<p>Put the following templates into the <cite>templates</cite> folder:</p>
+<div class="section" id="layout-html">
+<h2>layout.html<a class="headerlink" href="#layout-html" title="Permalink to this headline">¶</a></h2>
+<p>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 <tt class="docutils literal"><span class="pre">{%</span> <span class="pre">block</span> <span class="pre">body</span> <span class="pre">%}</span></tt> block can be
+replaced by a block of the same name (<tt class="docutils literal"><span class="pre">body</span></tt>) in a child template.</p>
+<p>The <a class="reference internal" href="../api.html#flask.session" title="flask.session"><tt class="xref py py-class docutils literal"><span class="pre">session</span></tt></a> 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 <tt class="docutils literal"><span class="pre">'logged_in'</span></tt> key in
+the session:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">&lt;!doctype html&gt;</span>
+<span class="nt">&lt;title&gt;</span>Flaskr<span class="nt">&lt;/title&gt;</span>
+<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">stylesheet</span> <span class="na">type=</span><span class="s">text/css</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;static&#39;</span><span class="o">,</span> <span class="nv">filename</span><span class="o">=</span><span class="s1">&#39;style.css&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>
+<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">page</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;h1&gt;</span>Flaskr<span class="nt">&lt;/h1&gt;</span>
+ <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">metanav</span><span class="nt">&gt;</span>
+ <span class="cp">{%</span> <span class="k">if</span> <span class="k">not</span> <span class="nv">session.logged_in</span> <span class="cp">%}</span>
+ <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;login&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>log in<span class="nt">&lt;/a&gt;</span>
+ <span class="cp">{%</span> <span class="k">else</span> <span class="cp">%}</span>
+ <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;logout&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span><span class="nt">&gt;</span>log out<span class="nt">&lt;/a&gt;</span>
+ <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/div&gt;</span>
+ <span class="cp">{%</span> <span class="k">for</span> <span class="nv">message</span> <span class="k">in</span> <span class="nv">get_flashed_messages</span><span class="o">()</span> <span class="cp">%}</span>
+ <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">flash</span><span class="nt">&gt;</span><span class="cp">{{</span> <span class="nv">message</span> <span class="cp">}}</span><span class="nt">&lt;/div&gt;</span>
+ <span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
+ <span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+<span class="nt">&lt;/div&gt;</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="show-entries-html">
+<h2>show_entries.html<a class="headerlink" href="#show-entries-html" title="Permalink to this headline">¶</a></h2>
+<p>This template extends the <cite>layout.html</cite> template from above to display the
+messages. Note that the <cite>for</cite> loop iterates over the messages we passed
+in with the <a class="reference internal" href="../api.html#flask.render_template" title="flask.render_template"><tt class="xref py py-func docutils literal"><span class="pre">render_template()</span></tt></a> function. We also tell the
+form to submit to your <cite>add_entry</cite> function and use <cite>POST</cite> as <cite>HTTP</cite>
+method:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
+ <span class="cp">{%</span> <span class="k">if</span> <span class="nv">session.logged_in</span> <span class="cp">%}</span>
+ <span class="nt">&lt;form</span> <span class="na">action=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;add_entry&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span> <span class="na">method=</span><span class="s">post</span> <span class="na">class=</span><span class="s">add-entry</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dl&gt;</span>
+ <span class="nt">&lt;dt&gt;</span>Title:
+ <span class="nt">&lt;dd&gt;&lt;input</span> <span class="na">type=</span><span class="s">text</span> <span class="na">size=</span><span class="s">30</span> <span class="na">name=</span><span class="s">title</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dt&gt;</span>Text:
+ <span class="nt">&lt;dd&gt;&lt;textarea</span> <span class="na">name=</span><span class="s">text</span> <span class="na">rows=</span><span class="s">5</span> <span class="na">cols=</span><span class="s">40</span><span class="nt">&gt;&lt;/textarea&gt;</span>
+ <span class="nt">&lt;dd&gt;&lt;input</span> <span class="na">type=</span><span class="s">submit</span> <span class="na">value=</span><span class="s">Share</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;/dl&gt;</span>
+ <span class="nt">&lt;/form&gt;</span>
+ <span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+ <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">entries</span><span class="nt">&gt;</span>
+ <span class="cp">{%</span> <span class="k">for</span> <span class="nv">entry</span> <span class="k">in</span> <span class="nv">entries</span> <span class="cp">%}</span>
+ <span class="nt">&lt;li&gt;&lt;h2&gt;</span><span class="cp">{{</span> <span class="nv">entry.title</span> <span class="cp">}}</span><span class="nt">&lt;/h2&gt;</span><span class="cp">{{</span> <span class="nv">entry.text</span><span class="o">|</span><span class="nf">safe</span> <span class="cp">}}</span>
+ <span class="cp">{%</span> <span class="k">else</span> <span class="cp">%}</span>
+ <span class="nt">&lt;li&gt;&lt;em&gt;</span>Unbelievable. No entries here so far<span class="nt">&lt;/em&gt;</span>
+ <span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
+ <span class="nt">&lt;/ul&gt;</span>
+<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="login-html">
+<h2>login.html<a class="headerlink" href="#login-html" title="Permalink to this headline">¶</a></h2>
+<p>Finally the login template which basically just displays a form to allow
+the user to login:</p>
+<div class="highlight-html+jinja"><div class="highlight"><pre><span class="cp">{%</span> <span class="k">extends</span> <span class="s2">&quot;layout.html&quot;</span> <span class="cp">%}</span>
+<span class="cp">{%</span> <span class="k">block</span> <span class="nv">body</span> <span class="cp">%}</span>
+ <span class="nt">&lt;h2&gt;</span>Login<span class="nt">&lt;/h2&gt;</span>
+ <span class="cp">{%</span> <span class="k">if</span> <span class="nv">error</span> <span class="cp">%}</span><span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">error</span><span class="nt">&gt;&lt;strong&gt;</span>Error:<span class="nt">&lt;/strong&gt;</span> <span class="cp">{{</span> <span class="nv">error</span> <span class="cp">}}{%</span> <span class="k">endif</span> <span class="cp">%}</span>
+ <span class="nt">&lt;form</span> <span class="na">action=</span><span class="s">&quot;</span><span class="cp">{{</span> <span class="nv">url_for</span><span class="o">(</span><span class="s1">&#39;login&#39;</span><span class="o">)</span> <span class="cp">}}</span><span class="s">&quot;</span> <span class="na">method=</span><span class="s">post</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dl&gt;</span>
+ <span class="nt">&lt;dt&gt;</span>Username:
+ <span class="nt">&lt;dd&gt;&lt;input</span> <span class="na">type=</span><span class="s">text</span> <span class="na">name=</span><span class="s">username</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dt&gt;</span>Password:
+ <span class="nt">&lt;dd&gt;&lt;input</span> <span class="na">type=</span><span class="s">password</span> <span class="na">name=</span><span class="s">password</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;dd&gt;&lt;input</span> <span class="na">type=</span><span class="s">submit</span> <span class="na">value=</span><span class="s">Login</span><span class="nt">&gt;</span>
+ <span class="nt">&lt;/dl&gt;</span>
+ <span class="nt">&lt;/form&gt;</span>
+<span class="cp">{%</span> <span class="k">endblock</span> <span class="cp">%}</span>
+</pre></div>
+</div>
+<p>Continue with <a class="reference internal" href="css.html#tutorial-css"><em>Step 7: Adding Style</em></a>.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Step 6: The Templates</a><ul>
+<li><a class="reference internal" href="#layout-html">layout.html</a></li>
+<li><a class="reference internal" href="#show-entries-html">show_entries.html</a></li>
+<li><a class="reference internal" href="#login-html">login.html</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="views.html" title="previous chapter">Step 5: The View Functions</a></li>
+ <li>Next: <a href="css.html" title="next chapter">Step 7: Adding Style</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/templates.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/testing.html b/studio/static/doc/flask-docs/tutorial/testing.html
new file mode 100644
index 0000000..ba82db6
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/testing.html
@@ -0,0 +1,113 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Bonus: Testing the Application &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Templates" href="../templating.html" />
+ <link rel="prev" title="Step 7: Adding Style" href="css.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="../templating.html" title="Templates"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="css.html" title="Step 7: Adding Style"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="bonus-testing-the-application">
+<span id="tutorial-testing"></span><h1>Bonus: Testing the Application<a class="headerlink" href="#bonus-testing-the-application" title="Permalink to this headline">¶</a></h1>
+<p>Now that you have finished the application and everything works as
+expected, it&#8217;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 <a class="reference internal" href="../testing.html#testing"><em>Testing Flask Applications</em></a> section of the
+documentation. Go there to see how easy it is to test Flask applications.</p>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p><h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="css.html" title="previous chapter">Step 7: Adding Style</a></li>
+ <li>Next: <a href="../templating.html" title="next chapter">Templates</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/testing.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/tutorial/views.html b/studio/static/doc/flask-docs/tutorial/views.html
new file mode 100644
index 0000000..feed988
--- /dev/null
+++ b/studio/static/doc/flask-docs/tutorial/views.html
@@ -0,0 +1,206 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Step 5: The View Functions &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="../_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '../',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="../index.html" />
+ <link rel="up" title="Tutorial" href="index.html" />
+ <link rel="next" title="Step 6: The Templates" href="templates.html" />
+ <link rel="prev" title="Step 4: Request Database Connections" href="dbcon.html" />
+
+
+ <link rel="apple-touch-icon" href="../_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="../_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="../genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="templates.html" title="Step 6: The Templates"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="dbcon.html" title="Step 4: Request Database Connections"
+ accesskey="P">previous</a> |</li>
+ <li><a href="../index.html">Flask 0.8 documentation</a> &raquo;</li>
+ <li><a href="index.html" accesskey="U">Tutorial</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="step-5-the-view-functions">
+<span id="tutorial-views"></span><h1>Step 5: The View Functions<a class="headerlink" href="#step-5-the-view-functions" title="Permalink to this headline">¶</a></h1>
+<p>Now that the database connections are working we can start writing the
+view functions. We will need four of them:</p>
+<div class="section" id="show-entries">
+<h2>Show Entries<a class="headerlink" href="#show-entries" title="Permalink to this headline">¶</a></h2>
+<p>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 <a class="reference internal" href="../patterns/sqlite3.html#easy-querying"><em>Easy Querying</em></a> example.</p>
+<p>The view function will pass the entries as dicts to the
+<cite>show_entries.html</cite> template and return the rendered one:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_entries</span><span class="p">():</span>
+ <span class="n">cur</span> <span class="o">=</span> <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s">&#39;select title, text from entries order by id desc&#39;</span><span class="p">)</span>
+ <span class="n">entries</span> <span class="o">=</span> <span class="p">[</span><span class="nb">dict</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="n">row</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">text</span><span class="o">=</span><span class="n">row</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">cur</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()]</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;show_entries.html&#39;</span><span class="p">,</span> <span class="n">entries</span><span class="o">=</span><span class="n">entries</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="add-new-entry">
+<h2>Add New Entry<a class="headerlink" href="#add-new-entry" title="Permalink to this headline">¶</a></h2>
+<p>This view lets the user add new entries if they are logged in. This only
+responds to <cite>POST</cite> requests, the actual form is shown on the
+<cite>show_entries</cite> page. If everything worked out well we will
+<a class="reference internal" href="../api.html#flask.flash" title="flask.flash"><tt class="xref py py-func docutils literal"><span class="pre">flash()</span></tt></a> an information message to the next request and
+redirect back to the <cite>show_entries</cite> page:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/add&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">add_entry</span><span class="p">():</span>
+ <span class="k">if</span> <span class="ow">not</span> <span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;logged_in&#39;</span><span class="p">):</span>
+ <span class="n">abort</span><span class="p">(</span><span class="mi">401</span><span class="p">)</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s">&#39;insert into entries (title, text) values (?, ?)&#39;</span><span class="p">,</span>
+ <span class="p">[</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;title&#39;</span><span class="p">],</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;text&#39;</span><span class="p">]])</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+ <span class="n">flash</span><span class="p">(</span><span class="s">&#39;New entry was successfully posted&#39;</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;show_entries&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>Note that we check that the user is logged in here (the <cite>logged_in</cite> key is
+present in the session and <cite>True</cite>).</p>
+<div class="admonition-security-note admonition ">
+<p class="first admonition-title">Security Note</p>
+<p class="last">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 <a class="reference internal" href="../patterns/sqlite3.html#sqlite3"><em>Using SQLite 3 with Flask</em></a> for more.</p>
+</div>
+</div>
+<div class="section" id="login-and-logout">
+<h2>Login and Logout<a class="headerlink" href="#login-and-logout" title="Permalink to this headline">¶</a></h2>
+<p>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
+<cite>logged_in</cite> key in the session. If the user logged in successfully, that
+key is set to <cite>True</cite>, and the user is redirected back to the <cite>show_entries</cite>
+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:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/login&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
+ <span class="n">error</span> <span class="o">=</span> <span class="bp">None</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;username&#39;</span><span class="p">]</span> <span class="o">!=</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;USERNAME&#39;</span><span class="p">]:</span>
+ <span class="n">error</span> <span class="o">=</span> <span class="s">&#39;Invalid username&#39;</span>
+ <span class="k">elif</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">&#39;password&#39;</span><span class="p">]</span> <span class="o">!=</span> <span class="n">app</span><span class="o">.</span><span class="n">config</span><span class="p">[</span><span class="s">&#39;PASSWORD&#39;</span><span class="p">]:</span>
+ <span class="n">error</span> <span class="o">=</span> <span class="s">&#39;Invalid password&#39;</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">session</span><span class="p">[</span><span class="s">&#39;logged_in&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span>
+ <span class="n">flash</span><span class="p">(</span><span class="s">&#39;You were logged in&#39;</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;show_entries&#39;</span><span class="p">))</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;login.html&#39;</span><span class="p">,</span> <span class="n">error</span><span class="o">=</span><span class="n">error</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>The logout function, on the other hand, removes that key from the session
+again. We use a neat trick here: if you use the <a class="reference external" href="http://docs.python.org/dev/library/stdtypes.html#dict.pop" title="(in Python v3.3)"><tt class="xref py py-meth docutils literal"><span class="pre">pop()</span></tt></a> 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&#8217;t have to check
+if the user was logged in.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/logout&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">logout</span><span class="p">():</span>
+ <span class="n">session</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">&#39;logged_in&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
+ <span class="n">flash</span><span class="p">(</span><span class="s">&#39;You were logged out&#39;</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">&#39;show_entries&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>Continue with <a class="reference internal" href="templates.html#tutorial-templates"><em>Step 6: The Templates</em></a>.</p>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="../index.html">
+ <img class="logo" src="../_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Step 5: The View Functions</a><ul>
+<li><a class="reference internal" href="#show-entries">Show Entries</a></li>
+<li><a class="reference internal" href="#add-new-entry">Add New Entry</a></li>
+<li><a class="reference internal" href="#login-and-logout">Login and Logout</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="../index.html">Documentation overview</a><ul>
+ <li><a href="index.html">Tutorial</a><ul>
+ <li>Previous: <a href="dbcon.html" title="previous chapter">Step 4: Request Database Connections</a></li>
+ <li>Next: <a href="templates.html" title="next chapter">Step 6: The Templates</a></li>
+ </ul></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="../_sources/tutorial/views.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="../search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/unicode.html b/studio/static/doc/flask-docs/unicode.html
new file mode 100644
index 0000000..d5e4800
--- /dev/null
+++ b/studio/static/doc/flask-docs/unicode.html
@@ -0,0 +1,215 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Unicode in Flask &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Flask Extension Development" href="extensiondev.html" />
+ <link rel="prev" title="Security Considerations" href="security.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="extensiondev.html" title="Flask Extension Development"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="security.html" title="Security Considerations"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="unicode-in-flask">
+<h1>Unicode in Flask<a class="headerlink" href="#unicode-in-flask" title="Permalink to this headline">¶</a></h1>
+<p>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&#8217;t know Unicode so far, you
+should probably read <a class="reference external" href="http://www.joelonsoftware.com/articles/Unicode.html">The Absolute Minimum Every Software Developer
+Absolutely, Positively Must Know About Unicode and Character Sets</a>. This part of the
+documentation just tries to cover the very basics so that you have a
+pleasant experience with Unicode related things.</p>
+<div class="section" id="automatic-conversion">
+<h2>Automatic Conversion<a class="headerlink" href="#automatic-conversion" title="Permalink to this headline">¶</a></h2>
+<p>Flask has a few assumptions about your application (which you can change
+of course) that give you basic and painless Unicode support:</p>
+<ul class="simple">
+<li>the encoding for text on your website is UTF-8</li>
+<li>internally you will always use Unicode exclusively for text except
+for literal strings with only ASCII character points.</li>
+<li>encoding and decoding happens whenever you are talking over a protocol
+that requires bytes to be transmitted.</li>
+</ul>
+<p>So what does this mean to you?</p>
+<p>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.</p>
+<p>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.</p>
+</div>
+<div class="section" id="the-golden-rule">
+<h2>The Golden Rule<a class="headerlink" href="#the-golden-rule" title="Permalink to this headline">¶</a></h2>
+<p>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?</p>
+<ul class="simple">
+<li>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 (<tt class="docutils literal"><span class="pre">'Hello</span> <span class="pre">World'</span></tt>).</li>
+<li>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 <cite>u</cite>.
+(like <tt class="docutils literal"><span class="pre">u'Hänsel</span> <span class="pre">und</span> <span class="pre">Gretel'</span></tt>)</li>
+<li>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 <tt class="docutils literal"><span class="pre">#</span> <span class="pre">-*-</span> <span class="pre">coding:</span> <span class="pre">utf-8</span> <span class="pre">-*-</span></tt> into the first or second line of
+your Python source file.</li>
+<li>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.</li>
+</ul>
+</div>
+<div class="section" id="encoding-and-decoding-yourself">
+<h2>Encoding and Decoding Yourself<a class="headerlink" href="#encoding-and-decoding-yourself" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>Anyways. To load such a file with Unicode you can use the built-in
+<tt class="xref py py-meth docutils literal"><span class="pre">str.decode()</span></tt> method:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">read_file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">charset</span><span class="o">=</span><span class="s">&#39;utf-8&#39;</span><span class="p">):</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">&#39;r&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="n">charset</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>To go from Unicode into a specific charset such as UTF-8 you can use the
+<tt class="xref py py-meth docutils literal"><span class="pre">unicode.encode()</span></tt> method:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">write_file</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">contents</span><span class="p">,</span> <span class="n">charset</span><span class="o">=</span><span class="s">&#39;utf-8&#39;</span><span class="p">):</span>
+ <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">&#39;w&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
+ <span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">contents</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">charset</span><span class="p">))</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="configuring-editors">
+<h2>Configuring Editors<a class="headerlink" href="#configuring-editors" title="Permalink to this headline">¶</a></h2>
+<p>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:</p>
+<ul>
+<li><p class="first">Vim: put <tt class="docutils literal"><span class="pre">set</span> <span class="pre">enc=utf-8</span></tt> to your <tt class="docutils literal"><span class="pre">.vimrc</span></tt> file.</p>
+</li>
+<li><p class="first">Emacs: either use an encoding cookie or put this into your <tt class="docutils literal"><span class="pre">.emacs</span></tt>
+file:</p>
+<div class="highlight-python"><pre>(prefer-coding-system 'utf-8)
+(setq default-buffer-file-coding-system 'utf-8)</pre>
+</div>
+</li>
+<li><p class="first">Notepad++:</p>
+<ol class="arabic simple">
+<li>Go to <em>Settings -&gt; Preferences ...</em></li>
+<li>Select the &#8220;New Document/Default Directory&#8221; tab</li>
+<li>Select &#8220;UTF-8 without BOM&#8221; as encoding</li>
+</ol>
+<p>It is also recommended to use the Unix newline format, you can select
+it in the same panel but this is not a requirement.</p>
+</li>
+</ul>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Unicode in Flask</a><ul>
+<li><a class="reference internal" href="#automatic-conversion">Automatic Conversion</a></li>
+<li><a class="reference internal" href="#the-golden-rule">The Golden Rule</a></li>
+<li><a class="reference internal" href="#encoding-and-decoding-yourself">Encoding and Decoding Yourself</a></li>
+<li><a class="reference internal" href="#configuring-editors">Configuring Editors</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="security.html" title="previous chapter">Security Considerations</a></li>
+ <li>Next: <a href="extensiondev.html" title="next chapter">Flask Extension Development</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/unicode.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/upgrading.html b/studio/static/doc/flask-docs/upgrading.html
new file mode 100644
index 0000000..258ab34
--- /dev/null
+++ b/studio/static/doc/flask-docs/upgrading.html
@@ -0,0 +1,421 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Upgrading to Newer Releases &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="Flask Changelog" href="changelog.html" />
+ <link rel="prev" title="Pocoo Styleguide" href="styleguide.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="changelog.html" title="Flask Changelog"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="styleguide.html" title="Pocoo Styleguide"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="upgrading-to-newer-releases">
+<h1>Upgrading to Newer Releases<a class="headerlink" href="#upgrading-to-newer-releases" title="Permalink to this headline">¶</a></h1>
+<p>Flask itself is changing like any software is changing over time. Most of
+the changes are the nice kind, the kind where you don&#8217;t have to change
+anything in your code to profit from a new release.</p>
+<p>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.</p>
+<p>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.</p>
+<p>If you want to use the <cite>easy_install</cite> command to upgrade your Flask
+installation, make sure to pass it the <tt class="docutils literal"><span class="pre">-U</span></tt> parameter:</p>
+<div class="highlight-python"><pre>$ easy_install -U Flask</pre>
+</div>
+<div class="section" id="version-0-8">
+<h2>Version 0.8<a class="headerlink" href="#version-0-8" title="Permalink to this headline">¶</a></h2>
+<p>Flask introduced a new session interface system. We also noticed that
+there was a naming collision between <cite>flask.session</cite> the module that
+implements sessions and <a class="reference internal" href="api.html#flask.session" title="flask.session"><tt class="xref py py-data docutils literal"><span class="pre">flask.session</span></tt></a> which is the global session
+object. With that introduction we moved the implementation details for
+the session system into a new module called <tt class="xref py py-mod docutils literal"><span class="pre">flask.sessions</span></tt>. If you
+used the previously undocumented session support we urge you to upgrade.</p>
+<p>If invalid JSON data was submitted Flask will now raise a
+<a class="reference external" href="http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.BadRequest" title="(in Werkzeug v0.7)"><tt class="xref py py-exc docutils literal"><span class="pre">BadRequest</span></tt></a> exception instead of letting the
+default <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#ValueError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">ValueError</span></tt></a> bubble up. This has the advantage that you no
+longer have to handle that error to avoid an internal server error showing
+up for the user. If you were catching this down explicitly in the past
+as <cite>ValueError</cite> you will need to change this.</p>
+<p>Due to a bug in the test client Flask 0.7 did not trigger teardown
+handlers when the test client was used in a with statement. This was
+since fixed but might require some changes in your testsuites if you
+relied on this behavior.</p>
+</div>
+<div class="section" id="version-0-7">
+<h2>Version 0.7<a class="headerlink" href="#version-0-7" title="Permalink to this headline">¶</a></h2>
+<p>In Flask 0.7 we cleaned up the code base internally a lot and did some
+backwards incompatible changes that make it easier to implement larger
+applications with Flask. Because we want to make upgrading as easy as
+possible we tried to counter the problems arising from these changes by
+providing a script that can ease the transition.</p>
+<p>The script scans your whole application and generates an unified diff with
+changes it assumes are safe to apply. However as this is an automated
+tool it won&#8217;t be able to find all use cases and it might miss some. We
+internally spread a lot of deprecation warnings all over the place to make
+it easy to find pieces of code that it was unable to upgrade.</p>
+<p>We strongly recommend that you hand review the generated patchfile and
+only apply the chunks that look good.</p>
+<p>If you are using git as version control system for your project we
+recommend applying the patch with <tt class="docutils literal"><span class="pre">path</span> <span class="pre">-p1</span> <span class="pre">&lt;</span> <span class="pre">patchfile.diff</span></tt> and then
+using the interactive commit feature to only apply the chunks that look
+good.</p>
+<p>To apply the upgrade script do the following:</p>
+<ol class="arabic">
+<li><p class="first">Download the script: <a class="reference external" href="https://raw.github.com/mitsuhiko/flask/master/scripts/flask-07-upgrade.py">flask-07-upgrade.py</a></p>
+</li>
+<li><p class="first">Run it in the directory of your application:</p>
+<div class="highlight-python"><pre>python flask-07-upgrade.py &gt; patchfile.diff</pre>
+</div>
+</li>
+<li><p class="first">Review the generated patchfile.</p>
+</li>
+<li><p class="first">Apply the patch:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">patch</span> <span class="o">-</span><span class="n">p1</span> <span class="o">&lt;</span> <span class="n">patchfile</span><span class="o">.</span><span class="n">diff</span>
+</pre></div>
+</div>
+</li>
+<li><p class="first">If you were using per-module template folders you need to move some
+templates around. Previously if you had a folder named <tt class="docutils literal"><span class="pre">templates</span></tt>
+next to a blueprint named <tt class="docutils literal"><span class="pre">admin</span></tt> the implicit template path
+automatically was <tt class="docutils literal"><span class="pre">admin/index.html</span></tt> for a template file called
+<tt class="docutils literal"><span class="pre">templates/index.html</span></tt>. This no longer is the case. Now you need
+to name the template <tt class="docutils literal"><span class="pre">templates/admin/index.html</span></tt>. The tool will
+not detect this so you will have to do that on your own.</p>
+</li>
+</ol>
+<p>Please note that deprecation warnings are disabled by default starting
+with Python 2.7. In order to see the deprecation warnings that might be
+emitted you have to enabled them with the <a class="reference external" href="http://docs.python.org/dev/library/warnings.html#warnings" title="(in Python v3.3)"><tt class="xref py py-mod docutils literal"><span class="pre">warnings</span></tt></a> module.</p>
+<p>If you are working with windows and you lack the <cite>patch</cite> command line
+utility you can get it as part of various Unix runtime environments for
+windows including cygwin, msysgit or ming32. Also source control systems
+like svn, hg or git have builtin support for applying unified diffs as
+generated by the tool. Check the manual of your version control system
+for more information.</p>
+<div class="section" id="bug-in-request-locals">
+<h3>Bug in Request Locals<a class="headerlink" href="#bug-in-request-locals" title="Permalink to this headline">¶</a></h3>
+<p>Due to a bug in earlier implementations the request local proxies now
+raise a <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#RuntimeError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">RuntimeError</span></tt></a> instead of an <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#AttributeError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">AttributeError</span></tt></a> when they
+are unbound. If you caught these exceptions with <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#AttributeError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">AttributeError</span></tt></a>
+before, you should catch them with <a class="reference external" href="http://docs.python.org/dev/library/exceptions.html#RuntimeError" title="(in Python v3.3)"><tt class="xref py py-exc docutils literal"><span class="pre">RuntimeError</span></tt></a> now.</p>
+<p>Additionally the <a class="reference internal" href="api.html#flask.send_file" title="flask.send_file"><tt class="xref py py-func docutils literal"><span class="pre">send_file()</span></tt></a> 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.</p>
+<p>Old code:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">return</span> <span class="n">send_file</span><span class="p">(</span><span class="n">my_file_object</span><span class="p">)</span>
+<span class="k">return</span> <span class="n">send_file</span><span class="p">(</span><span class="n">my_file_object</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>New code:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">return</span> <span class="n">send_file</span><span class="p">(</span><span class="n">my_file_object</span><span class="p">,</span> <span class="n">add_etags</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="upgrading-to-new-teardown-handling">
+<span id="id1"></span><h3>Upgrading to new Teardown Handling<a class="headerlink" href="#upgrading-to-new-teardown-handling" title="Permalink to this headline">¶</a></h3>
+<p>We streamlined the behavior of the callbacks for request handling. For
+things that modify the response the <a class="reference internal" href="api.html#flask.Flask.after_request" title="flask.Flask.after_request"><tt class="xref py py-meth docutils literal"><span class="pre">after_request()</span></tt></a>
+decorators continue to work as expected, but for things that absolutely
+must happen at the end of request we introduced the new
+<a class="reference internal" href="api.html#flask.Flask.teardown_request" title="flask.Flask.teardown_request"><tt class="xref py py-meth docutils literal"><span class="pre">teardown_request()</span></tt></a> decorator. Unfortunately that
+change also made after-request work differently under error conditions.
+It&#8217;s not consistently skipped if exceptions happen whereas previously it
+might have been called twice to ensure it is executed at the end of the
+request.</p>
+<p>If you have database connection code that looks like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.after_request</span>
+<span class="k">def</span> <span class="nf">after_request</span><span class="p">(</span><span class="n">response</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">response</span>
+</pre></div>
+</div>
+<p>You are now encouraged to use this instead:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.teardown_request</span>
+<span class="k">def</span> <span class="nf">after_request</span><span class="p">(</span><span class="n">exception</span><span class="p">):</span>
+ <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="s">&#39;db&#39;</span><span class="p">):</span>
+ <span class="n">g</span><span class="o">.</span><span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>On the upside this change greatly improves the internal code flow and
+makes it easier to customize the dispatching and error handling. This
+makes it now a lot easier to write unit tests as you can prevent closing
+down of database connections for a while. You can take advantage of the
+fact that the teardown callbacks are called when the response context is
+removed from the stack so a test can query the database after request
+handling:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">test_client</span><span class="p">()</span> <span class="k">as</span> <span class="n">client</span><span class="p">:</span>
+ <span class="n">resp</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;/&#39;</span><span class="p">)</span>
+ <span class="c"># g.db is still bound if there is such a thing</span>
+
+<span class="c"># and here it&#39;s gone</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="manual-error-handler-attaching">
+<h3>Manual Error Handler Attaching<a class="headerlink" href="#manual-error-handler-attaching" title="Permalink to this headline">¶</a></h3>
+<p>While it is still possible to attach error handlers to
+<tt class="xref py py-attr docutils literal"><span class="pre">Flask.error_handlers</span></tt> it&#8217;s discouraged to do so and in fact
+deprecated. In generaly we no longer recommend custom error handler
+attaching via assignments to the underlying dictionary due to the more
+complex internal handling to support arbitrary exception classes and
+blueprints. See <tt class="xref py py-meth docutils literal"><span class="pre">Flask.errorhandler()</span></tt> for more information.</p>
+<p>The proper upgrade is to change this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">error_handlers</span><span class="p">[</span><span class="mi">403</span><span class="p">]</span> <span class="o">=</span> <span class="n">handle_error</span>
+</pre></div>
+</div>
+<p>Into this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">register_error_handler</span><span class="p">(</span><span class="mi">403</span><span class="p">,</span> <span class="n">handle_error</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Alternatively you should just attach the function with a decorator:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.errorhandler</span><span class="p">(</span><span class="mi">403</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">handle_error</span><span class="p">(</span><span class="n">e</span><span class="p">):</span>
+ <span class="o">...</span>
+</pre></div>
+</div>
+<p>(Note that <tt class="xref py py-meth docutils literal"><span class="pre">register_error_handler()</span></tt> is new in Flask 0.7)</p>
+</div>
+<div class="section" id="blueprint-support">
+<h3>Blueprint Support<a class="headerlink" href="#blueprint-support" title="Permalink to this headline">¶</a></h3>
+<p>Blueprints replace the previous concept of “Modules” in Flask. They
+provide better semantics for various features and work better with large
+applications. The update script provided should be able to upgrade your
+applications automatically, but there might be some cases where it fails
+to upgrade. What changed?</p>
+<ul class="simple">
+<li>Blueprints need explicit names. Modules had an automatic name
+guesssing scheme where the shortname for the module was taken from the
+last part of the import module. The upgrade script tries to guess
+that name but it might fail as this information could change at
+runtime.</li>
+<li>Blueprints have an inverse behavior for <tt class="xref py py-meth docutils literal"><span class="pre">url_for()</span></tt>. Previously
+<tt class="docutils literal"><span class="pre">.foo</span></tt> told <tt class="xref py py-meth docutils literal"><span class="pre">url_for()</span></tt> that it should look for the endpoint
+<cite>foo</cite> on the application. Now it means “relative to current module”.
+The script will inverse all calls to <tt class="xref py py-meth docutils literal"><span class="pre">url_for()</span></tt> automatically for
+you. It will do this in a very eager way so you might end up with
+some unnecessary leading dots in your code if you&#8217;re not using
+modules.</li>
+<li>Blueprints do not automatically provide static folders. They will
+also no longer automatically export templates from a folder called
+<cite>templates</cite> next to their location however but it can be enabled from
+the constructor. Same with static files: if you want to continue
+serving static files you need to tell the constructor explicitly the
+path to the static folder (which can be relative to the blueprint&#8217;s
+module path).</li>
+<li>Rendering templates was simplified. Now the blueprints can provide
+template folders which are added to a general template searchpath.
+This means that you need to add another subfolder with the blueprint&#8217;s
+name into that folder if you want <tt class="docutils literal"><span class="pre">blueprintname/template.html</span></tt> as
+the template name.</li>
+</ul>
+<p>If you continue to use the <cite>Module</cite> object which is deprecated, Flask will
+restore the previous behavior as good as possible. However we strongly
+recommend upgrading to the new blueprints as they provide a lot of useful
+improvement such as the ability to attach a blueprint multiple times,
+blueprint specific error handlers and a lot more.</p>
+</div>
+</div>
+<div class="section" id="version-0-6">
+<h2>Version 0.6<a class="headerlink" href="#version-0-6" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>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 <cite>request</cite> 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.</p>
+</div>
+<div class="section" id="version-0-5">
+<h2>Version 0.5<a class="headerlink" href="#version-0-5" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<p>The following changes may be relevant to your application:</p>
+<ul class="simple">
+<li>autoescaping no longer happens for all templates. Instead it is
+configured to only happen on files ending with <tt class="docutils literal"><span class="pre">.html</span></tt>, <tt class="docutils literal"><span class="pre">.htm</span></tt>,
+<tt class="docutils literal"><span class="pre">.xml</span></tt> and <tt class="docutils literal"><span class="pre">.xhtml</span></tt>. If you have templates with different
+extensions you should override the
+<a class="reference internal" href="api.html#flask.Flask.select_jinja_autoescape" title="flask.Flask.select_jinja_autoescape"><tt class="xref py py-meth docutils literal"><span class="pre">select_jinja_autoescape()</span></tt></a> method.</li>
+<li>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.</li>
+<li>The <cite>create_jinja_loader</cite> function is gone. If you want to customize
+the Jinja loader now, use the
+<a class="reference internal" href="api.html#flask.Flask.create_jinja_environment" title="flask.Flask.create_jinja_environment"><tt class="xref py py-meth docutils literal"><span class="pre">create_jinja_environment()</span></tt></a> method instead.</li>
+</ul>
+</div>
+<div class="section" id="version-0-4">
+<h2>Version 0.4<a class="headerlink" href="#version-0-4" title="Permalink to this headline">¶</a></h2>
+<p>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 <tt class="docutils literal"><span class="pre">TESTING</span></tt> flag.</p>
+</div>
+<div class="section" id="version-0-3">
+<h2>Version 0.3<a class="headerlink" href="#version-0-3" title="Permalink to this headline">¶</a></h2>
+<p>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.</p>
+<div class="section" id="configuration-support">
+<h3>Configuration Support<a class="headerlink" href="#configuration-support" title="Permalink to this headline">¶</a></h3>
+<p>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).</p>
+<p>If you previously had code like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">debug</span> <span class="o">=</span> <span class="n">DEBUG</span>
+<span class="n">app</span><span class="o">.</span><span class="n">secret_key</span> <span class="o">=</span> <span class="n">SECRET_KEY</span>
+</pre></div>
+</div>
+<p>You no longer have to do that, instead you can just load a configuration
+into the config object. How this works is outlined in <a class="reference internal" href="config.html#config"><em>Configuration Handling</em></a>.</p>
+</div>
+<div class="section" id="logging-integration">
+<h3>Logging Integration<a class="headerlink" href="#logging-integration" title="Permalink to this headline">¶</a></h3>
+<p>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
+<a class="reference internal" href="errorhandling.html#application-errors"><em>Handling Application Errors</em></a>.</p>
+</div>
+<div class="section" id="categories-for-flash-messages">
+<h3>Categories for Flash Messages<a class="headerlink" href="#categories-for-flash-messages" title="Permalink to this headline">¶</a></h3>
+<p>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.</p>
+<p>Read all about that in the <a class="reference internal" href="patterns/flashing.html#message-flashing-pattern"><em>Message Flashing</em></a> pattern.</p>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Upgrading to Newer Releases</a><ul>
+<li><a class="reference internal" href="#version-0-8">Version 0.8</a></li>
+<li><a class="reference internal" href="#version-0-7">Version 0.7</a><ul>
+<li><a class="reference internal" href="#bug-in-request-locals">Bug in Request Locals</a></li>
+<li><a class="reference internal" href="#upgrading-to-new-teardown-handling">Upgrading to new Teardown Handling</a></li>
+<li><a class="reference internal" href="#manual-error-handler-attaching">Manual Error Handler Attaching</a></li>
+<li><a class="reference internal" href="#blueprint-support">Blueprint Support</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#version-0-6">Version 0.6</a></li>
+<li><a class="reference internal" href="#version-0-5">Version 0.5</a></li>
+<li><a class="reference internal" href="#version-0-4">Version 0.4</a></li>
+<li><a class="reference internal" href="#version-0-3">Version 0.3</a><ul>
+<li><a class="reference internal" href="#configuration-support">Configuration Support</a></li>
+<li><a class="reference internal" href="#logging-integration">Logging Integration</a></li>
+<li><a class="reference internal" href="#categories-for-flash-messages">Categories for Flash Messages</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="styleguide.html" title="previous chapter">Pocoo Styleguide</a></li>
+ <li>Next: <a href="changelog.html" title="next chapter">Flask Changelog</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/upgrading.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/flask-docs/views.html b/studio/static/doc/flask-docs/views.html
new file mode 100644
index 0000000..e8aa4ae
--- /dev/null
+++ b/studio/static/doc/flask-docs/views.html
@@ -0,0 +1,355 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <title>Pluggable Views &mdash; Flask 0.8 documentation</title>
+
+ <link rel="stylesheet" href="_static/flasky.css" type="text/css" />
+ <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+
+ <script type="text/javascript">
+ var DOCUMENTATION_OPTIONS = {
+ URL_ROOT: '',
+ VERSION: '0.8',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Flask 0.8 documentation" href="index.html" />
+ <link rel="next" title="The Request Context" href="reqcontext.html" />
+ <link rel="prev" title="Signals" href="signals.html" />
+
+
+ <link rel="apple-touch-icon" href="_static/touch-icon.png" />
+
+ <link media="only screen and (max-device-width: 480px)" href="_static/small_flask.css" type= "text/css" rel="stylesheet" />
+
+ </head>
+ <body>
+ <div class="related">
+ <h3>Navigation</h3>
+ <ul>
+ <li class="right" style="margin-right: 10px">
+ <a href="genindex.html" title="General Index"
+ accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="reqcontext.html" title="The Request Context"
+ accesskey="N">next</a> |</li>
+ <li class="right" >
+ <a href="signals.html" title="Signals"
+ accesskey="P">previous</a> |</li>
+ <li><a href="index.html">Flask 0.8 documentation</a> &raquo;</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <div class="section" id="pluggable-views">
+<span id="views"></span><h1>Pluggable Views<a class="headerlink" href="#pluggable-views" title="Permalink to this headline">¶</a></h1>
+<p class="versionadded">
+<span class="versionmodified">New in version 0.7.</span></p>
+<p>Flask 0.7 introduces pluggable views inspired by the generic views from
+Django which are based on classes instead of functions. The main
+intention is that you can replace parts of the implementations and this
+way have customizable pluggable views.</p>
+<div class="section" id="basic-principle">
+<h2>Basic Principle<a class="headerlink" href="#basic-principle" title="Permalink to this headline">¶</a></h2>
+<p>Consider you have a function that loads a list of objects from the
+database and renders into a template:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">&#39;/users/&#39;</span><span class="p">)</span>
+<span class="k">def</span> <span class="nf">show_users</span><span class="p">(</span><span class="n">page</span><span class="p">):</span>
+ <span class="n">users</span> <span class="o">=</span> <span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;users.html&#39;</span><span class="p">,</span> <span class="n">users</span><span class="o">=</span><span class="n">users</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>This is simple and flexible, but if you want to provide this view in a
+generic fashion that can be adapted to other models and templates as well
+you might want more flexibility. This is where pluggable class based
+views come into place. As the first step to convert this into a class
+based view you would do this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask.views</span> <span class="kn">import</span> <span class="n">View</span>
+
+<span class="k">class</span> <span class="nc">ShowUsers</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">users</span> <span class="o">=</span> <span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">&#39;users.html&#39;</span><span class="p">,</span> <span class="n">objects</span><span class="o">=</span><span class="n">users</span><span class="p">)</span>
+
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">ShowUsers</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s">&#39;show_users&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>As you can see what you have to do is to create a subclass of
+<a class="reference internal" href="api.html#flask.views.View" title="flask.views.View"><tt class="xref py py-class docutils literal"><span class="pre">flask.views.View</span></tt></a> and implement
+<a class="reference internal" href="api.html#flask.views.View.dispatch_request" title="flask.views.View.dispatch_request"><tt class="xref py py-meth docutils literal"><span class="pre">dispatch_request()</span></tt></a>. Then we have to convert that
+class into an actual view function by using the
+<a class="reference internal" href="api.html#flask.views.View.as_view" title="flask.views.View.as_view"><tt class="xref py py-meth docutils literal"><span class="pre">as_view()</span></tt></a> class method. The string you pass to
+that function is the name of the endpoint that view will then have. But
+this by itself is not helpful, so let&#8217;s refactor the code a bit:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask.views</span> <span class="kn">import</span> <span class="n">View</span>
+
+<span class="k">class</span> <span class="nc">ListView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">get_template_name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span>
+
+ <span class="k">def</span> <span class="nf">render_template</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">get_template_name</span><span class="p">(),</span> <span class="o">**</span><span class="n">context</span><span class="p">)</span>
+
+ <span class="k">def</span> <span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">context</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;objects&#39;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_objects</span><span class="p">()}</span>
+ <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
+
+<span class="k">class</span> <span class="nc">UserView</span><span class="p">(</span><span class="n">ListView</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">get_template_name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="s">&#39;users.html&#39;</span>
+
+ <span class="k">def</span> <span class="nf">get_objects</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+</pre></div>
+</div>
+<p>This of course is not that helpful for such a small example, but it&#8217;s good
+enough to explain the basic principle. When you have a class based view
+the question comes up what <cite>self</cite> points to. The way this works is that
+whenever the request is dispatched a new instance of the class is created
+and the <a class="reference internal" href="api.html#flask.views.View.dispatch_request" title="flask.views.View.dispatch_request"><tt class="xref py py-meth docutils literal"><span class="pre">dispatch_request()</span></tt></a> method is called with
+the parameters from the URL rule. The class itself is instanciated with
+the parameters passed to the <a class="reference internal" href="api.html#flask.views.View.as_view" title="flask.views.View.as_view"><tt class="xref py py-meth docutils literal"><span class="pre">as_view()</span></tt></a> function.
+For instance you can write a class like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">RenderTemplateView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">template_name</span><span class="p">):</span>
+ <span class="bp">self</span><span class="o">.</span><span class="n">template_name</span> <span class="o">=</span> <span class="n">template_name</span>
+ <span class="k">def</span> <span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">template_name</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>And then you can register it like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/about&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">RenderTemplateView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span>
+ <span class="s">&#39;about_page&#39;</span><span class="p">,</span> <span class="n">template_name</span><span class="o">=</span><span class="s">&#39;about.html&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="method-hints">
+<h2>Method Hints<a class="headerlink" href="#method-hints" title="Permalink to this headline">¶</a></h2>
+<p>Pluggable views are attached to the application like a regular function by
+either using <a class="reference internal" href="api.html#flask.Flask.route" title="flask.Flask.route"><tt class="xref py py-func docutils literal"><span class="pre">route()</span></tt></a> or better
+<a class="reference internal" href="api.html#flask.Flask.add_url_rule" title="flask.Flask.add_url_rule"><tt class="xref py py-meth docutils literal"><span class="pre">add_url_rule()</span></tt></a>. That however also means that you would
+have to provide the names of the HTTP methods the view supports when you
+attach this. In order to move that information to the class you can
+provide a <a class="reference internal" href="api.html#flask.views.View.methods" title="flask.views.View.methods"><tt class="xref py py-attr docutils literal"><span class="pre">methods</span></tt></a> attribute that has this
+information:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">MyView</span><span class="p">(</span><span class="n">View</span><span class="p">):</span>
+ <span class="n">methods</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">]</span>
+
+ <span class="k">def</span> <span class="nf">dispatch_request</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">&#39;POST&#39;</span><span class="p">:</span>
+ <span class="o">...</span>
+ <span class="o">...</span>
+
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/myview&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">MyView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s">&#39;myview&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="method-based-dispatching">
+<h2>Method Based Dispatching<a class="headerlink" href="#method-based-dispatching" title="Permalink to this headline">¶</a></h2>
+<p>For RESTful APIs it&#8217;s especially helpful to execute a different function
+for each HTTP method. With the <a class="reference internal" href="api.html#flask.views.MethodView" title="flask.views.MethodView"><tt class="xref py py-class docutils literal"><span class="pre">flask.views.MethodView</span></tt></a> you can
+easily do that. Each HTTP method maps to a function with the same name
+(just in lowercase):</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">flask.views</span> <span class="kn">import</span> <span class="n">MethodView</span>
+
+<span class="k">class</span> <span class="nc">UserAPI</span><span class="p">(</span><span class="n">MethodView</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">users</span> <span class="o">=</span> <span class="n">User</span><span class="o">.</span><span class="n">query</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
+ <span class="o">...</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">user</span> <span class="o">=</span> <span class="n">User</span><span class="o">.</span><span class="n">from_form_data</span><span class="p">(</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">)</span>
+ <span class="o">...</span>
+
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">UserAPI</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s">&#39;users&#39;</span><span class="p">))</span>
+</pre></div>
+</div>
+<p>That way you also don&#8217;t have to provide the
+<a class="reference internal" href="api.html#flask.views.View.methods" title="flask.views.View.methods"><tt class="xref py py-attr docutils literal"><span class="pre">methods</span></tt></a> attribute. It&#8217;s automatically set based
+on the methods defined in the class.</p>
+</div>
+<div class="section" id="decorating-views">
+<h2>Decorating Views<a class="headerlink" href="#decorating-views" title="Permalink to this headline">¶</a></h2>
+<p>Since the view class itself is not the view function that is added to the
+routing system it does not make much sense to decorate the class itself.
+Instead you either have to decorate the return value of
+<a class="reference internal" href="api.html#flask.views.View.as_view" title="flask.views.View.as_view"><tt class="xref py py-meth docutils literal"><span class="pre">as_view()</span></tt></a> by hand:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">view</span> <span class="o">=</span> <span class="n">rate_limited</span><span class="p">(</span><span class="n">UserAPI</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s">&#39;users&#39;</span><span class="p">))</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">view</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Starting with Flask 0.8 there is also an alternative way where you can
+specify a list of decorators to apply in the class declaration:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">UserAPI</span><span class="p">(</span><span class="n">MethodView</span><span class="p">):</span>
+ <span class="n">decorators</span> <span class="o">=</span> <span class="p">[</span><span class="n">rate_limited</span><span class="p">]</span>
+</pre></div>
+</div>
+<p>Due to the implicit self from the caller&#8217;s perspective you cannot use
+regular view decorators on the individual methods of the view however,
+keep this in mind.</p>
+</div>
+<div class="section" id="method-views-for-apis">
+<h2>Method Views for APIs<a class="headerlink" href="#method-views-for-apis" title="Permalink to this headline">¶</a></h2>
+<p>Web APIs are often working very closely with HTTP verbs so it makes a lot
+of sense to implement such an API based on the
+<a class="reference internal" href="api.html#flask.views.MethodView" title="flask.views.MethodView"><tt class="xref py py-class docutils literal"><span class="pre">MethodView</span></tt></a>. That said, you will notice that the API
+will require different URL rules that go to the same method view most of
+the time. For instance consider that you are exposing a user object on
+the web:</p>
+<table border="1" class="docutils">
+<colgroup>
+<col width="22%" />
+<col width="22%" />
+<col width="56%" />
+</colgroup>
+<tbody valign="top">
+<tr class="row-odd"><td>URL</td>
+<td>Method</td>
+<td>Description</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">/users/</span></tt></td>
+<td><tt class="docutils literal"><span class="pre">GET</span></tt></td>
+<td>Gives a list of all users</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">/users/</span></tt></td>
+<td><tt class="docutils literal"><span class="pre">POST</span></tt></td>
+<td>Creates a new user</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">/users/&lt;id&gt;</span></tt></td>
+<td><tt class="docutils literal"><span class="pre">GET</span></tt></td>
+<td>Shows a single user</td>
+</tr>
+<tr class="row-odd"><td><tt class="docutils literal"><span class="pre">/users/&lt;id&gt;</span></tt></td>
+<td><tt class="docutils literal"><span class="pre">PUT</span></tt></td>
+<td>Updates a single user</td>
+</tr>
+<tr class="row-even"><td><tt class="docutils literal"><span class="pre">/users/&lt;id&gt;</span></tt></td>
+<td><tt class="docutils literal"><span class="pre">DELETE</span></tt></td>
+<td>Deletes a single user</td>
+</tr>
+</tbody>
+</table>
+<p>So how would you go about doing that with the
+<a class="reference internal" href="api.html#flask.views.MethodView" title="flask.views.MethodView"><tt class="xref py py-class docutils literal"><span class="pre">MethodView</span></tt></a>? The trick is to take advantage of the
+fact that you can provide multiple rules to the same view.</p>
+<p>Let&#8217;s assume for the moment the view would look like this:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">UserAPI</span><span class="p">(</span><span class="n">MethodView</span><span class="p">):</span>
+
+ <span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user_id</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">user_id</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
+ <span class="c"># return a list of users</span>
+ <span class="k">pass</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="c"># expose a single user</span>
+ <span class="k">pass</span>
+
+ <span class="k">def</span> <span class="nf">post</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="c"># create a new user</span>
+ <span class="k">pass</span>
+
+ <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user_id</span><span class="p">):</span>
+ <span class="c"># delete a single user</span>
+ <span class="k">pass</span>
+
+ <span class="k">def</span> <span class="nf">put</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user_id</span><span class="p">):</span>
+ <span class="c"># update a single user</span>
+ <span class="k">pass</span>
+</pre></div>
+</div>
+<p>So how do we hook this up with the routing system? By adding two rules
+and explicitly mentioning the methods for each:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">user_view</span> <span class="o">=</span> <span class="n">UserAPI</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="s">&#39;user_api&#39;</span><span class="p">)</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">&#39;user_id&#39;</span><span class="p">:</span> <span class="bp">None</span><span class="p">},</span>
+ <span class="n">view_func</span><span class="o">=</span><span class="n">user_view</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+<span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;/users/&lt;int:user_id&gt;&#39;</span><span class="p">,</span> <span class="n">view_func</span><span class="o">=</span><span class="n">user_view</span><span class="p">,</span>
+ <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;PUT&#39;</span><span class="p">,</span> <span class="s">&#39;DELETE&#39;</span><span class="p">])</span>
+</pre></div>
+</div>
+<p>If you have a lot of APIs that look similar you can refactor that
+registration code:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">register_api</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">endpoint</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="s">&#39;id&#39;</span><span class="p">,</span> <span class="n">pk_type</span><span class="o">=</span><span class="s">&#39;int&#39;</span><span class="p">):</span>
+ <span class="n">view_func</span> <span class="o">=</span> <span class="n">view</span><span class="o">.</span><span class="n">as_view</span><span class="p">(</span><span class="n">endpoint</span><span class="p">)</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="n">pk</span><span class="p">:</span> <span class="bp">None</span><span class="p">},</span>
+ <span class="n">view_func</span><span class="o">=</span><span class="n">view_func</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;POST&#39;</span><span class="p">])</span>
+ <span class="n">app</span><span class="o">.</span><span class="n">add_url_rule</span><span class="p">(</span><span class="s">&#39;</span><span class="si">%s</span><span class="s">&lt;</span><span class="si">%s</span><span class="s">:</span><span class="si">%s</span><span class="s">&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">pk</span><span class="p">),</span> <span class="n">view_func</span><span class="o">=</span><span class="n">view_func</span><span class="p">,</span>
+ <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">&#39;GET&#39;</span><span class="p">,</span> <span class="s">&#39;PUT&#39;</span><span class="p">,</span> <span class="s">&#39;DELETE&#39;</span><span class="p">])</span>
+
+<span class="n">register_api</span><span class="p">(</span><span class="n">UserAPI</span><span class="p">,</span> <span class="s">&#39;user_api&#39;</span><span class="p">,</span> <span class="s">&#39;/users/&#39;</span><span class="p">,</span> <span class="n">pk</span><span class="o">=</span><span class="s">&#39;user_id&#39;</span><span class="p">)</span>
+</pre></div>
+</div>
+</div>
+</div>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper"><p class="logo"><a href="index.html">
+ <img class="logo" src="_static/flask.png" alt="Logo"/>
+</a></p>
+ <h3><a href="index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Pluggable Views</a><ul>
+<li><a class="reference internal" href="#basic-principle">Basic Principle</a></li>
+<li><a class="reference internal" href="#method-hints">Method Hints</a></li>
+<li><a class="reference internal" href="#method-based-dispatching">Method Based Dispatching</a></li>
+<li><a class="reference internal" href="#decorating-views">Decorating Views</a></li>
+<li><a class="reference internal" href="#method-views-for-apis">Method Views for APIs</a></li>
+</ul>
+</li>
+</ul>
+<h3>Related Topics</h3>
+<ul>
+ <li><a href="index.html">Documentation overview</a><ul>
+ <li>Previous: <a href="signals.html" title="previous chapter">Signals</a></li>
+ <li>Next: <a href="reqcontext.html" title="next chapter">The Request Context</a></li>
+ </ul></li>
+</ul>
+ <h3>This Page</h3>
+ <ul class="this-page-menu">
+ <li><a href="_sources/views.txt"
+ rel="nofollow">Show Source</a></li>
+ </ul>
+<div id="searchbox" style="display: none">
+ <h3>Quick search</h3>
+ <form class="search" action="search.html" method="get">
+ <input type="text" name="q" />
+ <input type="submit" value="Go" />
+ <input type="hidden" name="check_keywords" value="yes" />
+ <input type="hidden" name="area" value="default" />
+ </form>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </div>
+ </div>
+ <div class="clearer"></div>
+ </div>
+ <div class="footer">
+ &copy; Copyright 2010, Armin Ronacher.
+ Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/META-INF/container.xml b/studio/static/doc/myosa/META-INF/container.xml
new file mode 100644
index 0000000..93a63d0
--- /dev/null
+++ b/studio/static/doc/myosa/META-INF/container.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8'?>
+<container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
+ <rootfiles>
+ <rootfile media-type="application/oebps-package+xml" full-path="content.opf"/>
+ </rootfiles>
+</container>
diff --git a/studio/static/doc/myosa/ch000_table_of_contents.xhtml b/studio/static/doc/myosa/ch000_table_of_contents.xhtml
new file mode 100644
index 0000000..1f9f2ad
--- /dev/null
+++ b/studio/static/doc/myosa/ch000_table_of_contents.xhtml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Table Of Contents</h1><h2 _moz_dirty="">Sugar Activities</h2><p _moz_dirty=""><a _moz_dirty="" href="ch001_introduction.xhtml">Introduction</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch002_what-is-sugar.xhtml">What Is Sugar?</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch003_what-is-a-sugar-activity.xhtml">What Is A Sugar Activity?</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch004_what-do-i-need-to-know-to-write-a-sugar-activity.xhtml">What Do I Need To Know To Write A Sugar Activity?</a></p><h2 _moz_dirty="">Programming<br _moz_dirty=""/></h2><p _moz_dirty=""><a _moz_dirty="" href="ch006_setting-up-a-development-environment.xhtml">Setting Up A Development Environment</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch007_creating-your-first-activity.xhtml">Creating Your First Activity</a><br _moz_dirty=""/><br _moz_dirty=""/><a _moz_dirty="" href="ch008_a-standalone-python-program-for-reading-etexts.xhtml">A Standalone Python Program For Reading ETexts</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch009_inherit-from-sugaractivityactivity.xhtml">Inherit From sugar.activity.Activity</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch010_package-the-activity.xhtml">Package The Activity</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch011_add-refinements.xhtml">Add Refinements</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch012_add-your-activity-code-to-version-control.xhtml">Add Your Activity Code To Version Control</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch013_going-international-with-pootle.xhtml">Going International With Pootle</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch014_distribute-your-activity.xhtml">Distribute Your Activity</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch015_debugging-sugar-activities.xhtml">Debugging Sugar Activities</a></p><h2 _moz_dirty="">Advanced Topics</h2><p _moz_dirty=""><a _moz_dirty="" href="ch017_making-shared-activities.xhtml">Making Shared Activities</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch018_adding-text-to-speech.xhtml">Adding Text To Speech</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch019_fun-with-the-journal.xhtml">Fun With The Journal</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch020_making-activities-using-pygame.xhtml">Making Activities Using Pygame</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch021_making-new-style-toolbars.xhtml">Making New Style Toolbars</a></p><h2 _moz_dirty="">Appendix</h2><p _moz_dirty=""><a _moz_dirty="" href="ch023_where-to-go-from-here.xhtml">Where To Go From Here?</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch024_about-the-authors.xhtml">About The Authors</a></p><p _moz_dirty=""><a _moz_dirty="" href="ch025_credits.xhtml">Credits</a></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch001_introduction.xhtml b/studio/static/doc/myosa/ch001_introduction.xhtml
new file mode 100644
index 0000000..b397a37
--- /dev/null
+++ b/studio/static/doc/myosa/ch001_introduction.xhtml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1 style="text-align: center;">Make Your Own Sugar Activities!</h1><div _moz_dirty="" style="text-align: center;"><span _moz_dirty="" style="font-style: italic;">by James Simmons</span><br _moz_dirty="" type="_moz"/></div><br _moz_dirty="" type="_moz"/><h1 _moz_dirty="">Introduction
+</h1>
+<p>"This book is a record of a pleasure trip. If it were a record of a solemn scientific expedition, it would have about it that gravity, that profundity, and that impressive incomprehensibility which are so proper to works of that kind, and withal so attractive."
+</p>
+<p>From the Preface to <em>The Innocents Abroad</em>, by Mark Twain
+ <br/></p>
+<p>
+</p>
+<p>The purpose of this book is to teach you what you need to know to write Activities for Sugar, the operating environment developed for the One Laptop Per Child project. This book does not assume that you know how to program a computer, although those who do will find useful information in it.  My primary goal in writing it is to encourage non programmers, including children and their teachers, to create their own Sugar Activities.  Because of this goal I will include some details that other books would leave out and leave out things that others would include.  Impressive incomprehensibility will be kept to a minimum.
+ <br/></p>
+<p>If you just want to learn how to write computer programs Sugar provides many Activities to help you do that: Etoys, Turtle Art, Scratch, and Pippy. None of these are really suitable for creating Activities so I won't cover them in this book, but they're a great way to learn about programming. If you decide after playing with these that you'd like to try writing an Activity after all you'll have a good foundation of knowledge to build on.
+</p>
+<p>When you have done some programming then you'll know how satisfying it can be to use a program that you made yourself, one that does <em>exactly </em>what you want it to do.  Creating a Sugar Activity takes that enjoyment to the next level.  A useful Sugar Activity can be translated by volunteers into every language, be downloaded hundreds of times a week and used every day by students all over the world.
+</p>
+<p><img width="600" height="450" alt="Some Sugar Activities!" src="static/ActivitiesGuideSugar-CoverImage600x450-en.png"/><br/></p>
+<p>A book that teaches <em>everything</em> you need to know to write Activities would be really, really long and would duplicate material that is already available elsewhere. Because of this, I am going to write this as sort of a guided tour of Activity development. That means, for example, that I'll teach you what Python is and why it's important to learn it but I won't teach you the Python language itself. There are excellent tutorials on the Internet that will do that, and I'll refer you to those tutorials.
+</p>
+<p>There is much sample code in this book, but there is no need for you to type it in to try it out.  All of the code is in a Git repository that you can download to your own computer.  If you've never used Git there is a chapter that explains what it is and how to use it.
+ <br/></p>
+<p>I started writing Activities shortly after I received my XO laptop. When I started I didn't know <em>any</em> of the material that will be in this book. I had a hard time knowing where to begin. What I did have going for me though was a little less than 30 years as a professional programmer. As a result of that I think like a programmer. A good programmer can take a complex task and divide it up into manageable pieces. He can figure out how things <em>must</em> work, and from that figure out how they <em>do</em> work. He knows how to ask for help and where. If there is no obvious place to begin he can begin <em>somewhere</em> and eventually get where he needs to go.
+</p>
+<p>Because I went through this process I think I can be a pretty good guide to writing Sugar Activities.  Along the way I hope to also teach you how to think like a programmer does.
+</p>
+<p>From time to time I may add chapters to this book.  Sugar is a great application platform and this book can only begin to tell you what is possible.  It is my hope that future versions of the book will have guest chapters on more advanced topics written by other experienced Activity developers.
+</p>
+<h2>Formats For This Book
+</h2>
+<p>This book is part of the <em>FLOSS Manuals</em> project and is available for online viewing at their website:
+</p>
+<p><a href="http://en.flossmanuals.net">http://en.flossmanuals.net/</a>
+ <br/></p>
+<p>You can also purchase a printed and bound version of this book at <em>Lulu.com</em>:
+</p>
+<p><a href="http://www.lulu.com/product/paperback/make-your-own-sugar-activities/12995552?productTrackingContext=search_results/search_shelf/center/1">http://stores.lulu.com/flossmanuals</a>
+</p>
+<p>The <em>Internet Archive</em> has this book available as a full color PDF, as well as EPUB, MOBI, and DjVu versions, all of which you can download for free:
+</p>
+<p><a href="http://www.archive.org/details/MakeYourOwnSugarActivities">http://www.archive.org/details/MakeYourOwnSugarActivities</a>  
+</p>
+<p> The <em>Amazon Kindle Store</em> has exactly the same MOBI version as the Internet Archive does.
+</p>
+<p>If you choose to read this book on a Kindle be aware that the Kindle's narrow screen is not well suited for displaying program listings.  I suggest you refer to the FLOSS Manuals website to see what the code looks like properly formatted.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch002_what-is-sugar.xhtml b/studio/static/doc/myosa/ch002_what-is-sugar.xhtml
new file mode 100644
index 0000000..096ac7e
--- /dev/null
+++ b/studio/static/doc/myosa/ch002_what-is-sugar.xhtml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>What is Sugar?
+</h1>
+<p>Sugar is the user interface designed for the XO laptop. It can now be installed on most PCs, including older models that can't run the latest Windows software. You can also install it on a thumb drive (Sugar on a Stick) and boot your PC from that.
+</p>
+<p>When the XO laptop first came out some people questioned the need for a new user interface. Wouldn't it be better for children to learn something more like what they would use as adults? Why not give them Microsoft Windows instead?
+</p>
+<p>This would be a reasonable question if the goal was to train children to use computers and nothing else. It would be even more reasonable if we could be sure that the software they would use as adults looked and worked like the Microsoft Windows of today.&#160; These are of course not reasonable assumptions.
+</p>
+<p>The OLPC project is not just about teaching computer literacy. It is about teaching <em>everything</em>: reading, writing, arithmetic, history, science, arts and crafts, computer programming, music composition, and everything else. Not only do we expect the child to use the computer for her school work, we expect her to take it home and use it for her own explorations into subjects that interest her.&#160;
+</p>
+<p>This is a great deal more than anyone has done with computers for education, so it is reasonable to rethink how children should work with computers. Sugar is the result of that rethinking.
+</p>
+<p>Sugar has the following unique features:
+</p>
+<h2>The Journal
+</h2>
+<p>The Journal is where all the student's work goes. Instead of files and folders there is a list of Journal entries. The list is sorted in descending order by the date and time it was last worked on. In a way it's like the "Most Recently Used" document menu in Windows, except instead of containing just the last few items it contains everything and is the normal way to save and resume work on something.
+</p>
+<p>The Journal makes it easy to organize your work.&#160; Any work you do is saved to the Journal.&#160; Anything you download from the web goes in the Journal.&#160; If you've ever downloaded a file using a web browser, then had to look for it afterwards because it went in some directory other than the one you expected, or if you ever had to help your parents when they were in a similar situation, you can understand the value of the Journal.
+ <br/></p>
+<p>The Journal has metadata for each item in it. Metadata is information about information. Every Journal entry has a title, a description, a list of keywords, and a screen shot of what it looked like the last time it was used. It has an activity id that links it to the Activity that created it, and it may have a MIME type as well (which is a way of identifying Journal entries so that items not created by an Activity may still be used by an Activity that supports that MIME type).
+</p>
+<p>In addition to these common metadata items a Journal entry may be given custom metadata by an Activity. For instance, the <strong>Read</strong> Activity uses custom metadata to save the page number you were reading when you quit the Activity. When you resume reading later the Activity will put you on that page again.
+</p>
+<p>In addition to work created by Activities, the Journal can contain Activities themselves. To install an Activity you can use the <strong>Browse</strong> Activity to visit the website <a href="http://activities.sugarlabs.org/">http://activities.sugarlabs.org</a> and download it. It will automatically be saved to the Journal and be ready for use. If you don't want the Activity any more, simply delete it from the Journal and it's <em>completely gone</em>. No uninstall programs, no dialog boxes telling you that such and such a .DLL doesn't seem to be needed anymore and do you want to delete it? No odd bits and pieces left behind.
+</p>
+<h2>Collaboration
+</h2>
+<p>The second unique feature Sugar is Collaboration. Collaboration means that Activities can be used by more than one person at the same time. While not every Activity needs collaboration and not every Activity that could use it supports it, a really first rate Activity will provide some way to interact with other Sugar users on the network. For instance, all the e-book reading Activities provide a way of giving a copy of the book you're reading (with any notes you added to it) to a friend or to the whole class. The <strong>Write</strong> Activity lets several students work on the same document together. The <strong>Distance</strong> Activity lets two students see how far apart from each other they are.
+</p>
+<p>There are five views of the system you can switch to at the push of a button (Function Keys F1-4). They are:
+</p>
+<ul><li>The Neighborhood View</li>
+ <li> The Friends View</li>
+ <li> The Activity Ring</li>
+ <li> The Journal</li>
+</ul><p>Of these Views, the first two are used for Collaboration.
+</p>
+<p>The Neighborhood View shows icons for everyone on the network. Every icon looks like a stick figure made by putting an "O" above an "X". Each icon has a name, chosen by the student when she sets up her computer. Every icon is displayed in two colors, also chosen by the student. In addition to these "XO" icons there will be icons representing mesh networks and others representing WiFi hot spots. Finally there will be icons representing active Activities that their owners wish to share.
+</p>
+<p>To understand how this works consider the <strong>Chat</strong> Activity. The usual way applications do chat is to have all the participants start up a chat client and visit a particular chat room at the same time. With Sugar it's different. One student starts the Chat Activity on her own computer and goes to the Neighborhood View to invite others on the network to participate. They will see a Chat icon in their own Neighborhood View and they can accept. The act of accepting starts up their own Chat Activity and connects them to the other participants.
+</p>
+<p>The Friends View is similar to the Neighborhood View, but only contains icons for people you have designated as Friends. Collaboration can be offered at three levels: with individual persons, with the whole Neighborhood, and with Friends. Note that the student alone decides who her Friends are. There is no need to ask to be someone's Friend.&#160; It's more like creating a mailing list in email.
+ <br/></p>
+<h2>Security
+</h2>
+<p>Protecting computers from malicious users is very important, and if the computers belong to students it is doubly important. It is also more difficult, because we can't expect young students to remember passwords and keep them secret. Since Sugar runs on top of Linux viruses aren't much of a problem, but malicious Activities definitely are. If an Activity was allowed unrestricted access to the Journal, for instance, it could wipe it out completely. Somebody could write an Activity that seems to be harmless and amusing, but perhaps after some random number of uses it could wipe out a student's work.
+</p>
+<p>The most common way to prevent a program from doing malicious things is to make it run in a sandbox. A sandbox is a way to limit what a program is allowed to do. With the usual kind of sandbox you either have an untrusted program that can't do much of anything or a trusted program that is not restricted at all. An application becomes trusted when a third party vouches for it by giving it a <em>signature</em>. The signature is a mathematical operation done on the program that only remains valid if the program is not modified.
+</p>
+<p>Sugar has a more sophisticated sandbox for Activities than that. No Activity needs to be trusted or is trusted. Every Activity can only work with the Journal in a limited, indirect way. Each Activity has directories specific to it that it can write to, and all other directories and files are limited to read-only access. In this way no Activity can interfere with the workings of any other Activity. In spite of this, an Activity can be made to do what it needs to do.
+</p>
+<h2>Summary
+</h2>
+<p>Sugar is an operating environment designed to support the education of children. It organizes a child's work without needing files and folders. It supports collaboration between students. Finally, it provides a robust security model that prevents malicious programs from harming a student's work.
+</p>
+<p>It would not be surprising to see these features someday adopted by other desktop environments.
+</p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch003_what-is-a-sugar-activity.xhtml b/studio/static/doc/myosa/ch003_what-is-a-sugar-activity.xhtml
new file mode 100644
index 0000000..28280ce
--- /dev/null
+++ b/studio/static/doc/myosa/ch003_what-is-a-sugar-activity.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>What is a Sugar Activity?
+</h1>
+<p>A Sugar Activity is a self-contained Sugar application packaged in a .xo bundle.
+</p>
+<p>An .xo bundle is an archive file in the Zip format.&#160; It contains:
+</p>
+<ul><li>A MANIFEST file listing everything in the bundle</li>
+ <li>An <strong>activity.info</strong> file that has attributes describing the Activity as name=value pairs.&#160; These attributes include the Activity name, its version number, an identifier, and other things we will discuss when we write your first Activity.</li>
+ <li>An icon file (in SVG format)</li>
+ <li>Files containing translations of the text strings the Activity uses into many languages</li>
+ <li>The program code to run the Activity</li>
+</ul><p>A Sugar Activity will generally have some Python code that extends a Python class called Activity.&#160; It may also make use of code written in other languages if that code is written in a way that allows it to be used from Python (this is called having <strong>Python bindings</strong>).&#160; It is even possible to write a Sugar Activity without using Python at all, but this is beyond the scope of this book.
+</p>
+<p>There are only a few things that an Activity can depend on being included with every version of Sugar. These include modules like Evince (PDF and other document viewing), Gecko (rendering web pages), and Python libraries like PyGTK and PyGame.&#160; Everything needed to run the Activity that is <em>not</em> supplied by Sugar must go in the bundle file.&#160; A question sometimes heard on the mailing lists is "How do I make Sugar install <em>X</em> the first time my Activity is run?"&#160; The answer: you don't.&#160; If you need <em>X</em> it needs to go in the bundle.&#160;
+</p>
+<p>You can install an Activity by copying or downloading it to the Journal. You uninstall it by removing it from the Journal. There is no <em>Install Shield</em> to deal with, no deciding where you want the files installed, no possibility that installing a new Activity will make an already installed Activity stop working.
+</p>
+<p>An Activity generally creates and reads objects in the Journal.&#160; A first rate Activity will provide some way for the Activity to be shared by multiple users.
+</p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch004_what-do-i-need-to-know-to-write-a-sugar-activity.xhtml b/studio/static/doc/myosa/ch004_what-do-i-need-to-know-to-write-a-sugar-activity.xhtml
new file mode 100644
index 0000000..1faa0ca
--- /dev/null
+++ b/studio/static/doc/myosa/ch004_what-do-i-need-to-know-to-write-a-sugar-activity.xhtml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>What Do I Need To Know To Write A Sugar Activity?&#160;
+</h1>
+<p>If you are going to write Sugar Activities you should learn something about the topics described in this chapter. There is no need to become an expert in any of them, but you should bookmark their websites and skim through their tutorials. This will help you to understand the code samples we'll be looking at.
+</p>
+<h2>Python
+</h2>
+<p>Python is the most used language for writing Activities.&#160; While you can use other languages, most Activities have at least some Python in them.&#160; Sugar provides a Python API that simplifies creating Activities.&#160; While it is possible to write Activities using no Python at all (like <strong>Etoys</strong>), it is unusual.&#160;
+ <br/></p>
+<p>All of the examples in this book are written entirely in Python.
+ <br/></p>
+<p>There are compiled languages and interpreted languages. In a compiled language the code you write is translated into the language of the chip it will run on and it is this translation that is actually run by the OS. In an interpreted language there is a program called an interpreter that reads the code you write and does what the code tells it to do. (This is over simplified, but close enough to the truth for this chapter).
+</p>
+<p>Python is an interpreted language. There are advantages to having a language that is compiled and there are advantages to having an interpreted language. The advantages Python has for developing Activities are:
+</p>
+<ul><li>It is portable. In other words, you can make your program run on any chip and any OS without making a version specific to each one. Compiled programs only run on the OS and chip they are compiled for.</li>
+ <li> Since the source code is the thing being run, you can't give someone a Python program without giving them the source code. You can learn a lot about Activity programming by studying other people's code, and there is plenty of it to study.</li>
+ <li> It is an easy language for new programmers to learn, but has language features that experienced programmers need.</li>
+ <li> It is widely used. One of the best known Python users is Google. They use it enough that they have started a project named &#8220;Unladen Swallow&#8221; to make Python programs run faster.</li>
+</ul><p>The big advantage of a compiled language is that it can run much faster than an interpreted language. However, in actual practice a Python program can perform as well as a compiled program. To understand why this is you need to understand how a Python program is made.
+</p>
+<p>Python is known as a &#8220;glue&#8221; language. The idea is that you have components written in various languages (usually C and C++) and they have Python bindings. Python is used to &#8220;glue&#8221; these components together to create applications. In most applications the bulk of the application's function is done by these compiled components, and the application spends relatively little time running the Python code that glues the components together.
+</p>
+<p>In addition to Activities using Python most of the Sugar environment itself is written in Python.
+</p>
+<p>If you have programmed in other languages before there is a good tutorial for learning Python at the Python website: <a href="http://docs.python.org/tutorial/">http://docs.python.org/tutorial/</a>.&#160; If you're just starting out in programming you might check out <em>Invent Your Own Computer Games With Python</em>, which you can read for free at <a href="http://inventwithpython.com/" target="_top">http://inventwithpython.com/</a>.
+</p>
+<h2>PyGTK
+</h2>
+<p>GTK+ is a set of components for creating user interfaces. These components include things like buttons, scroll bars, list boxes, and so on. It is used by GNOME desktop environment and the applications that run under it. Sugar Activities use a special GNOME theme that give GTK+ controls a unique look.
+</p>
+<p>PyGTK is a set of Python bindings that let you use GTK+ components in Python programs. There is a tutorial showing how to use it at the PyGTK website: <a href="http://www.pygtk.org/tutorial.html">http://www.pygtk.org/tutorial.html</a>.
+</p>
+<h2>PyGame
+</h2>
+<p>The alternative to using PyGTK for your Activity is PyGame. PyGame can create images called sprites and move them around on the screen. As you might expect, PyGame is mostly used for writing games. It is less commonly used in Activities than PyGTK.
+</p>
+<p>The tutorial to learn about PyGame is at the PyGame website: <a href="http://www.pygame.org/wiki/tutorials">http://www.pygame.org/wiki/tutorials</a>. The website also has a bunch of pygame projects you can download and try out.
+</p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch006_setting-up-a-development-environment.xhtml b/studio/static/doc/myosa/ch006_setting-up-a-development-environment.xhtml
new file mode 100644
index 0000000..2556f82
--- /dev/null
+++ b/studio/static/doc/myosa/ch006_setting-up-a-development-environment.xhtml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Setting Up a Sugar Development Environment
+</h1>
+<p>It is not currently practical to develop Activities for the XO on the XO. It's not so much that you can't do it, but that it's easier and more productive to do your development and testing on another machine running a more conventional OS. This gives you access to better tools and it also enables you to simulate collaboration between two computers running Sugar using only one computer.
+</p>
+<h2>Install Linux Or Use A Virtual Machine?
+</h2>
+<p>Even though Sugar runs on Linux it is possible to run a complete instance of Sugar in a virtual machine that runs on Windows.&#160; A virtual machine is a way to run one operating system on top of another one. The operating system being run is fooled into thinking it has the whole computer to itself. (Computer industry pundits will tell you that using virtual machines is the newest new thing out there. Old timers like me know that IBM was doing it on their mainframe computers back in the 1970's).
+</p>
+<p>For awhile this was actually the recommended way to develop Activities. The version of Linux that Sugar used was different enough from regular Linux distributions that even Linux users were running Sugar in a virtual machine on top of Linux.
+</p>
+<p>The situation has improved, and most current Linux distributions have a usable Sugar environment.
+</p>
+<p>If you're used to Windows you might think that running Sugar in a VM from Windows instead of installing Linux might be the easier option. In practice it is not. Linux running in a VM is still Linux, so you're still going to have to learn some things about Linux to do Activity development. Also, running a second OS in a VM requires a really powerful machine with gigabytes of memory. On the other hand, I do my Sugar development using Linux on an IBM NetVista Pentium IV I bought used for a little over a hundred dollars, shipping included. It is more than adequate.
+</p>
+<p>Installing Linux is not the test of manhood it once was. Anyone can do it. The GNOME desktop provided with Linux is very much like Windows so you'll feel right at home using it.
+</p>
+<p>When you install Linux you have the option to do a dual boot, running Linux and Windows on the same computer (but not at the same time). This means you set aside a disk partition for use by Linux and when you start the computer a menu appears asking which OS you want to start up. The Linux install will even create the partition for you, and a couple of gigabytes is more than enough disk space. Sharing a computer with a Linux installation will not affect your Windows installation at all.
+ <br/></p>
+<p>Sugar Labs has been working to get Sugar included with all Linux distributions. If you already have a favorite distribution, chances are the latest version of it includes Sugar. Fedora, openSuse, Debian, and Ubuntu all include Sugar. If you already use Linux, see if Sugar is included in your distribution. If not, Fedora is what is used by the XO computer so Fedora 10 or later might be your best bet. You can download the Fedora install CD or DVD here: <a href="https://fedoraproject.org/get-fedora">https://fedoraproject.org/get-fedora</a>.
+</p>
+<p>It is worth pointing out that all of the other tools I'm recommending are included in every Linux distribution, and they can be installed with no more effort than checking a check box. The same tools often will run on Windows, but installing them there is more work than you would expect for Windows programs.
+</p>
+<p>If you are unwilling to install and learn about Linux but still want to develop Activities one option you have is to develop a standalone Python program that uses PyGame of PyGTK and make it do what you'd like your Activity to do.&#160; You could then turn over your program to someone else who could convert it into a Sugar Activity.&#160; You could develop such a Python program on Windows or on the Macintosh.
+ <br/></p>
+<p>If you want to do development on a Macintosh running Sugar in a virtual machine may be a more attractive option. If you want to try it details will be found here: <a href="http://wiki.laptop.org/go/Developers/Setup">http://wiki.laptop.org/go/Developers/Setup.</a>&#160; It may also be possible to install Fedora Linux on an Intel or Power PC Macintosh as a dual boot, just like you can do with Windows.&#160; Check the Fedora website for details.
+</p>
+<p>Another option for Mac users is to use <em>Sugar on a Stick</em> as a test environment.&#160; You can learn about that here: <a href="http://wiki.sugarlabs.org/go/Sugar_on_a_Stick">http://wiki.sugarlabs.org/go/Sugar_on_a_Stick</a>.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>What About Using sugar-jhbuild?
+</h2>
+<p><strong>Sugar-jhbuild</strong> is a script that downloads the source code for the latest version of all the Sugar modules and compiles it into a subdirectory of your home directory.&#160; It doesn't actually install Sugar on your system.&#160; Instead, you run it out of the directory you installed it in.&#160; Because of the way it is built and run it doesn't interfere with the modules that make up your normal desktop. If you are developing Sugar itself, or if you are developing Activities that depend on the very latest Sugar features you'll need to run sugar-jhbuild.
+</p>
+<p>Running this script is a bit more difficult than just installing the Sugar packages that come with the distribution.&#160; You'll need to install Git and Subversion, run a Git command from the terminal to download the sugar-jhbuild script, then run the script with several different options which download more code, ask you to install more packages, and ultimately compile everything.&#160; It may take you a couple of hours to do all the steps.&#160; When you're done you'll have an up to date test environment that you can run as an alternative to <strong>sugar-emulator</strong>.&#160; There is no need to uninstall sugar-emulator; both can coexist.
+</p>
+<p>You run it with these commands:
+</p>
+<pre>cd sugar-jhbuild
+./sugar-jhbuild run sugar-emulator
+</pre>
+<p>Should you consider using it? The short answer is no. A longer answer is <em>probably not yet</em>.
+</p>
+<p>If you want your Activities to reach the widest possible audience you <em>don't</em> want the latest Sugar. In fact, if you want a test environment that mimics what is on most XO computers right now you need to use Fedora 10. Because updating operating systems in the field can be a major undertaking for a school most XO's will be running Sugar .82 or older for quite some time.
+</p>
+<p>Of course it is also important to have developers that want to push the boundaries of what Sugar can do. If after developing some Activities you decide you need to be one of them you can learn about running sugar-jhbuild here: <a href="http://wiki.sugarlabs.org/go/DevelopmentTeam/Jhbuild">http://wiki.sugarlabs.org/go/DevelopmentTeam/Jhbuild.</a>
+</p>
+<p>Strictly speaking sugar-jhbuild is just the script that downloads and compiles Sugar.&#160; If you wanted to be correct you would say "Run the copy of <strong>sugar-emulator</strong> you made with sugar-jhbuild".&#160; Most Sugar developers would just say "Run sugar-jhbuild" and that's what I'll say in this book.
+ <br/></p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Python
+</h2>
+<p>We'll be doing all the code samples in Python so you'll need to have Python installed.&#160; Python comes with every Linux distribution.&#160; You can download installers for Windows and the Macintosh at <a href="http://www.python.org">http://www.python.org/</a>.
+ <br/></p>
+<h2>Eric
+</h2>
+<p>Developers today expect their languages to be supported by an <strong>Integrated Development Environment</strong> and Python is no exception. An <strong>IDE</strong> helps to organize your work and provides text editing and a built in set of programming and debugging tools.
+</p>
+<p><img alt="Eric the Python IDE" src="static/ActivitiesGuideSugar-eric-en.jpg" height="491" width="640"/>&#160;
+</p>
+<p>There are two Python IDE's I have tried: Eric and Idle. Eric is the fancier of the two and I recommend it. Every Linux distribution should include it. It looks like it might work on Windows too. You can learn more about it at the Eric website: <a href="http://eric-ide.python-projects.org/">http://eric-ide.python-projects.org/</a>.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>SPE (Stani's Python Editor)
+</h2>
+<p> This is an IDE I discovered while writing this book.&#160; It comes with Fedora and in addition to being a Python editor it will make <strong>UML</strong> diagrams of your code and show <strong>PyDoc</strong> for it.&#160; Here is SPE showing a UML diagram for one of the Activities in this book:
+</p>
+<p><img alt="spe.jpg" src="static/ActivitiesGuideSugar-spe-en.jpg" height="354" width="600"/></p>
+<p>If you're an experienced developer you might find this a useful alternative to Eric.&#160; If you're just starting out Eric should meet your needs pretty well.
+ <br/></p>
+<h2>Other IDE's
+ <br/></h2>
+<p>There is also a commercial Python IDE called Wingware, which has a version you can use for free.&#160; You can learn more about it at <a href="http://www.wingware.com">http://www.wingware.com/</a>.
+ <br/></p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Inkscape
+</h2>
+<p>Inkscape is a tool for creating images in SVG format. Sugar uses SVG for Activity icons and other kinds of artwork. The &#8220;XO&#8221; icon that represents each child in the Neighborhood view is an SVG file that can be modified.
+</p>
+<p><img alt="Using Inkscape to create an Activity icon" src="static/ActivitiesGuideSugar-inkscape-en.jpg" height="550" width="640"/>&#160;
+</p>
+<p>Inkscape comes with every Linux distribution, and can be installed on Windows as well. You can learn more about it here: <a href="http://www.inkscape.org/">http://www.inkscape.org/</a>.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Git
+</h2>
+<p>Git is a version control system. It stores versions of your program code in a way that makes them easy to get back. Whenever you make changes to your code you ask Git to store your code in its repository. If you need to look at an old version of that code later you can. Even better, if some problem shows up in your code you can compare your latest code to an old, working version and see exactly what lines you changed.
+</p>
+<p><img alt="git11_1.jpg" src="static/ActivitiesGuideSugar-git11_1-en.jpg" height="530" width="581"/>&#160;&#8286;
+</p>
+<p>If there are two people working on the same program independently a version control system will merge their changes together automatically.
+</p>
+<p>Suppose you're working on a major new version of your Activity when someone finds a really embarrassing bug in the version you just released. If you use Git you don't need to tell people to live with it until the next release, which could be months away. Instead you can create a branch of the previous version and work on it alongside the version you're enhancing. In effect Git treats the old version you're fixing and the version you're improving as two separate projects.
+</p>
+<p>You can learn more about Git at the Git website: <a href="http://git-scm.com/">http://git-scm.com/</a>.
+</p>
+<p>When you're ready for a Git repository for your project you can set one up here: <a href="http://git.sugarlabs.org/">http://git.sugarlabs.org/</a>.&#160; I will have more to say about setting up and using a Git repository later in this book.
+ <br/></p>
+<p>There is a Git repository containing all the code examples from this book.&#160; Once you have Git installed you can copy the repository to your computer with this command:
+</p>
+<pre><code>git clone git://git.sugarlabs.org/\
+myo-sugar-activities-examples/</code><code>mainline.git</code></pre>
+<p>This command should be typed all on one line.&#160; The backslash (\) character at the end of the first line is used in Linux to continue a long command to a second line.&#160; It is used here to make the command fit on the page of the printed version of this book.&#160; When you type in the command you can leave it out and type <strong>myo-sugar-activities-examples/mainline.git</strong> immediately following <strong>git.sugarlabs.org/</strong>.
+</p>
+<p>This convention of splitting long commands over multiple lines will be used throughout this book.&#160;&#160; In addition to that, the code in Git will generally have longer lines than you'll see in the code listings in the book.&#160; For that reason I'd recommend that you not try to type in the code from these listings, but use the code in Git instead.
+ <br/></p>
+<h2>The GIMP
+</h2>
+<p>The GIMP is one of the most useful and badly named programs ever developed. You can think of it as a free version of Adobe Photoshop. If you need to work with image files (other than SVG's) you need this program.
+</p>
+<p><img alt="Using The GIMP to make a screen capture" src="static/ActivitiesGuideSugar-thegimp-en.jpg" height="390" width="640"/>&#160;
+</p>
+<p>You may never need this program to develop the Activity itself, but when it's time to distribute the Activity you'll use it to create screen shots of your Activity in action. Nothing sells an Activity to a potential user like good screen shots.
+</p>
+<h2>Sugar Emulation
+</h2>
+<p>Most Linux distributions should have Sugar included. In Fedora you can run Sugar as an alternative desktop environment. When you log in to GDM Sugar appears as a desktop selection alongside GNOME, KDE, Window Maker, and any other window managers you have installed.
+</p>
+<p>This is not the normal way to use Sugar for testing. The normal way uses a tool called Xephyr to run a Sugar environment in a window on your desktop. In effect, Xephyr runs an X session inside a window and Sugar runs in that. You can easily take screen shots of Sugar in action, stop and restart Sugar sessions without restarting the computer, and run multiple copies of Sugar to test collaboration.
+</p>
+<p><img alt="sugar-emulator in action" src="static/ActivitiesGuideSugar-ReadEtexts_02-en.jpg" height="480" width="640"/>&#160;
+</p>
+<p>I'll have more to say about this when it's time to test your first Activity.
+</p> &#160;
+<p>
+</p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch007_creating-your-first-activity.xhtml b/studio/static/doc/myosa/ch007_creating-your-first-activity.xhtml
new file mode 100644
index 0000000..84c414c
--- /dev/null
+++ b/studio/static/doc/myosa/ch007_creating-your-first-activity.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Creating your First Sugar Activity
+</h1>
+<h2 class="western">Make A Standalone Python Program First
+</h2>
+<p class="western">The best advice I could give a beginning Activity developer is to make a version of your Activity that can run on its own, outside of the Sugar environment. Testing and debugging a Python program that stands alone is faster, easier and less tedious than doing the same thing with a similar Activity. You'll understand why when you start testing your first Activity.
+</p>
+<p class="western">The more bugs you find before you turn your code into an Activity the better. In fact, it's a good idea to keep a standalone version of your program around even after you have the Activity version well underway. I used my standalone version of <strong>Read Etexts</strong> to develop the text to speech with highlighting feature. This saved me a <em>lot</em> of time, which was especially important because I was figuring things out as I went.
+</p>
+<p>Our first project will be a version of the Read Etexts Activity I wrote.
+ <br/></p>
+<h2 class="western">Inherit From The sugar.activity.Activity Class
+</h2>
+<p>Next we're going to take our standalone Python program and make an Activity out of it.&#160; To do this we need to understand the concept of <em>inheritance</em>.&#160; In everyday speech inheritance means getting something from your parents that you didn't work for.&#160; A king will take his son to a castle window and say, "Someday, lad, this will all be yours!"&#160; That's inheritance.
+</p> In the world of computers programs can have parents and inherit things from them.&#160; Instead of inheriting property, they inherit code. There is a piece of Python code called sugar.activity.Activity that's the best parent an Activity could hope to have, and we're going to convince it to adopt our program.&#160; This doesn't mean that our program will never have to work again,&#160; but it won't have to work as much.
+<br/><h2>Package The Activity
+</h2>
+<p>Now we have to package up our code to make it something that can be run under Sugar and distributed as an .xo file.&#160; This involves setting up a MANIFEST, activity.info, setup.py, and creating a suitable icon with Inkscape.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Add Refinements
+</h2>
+<p>Every Activity will have the basic Activity toolbar. For most Activities this will not be enough, so we'll need to create some custom toolbars as well. Then we need to hook them up to the rest of the Activity code so that what happens to the toolbar triggers actions in the Activity and what happens outside the toolbar is reflected in the state of the toolbar.
+</p>
+<p>In addition to toolbars we'll look at some other ways to spiff up your Activity.
+ <br/></p>
+<h2> Put The Project Code In Version Control
+ <br/></h2>
+<p>By this time we'll have enough code written that it's worth protecting and sharing with the world.&#160; To do that we need to create a Git repository and add our code to it.&#160; We'll also go over the basics of using Git.
+ <br/></p>
+<h2> Going International With Pootle
+</h2>
+<p>Now that our code is in Git we can request help from our first collaborator: the Pootle translation system.&#160; With a little setup work we can get volunteers to make translated versions of our Activity available.
+</p>
+<h2>Distributing The Activity
+</h2>
+<p>In this task we'll take our Activity and set it up on <a href="http://activities.sugarlabs.org">http://activities.sugarlabs.org</a>&#160; plus we'll package up the source code so it can be included in Linux distributions.
+ <br/></p>
+<h2> Add Collaboration
+</h2>
+<p>Next we'll add code to share e-books with Friends and the Neighborhood.
+ <br/></p>
+<h2>Add Text To Speech
+</h2>
+<p>Text to Speech with word highlighting is next.&#160; Our simple project will become a Kindle-killer!
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch008_a-standalone-python-program-for-reading-etexts.xhtml b/studio/static/doc/myosa/ch008_a-standalone-python-program-for-reading-etexts.xhtml
new file mode 100644
index 0000000..4afdb21
--- /dev/null
+++ b/studio/static/doc/myosa/ch008_a-standalone-python-program-for-reading-etexts.xhtml
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1> A Standalone Python Program For Reading Etexts
+</h1>
+<h2>The Program
+ <br/></h2>
+<p>Our example program is based on the first Activity I wrote, <strong>Read Etexts</strong>.&#160; This is a program for reading free e-books.
+ <br/></p>
+<p>The oldest and best source of free e-books is a website called <em>Project Gutenberg</em> <a href="http://www.gutenberg.org/wiki/Main_Page).">(</a><a href="http://www.gutenberg.org/wiki/Main_Page" target="_top">http://www.gutenberg.org/wiki/Main_Page</a>).&#160; They create books in plain text format, in other words the kind of file you could make if you typed a book into Notepad and hit the Enter key at the end of each line.&#160; They have thousands of books that are out of copyright, including some of the best ever written.&#160; Before you read further go to that website and pick out a book that interests you.&#160; Check out the "Top 100" list to see the most popular books and authors.
+</p>
+<p>The program we're going to create will read books in plain text format only.
+</p>
+<p>There is a Git repository containing all the code examples in this book.&#160; Once you have Git installed you can copy the repository to your computer with this command:
+</p>
+<pre>git clone git://git.sugarlabs.org/\
+myo-sugar-activities-examples/mainline.git</pre>
+<p>The code for our standalone Python program will be found in the directory <strong>Make_Standalone_Python</strong> in a file named <strong>ReadEtexts.py</strong>.&#160; It looks like this:
+ <br/></p>
+<pre>#! /usr/bin/env python
+import sys
+import os
+import zipfile
+import pygtk
+import gtk
+import getopt
+import pango
+
+page=0
+PAGE_SIZE = 45
+
+class ReadEtexts():
+
+ def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the arrow keys"
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ if keyname == 'plus':
+ self.font_increase()
+ return True
+ if keyname == 'minus':
+ self.font_decrease()
+ return True
+ if keyname == 'Page_Up' :
+ self.page_previous()
+ return True
+ if keyname == 'Page_Down':
+ self.page_next()
+ return True
+ if keyname == 'Up' or keyname == 'KP_Up' \
+ or keyname == 'KP_Left':
+ self.scroll_up()
+ return True
+ if keyname == 'Down' or keyname == 'KP_Down' \
+ or keyname == 'KP_Right':
+ self.scroll_down()
+ return True
+ return False
+
+ def page_previous(self):
+ global page
+ page=page-1
+ if page &lt; 0: page=0
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.upper - \
+ v_adjustment.page_size
+
+ def page_next(self):
+ global page
+ page=page+1
+ if page &gt;&#8286;= len(self.page_index): page=0
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.lower
+
+ def font_decrease(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size - 1
+ if font_size &lt; 1:
+ font_size = 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def font_increase(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size + 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def scroll_down(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.upper - \
+ v_adjustment.page_size:
+ self.page_next()
+ return
+ if v_adjustment.value &lt; v_adjustment.upper -\
+ v_adjustment.page_size:
+ new_value = v_adjustment.value + \
+ v_adjustment.step_increment
+ if new_value &gt; v_adjustment.upper -\
+ v_adjustment.page_size:
+ new_value = v_adjustment.upper -\
+ v_adjustment.page_size
+ v_adjustment.value = new_value
+
+ def scroll_up(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.lower:
+ self.page_previous()
+ return
+ if v_adjustment.value &gt; v_adjustment.lower:
+ new_value = v_adjustment.value - \
+ v_adjustment.step_increment
+ if new_value &lt; v_adjustment.lower:
+ new_value = v_adjustment.lower
+ v_adjustment.value = new_value
+
+ def show_page(self, page_number):
+ global PAGE_SIZE, current_word
+ position = self.page_index[page_number]
+ self.etext_file.seek(position)
+ linecount = 0
+ label_text = '\n\n\n'
+ textbuffer = self.textview.get_buffer()
+ while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + unicode(line,
+ 'iso-8859-1')
+ linecount = linecount + 1
+ label_text = label_text + '\n\n\n'
+ textbuffer.set_text(label_text)
+ self.textview.set_buffer(textbuffer)
+
+ def save_extracted_file(self, zipfile, filename):
+ "Extract the file to a temp directory for viewing"
+ filebytes = zipfile.read(filename)
+ f = open("/tmp/" + filename, 'w')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close
+
+ def read_file(self, filename):
+ "Read the Etext file"
+ global PAGE_SIZE
+
+ if zipfile.is_zipfile(filename):
+ self.zf = zipfile.ZipFile(filename, 'r')
+ self.book_files = self.zf.namelist()
+ self.save_extracted_file(self.zf,
+ self.book_files[0])
+ currentFileName = "/tmp/" + self.book_files[0]
+ else:
+ currentFileName = filename
+
+ self.etext_file = open(currentFileName,"r")
+ self.page_index = [ 0 ]
+ linecount = 0
+ while self.etext_file:
+ line = self.etext_file.readline()
+ if not line:
+ break
+ linecount = linecount + 1
+ if linecount &gt;= PAGE_SIZE:
+ position = self.etext_file.tell()
+ self.page_index.append(position)
+ linecount = 0
+ if filename.endswith(".zip"):
+ os.remove(currentFileName)
+
+ def destroy_cb(self, widget, data=None):
+ gtk.main_quit()
+
+ def main(self, file_path):
+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.window.connect("destroy", self.destroy_cb)
+ self.window.set_title("Read Etexts")
+ self.window.set_size_request(640, 480)
+ self.window.set_border_width(0)
+ self.read_file(file_path)
+ self.scrolled_window = gtk.ScrolledWindow(
+ hadjustment=None, vadjustment=None)
+ self.textview = gtk.TextView()
+ self.textview.set_editable(False)
+ self.textview.set_left_margin(50)
+ self.textview.set_cursor_visible(False)
+ self.textview.connect("key_press_event",
+ self.keypress_cb)
+ buffer = self.textview.get_buffer()
+ self.font_desc = pango.FontDescription("sans 12")
+ font_size = self.font_desc.get_size()
+ self.textview.modify_font(self.font_desc)
+ self.show_page(0)
+ self.scrolled_window.add(self.textview)
+ self.window.add(self.scrolled_window)
+ self.textview.show()
+ self.scrolled_window.show()
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ self.window.show()
+ gtk.main()
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "")
+ ReadEtexts().main(args[0])
+ except getopt.error, msg:
+ print msg
+ print "This program has no options"
+ sys.exit(2)
+</pre>
+<h2> Running The Program
+</h2>
+<p>To run the program you should first make it executable.&#160; You only need to do this once:
+</p>
+<pre>chmod 755 ReadEtexts.py</pre>
+<p>For this example I downloaded the file for <em>Pride and Prejudice</em>.&#160; The program will work with either of the Plain text formats, which are either uncompressed text or a Zip file.&#160; The zip file is named <strong>1342.zip</strong>, and we can read the book by running this from a terminal:
+</p>
+<pre>./ReadEtexts.py 1342.zip</pre>
+<p>This is what the program looks like in action:
+</p>
+<p><img alt="The standalone Read Etexts program in action." src="static/ActivitiesGuideSugar-ReadEtexts_01_1-en.jpg" width="646" height="503"/></p>
+<p>You can use the <em>Page Up, Page Down, Up, Down, Left</em>, and <em>Right</em> keys to navigate through the book and the '+' and '-' keys to adjust the font size.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>How The Program Works
+</h2>
+<p>This program reads through the text file containing the book and divides it into pages of 45 lines each.&#160; We need to do this because the <strong>gtk.TextView</strong> component we use for viewing the text would need a lot of memory to scroll through the whole book and that would hurt performance.&#160; A second reason is that we want to make reading the e-book as much as possible like reading a regular book, and regular books have pages.&#160; If a teacher assigns reading from a book she might say "read pages 35-50 for tommorow".&#160; Finally, we want this program to remember what page you stopped reading on and bring you back to that page again when you read the book next time.&#160; (The program we have so far doesn't do that yet).
+</p>
+<p>To page through the book we use <strong>random access</strong> to read the file.&#160; To understand what random access means to a file, consider a VHS tape and a DVD.&#160; To get to a certain scene in a VHS tape you need to go through all the scenes that came before it, in order.&#160; Even though you do it at high speed you still have to look at all of them to find the place you want to start watching.&#160; This is <strong>sequential access</strong>.&#160; On the other hand a DVD has chapter stops and possibly a chapter menu.&#160; Using a chapter menu you can look at any scene in the movie right away, and you can skip around as you like.&#160; This is random access, and the chapter menu is like an <strong>index</strong>.&#160; Of course you can access the material in a DVD sequentially too.
+</p>
+<p>We need random access to skip to whatever page we like, and we need an index so that we know where each page begins.&#160; We make the index by reading the entire file one line at a time.&#160; Every 45 lines we make a note of how many characters into the file we've gotten and store this information in a Python list.&#160; Then we go back to the beginning of the file and display the first page.&#160; When the program user goes to the next or previous page we figure out what the new page number will be and look in the list entry for that page.&#160; This tells us that page starts 4,200 characters into the file.&#160; We use seek() on the file to go to that character and then we read 45 lines starting at that point and load them into the TextView.
+</p>
+<p>When you run this program notice how fast it is.&#160; Python programs take longer to run a line of code than a compiled language would, but in this program it doesn't matter because the heavy lifting in the program is done by the TextView, which was created in a compiled language.&#160; The Python parts don't do that much so the program doesn't spend much time running them.
+</p>
+<p>Sugar uses Python a lot, not just for Activities but for the Sugar environment itself.&#160; You may read somewhere that using so much Python is "a disaster" for performance.&#160; Don't believe it.
+</p>
+<p>There are no slow programming languages, only slow programmers.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch009_inherit-from-sugaractivityactivity.xhtml b/studio/static/doc/myosa/ch009_inherit-from-sugaractivityactivity.xhtml
new file mode 100644
index 0000000..94c0a02
--- /dev/null
+++ b/studio/static/doc/myosa/ch009_inherit-from-sugaractivityactivity.xhtml
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Inherit From sugar.activity.Activity
+</h1>
+<h2>Object Oriented Python
+ <br/></h2>
+<p>Python supports two styles of programming:<strong> procedural</strong> and <strong>object oriented</strong>. Procedural programming is when you have some input data, do some processing on it, and produce an output. If you want to calculate all the prime numbers under a hundred or convert a Word document into a plain text file you'll probably use the procedural style to do that.
+</p>
+<p class="western">Object oriented programs are built up from units called <strong>objects</strong>. An object is described as a collection of fields or attributes containing data along with methods for doing things with that data. In addition to doing work and storing data objects can send messages to one another.
+</p>
+<p>Consider a word processing program. It doesn't have just one input, some process, and one output. It can receive input from the keyboard, from the mouse buttons, from the mouse traveling over something, from the clipboard, etc. It can send output to the screen, to a file, to a printer, to the clipboard, etc. A word processor can edit several documents at the same time too. Any program with a GUI is a natural fit for the object oriented style of programming.
+</p>
+<p>Objects are described by <em>classes</em>. When you create an object you are creating an <em>instance</em> of a class.
+</p>
+<p>There's one other thing that a class can do, which is to<strong> inherit</strong> methods and attributes from another class. When you define a class you can say it <strong>extends</strong> some class, and by doing that in effect your class has the functionality of the other class plus its own functionality. The extended class becomes its parent.
+ <br/></p>
+<p>All Sugar Activities extend a Python class called <strong>sugar.activity.Activity</strong>. This class provides methods that all Activities need. In addition to that, there are methods that you can override in your own class that the parent class will call when it needs to. For the beginning Activity writer three methods are important:
+</p>
+<p><em>__init__()</em>
+</p>
+<p>This is called when your Activity is started up. This is where you will set up the user interface for your Activity, including toolbars.
+</p>
+<p><em>read_file(self, file_path)</em>
+</p>
+<p>This is called when you resume an Activity from a Journal entry. It is called after the <em>__init__()</em> method is called. The file_path parameter contains the name of a temporary file that is a copy of the file in the Journal entry. The file is deleted as soon as this method finishes, but because Sugar runs on Linux if you open the file for reading your program can continue to read it even after it is deleted and it the file will not actually go away until you close it.
+</p>
+<p><em>write_file(self, file_path)</em>
+</p>
+<p>This is called when the Activity updates the Journal entry. Just like with <em>read_file()</em> your Activity does not work with the Journal directly. Instead it opens the file named in file_path for output and writes to it. That file in turn is copied to the Journal entry.
+</p>
+<p>There are three things that can cause <em>write_file()</em> to be executed:
+</p>
+<ul><li>Your Activity closes.</li>
+ <li>Someone presses the <strong>Keep</strong> button in the Activity toolbar.</li>
+ <li>Your Activity ceases to be the active Activity, or someone moves from the Activity View to some other View.</li>
+</ul><p>In addition to updating the file in the Journal entry the <em>read_file()</em> and <em>write_file()</em> methods are used to read and update the metadata in the Journal entry.
+</p>
+<p> When we convert our standalone Python program to an Activity we'll take out much of the code we wrote and replace it with code inherited from the sugar.activity.Activity&#160; class.
+</p>
+<h2>Extending The Activity Class
+</h2>
+<p>Here's a version of our program that extends Activity.&#160; You'll find it in the Git repository in the directory <strong>Inherit_From_sugar.activity.Activity</strong> under the name <strong>ReadEtextsActivity.py</strong>:
+</p>
+<pre>import sys
+import os
+import zipfile
+import pygtk
+import gtk
+import pango
+from sugar.activity import activity
+from sugar.graphics import style
+
+page=0
+PAGE_SIZE = 45
+
+class ReadEtextsActivity(activity.Activity):
+ def __init__(self, handle):
+ "The entry point to the Activity"
+ global page
+ activity.Activity.__init__(self, handle)
+
+ toolbox = activity.ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.keep.props.visible = False
+ activity_toolbar.share.props.visible = False
+ self.set_toolbox(toolbox)
+
+ toolbox.show()
+ self.scrolled_window = gtk.ScrolledWindow()
+ self.scrolled_window.set_policy(gtk.POLICY_NEVER,
+ gtk.POLICY_AUTOMATIC)
+ self.scrolled_window.props.shadow_type = \
+ gtk.SHADOW_NONE
+
+ self.textview = gtk.TextView()
+ self.textview.set_editable(False)
+ self.textview.set_cursor_visible(False)
+ self.textview.set_left_margin(50)
+ self.textview.connect("key_press_event",
+ self.keypress_cb)
+
+ self.scrolled_window.add(self.textview)
+ self.set_canvas(self.scrolled_window)
+ self.textview.show()
+ self.scrolled_window.show()
+ page = 0
+ self.textview.grab_focus()
+ self.font_desc = pango.FontDescription("sans %d" %
+ style.zoom(10))
+ self.textview.modify_font(self.font_desc)
+
+ def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the arrow keys"
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ print keyname
+ if keyname == 'plus':
+ self.font_increase()
+ return True
+ if keyname == 'minus':
+ self.font_decrease()
+ return True
+ if keyname == 'Page_Up' :
+ self.page_previous()
+ return True
+ if keyname == 'Page_Down':
+ self.page_next()
+ return True
+ if keyname == 'Up' or keyname == 'KP_Up' \
+ or keyname == 'KP_Left':
+ self.scroll_up()
+ return True
+ if keyname == 'Down' or keyname == 'KP_Down' \
+ or keyname == 'KP_Right':
+ self.scroll_down()
+ return True
+ return False
+
+ def page_previous(self):
+ global page
+ page=page-1
+ if page &lt; 0: page=0
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.upper -\
+ v_adjustment.page_size
+
+ def page_next(self):
+ global page
+ page=page+1
+ if page &gt;= len(self.page_index): page=0
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.lower
+
+ def font_decrease(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size - 1
+ if font_size &lt; 1:
+ font_size = 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def font_increase(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size + 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def scroll_down(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.upper - \
+ v_adjustment.page_size:
+ self.page_next()
+ return
+ if v_adjustment.value &lt; v_adjustment.upper -\
+ v_adjustment.page_size:
+ new_value = v_adjustment.value +\
+ v_adjustment.step_increment
+ if new_value &gt; v_adjustment.upper -\
+ v_adjustment.page_size:
+ new_value = v_adjustment.upper -\
+ v_adjustment.page_size
+ v_adjustment.value = new_value
+
+ def scroll_up(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.lower:
+ self.page_previous()
+ return
+ if v_adjustment.value &gt; v_adjustment.lower:
+ new_value = v_adjustment.value - \
+ v_adjustment.step_increment
+ if new_value &lt; v_adjustment.lower:
+ new_value = v_adjustment.lower
+ v_adjustment.value = new_value
+
+ def show_page(self, page_number):
+ global PAGE_SIZE, current_word
+ position = self.page_index[page_number]
+ self.etext_file.seek(position)
+ linecount = 0
+ label_text = '\n\n\n'
+ textbuffer = self.textview.get_buffer()
+ while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + unicode(line,
+ 'iso-8859-1')
+ linecount = linecount + 1
+ label_text = label_text + '\n\n\n'
+ textbuffer.set_text(label_text)
+ self.textview.set_buffer(textbuffer)
+
+ def save_extracted_file(self, zipfile, filename):
+ "Extract the file to a temp directory for viewing"
+ filebytes = zipfile.read(filename)
+ outfn = self.make_new_filename(filename)
+ if (outfn == ''):
+ return False
+ f = open(os.path.join(self.get_activity_root(),
+ 'instance', outfn), 'w')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close
+
+ def read_file(self, filename):
+ "Read the Etext file"
+ global PAGE_SIZE
+
+ if zipfile.is_zipfile(filename):
+ self.zf = zipfile.ZipFile(filename, 'r')
+ self.book_files = self.zf.namelist()
+ self.save_extracted_file(self.zf,
+ self.book_files[0])
+ currentFileName = os.path.join(
+ self.get_activity_root(),
+ 'instance', self.book_files[0])
+ else:
+ currentFileName = filename
+
+ self.etext_file = open(currentFileName,"r")
+ self.page_index = [ 0 ]
+ linecount = 0
+ while self.etext_file:
+ line = self.etext_file.readline()
+ if not line:
+ break
+ linecount = linecount + 1
+ if linecount &gt;= PAGE_SIZE:
+ position = self.etext_file.tell()
+ self.page_index.append(position)
+ linecount = 0
+ if filename.endswith(".zip"):
+ os.remove(currentFileName)
+ self.show_page(0)
+
+ def make_new_filename(self, filename):
+ partition_tuple = filename.rpartition('/')
+ return partition_tuple[2]
+
+
+</pre>
+<p>This program has some significant differences from the standalone version.&#160; First, note that this line:
+</p>
+<pre>#! /usr/bin/env python
+</pre>
+<p>has been removed.&#160; We are no longer running the program directly from the Python interpreter.&#160; Now Sugar is running it as an Activity.&#160; Notice that much (but not all) of what was in the main() method has been moved to the <em>__init__()</em> method and the <em>main()</em> method has been removed.
+</p>
+<p>Notice too that the <em>class</em> statement has changed:
+</p>
+<p>
+</p>
+<pre>class ReadEtextsActivity(activity.Activity)
+</pre>
+<p>This statement now tells us that class ReadEtextsActivity extends the class <strong>sugar.activity.Activity</strong>.&#160;&#160; As a result it inherits the code that is in that class.&#160; Therefore we no longer need a GTK main loop, or to define a window.&#160; The code in this class we extend will do that for us.
+</p>
+<p> While we gain much from this inheritance, we lose something too: a title bar for the main window.&#160; In a graphical operating environment a piece of software called a <em>window manager</em> is responsible for putting borders on windows, making them resizeable, reducing them to icons, maximizing them, etc.&#160; Sugar uses a window manager named Matchbox which makes each window fill the whole screen and puts no border, title bar, or any other window decorations on the windows.&#160;&#160; As a result of that we can't close our application by clicking on the "X" in the title bar as before.&#160; To make up for this we need to have a toolbar that contains a Close button.&#160; Thus every Activity has an Activity toolbar that contains some standard controls and buttons.&#160; If you look at the code you'll see I'm hiding a couple of controls which we have no use for yet.
+</p>
+<p>The <em>read_file()</em> method is no longer called from the main() method and doesn't seem to be called from anywhere in the program.&#160; Of course it does get called, by some of the Activity code we inherited from our new parent class.&#160; Similarly the <em>__init__() </em>and <em>write_file() </em>methods (if we had a <em>write_file()</em> method) get called by the parent Activity class.
+</p>
+<p>If you're especially observant you might have noticed another change.&#160; Our original standalone program created a temporary file when it needed to extract something from a Zip file.&#160; It put that file in a directory called /tmp.&#160; Our new Activity still creates the file but puts it in a different directory, one specific to the Activity.
+</p>
+<p>
+</p>All writing to the file system is restricted to subdirectories of the path given by <em>self.get_activity_root()</em>.&#160; This method will give you a directory that belongs to your Activity alone.&#160; It will contain three subdirectories with different policies:
+<dl><dt><strong>data</strong></dt>
+ <dd> This directory is used for data such as configuration files.&#160; Files stored here will survive reboots and OS upgrades.</dd>
+</dl><dl><dt><strong>tmp</strong></dt>
+ <dd> This directory is used similar to the /tmp directory, being backed by RAM. It may be as small as 1 MB. This directory is deleted when the activity exits.</dd>
+</dl><dl><dt><strong>instance</strong></dt>
+ <dd> This directory is similar to the <strong>tmp</strong> directory, being backed by the computer's drive rather than by RAM. It is unique per instance. It is used for transfer to and from the Journal. This directory is deleted when the activity exits.</dd>
+</dl><p>
+</p>
+<p>Making these changes to the code is not enough to make our program an Activity.&#160; We have to do some packaging work and get it set up to run from the Sugar emulator.&#160; We also need to learn how to run the Sugar emulator.&#160; That comes next!
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch010_package-the-activity.xhtml b/studio/static/doc/myosa/ch010_package-the-activity.xhtml
new file mode 100644
index 0000000..527b06b
--- /dev/null
+++ b/studio/static/doc/myosa/ch010_package-the-activity.xhtml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Package The Activity
+</h1>
+<h2>Add setup.py
+</h2>
+<p> You'll need to add a Python program called <strong>setup.py</strong> to the same directory that you Activity program is in.&#160; Every setup.py is exactly the same as every other setup.py.&#160; The copies in our Git repository look like this:
+ <br/></p>
+<pre>#!/usr/bin/env python
+
+# Copyright (C) 2006, Red Hat, Inc.
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General
+# Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will
+# be useful, but WITHOUT ANY WARRANTY; without even
+# the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General
+# Public License along with this program; if not,
+# write to the Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+from sugar.activity import bundlebuilder
+
+bundlebuilder.start()</pre>
+<p>Be sure and copy the entire text above, including the comments.
+ <br/></p>
+<p>The setup.py program is used by sugar for a number of purposes.&#160; If you run setup.py from the command line you'll see the options that are used with it and what they do.
+</p>
+<pre>[jim@simmons bookexamples]$ ./setup.py
+/usr/lib/python2.6/site-packages/sugar/util.py:25:
+DeprecationWarning: the sha module is deprecated;
+use the hashlib module instead
+ import sha
+Available commands:
+
+build Build generated files
+dev Setup for development
+dist_xo Create a xo bundle package
+dist_source Create a tar source package
+fix_manifest Add missing files to the manifest
+genpot Generate the gettext pot file
+install Install the activity in the system
+
+(Type "./setup.py &lt;command&gt; --help" for help about a
+particular command's options.</pre>
+<p>We'll be running some of these commands later on.&#160; Don't be concerned about the <strong>DeprecationWarning</strong> message.&#160; That is just Python's way of telling us that it has a new way of doing something that is better but the old way we are using still works.&#160; The error is coming from code in Sugar itself and should be fixed in some future Sugar release.
+ <br/></p>
+<h2>Create activity.info
+</h2>
+<p> Next create a directory within the one your progam is in and name it <strong>activity</strong>.&#160; Create a file named <strong>activity.info</strong> within that directory and enter the lines below into it.&#160; Here is the one for our first Activity:
+ <br/></p>
+<pre>[Activity]
+name = Read ETexts II
+service_name = net.flossmanuals.ReadEtextsActivity
+icon = read-etexts
+exec = sugar-activity ReadEtextsActivity.ReadEtextsActivity
+show_launcher = no
+activity_version = 1
+mime_types = text/plain;application/zip
+license = GPLv2+</pre>
+<p>This file tells Sugar how to run your Activity.&#160; The properties needed in this file are:
+</p>
+<p>
+ </p><table border="0" cellpadding="4" cellspacing="0"><tbody><tr><td>&#160;<strong>name</strong></td>
+ <td>The name of your Activity as it will appear to the user.
+ <br/></td>
+ </tr><tr><td>&#160;<strong>service_name</strong></td>
+ <td>A unique name that Sugar will use to refer to your Activity.&#160; Any Journal entry created by your Activity will have this name stored in its metadata, so that when someone resumes the Journal entry Sugar knows to use the program that created it to read it.
+ <br/></td>
+ </tr><tr><td valign="top"><strong>icon </strong>
+ <br/></td>
+ <td>The name of the icon file you have created for the Activity.&#160; Since icons are always .svg files the icon file in the example is named read-etexts.svg.
+ <br/></td>
+ </tr><tr><td valign="top"><strong>exec</strong></td>
+ <td>This tells Sugar how to launch your Activity.&#160; What it says is to create an instance of the class <strong>ReadEtextsActivity </strong>which it will find in file <strong>ReadEtextsActivity.py</strong>.
+ <br/></td>
+ </tr><tr><td valign="top"><strong>show_launcher</strong></td>
+ <td>There are two ways to launch an Activity.&#160; The first is to click on the icon in the Activity view.&#160; The second is to resume an entry in the Journal. Activities that don't create Journal entries can only be resumed from the Journal, so there is no point in putting an icon in the Activity ring for them.&#160; Read Etexts is an Activity like that.
+ <br/></td>
+ </tr><tr><td valign="top"><strong>activity_version</strong></td>
+ <td>An integer that represents the version number of your program.&#160; The first version is 1, the next is 2, and so on.
+ <br/></td>
+ </tr><tr><td valign="top"><strong>mime_types</strong></td>
+ <td>Generally when you resume a Journal entry it launches the Activity that created it.&#160; In the case of an e-book it wasn't created by any Activity, so we need another way to tell the Journal which Activity it can use.&#160; A MIME type is the name of a common file format.&#160; Some examples are text/plain, text/html, application/zip and application/pdf.&#160; In this entry we're telling the Journal that our program can handle either plain text files or Zip archive files.
+ <br/></td>
+ </tr><tr><td valign="top"><strong>license</strong></td>
+ <td>Owning a computer program is not like buying a car.&#160; With a car, you're the owner and you can do what you like with it.&#160; You can sell it, rent it out, make it into a hot rod, whatever.&#160; With a computer program there is always a license that tells the person receiving the program what he is allowed to do with it.&#160; GPLv2+ is a popular standard license that can be used for Activities, and since this is <em>my</em> program that is what goes here.&#160; When you're ready to distribute one of <em>your</em> Activities I'll have more to say about licenses.
+ <br/></td>
+ </tr></tbody></table><h2>Create An Icon
+</h2>
+<p> Next we need to create an icon named <strong>read-etexts.svg</strong> and put it in the <strong>activity</strong> subdirectory.&#160;&#8286; We're going to use Inkscape to create the icon.&#160; From the <strong>New</strong> menu in Inkscape select <strong>icon_48x48</strong>.&#160; This will create a drawing area that is a good size.
+</p>
+<p>You don't need to be an expert in Inkscape to create an icon.&#160; In fact the less fancy your icon is the better.&#160; When drawing your icon remember the following points:
+</p>
+<ul><li>Your icon needs to look good in sizes ranging from really, really small to large.</li>
+ <li>It needs to be recognizeable when its really, really small.</li>
+ <li>You only get to use two colors: a stroke color and a fill color.&#160; It doesn't matter which ones you choose because Sugar will need to override your choices anyway, so just use black strokes on a white background.</li>
+ <li>A fill color is only applied to an area that is contained within an unbroken stroke.&#160; If you draw a box and one of the corners doesn't quite connect the area inside that box will not be filled.&#160; Free hand drawing is only for the talented.&#160; Circles, boxes, and arcs are easy to draw with Inkscape so use them when you can.</li>
+ <li>Inkscape will also draw 3D boxes using two point perspective.&#160; Don't use them.&#160; Icons should be flat images.&#160; 3D just doesn't look good in an icon.</li>
+ <li>Coming up with good ideas for icons is tough.&#160; I once came up with a rather nice picture of a library card catalog drawer for <strong>Get Internet Archive Books</strong>.&#160; The problem is, no child under the age of forty has ever seen a card catalog and fewer still understand its purpose.</li>
+</ul><p>When you're done making your icon you need to modify it so it can work with Sugar.&#160; Specifically, you need to make it show Sugar can use its own choice of stroke color and fill color.&#160; The SVG file format is based on XML, which means it is a text file with some special tags in it.&#160; This means that once we have finished editing it in Inkscape we can load the file into Eric and edit it as a text file.
+</p>
+<p>I'm not going to put the entire file in this chapter because most of it you'll just leave alone.&#160; The first part you need to modify is at the very beginning.
+</p>
+<p>Before:
+</p>
+<pre>&lt;?xml version="1.0" encoding="UTF-8" standalone="no"?&gt;
+&lt;!-- Created with Inkscape (http://www.inkscape.org/) --&gt;
+&lt;svg
+</pre>
+<p> After:
+</p>
+<pre>&lt;?xml version="1.0" ?&gt;
+&lt;!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'
+ 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ &lt;!ENTITY stroke_color "#000000"&gt;
+ &lt;!ENTITY fill_color "#FFFFFF"&gt;
+]&gt;&lt;svg
+</pre>
+<p>Now in the body of the document you'll find references to <em>fill</em> and <em>stroke</em> as part of an attribute called <em>style</em>.&#160; Every line or shape you draw will have these, like this:
+</p>
+<pre>&lt;rect
+ style="fill:#ffffff;stroke:#000000;stroke-opacity:1"
+ id="rect904"
+ width="36.142857"
+ height="32.142857"
+ x="4.1428571"
+ y="7.1428571" /&gt;</pre>
+<p>You need to change each one to look like this:
+</p>
+<pre>&lt;rect
+ style="fill:&amp;fill_color;;stroke:&amp;stroke_color;
+;stroke-opacity:1"
+ id="rect904"
+ width="36.142857"
+ height="32.142857"
+ x="4.1428571"
+ y="7.1428571" /&gt;</pre>
+<p>Note that <em>&amp;stroke_color;</em> and <em>&amp;fill_color;</em> both end with semicolons (;), and semicolons are also used to separate the properties for style.&#160; Because of this it is an extremely common beginner's mistake to leave off the trailing semicolon because two semicolons in a row don't look right.&#160; Be assured that the two semicolons in a row are intentional and absolutely necessary!&#160; Second, the value for style should all go <em>on one line</em>.&#160; We split it here to make it fit on the printed page; do not split it in your own icon!
+ <br/></p>
+<h2>Make a MANIFEST File
+</h2>
+<p> You should remember that setup.py has an option to update a manifest.&#160; Let's try it:
+</p>
+<pre>./setup.py fix_manifest
+/usr/lib/python2.6/site-packages/sugar/util.py:25:
+DeprecationWarning: the sha module is deprecated;
+use the hashlib module instead
+ import sha
+WARNING:root:Missing po/ dir, cannot build_locale
+WARNING:root:Activity directory lacks a MANIFEST file.
+</pre>
+<p>This actually will build a MANIFEST file containing everything in the directory and its subdirectories.&#160; The /po directory it is complaining about is used to translate Activities into different languages.&#160; We can ignore that for now.
+</p>
+<p> The MANIFEST file it creates will contain some extra stuff, so we need to get rid of the extra lines using Eric.&#160; The corrected MANIFEST should look like this:
+</p>
+<pre>setup.py
+ReadEtextsActivity.py
+activity/read-etexts.svg
+activity/activity.info</pre>
+<h2>Install The Activity
+</h2>
+<p> There's just one more thing to do before we can test our Activity under the Sugar emulator.&#160; We need to install it, which in this case means making a symbolic link between the directory we're using for our code in the ~/Activities/ directory.&#160; The symbol ~ refers to the "home" directory of the user we're running Sugar under, and a symbolic link is a way to make a file or directory appear to be located in more than one place without copying it.&#160; We make this symbolic link by running setup.py again:
+</p>
+<pre>./setup.py dev
+</pre>
+<h2>Running Our Activity
+</h2>
+<p>Now at last we can run our Activity under Sugar.&#160; To do that we need to learn how to run <strong>sugar-emulator</strong>.
+</p>
+<p>Fedora doesn't make a menu option for Sugar Emulator, but it's easy to add one yourself.&#160; The command to run is simply
+</p>
+<pre>sugar-emulator</pre>
+<p>If your screen resolution is smaller than the default size sugar-emulator runs at it will run full screen.&#160; This is not convenient for testing, so you may want to specify your own size:
+</p>
+<pre>sugar-emulator -i 800x600</pre>
+<p>Note that this option only exists in Fedora 11 and later.
+</p>
+<p>When you run sugar-emulator a window opens up and the Sugar environment starts up and runs inside it.&#160; It looks like this:
+</p>
+<p><img alt="ReadEtexts_02.jpg" src="static/ActivitiesGuideSugar-ReadEtexts_02-en.jpg" height="480" width="640"/></p>
+<p>When running sugar-emulator you may find that some keys don't seem to work in the Sugar environment.&#160; This is caused by bugs in the <strong>Xephyr</strong> software that creates the window that Sugar runs in.&#160; Sometimes it has difficulty identifying your keyboard and as a result some keys get misinterpreted.&#160; On Fedora 11 I noticed that my function keys did not work, and my regular arrow keys didn't work either although my keypad arrow keys did.&#160; I was able to get my function keys working again by putting this line in <em>~/.sugar/debug</em>:
+</p>
+<pre>run setxkbmap <em>&lt;keymap name&gt;</em></pre>
+<p>This needs more explanation.&#160; First, the symbol "~" refers to your home directory.&#160; Second, any file named starting with a period is considered hidden in Linux, so you'll need to use the option to show hidden files and directories in the GNOME directory browser to navigate to it.&#160; Finally, the keymap name is a two character country code: us for the United States, fr for France, de for Germany, etc.
+ <br/></p>
+<p>To test our Activity we're going to need to have a book in the Journal, so use the <strong>Browse</strong> Activity to visit Project Gutenberg again and download the book of your choice.&#160; This time it's important to download the book in Zip format, because Browse cannot download a plain text file to the Journal.&#160; Instead, it opens the file for viewing as if it was a web page.&#160; If you try the same thing with the Zip file it will create an entry in the Journal.
+</p>
+<p>We can't just open the file with one click in the Journal because our program did not create the Journal entry and there are several Activities that support the MIME type of the Journal entry.&#160; We need to use the Start With menu option like this:
+</p>
+<p><img alt="ReadEtexts_03.jpg" src="static/ActivitiesGuideSugar-ReadEtexts_03-en.jpg" height="480" width="640"/></p>
+<p>When we do open the Journal entry this is what we see:
+</p>
+<p><img alt="ReadEtexts_04.jpg" src="static/ActivitiesGuideSugar-ReadEtexts_04-en.jpg" height="480" width="640"/></p>
+<p>Technically, this is the first <strong>iteration</strong> of our Activity.&#160; (Iteration is a vastly useful word meaning something you do more than once.&#160; In this book we're building our Activity a bit at a time so I can demonstrate Activity writing principles, but actually building a program in pieces, testing it, getting feedback, and building a bit more can be a highly productive way of creating software.&#160; Using the word <em>iteration</em> to describe each step in the process makes the process sound more formal than it really is).
+</p>
+<p>While this Activity might be good enough to show your own mother, we really should improve it a bit before we do that.&#160; That part comes next.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch011_add-refinements.xhtml b/studio/static/doc/myosa/ch011_add-refinements.xhtml
new file mode 100644
index 0000000..0cd76dd
--- /dev/null
+++ b/studio/static/doc/myosa/ch011_add-refinements.xhtml
@@ -0,0 +1,625 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Add Refinements
+</h1>
+<h2>Toolbars
+</h2>
+<p>It is a truth universally acknowledged that a first rate Activity needs good Toolbars.&#160; In this chapter we'll learn how to make them.&#160; We're going to put the toolbar classes in a separate file from the rest, because there are two styles of toolbar (old and new) and we may want to support both in our Activity.&#160; If we have two different files containing toolbar classes our code can decide at runtime which one it wants to use.&#160; For now, this code supports the old style, which works with every version of Sugar.&#160; The new style is currently only supported by <em>Sugar on a Stick. </em>
+</p>
+<p>There is a file called <strong>toolbar.py</strong> in the <strong>Add_Refinements</strong> directory of the Git repository that looks like this:
+ <br/></p>
+<pre>from gettext import gettext as _
+import re
+
+import pango
+import gobject
+import gtk
+
+from sugar.graphics.toolbutton import ToolButton
+from sugar.activity import activity
+
+class ReadToolbar(gtk.Toolbar):
+ __gtype_name__ = 'ReadToolbar'
+
+ def __init__(self):
+ gtk.Toolbar.__init__(self)
+
+ self.back = ToolButton('go-previous')
+ self.back.set_tooltip(_('Back'))
+ self.back.props.sensitive = False
+ self.insert(self.back, -1)
+ self.back.show()
+
+ self.forward = ToolButton('go-next')
+ self.forward.set_tooltip(_('Forward'))
+ self.forward.props.sensitive = False
+ self.insert(self.forward, -1)
+ self.forward.show()
+
+ num_page_item = gtk.ToolItem()
+
+ self.num_page_entry = gtk.Entry()
+ self.num_page_entry.set_text('0')
+ self.num_page_entry.set_alignment(1)
+ self.num_page_entry.connect('insert-text',
+ self.num_page_entry_insert_text_cb)
+
+ self.num_page_entry.set_width_chars(4)
+
+ num_page_item.add(self.num_page_entry)
+ self.num_page_entry.show()
+
+ self.insert(num_page_item, -1)
+ num_page_item.show()
+
+ total_page_item = gtk.ToolItem()
+
+ self.total_page_label = gtk.Label()
+
+ label_attributes = pango.AttrList()
+ label_attributes.insert(pango.AttrSize(
+ 14000, 0, -1))
+ label_attributes.insert(pango.AttrForeground(
+ 65535, 65535, 65535, 0, -1))
+ self.total_page_label.set_attributes(
+ label_attributes)
+
+ self.total_page_label.set_text(' / 0')
+ total_page_item.add(self.total_page_label)
+ self.total_page_label.show()
+
+ self.insert(total_page_item, -1)
+ total_page_item.show()
+
+ def num_page_entry_insert_text_cb(self, entry, text,
+ length, position):
+ if not re.match('[0-9]', text):
+ entry.emit_stop_by_name('insert-text')
+ return True
+ return False
+
+ def update_nav_buttons(self):
+ current_page = self.current_page
+ self.back.props.sensitive = current_page &gt; 0
+ self.forward.props.sensitive = \
+ current_page &lt; self.total_pages - 1
+
+ self.num_page_entry.props.text = str(
+ current_page + 1)
+ self.total_page_label.props.label = \
+ ' / ' + str(self.total_pages)
+
+ def set_total_pages(self, pages):
+ self.total_pages = pages
+
+ def set_current_page(self, page):
+ self.current_page = page
+ self.update_nav_buttons()
+
+class ViewToolbar(gtk.Toolbar):
+ __gtype_name__ = 'ViewToolbar'
+
+ __gsignals__ = {
+ 'needs-update-size': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([])),
+ 'go-fullscreen': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([]))
+ }
+
+ def __init__(self):
+ gtk.Toolbar.__init__(self)
+ self.zoom_out = ToolButton('zoom-out')
+ self.zoom_out.set_tooltip(_('Zoom out'))
+ self.insert(self.zoom_out, -1)
+ self.zoom_out.show()
+
+ self.zoom_in = ToolButton('zoom-in')
+ self.zoom_in.set_tooltip(_('Zoom in'))
+ self.insert(self.zoom_in, -1)
+ self.zoom_in.show()
+
+ spacer = gtk.SeparatorToolItem()
+ spacer.props.draw = False
+ self.insert(spacer, -1)
+ spacer.show()
+
+ self.fullscreen = ToolButton('view-fullscreen')
+ self.fullscreen.set_tooltip(_('Fullscreen'))
+ self.fullscreen.connect('clicked',
+ self.fullscreen_cb)
+ self.insert(self.fullscreen, -1)
+ self.fullscreen.show()
+
+ def fullscreen_cb(self, button):
+ self.emit('go-fullscreen')
+</pre>
+<p>Another file in the same directory of the Git repository is named <strong>ReadEtextsActivity2.py.</strong>&#160; It looks like this:
+</p>
+<pre>import os
+import zipfile
+import gtk
+import pango
+from sugar.activity import activity
+from sugar.graphics import style
+from toolbar import ReadToolbar, ViewToolbar
+from gettext import gettext as _
+
+page=0
+PAGE_SIZE = 45
+TOOLBAR_READ = 2
+
+class ReadEtextsActivity(activity.Activity):
+ def __init__(self, handle):
+ "The entry point to the Activity"
+ global page
+ activity.Activity.__init__(self, handle)
+
+ toolbox = activity.ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.keep.props.visible = False
+ activity_toolbar.share.props.visible = False
+
+ self.edit_toolbar = activity.EditToolbar()
+ self.edit_toolbar.undo.props.visible = False
+ self.edit_toolbar.redo.props.visible = False
+ self.edit_toolbar.separator.props.visible = False
+ self.edit_toolbar.copy.set_sensitive(False)
+ self.edit_toolbar.copy.connect('clicked',
+ self.edit_toolbar_copy_cb)
+ self.edit_toolbar.paste.props.visible = False
+ toolbox.add_toolbar(_('Edit'), self.edit_toolbar)
+ self.edit_toolbar.show()
+
+ self.read_toolbar = ReadToolbar()
+ toolbox.add_toolbar(_('Read'), self.read_toolbar)
+ self.read_toolbar.back.connect('clicked',
+ self.go_back_cb)
+ self.read_toolbar.forward.connect('clicked',
+ self.go_forward_cb)
+ self.read_toolbar.num_page_entry.connect('activate',
+ self.num_page_entry_activate_cb)
+ self.read_toolbar.show()
+
+ self.view_toolbar = ViewToolbar()
+ toolbox.add_toolbar(_('View'), self.view_toolbar)
+ self.view_toolbar.connect('go-fullscreen',
+ self.view_toolbar_go_fullscreen_cb)
+ self.view_toolbar.zoom_in.connect('clicked',
+ self.zoom_in_cb)
+ self.view_toolbar.zoom_out.connect('clicked',
+ self.zoom_out_cb)
+ self.view_toolbar.show()
+
+ self.set_toolbox(toolbox)
+ toolbox.show()
+ self.scrolled_window = gtk.ScrolledWindow()
+ self.scrolled_window.set_policy(gtk.POLICY_NEVER,
+ gtk.POLICY_AUTOMATIC)
+ self.scrolled_window.props.shadow_type = \
+ gtk.SHADOW_NONE
+
+ self.textview = gtk.TextView()
+ self.textview.set_editable(False)
+ self.textview.set_cursor_visible(False)
+ self.textview.set_left_margin(50)
+ self.textview.connect("key_press_event",
+ self.keypress_cb)
+
+ self.scrolled_window.add(self.textview)
+ self.set_canvas(self.scrolled_window)
+ self.textview.show()
+ self.scrolled_window.show()
+ page = 0
+ self.clipboard = gtk.Clipboard(
+ display=gtk.gdk.display_get_default(),
+ selection="CLIPBOARD")
+ self.textview.grab_focus()
+ self.font_desc = pango.FontDescription("sans %d" %
+ style.zoom(10))
+ self.textview.modify_font(self.font_desc)
+
+ buffer = self.textview.get_buffer()
+ self.markset_id = buffer.connect("mark-set",
+ self.mark_set_cb)
+ self.toolbox.set_current_toolbar(TOOLBAR_READ)
+
+ def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the arrow keys"
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ print keyname
+ if keyname == 'plus':
+ self.font_increase()
+ return True
+ if keyname == 'minus':
+ self.font_decrease()
+ return True
+ if keyname == 'Page_Up' :
+ self.page_previous()
+ return True
+ if keyname == 'Page_Down':
+ self.page_next()
+ return True
+ if keyname == 'Up' or keyname == 'KP_Up' \
+ or keyname == 'KP_Left':
+ self.scroll_up()
+ return True
+ if keyname == 'Down' or keyname == 'KP_Down' \
+ or keyname == 'KP_Right':
+ self.scroll_down()
+ return True
+ return False
+
+ def num_page_entry_activate_cb(self, entry):
+ global page
+ if entry.props.text:
+ new_page = int(entry.props.text) - 1
+ else:
+ new_page = 0
+
+ if new_page &gt;= self.read_toolbar.total_pages:
+ new_page = self.read_toolbar.total_pages - 1
+ elif new_page &lt; 0:
+ new_page = 0
+
+ self.read_toolbar.current_page = new_page
+ self.read_toolbar.set_current_page(new_page)
+ self.show_page(new_page)
+ entry.props.text = str(new_page + 1)
+ self.read_toolbar.update_nav_buttons()
+ page = new_page
+
+ def go_back_cb(self, button):
+ self.page_previous()
+
+ def go_forward_cb(self, button):
+ self.page_next()
+
+ def page_previous(self):
+ global page
+ page=page-1
+ if page &lt; 0: page=0
+ self.read_toolbar.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.upper -\
+ v_adjustment.page_size
+
+ def page_next(self):
+ global page
+ page=page+1
+ if page &gt;= len(self.page_index): page=0
+ self.read_toolbar.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.lower
+
+ def zoom_in_cb(self, button):
+ self.font_increase()
+
+ def zoom_out_cb(self, button):
+ self.font_decrease()
+
+ def font_decrease(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size - 1
+ if font_size &lt; 1:
+ font_size = 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def font_increase(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size + 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def mark_set_cb(self, textbuffer, iter, textmark):
+
+ if textbuffer.get_has_selection():
+ begin, end = textbuffer.get_selection_bounds()
+ self.edit_toolbar.copy.set_sensitive(True)
+ else:
+ self.edit_toolbar.copy.set_sensitive(False)
+
+ def edit_toolbar_copy_cb(self, button):
+ textbuffer = self.textview.get_buffer()
+ begin, end = textbuffer.get_selection_bounds()
+ copy_text = textbuffer.get_text(begin, end)
+ self.clipboard.set_text(copy_text)
+
+ def view_toolbar_go_fullscreen_cb(self, view_toolbar):
+ self.fullscreen()
+
+ def scroll_down(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.upper - \
+ v_adjustment.page_size:
+ self.page_next()
+ return
+ if v_adjustment.value &lt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.value + \
+ v_adjustment.step_increment
+ if new_value &gt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.upper - \
+ v_adjustment.page_size
+ v_adjustment.value = new_value
+
+ def scroll_up(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.lower:
+ self.page_previous()
+ return
+ if v_adjustment.value &gt; v_adjustment.lower:
+ new_value = v_adjustment.value - \
+ v_adjustment.step_increment
+ if new_value &lt; v_adjustment.lower:
+ new_value = v_adjustment.lower
+ v_adjustment.value = new_value
+
+ def show_page(self, page_number):
+ global PAGE_SIZE, current_word
+ position = self.page_index[page_number]
+ self.etext_file.seek(position)
+ linecount = 0
+ label_text = '\n\n\n'
+ textbuffer = self.textview.get_buffer()
+ while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + unicode(line,
+ 'iso-8859-1')
+ linecount = linecount + 1
+ label_text = label_text + '\n\n\n'
+ textbuffer.set_text(label_text)
+ self.textview.set_buffer(textbuffer)
+
+ def save_extracted_file(self, zipfile, filename):
+ "Extract the file to a temp directory for viewing"
+ filebytes = zipfile.read(filename)
+ outfn = self.make_new_filename(filename)
+ if (outfn == ''):
+ return False
+ f = open(os.path.join(self.get_activity_root(),
+ 'tmp', outfn), 'w')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close()
+
+ def get_saved_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not title[len(title)- 1].isdigit():
+ page = 0
+ else:
+ i = len(title) - 1
+ newPage = ''
+ while (title[i].isdigit() and i &gt; 0):
+ newPage = title[i] + newPage
+ i = i - 1
+ if title[i] == 'P':
+ page = int(newPage) - 1
+ else:
+ # not a page number; maybe a volume number.
+ page = 0
+
+ def save_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not title[len(title)-1].isdigit():
+ title = title + ' P' + str(page + 1)
+ else:
+ i = len(title) - 1
+ while (title[i].isdigit() and i &gt; 0):
+ i = i - 1
+ if title[i] == 'P':
+ title = title[0:i] + 'P' + str(page + 1)
+ else:
+ title = title + ' P' + str(page + 1)
+ self.metadata['title'] = title
+
+ def read_file(self, filename):
+ "Read the Etext file"
+ global PAGE_SIZE, page
+
+ if zipfile.is_zipfile(filename):
+ self.zf = zipfile.ZipFile(filename, 'r')
+ self.book_files = self.zf.namelist()
+ self.save_extracted_file(self.zf,
+ self.book_files[0])
+ currentFileName = os.path.join(
+ self.get_activity_root(),
+ 'tmp', self.book_files[0])
+ else:
+ currentFileName = filename
+
+ self.etext_file = open(currentFileName,"r")
+ self.page_index = [ 0 ]
+ pagecount = 0
+ linecount = 0
+ while self.etext_file:
+ line = self.etext_file.readline()
+ if not line:
+ break
+ linecount = linecount + 1
+ if linecount &gt;= PAGE_SIZE:
+ position = self.etext_file.tell()
+ self.page_index.append(position)
+ linecount = 0
+ pagecount = pagecount + 1
+ if filename.endswith(".zip"):
+ os.remove(currentFileName)
+ self.get_saved_page_number()
+ self.show_page(page)
+ self.read_toolbar.set_total_pages(pagecount + 1)
+ self.read_toolbar.set_current_page(page)
+
+ def make_new_filename(self, filename):
+ partition_tuple = filename.rpartition('/')
+ return partition_tuple[2]
+
+ def write_file(self, filename):
+ "Save meta data for the file."
+ self.metadata['activity'] = self.get_bundle_id()
+ self.save_page_number()
+</pre>
+<p>This is the <strong>activity.info</strong> for this example:
+</p>
+<pre>[Activity]
+name = Read ETexts II
+service_name = net.flossmanuals.ReadEtextsActivity
+icon = read-etexts
+<strong>exec = sugar-activity ReadEtextsActivity2.ReadEtextsActivity</strong>
+show_launcher = no
+activity_version = 1
+mime_types = text/plain;application/zip
+license = GPLv2+
+
+</pre>
+<p>The line in <strong>bold</strong> is the only one that needs changing.&#160; When we run this new version this is what we'll see:
+</p>
+<p><img alt="ReadEtexts_05.jpg" src="static/ActivitiesGuideSugar-ReadEtexts_05-en.jpg" height="480" width="640"/></p>
+<p>There are a few things worth pointing out in this code.&#160; First, have a look at this import:
+</p>
+<pre>from gettext import gettext as _
+</pre>
+<p> We'll be using the <em>gettext</em> module of Python to support translating our Activity into other languages. We'll be using it in statements like this one:
+</p>
+<pre> self.back.set_tooltip(_('Back'))</pre>
+<p>The underscore acts the same way as the gettext function because of the way we imported gettext.&#160; The effect of this statement will be to look in a special translation file for a word or phrase that matches the key "Back" and replace it with its translation.&#160; If there is no translation file for the language we want then it will simply use the word "Back".&#160; We'll explore setting up these translation files later, but for now using gettext for all of the words and phrases we will show to our Activity users lays some important groundwork.
+</p>
+<p>The second thing worth pointing out is that while our revised Activity has four toolbars we only had to create two of them.&#160; The other two, <strong>Activity</strong> and <strong>Edit</strong>, are part of the Sugar Python library.&#160; We can use those toolbars as is, hide the controls we don't need, or even extend them by adding new controls.&#160; In the example we're hiding the <strong>Keep</strong> and <strong>Share</strong> controls of the Activity toolbar and the <strong>Undo</strong>, <strong>Redo</strong>, and <strong>Paste</strong> buttons of the Edit toolbar.&#160; We currently do not support sharing books or modifying the text in books so these controls are not needed.&#160; Note too that the Activity toolbar is part of the <strong>ActivityToolbox</strong>.&#160; There is no way to give your Activity a toolbox that does not contain the Activity toolbar as its first entry.
+</p>
+<p>Another thing to notice is that the Activity class doesn't just provide us with a window.&#160; The window has a VBox to hold our toolbars and the body of our Activity.&#160; We install the toolbox using <em>set_toolbox()</em> and the body of the Activity using <em>set_canvas()</em>.
+</p>
+<p>The <strong>Read</strong> and <strong>View</strong> toolbars are regular PyGtk programming, but notice that there is a special button for Sugar toolbars that can have a tooltip attached to it, plus the <strong>View</strong> toolbar has code to hide the toolbox and <strong>ReadEtextsActivity2</strong> has code to unhide it.&#160; This is an easy function to add to your own Activities and many games and other kinds of Activities can benefit from the increased screen area you get when you hide the toolbox.
+</p>
+<h2>Metadata And Journal Entries
+ <br/></h2>
+<p>Every Journal entry represents a single file plus <strong>metadata</strong>, or information describing the file.&#160; There are standard metadata entries that all Journal entries have and you can also create your own custom metadata.
+</p>
+<p> Unlike ReadEtextsActivity, this version has a <em>write_file()</em> method.
+</p>
+<pre> def write_file(self, filename):
+ "Save meta data for the file."
+ self.metadata['activity'] = self.get_bundle_id()
+ self.save_page_number()
+</pre>
+<p> We didn't have a <em>write_file()</em> method before because we weren't going to update the file the book is in, and we still aren't.&#160; We will, however, be updating the metadata for the Journal entry.&#160; Specifically, we'll be doing two things:
+</p>
+<ul><li>Save the page number our Activity user stopped reading on so when he launches the Activity again we can return to that page.</li>
+ <li>Tell the Journal entry that it belongs to our Activity, so that in the future it will use our Activity's icon and can launch our Activity with one click.</li>
+</ul><p>The way the <strong>Read</strong> Activity saves page number is to use a custom metadata property.&#160;
+</p>
+<pre> self.metadata['Read_current_page'] = \
+ str(self._document.get_page_cache().get_current_page())
+</pre>
+<p><strong>Read</strong> creates a custom metadata property named <em>Read_current_page </em>to store the current page number.&#160; You can create any number of custom metadata properties just this easily, so you may wonder why we aren't doing that with <strong>Read Etexts</strong>.&#160; Actually, the first version of <strong>Read Etexts</strong> did use a custom property, but in Sugar .82 or lower there was a bug in the Journal such that custom metadata did not survive after the computer was turned off.&#160; As a result my Activity would remember pages numbers while the computer was running, but would forget them as soon as it was shut down.&#160; XO laptops currently cannot upgrade to anything newer than .82, and when it is possible to upgrade it will be a big job for the schools.
+</p>
+<p>To get around this problem I created the following two methods:
+ <br/></p>
+<pre> def get_saved_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not title[len(title)-1].isdigit():
+ page = 0
+ else:
+ i = len(title) - 1
+ newPage = ''
+ while (title[i].isdigit() and i &gt; 0):
+ newPage = title[i] + newPage
+ i = i - 1
+ if title[i] == 'P':
+ page = int(newPage) - 1
+ else:
+ # not a page number; maybe a volume number.
+ page = 0
+
+ def save_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not title[len(title)-1].isdigit():
+ title = title + ' P' + str(page + 1)
+ else:
+ i = len(title) - 1
+ while (title[i].isdigit() and i &gt; 0):
+ i = i - 1
+ if title[i] == 'P':
+ title = title[0:i] + 'P' + str(page + 1)
+ else:
+ title = title + ' P' + str(page + 1)
+ self.metadata['title'] = title
+</pre>
+<p> <em>save_page_number()</em> looks at the current title metadata and either adds a page number to the end of it or updates the page number already there.&#160; Since title is standard metadata for all Journal entries the Journal bug does not affect it.
+</p>
+<p>These examples show how to read metadata too. &#160;
+ <br/></p>
+<pre> title = self.metadata.get('title', '')
+</pre>
+<p> This line of code says "Get the metadata property named <em>title</em> and put it in the variable named <em>title</em>, If there is no title property put an empty string in <em>title</em>.
+</p>
+<p>Generally&#160; you will save metadata in the <em>write_file()</em> method and read it in the <em>read_file()</em> method.
+</p>
+<p>In a normal Activity that writes out a file in write_file() this next line would be unnecessary:
+</p>
+<p>
+</p>
+<pre> self.metadata['activity'] = self.get_bundle_id()
+</pre>
+<p> Any Journal entry created by an Activity will automatically have this property set. In the case of <em>Pride and Prejudice</em>, our Activity did not create it.&#160; We are able to read it because our Activity supports its <em>MIME type</em>.&#160; Unfortunately, that MIME type, <em>application/zip</em>, is used by other Activities.&#160; I found it very frustrating to want to open a book in <strong>Read Etexts</strong> and accidentally have it opened in <strong>EToys</strong> instead.&#160; This line of code solves that problem.&#160; You only need to use <em>Start Using...</em> the first time you read a book.&#160; After that the book will use the <strong>Read Etexts</strong> icon and can be resumed with a single click.
+</p>
+<p>This does not at all affect the MIME type of the Journal entry, so if you wanted to deliberately open <em>Pride and Prejudice</em> with <strong>Etoys</strong> it is still possible.
+</p>
+<p>Before we leave the subject of Journal metadata let's look at all the standard metadata that every Activity has.&#160; Here is some code that creates a new Journal entry and updates a bunch of standard properties:
+</p>
+<pre> def create_journal_entry(self, tempfile):
+ journal_entry = datastore.create()
+ journal_title = self.selected_title
+ if self.selected_volume != '':
+ journal_title += ' ' + _('Volume') + ' ' + \
+ self.selected_volume
+ if self.selected_author != '':
+ journal_title = journal_title + ', by ' + \
+ self.selected_author
+ journal_entry.metadata['title'] = journal_title
+ journal_entry.metadata['title_set_by_user'] = '1'
+ journal_entry.metadata['keep'] = '0'
+ format = \
+ self._books_toolbar.format_combo.props.value
+ if format == '.djvu':
+ journal_entry.metadata['mime_type'] = \
+ 'image/vnd.djvu'
+ if format == '.pdf' or format == '_bw.pdf':
+ journal_entry.metadata['mime_type'] = \
+ 'application/pdf'
+ journal_entry.metadata['buddies'] = ''
+ journal_entry.metadata['preview'] = ''
+ journal_entry.metadata['icon-color'] = \
+ profile.get_color().to_string()
+ textbuffer = self.textview.get_buffer()
+ journal_entry.metadata['description'] = \
+ textbuffer.get_text(textbuffer.get_start_iter(),
+ textbuffer.get_end_iter())
+ journal_entry.file_path = tempfile
+ datastore.write(journal_entry)
+ os.remove(tempfile)
+ self._alert(_('Success'), self.selected_title + \
+ _(' added to Journal.'))
+</pre>
+<p>This code is taken from an Activity I wrote that downloads books from a website and creates Journal entries for them.&#160; The Journal entries contain a friendly title and a full description of the book.
+</p>
+<p>Most Activities will only deal with one Journal entry by using the <em>read_file()</em> and <em>write_file()</em> methods but you are not limited to that.&#160; In a later chapter I'll show you how to create and delete Journal entries, how to list the contents of the Journal, and more.
+ <br/></p>We've covered a lot of technical information in this chapter and there's more to come, but before we get to that we need to look at some other important topics:
+<ul><li>Putting your Activity in version control.&#160; This will enable you to share your code with the world and get other people to help work on it.</li>
+ <li>Getting your Activity translated into other languages.</li>
+ <li>Distributing your finished Activity.&#160; (Or your not quite finished but still useful Activity).
+ <br/></li>
+</ul></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch012_add-your-activity-code-to-version-control.xhtml b/studio/static/doc/myosa/ch012_add-your-activity-code-to-version-control.xhtml
new file mode 100644
index 0000000..0f7af14
--- /dev/null
+++ b/studio/static/doc/myosa/ch012_add-your-activity-code-to-version-control.xhtml
@@ -0,0 +1,336 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Add Your Activity Code To Version Control
+</h1>
+<h2>What Is Version Control?
+</h2>
+<p> <em>"If I have seen further it is only by standing on the shoulders of giants."</em>
+</p>
+<p>Isaac Newton, in a letter to Robert Hooke.
+</p>
+<p>Writing an Activity is usually not something you do by yourself.&#160; You will usually have collaborators in one form or another.&#160; When I started writing <strong>Read Etexts</strong> I copied much of the code from the <strong>Read</strong> Activity.&#160; When I implemented text to speech I adapted a toolbar from the <strong>Speak</strong> Activity.&#160; When I finally got my copied file sharing code working the author of <strong>Image Viewer</strong> thought it was good enough to copy into that Activity.&#160; Another programmer saw the work I did for text to speech and thought he could do it better.&#160; He was right, and his improvements got merged into my own code.&#160; When I wrote <strong>Get Internet Archive Books</strong> someone else took the user interface I came up with and made a more powerful and versatile Activity called <strong>Get Books</strong>.&#160; Like Newton, everyone benefits from the work others have done before.
+ <br/></p>
+<p>Even if I wanted to write Activities without help I would still need collaborators to translate them into other languages.
+</p>
+<p>To make collaboration possible you need to have a place where everyone can post their code and share it.&#160; This is called a code repository.&#160; It isn't enough to just share the latest version of your code.&#160; What you really want to do is share <em>every</em> version of your code.&#160; Every time you make a significant change to your code you want to have the new version and the previous version available.&#160; Not only do you want to have every version of your code available, you want to be able to compare any two versions your code to see what changed between them.&#160; This is what version control software does.
+</p>
+<p>The three most popular version control tools are <strong>CVS</strong>, <strong>Subversion</strong>, and <strong>Git</strong>.&#160; Git is the newest and is the one used by Sugar Labs.&#160; While not every Activity has its code in the Sugar Labs Git repository (other free code repositories exist) there is no good reason not to do it and significant benefits if you do.&#160; If you want to get your Activity translated into other languages using the Sugar Labs Git repository is a must. &#160;
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Git Along Little Dogies
+</h2>
+<p>Git is a <strong>distributed</strong> version control system.&#160; This means that not only are there copies of every version of your code in a central repository, the same copies exist on every user's computer.&#160; This means you can update your local repository while you are not connected to the Internet, then connect and share everything at one time.
+</p>
+<p> There are two ways you will interact with your Git repository: through Git commands and through the website at <a href="http://git.sugarlabs.org/.">http://git.sugarlabs.org/.</a>&#160;&#160; We'll look at this website first.
+</p>
+<p>Go to <a href="http://git.sugarlabs.org">http://git.sugarlabs.org/</a>&#160; and click on the <strong>Projects</strong> link in the upper right corner:
+</p>
+<p><img alt="git1.jpg" src="static/ActivitiesGuideSugar-git1-en.jpg" width="473" height="194"/></p>
+<p>You will see a list of projects in the repository.&#160; They will be listed from newest to oldest.&#160; You'll also see a <strong>New Project</strong> link but you'll need to create an account to use that and we aren't ready to do that yet.
+ <br/></p>
+<p><img alt="git2.jpg" src="static/ActivitiesGuideSugar-git2-en.jpg" width="535" height="453"/></p>
+<p>If you use the <strong>Search</strong> link in the upper right corner of the page you'll get a search form.&#160; Use it to search for "read etexts".&#160; Click on the link for that project when you find it.&#160; You should see something like this:
+ <br/></p>
+<p><img alt="git3.jpg" src="static/ActivitiesGuideSugar-git3-en.jpg" width="571" height="571"/></p>
+<p>This page lists <em>some</em> of the activity for the project but I don't find it particularly useful.&#160; To get a much better look at your project start by clicking on the repository name on the right side of the page.&#160; In this case the repository is named <strong>mainline</strong>.
+</p>
+<p><img alt="git4.jpg" src="static/ActivitiesGuideSugar-git4-en.jpg" width="399" height="236"/></p>
+<p>You'll see something like this at the top of the page:
+</p>
+<p><img alt="git5.jpg" src="static/ActivitiesGuideSugar-git5-en.jpg" width="574" height="509"/></p>
+<p>This page has some useful information on it.&#160; First, have a look at the <strong>Public clone url</strong> and the <strong>HTTP clone url</strong>.&#160; You need to click on <strong>More info...</strong> to see either one.&#160; If you run either of these commands from the console you will get a copy of the git repository for the project copied to your computer.&#160; This copy will include every version of every piece of code in the project.&#160; You would need to modify it a bit before you could share your changes back to the main repository, but everything would be there.
+</p>
+<p>The list under <strong>Activities</strong> is not that useful, but if you click on the <strong>Source Tree</strong> link you'll see something really good:
+</p>
+<p><img alt="git6.jpg" src="static/ActivitiesGuideSugar-git6-en.jpg" width="571" height="468"/></p>
+<p>Here is a list of every file in the project, the date it was last updated, and a comment on what was modified.&#160; Click on the link for <strong>ReadEtextsActivity.py</strong> and you'll see this:
+</p>
+<p><img alt="git7.jpg" src="static/ActivitiesGuideSugar-git7-en.jpg" width="597" height="642"/></p>
+<p>This is the latest code in that file in pretty print format.&#160; Python keywords are shown in a different color, there are line numbers, etc.&#160; This is a good page for looking at code on the screen, but it doesn't print well and it's not much good for copying snippets of code into Eric windows either.&#160; For either of those things you'll want to click on <strong>raw blob data</strong> at the top of the listing:
+</p>
+<p><img alt="git8.jpg" src="static/ActivitiesGuideSugar-git8-en.jpg" width="549" height="484"/></p>
+<p>We're not done yet.&#160; Use the <strong>Back</strong> button to get back to the pretty print listing and click on the <strong>Commits</strong> link.&#160; This will give us a list of everything that changed each time we committed code into Git:
+</p>
+<p><img alt="git9.jpg" src="static/ActivitiesGuideSugar-git9-en.jpg" width="636" height="540"/></p>
+<p>You may have noticed the odd combination of letters and numbers after the words <strong>James Simmons committed</strong>.&#160; This is a kind of version number.&#160; The usual practice with version control systems is to give each version of code you check in a version number, usually a simple sequence number.&#160; Git is distributed, with many separate copies of the repository being modified independently and then merged.&#160; That makes using just a sequential number to identify versions unworkable.&#160; Instead, Git gives each version a really, really large random number.&#160; The number is expressed in base 16, which uses the symbols 0-9 and a-f.&#160; What you see in green is only a small part of the complete number.&#160; The number is a link, and if you click on it you'll see this:
+</p>
+<p><img alt="git10.jpg" src="static/ActivitiesGuideSugar-git10-en.jpg" width="561" height="607"/></p>
+<p>At the top of the page we see the complete version number used for this commit.&#160; Below the gray box we see the full comment that was used to commit the changes.&#160; Below that is a listing of what files were changed.&#160;&#160; If we look further down the page we see this:
+</p>
+<p><img alt="git11_1.jpg" src="static/ActivitiesGuideSugar-git11_1-en.jpg" width="581" height="530"/></p>
+<p>This is a <em>diff</em> report which shows the lines that have changed between this version and the previous version.&#160; For each change it shows a few lines before and after the change to give you a better idea of what the change does.&#160; Every change shows line numbers too.
+</p>
+<p>A report like this is a wonderful aid to programming.&#160; Sometimes when you're working on an enhancement to your program something that had been working mysteriously stops working.&#160; When that happens you will wonder just what you changed that could have caused the problem.&#160; A diff report can help you find the source of the problem.
+</p>
+<p>By now you must be convinced that you want your project code in Git.&#160; Before we can do that we need to create an account on this website.&#160; That is no more difficult than creating an account on any other website, but it will need an important piece of information from us that we don't have yet.&#160; Getting that information is our next task.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Setting Up SSH Keys
+</h2>
+<p>To send your code to the <strong>Gitorious</strong> code repository you need an SSH public/private key pair.&#160;&#8286; SSH is a way of sending data over the network in encrypted format.&#160; (In other words, it uses a secret code so nobody but the person getting the data can read it).&#160; Public/private key encryption is a way of encrypting data that provides a way to guarantee that the person who is sending you the data is who he claims to be.
+</p>
+<p>In simple terms it works like this: the SSH software generates two very large numbers that are used to encode and decode the data going over the network.&#160; The first number, called the<strong> private key</strong>, is kept secret and is only used by you to encode the data.&#160; The second number, called the <strong>public key</strong>, is given to anyone who needs to decode your data.&#160; He can decode it using the public key; there is no need for him to know the private key.&#160; He can also use the public key to encode a message to send back to you and you can decode it using your private key.
+</p>
+<p>Git uses SSH like an electronic signature to verify that code changes that are supposed to be coming from you actually are coming from you.&#160; The Git repository is given your public key.&#160; It knows that anything it decodes with that key must have been sent by you because only you have the private key needed to encode it.
+</p>
+<p> We will be using a tool called <strong>OpenSSH</strong> to generate the public and private keys.&#160; This is included with every version of Linux so you just need to verify that it has been installed.&#160; Then use the <strong>ssh-keygen</strong> utility that comes with OpenSSH to generate the keys:
+ <br/></p>
+<pre>[jim@olpc2 ~]$ ssh-keygen
+Generating public/private rsa key pair.
+Enter file in which to save the key (/home/jim/.ssh/id_rsa): </pre>
+<p>By default ssh-keygen generates an <strong>RSA</strong> key, which is the kind we want.&#160; By default it puts the keyfiles in a directory called <strong>/<em>yourhome</em>/.ssh</strong> and we want that too, so DO NOT enter a filename when it asks you to.&#160; Just hit the <strong>Enter</strong> key to continue.
+</p>
+<pre>[jim@olpc2 ~]$ ssh-keygen
+Generating public/private rsa key pair.
+Enter file in which to save the key (/home/jim/.ssh/id_rsa):
+Created directory '/home/jim/.ssh'.
+Enter passphrase (empty for no passphrase):
+</pre>
+<p>Now we DO want a <strong>passphrase</strong> here.&#160; A passphrase is like a password that is used with the public and private keys to do the encrypting.&#160; When you type it in you will not be able to see what you typed.&#160; Because of that it will ask you to type the same thing again, and it will check to see that you typed them in the same way both times.
+</p>
+<pre>[jim@olpc2 ~]$ ssh-keygen
+Generating public/private rsa key pair.
+Enter file in which to save the key (/home/jim/.ssh/id_rsa):
+Created directory '/home/jim/.ssh'.
+Enter passphrase (empty for no passphrase):
+Enter same passphrase again:
+Your identification has been saved in /home/jim/.ssh/id_rsa.
+Your public key has been saved in /home/jim/.ssh/id_rsa.pub.
+The key fingerprint is:
+d0:fe:c0:0c:1e:72:56:7a:19:cd:f3:85:c7:4c:9e:18
+jim@olpc2.simmons
+The key's randomart image is:
++--[ RSA 2048]----+
+| oo E=. |
+| + o+ .+=. |
+| . B + o.oo |
+| = O . |
+| . S |
+| o |
+| . |
+| |
+| |
++-----------------+
+</pre>
+<p> When choosing a passphrase remember that it needs to be something you can type reliably without seeing it and it would be better if it was <em>not</em> a word you can find in the dictionary, because those are easily broken. When I need to make a password I use the tool at <a href="http://www.multicians.org/thvv/gpw.html.">http://www.multicians.org/thvv/gpw.html.</a>&#160; This tool generates a bunch of nonsense words that are pronounceable.&#160; Pick one that appeals to you and use that.
+</p>
+<p>Now have a look inside the .ssh directory.&#160; By convention every file or directory name that begins with a period is considered hidden by Linux, so it won't show up in a GNOME file browser window unless you use the option on the View menu to Show Hidden Files.&#160; When you display the contents of that directory you'll see two files: <strong>id_rsa</strong> and <strong>id_rsa.pub</strong>.&#160; The public key is in id_rsa.pub.&#160; Try opening that file with gedit (Open With Text Editor) and you'll see something like this:
+</p>
+<p><img alt="git12.jpg" src="static/ActivitiesGuideSugar-git12-en.jpg" width="640" height="278"/></p>
+<p>When you create your account on <a href="http://git.sugarlabs.org">git.sugarlabs.org</a> there will be a place where you can add your public SSH key.&#160; To do that use <strong>Select All</strong> from the <strong>Edit</strong> menu in gedit, then <strong>Copy</strong> and <strong>Paste</strong> into the field provided on the web form.
+</p>
+<h2>Create A New Project
+</h2>
+<p>I'm going to create a new Project in Git for the examples for this book.&#160; I need to log in with my new account and click the <strong>New Project</strong> link we saw earlier.&#160; I get this form, which I have started filling in:
+</p>
+<p><img alt="git13.jpg" src="static/ActivitiesGuideSugar-git13-en.jpg" width="398" height="343"/></p>
+<p>The <strong>Title</strong> is used on the website, the <strong>Slug</strong> is a shortened version of the title without spaces used to name the Git repository.&#160; <strong>Categories</strong> are optional.&#160; <strong>License</strong> is GPL v2 for my projects.&#160; You can choose from any of the licenses in the list for your own Projects, and you can change the license entry later if you want to.&#160; You will also need to enter a <strong>Description</strong> for your project.
+</p>
+<p>Once you have this set up you'll be able to click on the mainline entry for the Project (like we did with Read Etexts before) and see something like this:
+</p>
+<p><img alt="git14.jpg" src="static/ActivitiesGuideSugar-git14-en.jpg" width="640" height="454"/></p>
+<p>The next step is to convert our project files into a local Git repository, add the files to it, then push it to the repository on <a href="http://git.sugarlabs.org">git.sugarlabs.org.</a>&#160; We need to do this because you cannot <strong>clone</strong> an empty repository, and our remote repository is currently empty.&#160; To get around that problem we'll push the local repository out to the new remote repository we just created, then clone the remote one and delete our existing project and its Git repository.&#160; From then on we'll do all our work in the cloned repository.
+ <br/></p>
+<p>This process may remind you of the Edward Albee quote, "<span class="body">Sometimes a person has to go a very long distance out of his way to come back a short distance correctly".</span> Fortunately we only need to do it once per project.&#160; Enter the commands shown below in <strong>bold</strong> after making you project directory the current one:
+ <br/></p>
+<pre><strong>git init</strong>
+Initialized empty Git repository in
+/home/jim/olpc/bookexamples/.git/
+<strong>git add *.py</strong>
+<strong>git add activity
+git add MANIFEST</strong>
+<strong>git add .gitignore
+git commit -a -m "Create repository and load"</strong>
+[master (root-commit) 727bfe8] Create repository and load
+ 9 files changed, 922 insertions(+), 0 deletions(-)
+ create mode 100644 .gitignore
+ create mode 100644 MANIFEST
+ create mode 100755 ReadEtexts.py
+ create mode 100644 ReadEtextsActivity.py
+ create mode 100644 ReadEtextsActivity2.py
+ create mode 100644 activity/activity.info
+ create mode 100644 activity/read-etexts.svg
+ create mode 100755 setup.py
+ create mode 100644 toolbar.py
+</pre>
+<p> I have made an empty local Git repository with <strong>git init</strong>, then I've used <strong>git add</strong> to add the important files to it.&#160; (In fact <strong>git add</strong> doesn't actually add anything itself; it just tells Git to add the file on the next <strong>git commit</strong>).&#160; Finally <strong>git commit</strong> with the options shown will actually put the latest version of these files in my new local repository.
+</p>
+<p>To push this local repository to <a href="http://git.sugarlabs.org">git.sugarlabs.org</a>&#160; we use the commands from the web page:
+</p>
+<pre><strong>git remote add origin \
+gitorious@git.sugarlabs.org:\
+myo-sugar-activities-examples/mainline.git
+git push origin master</strong>
+Counting objects: 17, done.
+Compressing objects: 100% (14/14), done.
+Writing objects: 100% (15/15), 7.51 KiB, done.
+Total 15 (delta 3), reused 0 (delta 0)
+To gitorious@git.sugarlabs.org:myo-sugar-activities-examples/
+mainline.git
+ 2cb3a1e..700789d master -&gt; master
+=&gt; Syncing Gitorious...
+Heads up: head of changed to
+700789d3333a7257999d0a69bdcafb840e6adc09 on master
+Notify cia.vc of 727bfe819d5b7b70f4f2b31d02f5562709284ac4 on
+myo-sugar-activities-examples
+Notify cia.vc of 700789d3333a7257999d0a69bdcafb840e6adc09 on
+myo-sugar-activities-examples
+[OK]
+<strong>rm *</strong>
+<strong>rm activity -rf
+rm .git -rf
+cd ~
+rm Activity/ReadEtextsII</strong>
+<strong>mkdir olpc</strong>
+<strong>cd olpc</strong>
+<strong>mkdir bookexamples</strong>
+<strong>cd bookexamples
+git clone \
+git://git.sugarlabs.org/\
+myo-sugar-activities-examples/mainline.git</strong>
+Initialized empty Git repository in
+/home/jim/olpc/bookexamples/mainline/.git/
+remote: Counting objects: 18, done.
+remote: Compressing objects: 100% (16/16), done.
+remote: Total 18 (delta 3), reused 0 (delta 0)
+Receiving objects: 100% (18/18), 8.53 KiB, done.
+Resolving deltas: 100% (3/3), done.
+</pre>
+<p> The lines in <strong>bold</strong> are the commands to enter, and everything else is messages that Git sends to the console.&#160; I've split some of the longer Git commands with the backslash (\) to make them fit better on the printed page, and wrapped some output lines that would normally print on one line for the same reason.&#160; It probably isn't clear what we're doing here and why, so let's take it step by step:
+</p>
+<ul><li>The first command <strong>git remote add origin</strong> tells the remote Git repository that we are going to send it stuff from our local repository.
+ <br/></li>
+ <li>The second command <strong>git push origin master</strong> actually sends your local Git repository to the remote one and its contents will be copied in.&#160; When you enter this command you will be asked to enter the SSH pass phrase you created in the last section.&#160; GNOME will remember this phrase for you and enter it for every Git command afterwards so you don't need to.&#160; It will keep doing this until you log out or turn off the computer.
+ <br/></li>
+ <li>The next step is to delete our existing files and our local Git repository (which is contained in the hidden directory .git).&#160; The <strong>rm .git -rf</strong> means "Delete the directory .git and everything in it".&#160; <strong>rm</strong> is a Unix command, not part of Git.&#160; If you like you can delete your existing files <em>after</em> you create the cloned repository in the next step.&#160; Note the command <strong>rm Activity/ReadEtextsII</strong>, which deletes the symbolic link to our old project that we created by running <strong>./setup.py dev</strong>.&#160; We'll need to go to our new cloned project directory and run that again before we can test our Activity again.
+ <br/></li>
+ <li>Now we do the <strong>git clone</strong> command from the web page.&#160; This takes the remote Git repository we just added our MANIFEST file to and makes a new local repository in directory <strong>/<em>yourhome</em>/olpc/bookexamples/mainline.</strong></li>
+</ul><p>Finally we have a local repository we can use.&#160; Well, not quite.&#160; We can commit our code to it but we cannot push anything back to the remote repository because our local repository isn't configured correctly yet.
+</p>
+<p>What we need to do is edit the file <strong>config</strong> in directory <strong>.git</strong> in <strong>/<em>yourhome</em>/olpc/bookexamples/mainline.&#160; </strong>We can use gedit to do that.&#160; We need to change the <strong>url=</strong> entry to point to the <strong>Push url</strong> shown on the mainline web page.&#160; When we're done our <strong>config</strong> file should look like this:
+</p>
+<pre>[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[remote "origin"]
+<strong> url = gitorious@git.sugarlabs.org:
+myo-sugar-activities-examples/mainline.git</strong>
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
+</pre>
+<p> The line in <strong>bold</strong> is the only one that gets changed.&#160; It is split here to make it fit on the printed page.&#160; In your own files it should all be one line with no spaces between the colon(:) that ends the first line and the beginning of the second line.
+ <br/></p>
+<p>From now on anyone who wants to work on our project can get a local copy of the Git repository by doing this from within the directory where he wants the repository to go:
+</p>
+<p>
+</p>
+<pre><strong>git clone git://git.sugarlabs.org/\
+myo-sugar-activities-examples/mainline.git</strong></pre>
+<p> He'll have to change his <strong>.git/config</strong> file just like we did, then he'll be ready to go.
+</p>
+<h2>Everyday Use Of Git
+</h2>
+<p> While getting the repositories set up to begin with is a chore, daily use is not.&#160; There are only a few commands you'll need to work with.&#160; When we left off we had a repository in <strong>/<em>yourhome</em>/olpc/bookexamples/mainline</strong> with our files in it.&#160; We will need to add any new files we create too.
+ <br/></p>
+<p>We use the <strong>git add</strong> command to tell Git that we want to use Git to store a particular file.&#160; This doesn't actually store anything, it just tells Git our intentions.&#160; The format of the command is simply:
+</p>
+<pre>git add <em>file_or_directory_name</em></pre>
+<p>There are files we <em>don't</em> want to add to Git, to begin with those files that end in <strong>.pyc</strong>.&#160; If we never do a <strong>git add</strong> on them they'll never get added, but Git will constantly ask us why we aren't adding them.&#160; Fortunately there is a way to tell Git that we really, really don't want to add those files.&#160; We need to create a file named <strong>.gitignore</strong> using gedit and put in entries like this:
+</p>
+<pre>*.pyc
+*.e4p
+*.zip
+.eric4project/
+.ropeproject/</pre>
+<p>These entries will also ignore project files used by Eric and zip files containing ebooks,&#160; Once we have this file created in the mainline directory we can add it to the repository:
+</p>
+<pre>git add .gitignore
+git commit -a -m "Add .gitignore file"
+</pre>
+<p>From now on Git will no longer ask us to add .pyc or other unwanted&#160; files that match our patterns. If there are other files we don't want in the repository we can add them to .gitignore either as full file names or directory names or as patterns like *.pyc.
+</p>
+<p>&#160;In addition to adding files to Git we can remove them too:
+</p>
+<pre>git rm <em>filename</em></pre>
+<p>Note that this just tells Git that from now on it will not be keeping track of a given filename, and that will take effect at the next commit.&#160; Old versions of the file are still in the repository.
+</p>
+<p>If you want to see what&#160;changes will be applied at the next commit run this:
+</p>
+<pre><strong>git status</strong>
+# On branch master
+# Changed but not updated:
+# (use "git add &lt;file&gt;..." to update what will
+# be committed)
+#
+# modified: ReadEtextsActivity.py
+#
+no changes added to commit (use "git add" and/or
+"git commit -a")
+</pre>
+<p>Finally, to put your latest changes in the repository use this:
+</p>
+<pre><strong>git commit -a -m "Change use of instance directory to tmp"</strong>
+Created commit a687b27: Change use of instance
+directory to tmp
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+</pre>
+<p>If you leave off the -m an editor will open up and you can type in a comment, then save and exit. Unfortunately by default the editor is <strong>vi</strong>, an old text mode editor that is not friendly like gedit.
+</p>
+<p>When we have all our changes done we can send them to the central repository using <strong>git push</strong>:
+</p>
+<pre><strong>git push</strong>
+Counting objects: 5, done.
+Compressing objects: 100% (3/3), done.
+Writing objects: 100% (3/3), 322 bytes, done.
+Total 3 (delta 2), reused 0 (delta 0)
+To gitorious@git.sugarlabs.org:
+myo-sugar-activities-examples/mainline.git
+ 700789d..a687b27 master -&gt; master
+=&gt; Syncing Gitorious...
+Heads up: head of changed to
+a687b27e2f034e5a17d2ca2fe9f2787c7f633e64 on master
+Notify cia.vc of a687b27e2f034e5a17d2ca2fe9f2787c7f633e64
+on myo-sugar-activities-examples
+[OK]
+
+</pre>
+<p>We can get the latest changes from other developers by doing <strong>git pull</strong>:
+</p>
+<pre><strong>git pull</strong>
+remote: Counting objects: 17, done.
+remote: Compressing objects: 100% (14/14), done.
+remote: Total 15 (delta 3), reused 0 (delta 0)
+Unpacking objects: 100% (15/15), done.
+From gitorious@git.sugarlabs.org:
+myo-sugar-activities-examples/mainline
+ 2cb3a1e..700789d master -&gt; origin/master
+Updating 2cb3a1e..700789d
+Fast forward
+ .gitignore | 6 +
+ MANIFEST | 244 +--------------------------
+---------
+ ReadEtexts.py | 182 +++++++++++++++++++++++++++
+ ReadEtextsActivity.py | 182 +++++++++++++++++++++++++++
+ ReadEtextsActivity2.py | 311 +++++++++++++++++++++++++++
++++++++++++++++++++
+ activity/activity.info | 9 ++
+ activity/read-etexts.svg | 71 +++++++++++
+ setup.py | 21 +++
+ toolbar.py | 136 ++++++++++++++++++++
+ 9 files changed, 921 insertions(+), 241 deletions(-)
+ create mode 100644 .gitignore
+ create mode 100755 ReadEtexts.py
+ create mode 100644 ReadEtextsActivity.py
+ create mode 100644 ReadEtextsActivity2.py
+ create mode 100644 activity/activity.info
+ create mode 100644 activity/read-etexts.svg
+ create mode 100755 setup.py
+ create mode 100644 toolbar.py
+&#160;</pre></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch013_going-international-with-pootle.xhtml b/studio/static/doc/myosa/ch013_going-international-with-pootle.xhtml
new file mode 100644
index 0000000..46c8b47
--- /dev/null
+++ b/studio/static/doc/myosa/ch013_going-international-with-pootle.xhtml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Going International With Pootle
+</h1>
+<h2>Introduction
+ <br/></h2>
+<p>The goal of Sugar Labs and One Laptop Per Child is to educate all the children of the world, and we can't do that with Activities that are only available in one language.&#160; It is equally true that making separate versions of each Activity for every language is not going to work, and expecting Activity developers to be fluent in many languages is not realistic either.&#160; We need a way for Activity developers to be able to concentrate on creating Activities and for those who can translate to just do that.&#160; Fortunately, this is possible and the way it's done is by using <em>gettext</em>.
+</p>
+<h2>Getting Text With gettext
+</h2>
+<p>You should remember that our latest code example made use of an odd import:
+</p>
+<pre>from gettext import gettext as _
+</pre>
+<p> The "_()" function was used in statements like this:
+</p>
+<pre> self.back.set_tooltip(_('Back'))
+</pre>
+<p> At the time I explained that this odd looking function was used to translate the word "Back" into other languages, so that when someone looks at the Back button's tool tip he'll see the text in his own language.&#160; I also said that if it was not possible to translate this text the user would see the word "Back" untranslated.&#160; In this chapter we'll learn more about how this works and what we have to do to support the volunteers who translate these text strings into other languages.
+</p>
+<p>The first thing you need to learn is how to properly format the text strings to be translated.&#160; This is an issue when the text strings are actual sentences containing information.&#160; For example, you might write such a message this way:
+</p>
+<pre> message = _("User ") + username + \
+ _(" has joined the chat room.")
+</pre>
+<p>This would work, but you've made things difficult for the translator. &#160; He has two separate strings to translate and no clue that they belong together.&#160; It is much better to do this:
+</p>
+<pre> message = _("User %s has joined the chat room.") % \
+ username
+</pre>
+<p>If you know both statements give the same resulting string then you can easily see why a translator would prefer the second one.&#160; Use this technique whenever you need a message that has some information inserted into it.&#160; When you use it, try and limit yourself to only one format code (the %s) per string.&#160; If you use more than one it can cause problems for the translator.
+ <br/></p>
+<h2>Going To Pot
+</h2>
+<p>Assuming that every string of text a user might be shown by our Activity is passed through "_()" the next step is to generate a pot file.&#160; You can do this by running setup.py with a special option:
+</p>
+<pre>./setup.py genpot</pre>
+<p>This creates a directory called <strong>po</strong> and puts a file <strong><em>ActivityName</em>.pot</strong> in that directory.&#160; In the case of our example project <em>ActivityName</em> is <strong>ReadEtextsII</strong>.&#160; This is the contents of that file:
+</p>
+<pre># SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the
+# PACKAGE package.
+# FIRST AUTHOR &lt;EMAIL@ADDRESS&gt;, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-01-06 18:31-0600\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME &lt;EMAIL@ADDRESS&gt;\n"
+"Language-Team: LANGUAGE &lt;LL@li.org&gt;\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activity/activity.info:2
+msgid "Read ETexts II"
+msgstr ""
+
+#: toolbar.py:34
+msgid "Back"
+msgstr ""
+
+#: toolbar.py:40
+msgid "Forward"
+msgstr ""
+
+#: toolbar.py:115
+msgid "Zoom out"
+msgstr ""
+
+#: toolbar.py:120
+msgid "Zoom in"
+msgstr ""
+
+#: toolbar.py:130
+msgid "Fullscreen"
+msgstr ""
+
+#: ReadEtextsActivity2.py:34
+msgid "Edit"
+msgstr ""
+
+#: ReadEtextsActivity2.py:38
+msgid "Read"
+msgstr ""
+
+#: ReadEtextsActivity2.py:46
+msgid "View"
+msgstr ""
+</pre>
+<p> This file contains an entry for every text string in our Activity (as msgid) and a place to put a translation of that string (msgstr).&#160;&#160; Copies of this file will be made by the Pootle server for every language desired, and the msgstr entries will be filled in by volunteer translators.
+</p>
+<h2>Going To Pootle
+ <br/></h2>
+<p>Before any of that can happen we need to get our POT file into Pootle.&#160; The first thing we need to do is get the new directory into our Git repository and push it out to Gitorious.&#160; You should be familiar with the needed commands by now:
+</p>
+<pre>git add po
+git commit -a -m "Add POT file"
+git push
+</pre>
+<p>Next we need to give the user "pootle" commit authority to our Git project.&#160; Go to <a href="http://git.sugarlabs.org">git.sugarlabs.org,</a>&#160; sign in, and find your Project page and click on the mainline link.&#160; You should see this on the page that takes you to:
+</p>
+<p>&#160;<img alt="Add pootle as a committer" src="static/ActivitiesGuideSugar-pootle1-en.jpg" height="456" width="376"/></p>
+<p>Click on the <strong>Add committer</strong> link and type in the name <strong>pootle </strong>in the form that takes you to.&#160; When you come back to this page <strong>pootle</strong> will be listed under Committers.
+ <br/></p>
+<p> Your next step is to go to web site <a href="http://bugs.sugarlabs.org">http://bugs.sugarlabs.org</a>&#160; and register for a user id.&#160; When you get that open up a ticket something like this:
+</p>
+<p><img alt="pootle2.jpg" src="static/ActivitiesGuideSugar-pootle2-en.jpg" height="527" width="640"/></p>
+<p>The <strong>Component</strong> entry <em>localization</em> should be used, along with <strong>Type</strong> <em>task</em>.
+</p>
+<p>Believe it or not, this is all you need to do to get your Activity set up to be translated.
+</p>
+<h2>Pay No Attention To That Man Behind The Curtain
+</h2>
+<p>After this you'll need to do a few things to get translations from Pootle into your Activity.
+</p>
+<ul><li>When you add text strings (labels, error messages, etc.) to your Activity always use the <strong>_()</strong> function with them so they can be translated.</li>
+ <li>After adding new strings always run <strong>./setup.py genpot</strong> to recreate the POT file.</li>
+ <li>After that commit and push your changes to Gitorious.</li>
+ <li>Every so often, and especially before releasing a new version, do a <strong>git pull</strong>.&#160; If there are any localization files added to Gitorious this will bring them to you.</li>
+ <li>After getting a bunch of new files run <strong>./setup.py fix_manifest</strong> to get the new files included in your <strong>MANIFEST</strong> file.&#160; Afterwards edit the <strong>MANIFEST</strong> with gedit to remove any unwanted entries (which will be Eric project files, etc.).
+ <br/></li>
+</ul><p>Localization with Pootle will create a large number of files in your project, some in the <strong>po</strong> directory and others in a new directory called <strong>locale</strong>.&#160; As long as these are listed in the MANIFEST they will be included in the .xo file that you will use to distribute your Activity.
+</p>
+<h2>C'est Magnifique!
+</h2>
+<p>Here is a screen shot of the French language version of <strong>Read Etexts</strong> reading Jules Verne's novel <em>Le tour du monde en quatre-vingts jours</em>:
+</p>
+<p><img alt="Jules Verne in French" src="static/ActivitiesGuideSugar-pootle3-en.jpg" height="480" width="640"/>&#160;
+</p>
+<p>There is reason to believe that the book is in French too.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch014_distribute-your-activity.xhtml b/studio/static/doc/myosa/ch014_distribute-your-activity.xhtml
new file mode 100644
index 0000000..01ad352
--- /dev/null
+++ b/studio/static/doc/myosa/ch014_distribute-your-activity.xhtml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Distribute Your Activity
+</h1>
+<h2>Choose A License
+</h2>
+<p>Before you give your Activity to anyone you need to choose a license that it will be distributed under.&#160; Buying software is like buying a book.&#160; There are certain rights you have with a book and others you don't have.&#160; If you buy a copy of <em>The DaVinci Code</em> you have the right to read it, to loan it out, to sell it to a used bookstore, or to burn it.&#160; You do <em>not</em> have the right to make copies of it or to make a movie out of it.&#160; Software is the same way, but often worse.&#160; Those long license agreements we routinely accept by clicking a button might not allow you to sell the software when you're done with it, or even give it away.&#160; If you sell your computer you may find that the software you bought is only good for that computer, and only while you are the owner of the computer.&#160; (You can get good deals on reconditioned computers with no operating system installed for that very reason).
+</p>
+<p>If you are in the business of selling software you might have to hire a lawyer to draw up a license agreement, but if you're giving away software there are several standard licenses you can choose from for free.&#160; The most popular by far is called the <em>General Public License</em>, or GPL.&#160; Like the licenses Microsoft uses it allows the people who get your program to do some things with it but not others.&#160; What makes it interesting is not what it allows them to do (which is pretty much anything they like) but what it forbids them to do.
+</p>
+<p>If someone distributes a program licensed under the GPL they are also required to make the source code of the program available to anyone who wants it.&#160; That person may do as he likes with the code, with one important restriction: if he distributes a program based on that code he must <em>also</em> license that code using the GPL.&#160; This makes it impossible for someone to take a GPL licensed work, improve it, and sell it to someone without giving him the source code to the new version.
+</p>
+<p>While the GPL is not the only license available for Activities to be distributed on <a href="http://activities.sugarlabs.org">http://activities.sugarlabs.org</a>&#160; all the licenses require that anyone getting the Activity also gets the complete source code for it.&#160; You've already taken care of that requirement by putting your source code in Gitorious.&#160; If you used any code from an existing Activity licensed with the GPL you <em>must</em> license your own code the same way.&#160; If you used a significant amount of code from this book (which is also GPL licensed) you may be required to use the GPL too.
+</p>
+<p>Is licensing something you should worry about?&#160; Not really.&#160; The only reason you'd want to use a license other than the GPL is if you wanted to sell your Activity instead of give it away.&#160; Consider what you'd have to do to make that possible:
+</p>
+<ul><li>You'd have to use some language other than Python so you could give someone the program without giving them the source code.</li>
+ <li>You would have to have your own source code repository not available to the general public and make arrangements to have the data backed up regularly.</li>
+ <li>You would have to have your own website to distribute the Activity.&#160; The website would have to be set up to accept payments somehow.
+ <br/></li>
+ <li>You would have to advertise this website somehow or nobody would know your Activity existed.</li>
+ <li>You would have to have a lawyer draw up a license for your Activity.</li>
+ <li>You would have to come up with some mechanism to keep your customers from giving away copies of your Activity.</li>
+ <li>You would have to create an Activity so astoundingly clever that nobody else could make something similar and give it away.</li>
+ <li>You would have to deal with the fact that your "customers" would be children with no money or credit cards.</li>
+</ul><p>In summary, <a href="http://activities.sugarlabs.org">activities.sugarlabs.org</a>&#160; is not the <em>iPhone App Store</em>.&#160; It is a place where programmers share and build upon each other's work and give the results to children for free.&#160; The GPL encourages that to happen, and I recommend that you choose that for your license.
+ <br/></p>
+<h2>Add License Comments To Your Python Code
+</h2>
+<p>At the top of each Python source file in your project (except <strong>setup.py</strong>, which is already commented) put comments like this:
+</p>
+<p>&#160;
+</p>
+<pre># <em>filename</em> <em>Program description</em>
+#
+# Copyright (C) 2010 <em>Your Name Here</em>
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General
+# Public License as published by the Free Software
+# Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will
+# be useful, but WITHOUT ANY WARRANTY; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public
+# License for more details.
+#
+# You should have received a copy of the GNU General
+# Public License along with this program; if not, write
+# to the Free Software Foundation, Inc., 51 Franklin
+# St, Fifth Floor, Boston, MA 02110-1301 USA
+</pre>
+<p> If the code is based on someone else's code you should mention that as a courtesy.
+</p>
+<h2>Create An .xo File
+</h2>
+<p>Make certain that activity.info has the version number you want to give your Activity (currently it must be a positive integer) and run this command:
+</p>
+<pre>./setup.py dist_xo</pre>
+<p> This will create a <strong>dist</strong> directory if one does not exist and put a file named something like <strong>ReadETextsII-1.xo</strong> in it.&#160; The "1" indicates version 1 of the Activity.
+ <br/></p>
+<p>If you did everything right this <strong>.xo</strong> file should be ready to distribute.&#160; You can copy it to a thumb drive and install it on an XO laptop or onto another thumb drive running <em>Sugar on a Stick</em>.&#160; You probably should do that before distributing it any further.&#160; I like to live with new versions of my Activities for a week or so before putting them on <a href="http://activities.sugarlabs.org">activities.sugarlabs.org.</a>
+ <br/></p>
+<p>Now would be a good time to add <strong>dist</strong> to your <strong>.gitignore</strong> file, then commit it and push it to Gitorious.&#160; You don't want to have copies of your .xo files in Git.&#160; Another good thing to do at this point would be to tag your Git repository with the version number so you can identify which code goes with which version.
+</p>
+<p>
+</p>
+<pre>git tag -m "Release 1" v1 HEAD
+git push --tags
+</pre>
+<h2>Add Your Activity To ASLO
+ <br/></h2>
+<p>When you're ready to post the .xo file on ASLO you'll create an account as you did with the other websites.&#160; When you've logged in there you'll see a <strong>Tools</strong> link in the upper right corner of the page. Click on that and you'll see a popup menu with an option for <strong>Developer Hub</strong>, which you should click on.&#160; That will take you to the pages where you can add new Activities.&#160; The first thing it asks for when setting up a new Activity is what license you will use.&#160; After that you should have no problem getting your Activity set up.
+</p>
+<p>You will need to create an Activity icon as a .gif file and create screen shots of your Activity in action.&#160; You can do both of these things with <em>The GIMP</em> (GNU Image Manipulation Program).&#160; For the icon all you need to do is open the .svg file with The GIMP and <strong>Save As</strong> a .gif file.
+</p>
+<p>For the screen shots use sugar-emulator to display your Activity in action, then use the <strong>Screenshot</strong> option from the <strong>Create</strong> submenu of the <strong>File</strong> menu with these options:
+</p>
+<p><img alt="gimp1.jpg" src="static/ActivitiesGuideSugar-gimp1-en.jpg" height="346" width="358"/></p>
+<p>This tells GIMP to wait 10 seconds, then take a screenshot of the window you click on with the mouse.&#160; You'll know that the 10 seconds are up because the mouse pointer will change shape to a plus (+) sign.&#160; You also tell it <em>not</em> to include the window decoration (which means the window title bar and border).&#160; Since windows in Sugar do not have decorations eliminating the decorations used by sugar-emulator will give you a screenshot that looks exactly like a Sugar Activity in action.
+</p>
+<p>Every Activity needs one screenshot, but you can have more if you like.&#160; Screenshots help sell the Activity and instruct those who will use it on what the Activity can do.&#160; Unfortunately, ASLO cannot display pictures in a predictable sequence, so it is not suited to displaying steps to perform.
+</p>
+<p> Another thing you'll need to provide is a home page for your Activity.&#160; The one for <strong>Read Etexts</strong> is here:
+</p>
+<p><a href="http://wiki.sugarlabs.org/go/Activities/Read_Etexts">http://wiki.sugarlabs.org/go/Activities/Read_Etexts</a>
+ <br/></p>
+<p>Yes, one more website to get an account for.&#160; Once you do you can specify a link with <em>/go/Activities/some_name</em> and when you click on that link the Wiki will create a page for you.&#160; The software used for the Wiki is <em>MediaWiki</em>, the same as used for <em>Wikipedia</em>.&#160; Your page does not need to be as elaborate as mine is, but you definitely should provide a link to your source code in Gitorious.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch015_debugging-sugar-activities.xhtml b/studio/static/doc/myosa/ch015_debugging-sugar-activities.xhtml
new file mode 100644
index 0000000..7c8dbfb
--- /dev/null
+++ b/studio/static/doc/myosa/ch015_debugging-sugar-activities.xhtml
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Debugging Sugar Activities
+</h1>
+<h2>Introduction
+</h2>
+<p>No matter how careful you are it is reasonably likely that your Activity will not work perfectly the first time you try it out.&#160; Debugging a Sugar Activity is a bit different than debugging a standalone program.&#160; When you test a standalone program you just run the program itself.&#160; If there are syntax errors in the code you'll see the error messages on the console right away, and if you're running under the <strong>Eric</strong> IDE the offending line of code will be selected in the editor so you can correct it and keep going.
+</p>
+<p>With Sugar it's a bit different.&#160; It's the Sugar environment, not Eric, that runs your program.&#160; If there are syntax errors in your code you won't see them right away.&#160; Instead, the blinking Activity icon you see when your Activity starts up will just keep on blinking for several minutes and then will just go away, and your Activity won't start up.&#160; The only way you'll see the error that caused the problem will be to use the <strong>Log Activity</strong>.&#160; If your program has no syntax errors but does have logic errors you won't be able to step through your code with a debugger to find them.&#160; Instead, you'll need to use some kind of logging to trace through what's happening in your code, and again use the Log Activity to view the trace messages.&#160; Now would be a good time to repeat some advice I gave before:
+</p>
+<h2>Make A Standalone Version Of Your Program First
+</h2>
+<p>Whatever your Activity does, it's a good bet that 80% of it could be done by a standalone program which would be much less tedious to debug.&#160; If you can think of a way to make your Activity runnable as either an Activity or a standalone Python program then by all means do it.
+</p>
+<h2>Use PyLint, PyChecker, or PyFlakes
+</h2>
+<p>One of the advantages of a compiled language like <strong>C</strong> over an interpreted language like Python is that the compiler does a complete syntax check of the code before converting it to machine language.&#160; If there are syntax errors the compiler gives you informative error messages and stops the compile.&#160; There is a utility call <strong>lint</strong> which C programmers can use to do even more thorough checks than the compiler would do and find questionable things going on in the code.
+</p>
+<p>Python does not have a compiler but it does have several lint-like utilities you can run on your code before you test it.&#160; These utilities are <strong>pyflakes</strong>, <strong>pychecker</strong>, and <strong>pylint</strong>.&#160; Any Linux distribution should have all three available.
+</p>
+<h3>PyFlakes
+</h3>
+<p>Here is an example of using PyFlakes:
+</p>
+<pre><strong>pyflakes minichat.py</strong>
+minichat.py:25: 'COLOR_BUTTON_GREY' imported but unused
+minichat.py:28: 'XoColor' imported but unused
+minichat.py:29: 'Palette' imported but unused
+minichat.py:29: 'CanvasInvoker' imported but unused
+</pre>
+<p>PyFlakes seems to do the least checking of the three, but it does find errors like these above that a human eye would miss.
+</p>
+<h3>PyChecker
+</h3>
+<p>Here is PyChecker in action:
+ <br/></p>
+<pre><strong>pychecker ReadEtextsActivity.py</strong>
+Processing ReadEtextsActivity...
+/usr/lib/python2.5/site-packages/dbus/_dbus.py:251:
+DeprecationWarning: The dbus_bindings module is not public
+API and will go away soon.
+
+Most uses of dbus_bindings are applications catching
+the exception dbus.dbus_bindings.DBusException.
+You should use dbus.DBusException instead (this is
+compatible with all dbus-python versions since 0.40.2).
+
+If you need additional public API, please contact
+the maintainers via &lt;dbus@lists.freedesktop.org&gt;.
+
+ import dbus.dbus_bindings as m
+
+Warnings...
+
+/usr/lib/python2.5/site-packages/sugar/activity/activity.py:847:
+Parameter (ps) not used
+/usr/lib/python2.5/site-packages/sugar/activity/activity.py:992:
+Parameter (event) not used
+/usr/lib/python2.5/site-packages/sugar/activity/activity.py:992:
+Parameter (widget) not used
+/usr/lib/python2.5/site-packages/sugar/activity/activity.py:996:
+Parameter (widget) not used
+
+/usr/lib/python2.5/site-packages/sugar/graphics/window.py:157:
+No class attribute (_alert) found
+/usr/lib/python2.5/site-packages/sugar/graphics/window.py:164:
+Parameter (window) not used
+/usr/lib/python2.5/site-packages/sugar/graphics/window.py:188:
+Parameter (widget) not used
+/usr/lib/python2.5/site-packages/sugar/graphics/window.py:200:
+Parameter (event) not used
+/usr/lib/python2.5/site-packages/sugar/graphics/window.py:200:
+Parameter (widget) not used
+
+ReadEtextsActivity.py:62: Parameter (widget) not used
+
+4 errors suppressed, use -#/--limit to increase the number
+of errors displayed
+</pre>
+<p>PyChecker not only checks your code, it checks the code you import, including Sugar code.
+ <br/></p>
+<h3>PyLint
+</h3>
+<p> Here is PyLint, the most thorough of the three:
+</p>
+<pre><strong>pylint ReadEtextsActivity.py</strong>
+No config file found, using default configuration
+************* Module ReadEtextsActivity
+C:177: Line too long (96/80)
+C: 1: Missing docstring
+C: 27: Operator not preceded by a space
+page=0
+ ^
+C: 27: Invalid name "page" (should match
+(([A-Z_][A-Z0-9_]*)|(__.*__))$)
+C: 30:ReadEtextsActivity: Missing docstring
+C:174:ReadEtextsActivity.read_file: Invalid name "zf" (should
+match [a-z_][a-z0-9_]{2,30}$)
+W: 30:ReadEtextsActivity: Method 'write_file' is abstract
+in class 'Activity' but is not overridden
+R: 30:ReadEtextsActivity: Too many ancestors (12/7)
+W: 33:ReadEtextsActivity.__init__: Using the global statement
+R: 62:ReadEtextsActivity.keypress_cb:
+Too many return statements (7/6)
+C: 88:ReadEtextsActivity.page_previous: Missing docstring
+W: 89:ReadEtextsActivity.page_previous:
+Using the global statement
+C: 90:ReadEtextsActivity.page_previous:
+Operator not preceded by a space
+ page=page-1
+ ^
+C: 91:ReadEtextsActivity.page_previous:
+Operator not preceded by a space
+ if page &lt; 0: page=0
+ ^
+C: 91:ReadEtextsActivity.page_previous: More than one
+statement on a single line
+C: 96:ReadEtextsActivity.page_next: Missing docstring
+W: 97:ReadEtextsActivity.page_next: Using the global
+statement
+C: 98:ReadEtextsActivity.page_next: Operator not preceded
+by a space
+ page=page+1
+ ^
+C: 99:ReadEtextsActivity.page_next: More than one
+statement on a single line
+C:104:ReadEtextsActivity.font_decrease: Missing docstring
+C:112:ReadEtextsActivity.font_increase: Missing docstring
+C:118:ReadEtextsActivity.scroll_down: Missing docstring
+C:130:ReadEtextsActivity.scroll_up: Missing docstring
+C:142:ReadEtextsActivity.show_page: Missing docstring
+W:143:ReadEtextsActivity.show_page: Using global for
+'PAGE_SIZE' but no assigment is done
+W:143:ReadEtextsActivity.show_page: Using global for
+'current_word' but no assigment is done
+W:157:ReadEtextsActivity.save_extracted_file: Redefining
+name 'zipfile' from outer scope (line 21)
+C:163:ReadEtextsActivity.save_extracted_file: Invalid
+name "f" (should match [a-z_][a-z0-9_]{2,30}$)
+W:171:ReadEtextsActivity.read_file: Using global
+for 'PAGE_SIZE' but no assigment is done
+C:177:ReadEtextsActivity.read_file: Invalid name
+"currentFileName" (should match [a-z_][a-z0-9_]{2,30}$)
+C:179:ReadEtextsActivity.read_file: Invalid name
+"currentFileName" (should match [a-z_][a-z0-9_]{2,30}$)
+C:197:ReadEtextsActivity.make_new_filename: Missing
+docstring
+R:197:ReadEtextsActivity.make_new_filename: Method could be
+a function
+R: 30:ReadEtextsActivity: Too many public methods (350/20)
+W:174:ReadEtextsActivity.read_file: Attribute
+'zf' defined outside __init__
+W:181:ReadEtextsActivity.read_file: Attribute
+'etext_file' defined outside __init__
+W:175:ReadEtextsActivity.read_file: Attribute
+'book_files' defined outside __init__
+W:182:ReadEtextsActivity.read_file: Attribute
+'page_index' defined outside __init__
+
+<em>... A bunch of tables appear here ...</em>
+
+Global evaluation
+-----------------
+Your code has been rated at 7.52/10 (previous run: 7.52/10)
+</pre>
+<p>PyLint is the toughest on your code and your ego.&#160; It not only tells you about syntax errors, it tells you everything someone might find fault with in your code.&#160; This includes style issues that won't affect how your code runs but will affect how readable it is to other programmers.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>The Log Activity
+</h2>
+<p>When you start testing your Activities the Log Activity will be like your second home. It displays a list of log files in the left pane and when you select one it will display the contents of the file in the right pane.&#160; Every time you run your Activity a new log file is created for it, so you can compare the log you got this time with what you got on previous runs.&#160; The <strong>Edit</strong> toolbar is especially useful.&#160; It contains a button to show the log file with lines wrapped (which is not turned on by default but probably should be).&#160; It has another button to copy selections from the log to the clipboard, which will be handy if you want to show log messages to other developers.
+</p>
+<p>The <strong>Tools</strong> toolbar has a button to delete log files.&#160; I've never found a reason to use it.&#160; Log files go away on their own when you shut down sugar-emulator.
+ <br/></p>
+<p>&#160;<img alt="The Log Activity" src="static/ActivitiesGuideSugar-logactivity1-en.jpg" height="450" width="600"/></p>
+<p>
+</p>
+<p>Here is what the Log Activity looks like showing a syntax error in your code:&#160;
+</p>
+<p>
+</p>
+<p>&#160;<img alt="The Log Activity displaying a syntax error in Speak." src="static/ActivitiesGuideSugar-logactivity2-en.jpg" height="450" width="600"/></p>
+<h2>Logging
+</h2>
+<p>Without a doubt the oldest debugging technique there is would be the simple print statement.&#160; If you have a running program that misbehaves because of logic errors and you can't step through the code in a debugger to figure out what's happening you might print statements in your code.&#160; For instance, if you aren't sure that a method is ever getting executed you might put a statement like this as the first line of the method:
+</p>
+<pre> def my_method():
+ <strong>print 'my_method() begins'</strong>
+</pre>
+<p>You can include data in your print statements too.&#160; Suppose you need to know how many times a loop is run.&#160; You could do this:
+</p>
+<pre> while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + unicode(line,
+ 'iso-8859-1')
+ linecount = linecount + 1
+ <strong>print 'linecount=', linecount </strong>
+</pre>
+<p>The output of these print statements can be seen in the Log Activity.&#160; When you're finished debugging your program you would remove these statements.
+</p>
+<p> An old programming book I read once made the case for leaving the statements in the finished program.&#160; The authors felt that using these statements for debugging and them removing them is a bit like wearing a parachute when the plane is on the ground and taking it off when it's airborne.&#160; If the program is out in the world and has problems you might well wish you had those statements in the code so you could help the user and yourself figure out what's going on.&#160; On the other hand, print statements aren't free.&#160; They do take time to run and they fill up the log files with junk.&#160; What we need are print statements that you can turn on an off.
+</p>
+<p>The way you can do this is with Python Standard Logging.&#160; In the form used by most Activities it looks like this:
+</p>
+<pre> self._logger = logging.getLogger(
+ 'read-etexts-activity')
+</pre>
+<p>These statements would go in the <em>__init__()</em> method of your Activity.&#160; Every time you want to do a print() statement you would do this instead:
+</p>
+<pre> def _shared_cb(self, activity):
+ <strong>self._logger.debug('My activity was shared')</strong>
+ self.initiating = True
+ self._sharing_setup()
+
+ <strong>self._logger.debug(
+ 'This is my activity: making a tube...')</strong>
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ OfferDBusTube(SERVICE, {})
+
+ def _sharing_setup(self):
+ if self._shared_activity is None:
+ <strong>self._logger.error(
+ 'Failed to share or join activity')</strong>
+ return
+</pre>
+<p>Notice that there are two kinds of logging going on here: <strong>debug</strong> and <strong>error</strong>. &#160; These are error levels.&#160; Every statement has one, and they control which log statements are run and which are ignored.&#160; There are several levels of error logging, from lowest severity to highest:
+</p>
+<p>
+</p>
+<pre><code> self._logger.debug("debug message")
+ self._logger.info("info message")
+ self._logger.warn("warn message")
+ self._logger.error("error message")
+ self._logger.critical("critical message")
+</code></pre>
+<p>When you set the error level in your program to one of these values you get messages with that level and higher.&#160; You can set the level in your program code like this:
+</p>
+<p>
+</p>
+<pre><code> self._logger.setLevel(logging.DEBUG)
+</code></pre>
+<p>You can also set the logging level outside your program code using an <strong>environment variable</strong>.&#160; For instance, in Sugar .82 and lower you can start sugar-emulator like this:
+</p>
+<pre>SUGAR_LOGGER_LEVEL=debug sugar-emulator
+</pre>
+<p> You can do the same thing with .84 and later, but there is a more convenient way.&#160; Edit the file <strong>~/.sugar/debug</strong> and uncomment the line that sets the SUGAR_LOGGER_LEVEL.&#160; Whatever value you have for SUGAR_LOGGER_LEVEL in ~/.sugar/debug will override the one set by the environment variable, so either change the setting in the file or use the environment variable, but don't do both.
+ <br/></p>
+<h2>The Analyze Activity
+</h2>
+<p>Another Activity you may find yourself using at some point is <strong>Analyze</strong>.&#160; This is more likely to be used to debug Sugar itself than to debug your Activity.&#160; If, for instance, your collaboration test environment doesn't seem to be working this Activity might help you or someone else figure out why.
+</p>
+<p>I don't have a lot to say about this Activity here, but you should be aware that it exists.
+ <br/></p>
+<p><img alt="The Analyze Activity" src="static/ActivitiesGuideSugar-analyzeactivity_1-en.jpg" height="450" width="600"/><br/>&#160;
+</p>
+<p>
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch017_making-shared-activities.xhtml b/studio/static/doc/myosa/ch017_making-shared-activities.xhtml
new file mode 100644
index 0000000..a5a6f25
--- /dev/null
+++ b/studio/static/doc/myosa/ch017_making-shared-activities.xhtml
@@ -0,0 +1,2185 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Making Shared Activities
+</h1>
+<h2>Introduction
+</h2>
+<p>One of the distinctive features of Sugar is how many Activities support being used by more than one person at a time.&#160; More and more computers are being used as a communications medium.&#160; The latest computer games don't just pit the player against the computer; they create a world where players compete against each other.&#160; Websites like <em>Facebook</em> are increasingly popular because they allow people to interact with each other and even play games.&#160; It is only natural that educational software should support these kinds of interactions.
+</p>
+<p> I have a niece that is an enthusiastic member of the <em>Club Penguin</em> website created by Disney.&#160; When I gave her Sugar on a Stick Blueberry as an extra Christmas gift I demonstrated the Neighborhood view and told her that Sugar would make her whole computer like <em>Club Penguin</em>.&#160; She thought that was a pretty cool idea.&#160; I felt pretty cool saying it.
+ <br/></p>
+<h2>Running Sugar As More Than One User
+ <br/></h2>
+<p>Before you write any piece of software you need to give some thought to how you will test it.&#160; In the case of a shared Activity you might think you'd need more than one computer available to do testing, but those who designed Sugar did give some thought to testing shared Activities and gave us ways to test them using only one computer.&#160; These methods have been evolving so there are slight variations in how you test depending on the version of Sugar you're using.&#160; The first thing you have to know is how to run multiple copies of Sugar as different users.
+ <br/></p>
+<h3>Fedora 10 (Sugar .82)
+</h3>
+<p>In Sugar .82 there is a handy way to run multiple copies of sugar-emulator and have each copy be a different user, without having to be logged into your Linux box as more than one user.&#160; On the command line for each additional user you want add a SUGAR_PROFILE environment variable like this:
+</p>
+<pre>SUGAR_PROFILE=austen sugar-emulator</pre>
+<p>When you do this sugar-emulator will create a directory named austen under ~/.sugar to store profile information, etc.&#160; You will be prompted to enter a name and select colors for your icon.&#160; Every time you launch using the SUGAR_PROFILE of austen you will be this user.&#160; If you launch with no SUGAR_PROFILE you will be the regular user you set up before.
+ <br/></p>
+<h3>Fedora 11 (Sugar .84)
+</h3>
+<p>As handy as using SUGAR_PROFILE is the developers of Sugar decided it had limitations so with version .84 and later it no longer works.&#160; With .84 and later you need to create a second Linux user and run your sugar-emulators as two separate Linux users.&#160; In the GNOME environment there is an option <strong>Users and Groups</strong> in the <strong>Administration</strong> submenu of the <strong>System</strong> menu which will enable you to set up a second user.&#160; Before it comes up it will prompt you for the administrative password you created when you first set up Linux.
+</p>
+<p>Creating the second user is simple enough, but how do you go about being logged in as two different users at the same time?&#160; It's actually pretty simple.&#160; You need to open a terminal window and type this:
+</p>
+<pre>ssh -XY <em>jausten</em>@localhost</pre>
+<p>where "jausten" is the userid of the second user.&#160; You will be asked to verify that the computer at "localhost" should be trusted.&#160; Since "localhost" just means that you are using the network to connect to another account on the same computer it is safe to answer "yes".&#160; Then you will be prompted to enter her password, and from then on everything you do in that terminal window will be done as her.&#160; You can launch sugar-emulator from that terminal and the first time you do it will prompt you for a name and icon colors.
+ <br/></p>
+<h3>sugar-jhbuild
+</h3>
+<p>With sugar-jhbuild (the latest version of Sugar) things are a bit different again.&#160; You will use the method of logging in as multiple Linux users like you did in .84, but you won't get prompted for a name.&#160; Instead the name associated with the userid you're running under will be the name you'll use in Sugar.&#160; You won't be able to change it, but you will be able to choose your icon colors as before.
+</p>
+<p>You will need a separate install of sugar-jhbuild for each user.&#160; These additional installs will go quickly because you installed all the dependencies the first time.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Connecting To Other Users
+</h2>
+<p>Sugar uses software called <strong>Telepathy </strong>that implements an instant messaging protocol called <strong>XMPP</strong> (<em>Extended Messaging and Presence Protocol</em>).&#160; This protocol used to be called <strong>Jabber</strong>.&#160; In essence Telepathy lets you put an instant messaging client in your Activity.&#160; You can use this to send messages from user to user, execute methods remotely, and do file transfers.
+ <br/></p>
+<p>There are actually two ways that Sugar users can join together in a network:
+</p>
+<h3>Salut
+</h3>
+<p>If two computer users are connected to the same segment of a network they should be able to find each other and share Activities.&#160; If you have a home network where everyone uses the same router you can share with others on that network.&#160; This is sometimes called <em>Link-Local XMPP</em>. &#160; The Telepathy software that makes this possible is called <strong>Salut</strong>.
+</p>
+<p>The XO laptop has special hardware and software to support <em>Mesh Networking</em>, where XO laptops that are near each other can automatically start networking with each other without needing a router.&#160; As far as Sugar is concerned, it doesn't matter what kind of network you have.&#160; Wired or wireless, Mesh or not, they all work.
+ <br/></p>
+<h3>Jabber Server
+</h3>
+<p>The other way to connect to other users is by going through a Jabber Server.&#160; The advantage of using a Jabber server is you can contact and share Activities with people outside your own network.&#160; These people might even be on the other side of the world.&#160; Jabber allows Activities in different networks to connect when both networks are protected by firewalls.&#160; The part of Telepathy that works with a Jabber server is called <strong>Gabble</strong>.
+ <br/></p>
+<p>Generally you should use Salut for testing if at all possible. This simplifies testing and doesn't use up resources on a Jabber server.
+ <br/></p>
+<p>It does not matter if your Activity connects to others using Gabble or Salut.&#160; In fact, the Activity has no idea which it is using.&#160; Those details are hidden from the Activity by Telepathy.&#160; Any Activity that works with Salut will work with Gabble and vice versa.
+</p>
+<p>To set up sugar-emulator to use Salut go to the Sugar control panel:
+ <br/></p>
+<p> <img alt="collab1_1.jpg" src="static/ActivitiesGuideSugar-collab1_1-en.jpg" width="584" height="569"/></p>
+<p>In Sugar .82 this menu option is <strong>Control Panel</strong>.&#160; In later versions it is <strong>My Settings</strong>.
+</p>
+<p><img alt="collab2_1.jpg" src="static/ActivitiesGuideSugar-collab2_1-en.jpg" width="600" height="505"/></p>
+<p>Click on the <strong>Network</strong> icon.
+</p>
+<p><img alt="collab3_1.jpg" src="static/ActivitiesGuideSugar-collab3_1-en.jpg" width="538" height="517"/></p>
+<p>The <strong>Server</strong> field in this screen should be empty to use Salut.&#160; You can use the backspace key to remove any entry there.
+</p>
+<p>You will need to follow these steps for every Sugar user that will take part in your test.
+</p>
+<p>If for some reason you wish to test your Activity using a Jabber server the OLPC Wiki maintains a list of publicly available servers at <a href="http://wiki.laptop.org/go/Community_Jabber_Servers">http://wiki.laptop.org/go/Community_Jabber_Servers</a>.
+</p>
+<p> Once you have either Salut or a Jabber server set up in both instances of Sugar that you are running you should look at the Neighborhood view of both to see if they can detect each other, and perhaps try out the <strong>Chat</strong> Activity between the two.&#160; If you have that working you're ready to try programming a shared Activity.
+</p>
+<h2>The MiniChat Activity
+</h2>
+<p>Just as we took the <strong>Read Etexts</strong> Activity and stripped it down to the basics we're going to do the same to the <strong>Chat</strong> Activity to create a new Activity called <strong>MiniChat</strong>.&#160; The real Chat Activity has a number of features that we don't need to demonstrate shared Activity messaging:
+</p>
+<ul><li>It has the ability to load its source code into <strong>Pippy</strong> for viewing.&#160; This was a feature that all Activities on the XO were supposed to have, but Chat is one of the few that implemented it.&#160; Personally, if I want to see an Activity's code I prefer to go to <a href="http://git.sugarlabs.org">git.sugarlabs.org</a>&#160; where I can see old versions of the code as well as the latest.</li>
+ <li>Chat can connect one to one with a conventional <strong>XMPP</strong> client.&#160; This may be useful for Chat but would not be needed or desirable for most shared Activities.</li>
+ <li>If you include a URL in a Chat message the user interface enables you to click on the URL make a Journal entry for that URL.&#160; You can then use the Journal to open it with the <strong>Browse</strong> Activity.&#160; (This is necessary because activities cannot launch each other).&#160; Pretty cool, but not needed to demonstrate how to make a shared Activity.</li>
+ <li>The chat session is stored in the Journal.&#160; When you resume a Chat entry from the Journal it restores the messages from your previous chat session into the user interface.&#160; We already know how to save things to the Journal and restore things from the Journal, so MiniChat won't do this.</li>
+</ul><p>The resulting code is about half as long as the original.&#160; I made a few other changes too:
+</p>
+<ul><li>The text entry field is above the chat messages, instead of below.&#160; This makes it easier to do partial screenshots of the Activity in action.</li>
+ <li>I removed the new style toolbar and added an old style toolbar, so I could test it in Fedora 10 and 11 which don't support the new toolbars.
+ <br/></li>
+ <li>I took the class <span class="TypeName"><strong>TextChannelWrapper</strong> and put it in its own file.&#160; I did this because the class looked like it might be useful for other projects.</span></li>
+</ul><p><span class="TypeName"/>The code and all supporting files for <strong>MiniChat</strong> are in the <strong>MiniChat</strong> directory of the Git repository.&#160; You'll need to run
+</p>
+<pre>./setup.py dev</pre>
+<p><span class="TypeName">on the project to make it ready to test.&#160; The <strong>activity.info</strong> looks like this:</span>
+</p>
+<pre>[Activity]
+name = Mini Chat
+service_name = net.flossmanuals.MiniChat
+icon = chat
+exec = sugar-activity minichat.MiniChat
+show_launcher = yes
+activity_version = 1
+license = GPLv2+
+</pre>
+<p>Here is the code for <strong>textchannel.py</strong>:
+</p>
+<pre>import logging
+
+from telepathy.client import Connection, Channel
+from telepathy.interfaces import (
+ CHANNEL_INTERFACE, CHANNEL_INTERFACE_GROUP,
+ CHANNEL_TYPE_TEXT, CONN_INTERFACE_ALIASING)
+from telepathy.constants import (
+ CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES,
+ CHANNEL_TEXT_MESSAGE_TYPE_NORMAL)
+
+class TextChannelWrapper(object):
+ """Wrap a telepathy Text Channel to make
+ usage simpler."""
+ def __init__(self, text_chan, conn):
+ """Connect to the text channel"""
+ self._activity_cb = None
+ self._activity_close_cb = None
+ self._text_chan = text_chan
+ self._conn = conn
+ self._logger = logging.getLogger(
+ 'minichat-activity.TextChannelWrapper')
+ self._signal_matches = []
+ m = self._text_chan[CHANNEL_INTERFACE].\
+ connect_to_signal(
+ 'Closed', self._closed_cb)
+ self._signal_matches.append(m)
+
+ def send(self, text):
+ """Send text over the Telepathy text channel."""
+ # XXX Implement CHANNEL_TEXT_MESSAGE_TYPE_ACTION
+ if self._text_chan is not None:
+ self._text_chan[CHANNEL_TYPE_TEXT].Send(
+ CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, text)
+
+ def close(self):
+ """Close the text channel."""
+ self._logger.debug('Closing text channel')
+ try:
+ self._text_chan[CHANNEL_INTERFACE].Close()
+ except:
+ self._logger.debug('Channel disappeared!')
+ self._closed_cb()
+
+ def _closed_cb(self):
+ """Clean up text channel."""
+ self._logger.debug('Text channel closed.')
+ for match in self._signal_matches:
+ match.remove()
+ self._signal_matches = []
+ self._text_chan = None
+ if self._activity_close_cb is not None:
+ self._activity_close_cb()
+
+ def set_received_callback(self, callback):
+ """Connect the function callback to the signal.
+
+ callback -- callback function taking buddy
+ and text args
+ """
+ if self._text_chan is None:
+ return
+ self._activity_cb = callback
+ m = self._text_chan[CHANNEL_TYPE_TEXT].\
+ connect_to_signal(
+ 'Received', self._received_cb)
+ self._signal_matches.append(m)
+
+ def handle_pending_messages(self):
+ """Get pending messages and show them as
+ received."""
+ for id, timestamp, sender, type, flags, text \
+ in self._text_chan[
+ CHANNEL_TYPE_TEXT].ListPendingMessages(
+ False):
+ self._received_cb(id, timestamp, sender,
+ type, flags, text)
+
+ def _received_cb(self, id, timestamp, sender,
+ type, flags, text):
+ """Handle received text from the text channel.
+
+ Converts sender to a Buddy.
+ Calls self._activity_cb which is a callback
+ to the activity.
+ """
+ if self._activity_cb:
+ buddy = self._get_buddy(sender)
+ self._activity_cb(buddy, text)
+ self._text_chan[
+ CHANNEL_TYPE_TEXT].
+ AcknowledgePendingMessages([id])
+ else:
+ self._logger.debug(
+ 'Throwing received message on the floor'
+ ' since there is no callback connected. See '
+ 'set_received_callback')
+
+ def set_closed_callback(self, callback):
+ """Connect a callback for when the text channel
+ is closed.
+
+ callback -- callback function taking no args
+
+ """
+ self._activity_close_cb = callback
+
+ def _get_buddy(self, cs_handle):
+ """Get a Buddy from a (possibly channel-specific)
+ handle."""
+ # XXX This will be made redundant once Presence
+ # Service provides buddy resolution
+ from sugar.presence import presenceservice
+ # Get the Presence Service
+ pservice = presenceservice.get_instance()
+ # Get the Telepathy Connection
+ tp_name, tp_path = \
+ pservice.get_preferred_connection()
+ conn = Connection(tp_name, tp_path)
+ group = self._text_chan[CHANNEL_INTERFACE_GROUP]
+ my_csh = group.GetSelfHandle()
+ if my_csh == cs_handle:
+ handle = conn.GetSelfHandle()
+ elif group.GetGroupFlags() &amp; \
+ CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES:
+ handle = group.GetHandleOwners([cs_handle])[0]
+ else:
+ handle = cs_handle
+
+ # XXX: deal with failure to get the handle owner
+ assert handle != 0
+
+ return pservice.get_buddy_by_telepathy_handle(
+ tp_name, tp_path, handle)
+</pre>
+<p>Here is the code for <strong>minichat.py</strong>:
+</p>
+<pre>from gettext import gettext as _
+import hippo
+import gtk
+import pango
+import logging
+from sugar.activity.activity import (Activity,
+ ActivityToolbox, SCOPE_PRIVATE)
+from sugar.graphics.alert import NotifyAlert
+from sugar.graphics.style import (Color, COLOR_BLACK,
+ COLOR_WHITE, COLOR_BUTTON_GREY, FONT_BOLD,
+ FONT_NORMAL)
+from sugar.graphics.roundbox import CanvasRoundBox
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.palette import Palette, CanvasInvoker
+
+from textchannel import TextChannelWrapper
+
+logger = logging.getLogger('minichat-activity')
+
+class MiniChat(Activity):
+ def __init__(self, handle):
+ Activity.__init__(self, handle)
+
+ root = self.make_root()
+ self.set_canvas(root)
+ root.show_all()
+ self.entry.grab_focus()
+
+ toolbox = ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.keep.props.visible = False
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ self.owner = self._pservice.get_owner()
+ # Auto vs manual scrolling:
+ self._scroll_auto = True
+ self._scroll_value = 0.0
+ # Track last message, to combine several
+ # messages:
+ self._last_msg = None
+ self._last_msg_sender = None
+ self.text_channel = None
+
+ if self._shared_activity:
+ # we are joining the activity
+ self.connect('joined', self._joined_cb)
+ if self.get_shared():
+ # we have already joined
+ self._joined_cb()
+ else:
+ # we are creating the activity
+ if not self.metadata or self.metadata.get(
+ 'share-scope',
+ SCOPE_PRIVATE) == SCOPE_PRIVATE:
+ # if we are in private session
+ self._alert(_('Off-line'),
+ _('Share, or invite someone.'))
+ self.connect('shared', self._shared_cb)
+
+ def _shared_cb(self, activity):
+ logger.debug('Chat was shared')
+ self._setup()
+
+ def _setup(self):
+ self.text_channel = TextChannelWrapper(
+ self._shared_activity.telepathy_text_chan,
+ self._shared_activity.telepathy_conn)
+ self.text_channel.set_received_callback(
+ self._received_cb)
+ self._alert(_('On-line'), _('Connected'))
+ self._shared_activity.connect('buddy-joined',
+ self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left',
+ self._buddy_left_cb)
+ self.entry.set_sensitive(True)
+ self.entry.grab_focus()
+
+ def _joined_cb(self, activity):
+ """Joined a shared activity."""
+ if not self._shared_activity:
+ return
+ logger.debug('Joined a shared chat')
+ for buddy in \
+ self._shared_activity.get_joined_buddies():
+ self._buddy_already_exists(buddy)
+ self._setup()
+
+ def _received_cb(self, buddy, text):
+ """Show message that was received."""
+ if buddy:
+ nick = buddy.props.nick
+ else:
+ nick = '???'
+ logger.debug(
+ 'Received message from %s: %s', nick, text)
+ self.add_text(buddy, text)
+
+ def _alert(self, title, text=None):
+ alert = NotifyAlert(timeout=5)
+ alert.props.title = title
+ alert.props.msg = text
+ self.add_alert(alert)
+ alert.connect('response', self._alert_cancel_cb)
+ alert.show()
+
+ def _alert_cancel_cb(self, alert, response_id):
+ self.remove_alert(alert)
+
+ def _buddy_joined_cb (self, activity, buddy):
+ """Show a buddy who joined"""
+ if buddy == self.owner:
+ return
+ if buddy:
+ nick = buddy.props.nick
+ else:
+ nick = '???'
+ self.add_text(buddy, buddy.props.nick+'
+ '+_('joined the chat'),
+ status_message=True)
+
+ def _buddy_left_cb (self, activity, buddy):
+ """Show a buddy who joined"""
+ if buddy == self.owner:
+ return
+ if buddy:
+ nick = buddy.props.nick
+ else:
+ nick = '???'
+ self.add_text(buddy, buddy.props.nick+'
+ '+_('left the chat'),
+ status_message=True)
+
+ def _buddy_already_exists(self, buddy):
+ """Show a buddy already in the chat."""
+ if buddy == self.owner:
+ return
+ if buddy:
+ nick = buddy.props.nick
+ else:
+ nick = '???'
+ self.add_text(buddy, buddy.props.nick+
+ ' '+_('is here'),
+ status_message=True)
+
+ def make_root(self):
+ conversation = hippo.CanvasBox(
+ spacing=0,
+ background_color=COLOR_WHITE.get_int())
+ self.conversation = conversation
+
+ entry = gtk.Entry()
+ entry.modify_bg(gtk.STATE_INSENSITIVE,
+ COLOR_WHITE.get_gdk_color())
+ entry.modify_base(gtk.STATE_INSENSITIVE,
+ COLOR_WHITE.get_gdk_color())
+ entry.set_sensitive(False)
+ entry.connect('activate',
+ self.entry_activate_cb)
+ self.entry = entry
+
+ hbox = gtk.HBox()
+ hbox.add(entry)
+
+ sw = hippo.CanvasScrollbars()
+ sw.set_policy(hippo.ORIENTATION_HORIZONTAL,
+ hippo.SCROLLBAR_NEVER)
+ sw.set_root(conversation)
+ self.scrolled_window = sw
+
+ vadj = self.scrolled_window.props.widget.\
+ get_vadjustment()
+ vadj.connect('changed', self.rescroll)
+ vadj.connect('value-changed',
+ self.scroll_value_changed_cb)
+
+ canvas = hippo.Canvas()
+ canvas.set_root(sw)
+
+ box = gtk.VBox(homogeneous=False)
+ box.pack_start(hbox, expand=False)
+ box.pack_start(canvas)
+
+ return box
+
+ def rescroll(self, adj, scroll=None):
+ """Scroll the chat window to the bottom"""
+ if self._scroll_auto:
+ adj.set_value(adj.upper-adj.page_size)
+ self._scroll_value = adj.get_value()
+
+ def scroll_value_changed_cb(self, adj, scroll=None):
+ """Turn auto scrolling on or off.
+
+ If the user scrolled up, turn it off.
+ If the user scrolled to the bottom, turn it back on.
+ """
+ if adj.get_value() &lt; self._scroll_value:
+ self._scroll_auto = False
+ elif adj.get_value() == adj.upper-adj.page_size:
+ self._scroll_auto = True
+
+ def add_text(self, buddy, text, status_message=False):
+ """Display text on screen, with name and colors.
+
+ buddy -- buddy object
+ text -- string, what the buddy said
+ status_message -- boolean
+ False: show what buddy said
+ True: show what buddy did
+
+ hippo layout:
+ .------------- rb ---------------.
+ | +name_vbox+ +----msg_vbox----+ |
+ | | | | | |
+ | | nick: | | +--msg_hbox--+ | |
+ | | | | | text | | |
+ | +---------+ | +------------+ | |
+ | | | |
+ | | +--msg_hbox--+ | |
+ | | | text | | |
+ | | +------------+ | |
+ | +----------------+ |
+ `--------------------------------'
+ """
+ if buddy:
+ nick = buddy.props.nick
+ color = buddy.props.color
+ try:
+ color_stroke_html, color_fill_html = \
+ color.split(',')
+ except ValueError:
+ color_stroke_html, color_fill_html = (
+ '#000000', '#888888')
+ # Select text color based on fill color:
+ color_fill_rgba = Color(
+ color_fill_html).get_rgba()
+ color_fill_gray = (color_fill_rgba[0] +
+ color_fill_rgba[1] +
+ color_fill_rgba[2])/3
+ color_stroke = Color(
+ color_stroke_html).get_int()
+ color_fill = Color(color_fill_html).get_int()
+ if color_fill_gray &lt; 0.5:
+ text_color = COLOR_WHITE.get_int()
+ else:
+ text_color = COLOR_BLACK.get_int()
+ else:
+ nick = '???'
+ # XXX: should be '' but leave for debugging
+ color_stroke = COLOR_BLACK.get_int()
+ color_fill = COLOR_WHITE.get_int()
+ text_color = COLOR_BLACK.get_int()
+ color = '#000000,#FFFFFF'
+
+ # Check for Right-To-Left languages:
+ if pango.find_base_dir(nick, -1) == \
+ pango.DIRECTION_RTL:
+ lang_rtl = True
+ else:
+ lang_rtl = False
+
+ # Check if new message box or add text to previous:
+ new_msg = True
+ if self._last_msg_sender:
+ if not status_message:
+ if buddy == self._last_msg_sender:
+ # Add text to previous message
+ new_msg = False
+
+ if not new_msg:
+ rb = self._last_msg
+ msg_vbox = rb.get_children()[1]
+ msg_hbox = hippo.CanvasBox(
+ orientation=hippo.ORIENTATION_HORIZONTAL)
+ msg_vbox.append(msg_hbox)
+ else:
+ rb = CanvasRoundBox(
+ background_color=color_fill,
+ border_color=color_stroke,
+ padding=4)
+ rb.props.border_color = color_stroke
+ self._last_msg = rb
+ self._last_msg_sender = buddy
+ if not status_message:
+ name = hippo.CanvasText(text=nick+': ',
+ color=text_color,
+ font_desc=FONT_BOLD.get_pango_desc())
+ name_vbox = hippo.CanvasBox(
+ orientation=hippo.ORIENTATION_VERTICAL)
+ name_vbox.append(name)
+ rb.append(name_vbox)
+ msg_vbox = hippo.CanvasBox(
+ orientation=hippo.ORIENTATION_VERTICAL)
+ rb.append(msg_vbox)
+ msg_hbox = hippo.CanvasBox(
+ orientation=hippo.ORIENTATION_HORIZONTAL)
+ msg_vbox.append(msg_hbox)
+
+ if status_message:
+ self._last_msg_sender = None
+
+ if text:
+ message = hippo.CanvasText(
+ text=text,
+ size_mode=hippo.CANVAS_SIZE_WRAP_WORD,
+ color=text_color,
+ font_desc=FONT_NORMAL.get_pango_desc(),
+ xalign=hippo.ALIGNMENT_START)
+ msg_hbox.append(message)
+
+ # Order of boxes for RTL languages:
+ if lang_rtl:
+ msg_hbox.reverse()
+ if new_msg:
+ rb.reverse()
+
+ if new_msg:
+ box = hippo.CanvasBox(padding=2)
+ box.append(rb)
+ self.conversation.append(box)
+
+ def entry_activate_cb(self, entry):
+ text = entry.props.text
+ logger.debug('Entry: %s' % text)
+ if text:
+ self.add_text(self.owner, text)
+ entry.props.text = ''
+ if self.text_channel:
+ self.text_channel.send(text)
+ else:
+ logger.debug(
+ 'Tried to send message but text '
+ 'channel not connected.')
+</pre>
+<p>And this is what the Activity looks like in action:
+</p>
+<p> <img alt="MiniChat in action" src="static/ActivitiesGuideSugar-collab4-en.jpg" width="600" height="373"/></p>
+<p>Try launching more than one copy of sugar-emulator, with this Activity installed in each.&#160; If you're using Fedora 10 and SUGAR_PROFILE the Activity does not need to be installed more than once, but if you're using a later version of Sugar that requires separate Linux userids for each instance you'll need to maintain separate copies of the code for each user.&#160; In your own projects using a central Git repository at <a href="http://git.sugarlabs.org">git.sugarlabs.org</a>&#160; will make this easy.&#160; You just do a git push to copy your changes to the central repository and a git pull to copy them to your second userid.&#160; The second userid can use the public URL.&#160; There's no need to set up SSH for any user other than the primary one.
+</p>
+<p>You may have read somewhere that you can install an Activity on one machine and share that Activity with another that does not have the activity installed.&#160; In such a case the second machine would get a copy of the Activity from the first machine and install it automatically.&#160; You may have also read that if two users of a shared Activity have different versions of that Activity then the one who has the newer version will automatically update the older.&#160; Neither statement is true now or is likely to be true in the near future.&#160; These ideas are discussed on the mailing lists from time to time but there are practical difficulties to overcome before anything like that could work, mostly having to do with security.&#160; For now both users of a shared Activity must have the Activity installed.&#160; On the other hand, depending on how the Activity is written two different versions of an Activity may be able to communicate with one another.&#160; If the messages they exchange are in the same format there should be no problem.
+ <br/></p>
+<p>Once you have both instances of sugar-emulator going you can launch MiniChat on one and invite the second user to Join the Chat session.&#160; You can do both with the Neighborhood panes of each instance.&#160; Making the invitation looks like this:
+</p>
+<p><img alt="Making the invitation" src="static/ActivitiesGuideSugar-collab5-en.jpg" width="391" height="246"/></p>
+<p>Accepting it looks like this:
+</p>
+<p><img alt="collab6.jpg" src="static/ActivitiesGuideSugar-collab6-en.jpg" width="390" height="215"/></p>
+<p>After you've played with <strong>MiniChat</strong> for awhile come back and we'll discuss the secrets of using Telepathy to create a shared Activity.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Know who Your Buddies Are
+ <br/></h2>
+<p> XMPP, as we said before, is the <strong>Extended Messaging and Presence Protocol</strong>.&#160;&#8286; <strong>Presence</strong> is just what it sounds like; it handles letting you know who is available to share your Activity, as well as what other Activities are available to share.&#160; There are two ways to share your Activity.&#160; The first one is when you change the <strong>Share with:</strong> pulldown on the standard toolbar so it reads <strong>My Neighborhood</strong> instead of <strong>Private</strong>.&#160; That means anyone on the network can share your Activity.&#160; The other way to share is to go to the Neighborhood view and invite someone specific to share.&#160; The person getting the invitation has no idea of the invitation was specifically for him or broadcast to the Neighborhood.&#160; The technical term for persons sharing your Activity is <strong>Buddies</strong>.&#160; The place where Buddies meet and collaborate is called an <strong>MUC</strong> or <strong>Multi User Chatroom</strong>.
+ <br/></p>
+<p>The code used by our Activity for inviting Buddies and joining the Activity as a Buddy is in the <em>__init__() </em>method:
+</p>
+<pre> <strong>if self._shared_activity:</strong>
+ # we are joining the activity
+ self.connect('joined', self._joined_cb)
+ if self.get_shared():
+ # we have already joined
+ self._joined_cb()
+ else:
+ # we are creating the activity
+ if not self.metadata or self.metadata.get(
+ 'share-scope',
+ SCOPE_PRIVATE) == SCOPE_PRIVATE:
+ # if we are in private session
+ self._alert(_('Off-line'),
+ _('Share, or invite someone.'))
+ self.connect('shared', self._shared_cb)
+
+ def _shared_cb(self, activity):
+ logger.debug('Chat was shared')
+ self._setup()
+
+ def _joined_cb(self, activity):
+ """Joined a shared activity."""
+ if not self._shared_activity:
+ return
+ logger.debug('Joined a shared chat')
+ for buddy in \
+ self._shared_activity.get_joined_buddies():
+ self._buddy_already_exists(buddy)
+ self._setup()
+
+ def _setup(self):
+ self.text_channel = TextChannelWrapper(
+ self._shared_activity.telepathy_text_chan,
+ self._shared_activity.telepathy_conn)
+ self.text_channel.set_received_callback(
+ self._received_cb)
+ self._alert(_('On-line'), _('Connected'))
+ self._shared_activity.connect('buddy-joined',
+ self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left',
+ self._buddy_left_cb)
+ self.entry.set_sensitive(True)
+ self.entry.grab_focus()
+</pre>
+<p>There are two ways to launch an Activity: as the first user of an Activity or by joining an existing Activity.&#160; The first line above in <strong>bold</strong> determines whether we are joining or are the first user of the Activity.&#160; If so we ask for the <em>_joined_cb()</em> method to be run when the 'joined' event occurs. This method gets a buddy list from the <em>_shared_activity</em> object and creates messages in the user interface informing the user that these buddies are already in the chat room.&#160; Then it runs the <em>_setup()</em> method.
+ <br/></p>
+<p>If we are not joining an existing Activity then we check to see if we are currently sharing the Activity with anyone.&#160; If we aren't we pop up a message telling the user to invite someone to chat.&#160; We also request that when the 'shared' even happens the <em>_shared_cb()</em> method should run.&#160; This method just runs the <em>_setup()</em> method.
+</p>
+<p>The <em>_setup()</em> method creates a <strong>TextChannelWrapper</strong> object using the code in <strong>textchannel.py</strong>.&#160; It also tells the _shared_activity object that it wants some callback methods run when new buddies join the Activity and when existing buddies leave the Activity.&#160; Everything you need to know about your buddies can be found in the code above, except how to send messages to them.&#160; For that we use the <strong>Text Channel</strong>.&#160; There is no need to learn about the Text Channel in great detail because the TextChannelWrapper class does everything you'll ever need to do with the TextChannel and hides the details from you.
+</p>
+<pre> def entry_activate_cb(self, entry):
+ text = entry.props.text
+ logger.debug('Entry: %s' % text)
+ if text:
+ self.add_text(self.owner, text)
+ entry.props.text = ''
+ if self.text_channel:
+ self.text_channel.send(text)
+ else:
+ logger.debug(
+ 'Tried to send message but text '
+ 'channel not connected.')
+</pre>
+<p>The <em>add_text()</em> method is of interest.&#160; It takes the owner of the message and figures out what colors belong to that owner and displays the message in those colors.&#160; In the case of messages sent by the Activity it gets the owner like this in the <em>__init__()</em> method:
+</p>
+<p>
+</p>
+<pre>&#160;&#160;&#160;&#160;&#160;&#160;&#160; self.owner = self._pservice.get_owner()</pre>
+<p>In the case of received messages it gets the buddy the message came from:
+</p>
+<pre> def _received_cb(self, buddy, text):
+ """Show message that was received."""
+ if buddy:
+ nick = buddy.props.nick
+ else:
+ nick = '???'
+ logger.debug('Received message from %s: %s',
+ nick, text)
+ self.add_text(buddy, text)
+</pre>
+<p>But what if we want to do more than just send text messages back and forth?&#160; What do we use for that?
+ <br/></p>
+<h2>It's A Series Of Tubes!
+</h2>
+<p> No, not the Internet.&#160; Telepathy has a concept called <strong>Tubes</strong> which describes the way instances of an Activity can communicate together.&#160; What Telepathy does is take the Text Channel and build Tubes on top of it.&#160; There are two kinds of Tubes:
+</p>
+<ul><li>D-Bus Tubes</li>
+ <li>Stream Tubes</li>
+</ul><p>A <strong>D-Bus Tube</strong> is used to enable one instance of an Activity to call methods in the Buddy instances of the Activity.&#160; A <strong>Stream Tube</strong> is used for sending data over <strong>Sockets</strong>, for instance for copying a file from one instance of an Activity to another.&#160; A Socket is a way of communicating over a network using Internet Protocols.&#160; For instance the HTTP protocol used by the World Wide Web is implemented with Sockets.&#160; In the next example we'll use HTTP to transfer books from one instance of <strong>Read Etexts III</strong> to another.
+</p>
+<h2>Read Etexts III, Now with Book Sharing!
+ <br/></h2>
+<p> The Git repository with the code samples for this book has a file named <strong>ReadEtextsActivity3.py</strong> in the <strong>Making_Shared_Activities</strong> directory which looks like this:
+</p>
+<pre>import sys
+import os
+import logging
+import tempfile
+import time
+import zipfile
+import pygtk
+import gtk
+import pango
+import dbus
+import gobject
+import telepathy
+from sugar.activity import activity
+from sugar.graphics import style
+from sugar import network
+from sugar.datastore import datastore
+from sugar.graphics.alert import NotifyAlert
+from toolbar import ReadToolbar, ViewToolbar
+from gettext import gettext as _
+
+page=0
+PAGE_SIZE = 45
+TOOLBAR_READ = 2
+
+logger = logging.getLogger('read-etexts2-activity')
+
+class ReadHTTPRequestHandler(
+ network.ChunkedGlibHTTPRequestHandler):
+ """HTTP Request Handler for transferring document
+ while collaborating.
+
+ RequestHandler class that integrates with Glib
+ mainloop. It writes the specified file to the
+ client in chunks, returning control to the
+ mainloop between chunks.
+
+ """
+ def translate_path(self, path):
+ """Return the filepath to the shared document."""
+ return self.server.filepath
+
+
+class ReadHTTPServer(network.GlibTCPServer):
+ """HTTP Server for transferring document while
+ collaborating."""
+ def __init__(self, server_address, filepath):
+ """Set up the GlibTCPServer with the
+ ReadHTTPRequestHandler.
+
+ filepath -- path to shared document to be served.
+ """
+ self.filepath = filepath
+ network.GlibTCPServer.__init__(self,
+ server_address, ReadHTTPRequestHandler)
+
+
+class ReadURLDownloader(network.GlibURLDownloader):
+ """URLDownloader that provides content-length and
+ content-type."""
+
+ def get_content_length(self):
+ """Return the content-length of the download."""
+ if self._info is not None:
+ return int(self._info.headers.get(
+ 'Content-Length'))
+
+ def get_content_type(self):
+ """Return the content-type of the download."""
+ if self._info is not None:
+ return self._info.headers.get('Content-type')
+ return None
+
+READ_STREAM_SERVICE = 'read-etexts-activity-http'
+
+class ReadEtextsActivity(activity.Activity):
+ def __init__(self, handle):
+ "The entry point to the Activity"
+ global page
+ activity.Activity.__init__(self, handle)
+
+ self.fileserver = None
+ self.object_id = handle.object_id
+
+ toolbox = activity.ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.keep.props.visible = False
+
+ self.edit_toolbar = activity.EditToolbar()
+ self.edit_toolbar.undo.props.visible = False
+ self.edit_toolbar.redo.props.visible = False
+ self.edit_toolbar.separator.props.visible = False
+ self.edit_toolbar.copy.set_sensitive(False)
+ self.edit_toolbar.copy.connect('clicked',
+ self.edit_toolbar_copy_cb)
+ self.edit_toolbar.paste.props.visible = False
+ toolbox.add_toolbar(_('Edit'), self.edit_toolbar)
+ self.edit_toolbar.show()
+
+ self.read_toolbar = ReadToolbar()
+ toolbox.add_toolbar(_('Read'), self.read_toolbar)
+ self.read_toolbar.back.connect('clicked',
+ self.go_back_cb)
+ self.read_toolbar.forward.connect('clicked',
+ self.go_forward_cb)
+ self.read_toolbar.num_page_entry.connect('activate',
+ self.num_page_entry_activate_cb)
+ self.read_toolbar.show()
+
+ self.view_toolbar = ViewToolbar()
+ toolbox.add_toolbar(_('View'), self.view_toolbar)
+ self.view_toolbar.connect('go-fullscreen',
+ self.view_toolbar_go_fullscreen_cb)
+ self.view_toolbar.zoom_in.connect('clicked',
+ self.zoom_in_cb)
+ self.view_toolbar.zoom_out.connect('clicked',
+ self.zoom_out_cb)
+ self.view_toolbar.show()
+
+ self.set_toolbox(toolbox)
+ toolbox.show()
+ self.scrolled_window = gtk.ScrolledWindow()
+ self.scrolled_window.set_policy(gtk.POLICY_NEVER,
+ gtk.POLICY_AUTOMATIC)
+ self.scrolled_window.props.shadow_type = \
+ gtk.SHADOW_NONE
+
+ self.textview = gtk.TextView()
+ self.textview.set_editable(False)
+ self.textview.set_cursor_visible(False)
+ self.textview.set_left_margin(50)
+ self.textview.connect("key_press_event",
+ self.keypress_cb)
+
+ self.progressbar = gtk.ProgressBar()
+ self.progressbar.set_orientation(
+ gtk.PROGRESS_LEFT_TO_RIGHT)
+ self.progressbar.set_fraction(0.0)
+
+ self.scrolled_window.add(self.textview)
+ self.textview.show()
+ self.scrolled_window.show()
+
+ vbox = gtk.VBox()
+ vbox.pack_start(self.progressbar, False,
+ False, 10)
+ vbox.pack_start(self.scrolled_window)
+ self.set_canvas(vbox)
+ vbox.show()
+
+ page = 0
+ self.clipboard = gtk.Clipboard(
+ display=gtk.gdk.display_get_default(),
+ selection="CLIPBOARD")
+ self.textview.grab_focus()
+ self.font_desc = pango.FontDescription("sans %d" %
+ style.zoom(10))
+ self.textview.modify_font(self.font_desc)
+
+ buffer = self.textview.get_buffer()
+ self.markset_id = buffer.connect("mark-set",
+ self.mark_set_cb)
+
+ self.toolbox.set_current_toolbar(TOOLBAR_READ)
+ self.unused_download_tubes = set()
+ self.want_document = True
+ self.download_content_length = 0
+ self.download_content_type = None
+ # Status of temp file used for write_file:
+ self.tempfile = None
+ self.close_requested = False
+ self.connect("shared", self.shared_cb)
+
+ self.is_received_document = False
+
+ if self._shared_activity and \
+ handle.object_id == None:
+ # We're joining, and we don't already have
+ # the document.
+ if self.get_shared():
+ # Already joined for some reason, just get the
+ # document
+ self.joined_cb(self)
+ else:
+ # Wait for a successful join before trying to get
+ # the document
+ self.connect("joined", self.joined_cb)
+
+ def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the arrow keys"
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ print keyname
+ if keyname == 'plus':
+ self.font_increase()
+ return True
+ if keyname == 'minus':
+ self.font_decrease()
+ return True
+ if keyname == 'Page_Up' :
+ self.page_previous()
+ return True
+ if keyname == 'Page_Down':
+ self.page_next()
+ return True
+ if keyname == 'Up' or keyname == 'KP_Up' \
+ or keyname == 'KP_Left':
+ self.scroll_up()
+ return True
+ if keyname == 'Down' or keyname == 'KP_Down' \
+ or keyname == 'KP_Right':
+ self.scroll_down()
+ return True
+ return False
+
+ def num_page_entry_activate_cb(self, entry):
+ global page
+ if entry.props.text:
+ new_page = int(entry.props.text) - 1
+ else:
+ new_page = 0
+
+ if new_page &gt;= self.read_toolbar.total_pages:
+ new_page = self.read_toolbar.total_pages - 1
+ elif new_page &lt; 0:
+ new_page = 0
+
+ self.read_toolbar.current_page = new_page
+ self.read_toolbar.set_current_page(new_page)
+ self.show_page(new_page)
+ entry.props.text = str(new_page + 1)
+ self.read_toolbar.update_nav_buttons()
+ page = new_page
+
+ def go_back_cb(self, button):
+ self.page_previous()
+
+ def go_forward_cb(self, button):
+ self.page_next()
+
+ def page_previous(self):
+ global page
+ page=page-1
+ if page &lt; 0: page=0
+ self.read_toolbar.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.upper - \
+ v_adjustment.page_size
+
+ def page_next(self):
+ global page
+ page=page+1
+ if page &gt;= len(self.page_index): page=0
+ self.read_toolbar.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.lower
+
+ def zoom_in_cb(self, button):
+ self.font_increase()
+
+ def zoom_out_cb(self, button):
+ self.font_decrease()
+
+ def font_decrease(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size - 1
+ if font_size &lt; 1:
+ font_size = 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def font_increase(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size + 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def mark_set_cb(self, textbuffer, iter, textmark):
+
+ if textbuffer.get_has_selection():
+ begin, end = textbuffer.get_selection_bounds()
+ self.edit_toolbar.copy.set_sensitive(True)
+ else:
+ self.edit_toolbar.copy.set_sensitive(False)
+
+ def edit_toolbar_copy_cb(self, button):
+ textbuffer = self.textview.get_buffer()
+ begin, end = textbuffer.get_selection_bounds()
+ copy_text = textbuffer.get_text(begin, end)
+ self.clipboard.set_text(copy_text)
+
+ def view_toolbar_go_fullscreen_cb(self, view_toolbar):
+ self.fullscreen()
+
+ def scroll_down(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.upper - \
+ v_adjustment.page_size:
+ self.page_next()
+ return
+ if v_adjustment.value &lt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.value + \
+ v_adjustment.step_increment
+ if new_value &gt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.upper - \
+ v_adjustment.page_size
+ v_adjustment.value = new_value
+
+ def scroll_up(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.lower:
+ self.page_previous()
+ return
+ if v_adjustment.value &gt; v_adjustment.lower:
+ new_value = v_adjustment.value - \
+ v_adjustment.step_increment
+ if new_value &lt; v_adjustment.lower:
+ new_value = v_adjustment.lower
+ v_adjustment.value = new_value
+
+ def show_page(self, page_number):
+ global PAGE_SIZE, current_word
+ position = self.page_index[page_number]
+ self.etext_file.seek(position)
+ linecount = 0
+ label_text = '\n\n\n'
+ textbuffer = self.textview.get_buffer()
+ while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + unicode(line,
+ 'iso-8859-1')
+ linecount = linecount + 1
+ label_text = label_text + '\n\n\n'
+ textbuffer.set_text(label_text)
+ self.textview.set_buffer(textbuffer)
+
+ def save_extracted_file(self, zipfile, filename):
+ "Extract the file to a temp directory for viewing"
+ filebytes = zipfile.read(filename)
+ outfn = self.make_new_filename(filename)
+ if (outfn == ''):
+ return False
+ f = open(os.path.join(self.get_activity_root(),
+ 'tmp', outfn), 'w')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close()
+
+ def get_saved_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not \
+ title[len(title)-1].isdigit():
+ page = 0
+ else:
+ i = len(title) - 1
+ newPage = ''
+ while (title[i].isdigit() and i &gt; 0):
+ newPage = title[i] + newPage
+ i = i - 1
+ if title[i] == 'P':
+ page = int(newPage) - 1
+ else:
+ # not a page number; maybe a
+ # volume number.
+ page = 0
+
+ def save_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not \
+ title[len(title)- 1].isdigit():
+ title = title + ' P' + str(page + 1)
+ else:
+ i = len(title) - 1
+ while (title[i].isdigit() and i &gt; 0):
+ i = i - 1
+ if title[i] == 'P':
+ title = title[0:i] + 'P' + str(page + 1)
+ else:
+ title = title + ' P' + str(page + 1)
+ self.metadata['title'] = title
+
+ def read_file(self, filename):
+ "Read the Etext file"
+ global PAGE_SIZE, page
+
+ tempfile = os.path.join(self.get_activity_root(),
+ 'instance', 'tmp%i' % time.time())
+ os.link(filename, tempfile)
+ self.tempfile = tempfile
+
+ if zipfile.is_zipfile(filename):
+ self.zf = zipfile.ZipFile(filename, 'r')
+ self.book_files = self.zf.namelist()
+ self.save_extracted_file(self.zf,
+ self.book_files[0])
+ currentFileName = os.path.join(
+ self.get_activity_root(),
+ 'tmp', self.book_files[0])
+ else:
+ currentFileName = filename
+
+ self.etext_file = open(currentFileName,"r")
+ self.page_index = [ 0 ]
+ pagecount = 0
+ linecount = 0
+ while self.etext_file:
+ line = self.etext_file.readline()
+ if not line:
+ break
+ linecount = linecount + 1
+ if linecount &gt;= PAGE_SIZE:
+ position = self.etext_file.tell()
+ self.page_index.append(position)
+ linecount = 0
+ pagecount = pagecount + 1
+ if filename.endswith(".zip"):
+ os.remove(currentFileName)
+ self.get_saved_page_number()
+ self.show_page(page)
+ self.read_toolbar.set_total_pages(
+ pagecount + 1)
+ self.read_toolbar.set_current_page(page)
+
+ # We've got the document, so if we're a shared
+ # activity, offer it
+ if self.get_shared():
+ self.watch_for_tubes()
+ self.share_document()
+
+ def make_new_filename(self, filename):
+ partition_tuple = filename.rpartition('/')
+ return partition_tuple[2]
+
+ def write_file(self, filename):
+ "Save meta data for the file."
+ if self.is_received_document:
+ # This document was given to us by someone, so
+ # we have to save it to the Journal.
+ self.etext_file.seek(0)
+ filebytes = self.etext_file.read()
+ f = open(filename, 'wb')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close()
+ elif self.tempfile:
+ if self.close_requested:
+ os.link(self.tempfile, filename)
+ logger.debug(
+ "Removing temp file %s because we "
+ "will close",
+ self.tempfile)
+ os.unlink(self.tempfile)
+ self.tempfile = None
+ else:
+ # skip saving empty file
+ raise NotImplementedError
+
+ self.metadata['activity'] = self.get_bundle_id()
+ self.save_page_number()
+
+ def can_close(self):
+ self.close_requested = True
+ return True
+
+ def joined_cb(self, also_self):
+ """Callback for when a shared activity is joined.
+
+ Get the shared document from another participant.
+ """
+ self.watch_for_tubes()
+ gobject.idle_add(self.get_document)
+
+ def get_document(self):
+ if not self.want_document:
+ return False
+
+ # Assign a file path to download if one
+ # doesn't exist yet
+ if not self._jobject.file_path:
+ path = os.path.join(self.get_activity_root(),
+ 'instance',
+ 'tmp%i' % time.time())
+ else:
+ path = self._jobject.file_path
+
+ # Pick an arbitrary tube we can try to
+ # download the document from
+ try:
+ tube_id = self.unused_download_tubes.pop()
+ except (ValueError, KeyError), e:
+ logger.debug(
+ 'No tubes to get the document '
+ 'from right now: %s',
+ e)
+ return False
+
+ # Avoid trying to download the document multiple
+ # timesat once
+ self.want_document = False
+ gobject.idle_add(self.download_document, tube_id, path)
+ return False
+
+ def download_document(self, tube_id, path):
+ chan = self._shared_activity.telepathy_tubes_chan
+ iface = chan[telepathy.CHANNEL_TYPE_TUBES]
+ addr = iface.AcceptStreamTube(tube_id,
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0,
+ utf8_strings=True)
+ logger.debug('Accepted stream tube: '
+ 'listening address is %r',
+ addr)
+ assert isinstance(addr, dbus.Struct)
+ assert len(addr) == 2
+ assert isinstance(addr[0], str)
+ assert isinstance(addr[1], (int, long))
+ assert addr[1] &gt; 0 and addr[1] &lt; 65536
+ port = int(addr[1])
+
+ self.progressbar.show()
+ getter = ReadURLDownloader(
+ "http://%s:%d/document"
+ % (addr[0], port))
+ getter.connect("finished",
+ self.download_result_cb, tube_id)
+ getter.connect("progress",
+ self.download_progress_cb, tube_id)
+ getter.connect("error",
+ self.download_error_cb, tube_id)
+ logger.debug("Starting download to %s...", path)
+ getter.start(path)
+ self.download_content_length = \
+ getter.get_content_length()
+ self.download_content_type = \
+ getter.get_content_type()
+ return False
+
+ def download_progress_cb(self, getter,
+ bytes_downloaded, tube_id):
+ if self.download_content_length &gt; 0:
+ logger.debug(
+ "Downloaded %u of %u bytes from tube %u...",
+ bytes_downloaded,
+ self.download_content_length,
+ tube_id)
+ else:
+ logger.debug("Downloaded %u bytes from tube %u...",
+ bytes_downloaded, tube_id)
+ total = self.download_content_length
+ self.set_downloaded_bytes(bytes_downloaded, total)
+ gtk.gdk.threads_enter()
+ while gtk.events_pending():
+ gtk.main_iteration()
+ gtk.gdk.threads_leave()
+
+ def set_downloaded_bytes(self, bytes, total):
+ fraction = float(bytes) / float(total)
+ self.progressbar.set_fraction(fraction)
+ logger.debug("Downloaded percent", fraction)
+
+ def clear_downloaded_bytes(self):
+ self.progressbar.set_fraction(0.0)
+ logger.debug("Cleared download bytes")
+
+ def download_error_cb(self, getter, err, tube_id):
+ self.progressbar.hide()
+ logger.debug(
+ "Error getting document from tube %u: %s",
+ tube_id, err)
+ self.alert(_('Failure'),
+ _('Error getting document from tube'))
+ self.want_document = True
+ self.download_content_length = 0
+ self.download_content_type = None
+ gobject.idle_add(self.get_document)
+
+ def download_result_cb(self, getter, tempfile,
+ suggested_name, tube_id):
+ if self.download_content_type.startswith(
+ 'text/html'):
+ # got an error page instead
+ self.download_error_cb(getter,
+ 'HTTP Error', tube_id)
+ return
+
+ del self.unused_download_tubes
+
+ self.tempfile = tempfile
+ file_path = os.path.join(self.get_activity_root(),
+ 'instance', '%i' % time.time())
+ logger.debug(
+ "Saving file %s to datastore...", file_path)
+ os.link(tempfile, file_path)
+ self._jobject.file_path = file_path
+ datastore.write(self._jobject,
+ transfer_ownership=True)
+
+ logger.debug(
+ "Got document %s (%s) from tube %u",
+ tempfile, suggested_name, tube_id)
+ self.is_received_document = True
+ self.read_file(tempfile)
+ self.save()
+ self.progressbar.hide()
+
+ def shared_cb(self, activityid):
+ """Callback when activity shared.
+
+ Set up to share the document.
+
+ """
+ # We initiated this activity and have now shared it,
+ # so by definition we have the file.
+ logger.debug('Activity became shared')
+ self.watch_for_tubes()
+ self.share_document()
+
+ def share_document(self):
+ """Share the document."""
+ h = hash(self._activity_id)
+ port = 1024 + (h % 64511)
+ logger.debug(
+ 'Starting HTTP server on port %d', port)
+ self.fileserver = ReadHTTPServer(("", port),
+ self.tempfile)
+
+ # Make a tube for it
+ chan = self._shared_activity.telepathy_tubes_chan
+ iface = chan[telepathy.CHANNEL_TYPE_TUBES]
+ self.fileserver_tube_id = iface.OfferStreamTube(
+ READ_STREAM_SERVICE,
+ {},
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ ('127.0.0.1', dbus.UInt16(port)),
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST,
+ 0)
+
+ def watch_for_tubes(self):
+ """Watch for new tubes."""
+ tubes_chan = self._shared_activity.telepathy_tubes_chan
+
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ connect_to_signal(
+ 'NewTube', self.new_tube_cb)
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self.list_tubes_reply_cb,
+ error_handler=self.list_tubes_error_cb)
+
+ def new_tube_cb(self, tube_id, initiator, tube_type,
+ service, params, state):
+ """Callback when a new tube becomes available."""
+ logger.debug(
+ 'New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d', tube_id, initiator,
+ tube_type, service, params, state)
+ if service == READ_STREAM_SERVICE:
+ logger.debug('I could download from that tube')
+ self.unused_download_tubes.add(tube_id)
+ # if no download is in progress, let's fetch
+ # the document
+ if self.want_document:
+ gobject.idle_add(self.get_document)
+
+ def list_tubes_reply_cb(self, tubes):
+ """Callback when new tubes are available."""
+ for tube_info in tubes:
+ self.new_tube_cb(*tube_info)
+
+ def list_tubes_error_cb(self, e):
+ """Handle ListTubes error by logging."""
+ logger.error('ListTubes() failed: %s', e)
+
+ def alert(self, title, text=None):
+ alert = NotifyAlert(timeout=20)
+ alert.props.title = title
+ alert.props.msg = text
+ self.add_alert(alert)
+ alert.connect('response', self.alert_cancel_cb)
+ alert.show()
+
+ def alert_cancel_cb(self, alert, response_id):
+ self.remove_alert(alert)
+ self.textview.grab_focus()
+</pre>
+<p>The contents of <strong>activity.info</strong> are these lines:
+</p>
+<pre>[Activity]
+name = Read Etexts III
+service_name = net.flossmanuals.ReadEtextsActivity
+icon = read-etexts
+exec = sugar-activity ReadEtextsActivity3.ReadEtextsActivity
+show_launcher = no
+activity_version = 1
+mime_types = text/plain;application/zip
+license = GPLv2+
+</pre>
+<p>To try it out, download a <em>Project Gutenberg</em> book to the Journal, open it with this latest <strong>Read Etexts III</strong>, then share it with a second user who has the program installed but not running.&#160; She should accept the invitation to join that appears in her Neighborhood view.&#160; When she does Read Etexts II will start up and copy the book from the first user over the network and load it.&#160; The Activity will first show a blank screen, but then a progress bar will appear just under the toolbar and indicate the progress of the copying.&#160; When it is finished the first page of the book will appear.
+</p>
+<p>So how does it work?&#160; Let's look at the code.&#160; The first points of interest are the class definitions that appear at the beginning: <strong>ReadHTTPRequestHandler</strong>, <strong>ReadHTTPServer</strong>, and <strong>ReadURLDownloader</strong>.&#160; These three classes extend (that is to say, inherit code from) classes provided by the <strong>sugar.network</strong> package.&#160; These classes provide an <strong>HTTP client</strong> for receiving the book and an <strong>HTTP Server</strong> for sending the book.
+</p>
+<p>This is the code used to send a book:
+</p>
+<pre> def shared_cb(self, activityid):
+ """Callback when activity shared.
+
+ Set up to share the document.
+
+ """
+ # We initiated this activity and have now shared it,
+ # so by definition we have the file.
+ logger.debug('Activity became shared')
+ self.watch_for_tubes()
+ self.share_document()
+
+ def share_document(self):
+ """Share the document."""
+ h = hash(self._activity_id)
+ port = 1024 + (h % 64511)
+ logger.debug(
+ 'Starting HTTP server on port %d', port)
+ self.fileserver = ReadHTTPServer(("", port),
+ self.tempfile)
+
+ # Make a tube for it
+ chan = self._shared_activity.telepathy_tubes_chan
+ iface = chan[telepathy.CHANNEL_TYPE_TUBES]
+ self.fileserver_tube_id = iface.OfferStreamTube(
+ READ_STREAM_SERVICE,
+ {},
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ ('127.0.0.1', dbus.UInt16(port)),
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST,
+ 0)
+</pre>
+<p>You will notice that a hash of the <em>_activity_id</em> is used to get a port number.&#160; That port is used for the HTTP server and is passed to Telepathy, which offers it as a <strong>Stream Tube</strong>.&#160; On the receiving side we have this code:
+</p>
+<pre> def joined_cb(self, also_self):
+ """Callback for when a shared activity is joined.
+
+ Get the shared document from another participant.
+ """
+ self.watch_for_tubes()
+ gobject.idle_add(self.get_document)
+
+ def get_document(self):
+ if not self.want_document:
+ return False
+
+ # Assign a file path to download if one doesn't
+ # exist yet
+ if not self._jobject.file_path:
+ path = os.path.join(self.get_activity_root(),
+ 'instance',
+ 'tmp%i' % time.time())
+ else:
+ path = self._jobject.file_path
+
+ # Pick an arbitrary tube we can try to download the
+ # document from
+ try:
+ tube_id = self.unused_download_tubes.pop()
+ except (ValueError, KeyError), e:
+ logger.debug(
+ 'No tubes to get the document from '
+ 'right now: %s',
+ e)
+ return False
+
+ # Avoid trying to download the document multiple
+ # times at once
+ self.want_document = False
+ gobject.idle_add(self.download_document,
+ tube_id, path)
+ return False
+
+ def download_document(self, tube_id, path):
+ chan = self._shared_activity.telepathy_tubes_chan
+ iface = chan[telepathy.CHANNEL_TYPE_TUBES]
+ addr = iface.AcceptStreamTube(tube_id,
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST,
+ 0,
+ utf8_strings=True)
+ logger.debug(
+ 'Accepted stream tube: listening address is %r',
+ addr)
+ assert isinstance(addr, dbus.Struct)
+ assert len(addr) == 2
+ assert isinstance(addr[0], str)
+ assert isinstance(addr[1], (int, long))
+ assert addr[1] &gt; 0 and addr[1] &lt; 65536
+ port = int(addr[1])
+
+ self.progressbar.show()
+ getter = ReadURLDownloader(
+ "http://%s:%d/document"
+ % (addr[0], port))
+ getter.connect("finished",
+ self.download_result_cb, tube_id)
+ getter.connect("progress",
+ self.download_progress_cb, tube_id)
+ getter.connect("error",
+ self.download_error_cb, tube_id)
+ logger.debug(
+ "Starting download to %s...", path)
+ getter.start(path)
+ self.download_content_length = \
+ getter.get_content_length()
+ self.download_content_type = \
+ getter.get_content_type()
+ return False
+
+ def download_progress_cb(self, getter,
+ bytes_downloaded, tube_id):
+ if self.download_content_length &gt; 0:
+ logger.debug(
+ "Downloaded %u of %u bytes from tube %u...",
+ bytes_downloaded,
+ self.download_content_length,
+ tube_id)
+ else:
+ logger.debug(
+ "Downloaded %u bytes from tube %u...",
+ bytes_downloaded, tube_id)
+ total = self.download_content_length
+ self.set_downloaded_bytes(bytes_downloaded,
+ total)
+ gtk.gdk.threads_enter()
+ while gtk.events_pending():
+ gtk.main_iteration()
+ gtk.gdk.threads_leave()
+
+ def download_error_cb(self, getter, err, tube_id):
+ self.progressbar.hide()
+ logger.debug(
+ "Error getting document from tube %u: %s",
+ tube_id, err)
+ self.alert(_('Failure'),
+ _('Error getting document from tube'))
+ self.want_document = True
+ self.download_content_length = 0
+ self.download_content_type = None
+ gobject.idle_add(self.get_document)
+
+ def download_result_cb(self, getter, tempfile,
+ suggested_name, tube_id):
+ if self.download_content_type.startswith(
+ 'text/html'):
+ # got an error page instead
+ self.download_error_cb(getter,
+ 'HTTP Error', tube_id)
+ return
+
+ del self.unused_download_tubes
+
+ self.tempfile = tempfile
+ file_path = os.path.join(self.get_activity_root(),
+ 'instance',
+ '%i' % time.time())
+ logger.debug(
+ "Saving file %s to datastore...", file_path)
+ os.link(tempfile, file_path)
+ self._jobject.file_path = file_path
+ datastore.write(self._jobject,
+ transfer_ownership=True)
+
+ logger.debug(
+ "Got document %s (%s) from tube %u",
+ tempfile, suggested_name, tube_id)
+ self.is_received_document = True
+ self.read_file(tempfile)
+ self.save()
+ self.progressbar.hide()
+</pre>
+<p>Telepathy gives us the address and port number associated with a Stream Tube and we set up the HTTP Client to read from it.&#160; The client reads the file in chunks and calls <em>download_progress_cb()</em> after every chunk so we can update a progress bar to show the user how the download is progressing.&#160; There are also callback methods for when there is a download error and for when the download is finished,&#160;
+</p>
+<p>The <strong>ReadURLDownloader</strong> class is not only useful for transferring files over Stream Tubes, it can also be used to interact with websites and web services.&#160; My Activity <strong>Get Internet Archive Books</strong> uses this class for that purpose.
+</p>
+<p>The one remaining piece is the code which handles getting Stream Tubes to download the book from.&#160; In this code, adapted from the <strong>Read</strong> Activity, as soon as an instance of an Activity receives a book it turns around and offers to share it, thus the Activity may have several possible Tubes it could get the book from:
+</p>
+<pre>READ_STREAM_SERVICE = 'read-etexts-activity-http'
+
+ <em>...</em>
+
+ def watch_for_tubes(self):
+ """Watch for new tubes."""
+ tubes_chan = self._shared_activity.\
+ telepathy_tubes_chan
+
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ connect_to_signal(
+ 'NewTube',
+ self.new_tube_cb)
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ ListTubes(
+ reply_handler=self.list_tubes_reply_cb,
+ error_handler=self.list_tubes_error_cb)
+
+ def new_tube_cb(self, tube_id, initiator,
+ tube_type, service, params, state):
+ """Callback when a new tube becomes available."""
+ logger.debug(
+ 'New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d', tube_id, initiator,
+ tube_type,
+ service, params, state)
+ if service == READ_STREAM_SERVICE:
+ logger.debug('I could download from that tube')
+ self.unused_download_tubes.add(tube_id)
+ # if no download is in progress,
+ # let's fetch the document
+ if self.want_document:
+ gobject.idle_add(self.get_document)
+
+ def list_tubes_reply_cb(self, tubes):
+ """Callback when new tubes are available."""
+ for tube_info in tubes:
+ self.new_tube_cb(*tube_info)
+
+ def list_tubes_error_cb(self, e):
+ """Handle ListTubes error by logging."""
+ logger.error('ListTubes() failed: %s', e)</pre>
+<p>The <strong>READ_STREAM_SERVICE</strong> constant is defined near the top of the source file.
+</p>
+<h2>Using D-Bus Tubes
+</h2>
+<p> <strong>D-Bus</strong> is a method of supporting <strong>IPC</strong>, or <strong>Inter-Process Communication</strong>, that was created for the GNOME desktop environment.&#160; The idea of IPC is to allow two running programs to communicate with each other and execute each other's code.&#160; GNOME uses D-Bus to provide communication between the desktop environment and the programs running in it, and also between GNOME and the operating system.&#160; A <strong>D-Bus Tube</strong> is how Telepathy makes it possible for an instance of an Activity running on one computer to execute methods in another instance of the same Activity running on a different computer.&#160; Instead of just sending simple text messages back and forth or doing file transfers, your Activities can be truly shared.&#160; That is, your Activity can allow many people to work on the same task together.
+</p>
+<p>I have never written an Activity that uses D-Bus Tubes myself, but many others have.&#160; We're going to take a look at code from two of them: <strong>Scribble</strong> by Sayamindu Dasgupta and <strong>Batalla Naval</strong>, by Gerard J. Cerchio and Andr&#233;s Ambrois, which was written for the Ceibal Jam.
+</p>
+<p><strong>Scribble</strong> is a drawing program that allows many people to work on the same drawing at the same time.&#160; Instead of allowing you to choose what colors you will draw with, it uses the background and foreground colors of your Buddy icon (the XO stick figure) to draw with.&#160; That way, with many people drawing shapes it's easy to know who drew what.&#160; If you join the Activity in progress Scribble will update your screen so your drawing matches everyone else's screen.&#160; Scribble in action looks like this:
+</p>
+<p><img alt="Scribble in action" src="static/ActivitiesGuideSugar-scribble-en.jpg" width="600" height="440"/><br/></p>
+<p><strong>Batalla Naval</strong> is a version of the classic game <em>Battleship</em>.&#160; Each player has two grids: one for placing his own ships (actually the computer places the ships for you) and another blank grid representing the area where your opponent's ships are.&#160; You can't see his ships and he can't see yours.&#160; You click on the opponent's grid (on the right) to indicate where you want to aim an artillery shell.&#160; When you do the corresponding square will light up in both your grid and your opponent's own ship grid.&#160; If the square you picked corresponds to a square where your opponent has placed a ship that square will show up in a different color.&#160; The object is to find the squares containing your opponent's ships before he finds yours.&#160; The game in action looks like this:
+</p>
+<p><img alt="Batalla Naval in action!" src="static/ActivitiesGuideSugar-batallanaval-en.jpg" width="600" height="440"/></p>
+<p>I suggest that you download the latest code for these two Activities from Gitorious using these commands:
+</p>
+<pre>mkdir scribble
+cd scribble
+<code>git clone git://git.sugarlabs.org/scribble/mainline.git</code>
+<code/>cd ..
+mkdir batallanaval
+cd batallanaval
+<code>git clone git://git.sugarlabs.org/batalla-naval/mainline.git</code></pre>
+<p>You'll need to do some setup work to get these running in sugar-emulator. Scribble requires the <strong>goocanvas</strong> GTK component and the Python bindings that go with it. These were not installed by default in Fedora 10 but I was able to install them using <strong>Add/Remove Programs</strong> from the <strong>System</strong> menu in GNOME. Batalla Naval is missing <strong>setup.py</strong>, but that's easily fixed since every setup.py is identical.&#160; Copy the one from the book examples into the <strong>mainline/BatallaNaval.activity</strong> directory and run <strong>./setup.py dev</strong> on both Activities.
+</p>
+<p> These Activities use different strategies for collaboration.&#160; Scribble creates lines of Python code which it passes to all Buddies and the Buddies use <strong>exec</strong> to run the commands.&#160; This is the code used for drawing a circle:
+</p>
+<pre> def process_item_finalize(self, x, y):
+ if self.tool == 'circle':
+ self.cmd = "goocanvas.Ellipse(
+ parent=self._root,
+ center_x=%d,
+ center_y=%d, radius_x = %d,
+ radius_y = %d,
+ fill_color_rgba = %d,
+ stroke_color_rgba = %d,
+ title = '%s')" % (self.item.props.center_x,
+ self.item.props.center_y,
+ self.item.props.radius_x,
+ self.item.props.radius_y,
+ self._fill_color,
+ self._stroke_color, self.item_id)
+...
+
+ def process_cmd(self, cmd):
+ #print 'Processing cmd :' + cmd
+ exec(cmd)
+ #FIXME: Ugly hack, but I'm too lazy to
+ # do this nicely
+
+ if len(self.cmd_list) &gt; 0:
+ self.cmd_list += (';' + cmd)
+ else:
+ self.cmd_list = cmd</pre>
+<p> The <strong>cmd_list</strong> variable is used to create a long string containing all of the commands executed so far.&#160; When a new Buddy joins the Activity she is sent this variable to execute so that her drawing area has the same contents as the other Buddies have.
+</p>
+<p>This is an interesting approach but you could do the same thing with the TextChannel so it isn't the best use of D-Bus Tubes.&#160; Batalla Naval's use of D-Bus is more typical.
+</p>
+<h2>How D-Bus Tubes Work, More Or Less
+ <br/></h2>
+<p>D-Bus enables you to have two running programs send messages to each other.&#160; The programs have to be running on the same computer.&#160; Sending a message is sort of a roundabout way of having one program run code in another.&#160; A program defines the kind of messages it is willing to receive and act on.&#160;&#160; In the case of Batalla Naval it defines a message "tell me what square you want to fire a shell at and I'll figure out if part of one of my ships is in that square and tell you."&#160; The first program doesn't actually run code in the second one, but the end result is similar.&#160; D-Bus Tubes is a way of making D-Bus able to send messages like this to a program running on another computer.
+ <br/></p>
+<p>Think for a minute about how you might make a program on one computer run code in a running program on a different computer.&#160; You'd have to use the network, of course.&#160; Everyone is familiar with sending data over a network, but in this case you would have to send program code over the network.&#160; You would need to be able to tell the running program on the second computer what code you wanted it to run.&#160; You would have to send it a method call and all the parameters you needed to pass into the method, and you'd need a way to get a return value back.
+</p>
+<p>Isn't that kind of like what <strong>Scribble</strong> is doing in the code we just looked at?&#160; Maybe we could make our code do something like that?
+</p>
+<p>Of course if you did that then every program you wanted to run code in remotely would have to be written to deal with that.&#160; If you had a bunch of programs you wanted to do that with you'd have to have some way of letting the programs know which requests were meant for it.&#160; It would be nice if there was a program running on each machine that dealt with making the network connections, converting method calls to data that could be sent over the network and then converting the data back into method calls and running them, plus sending any return values back.&#160; This program should be able to know which program you wanted to run code in and see that the method call is run there.&#160; The program should run all the time, and it would be really good if it made running a method on a remote program as simple as running a method in my own program.
+</p>
+<p>As you might guess, what I've just described is more or less what D-Bus Tubes are.&#160; There are articles explaining how it works in detail but it is not necessary to know how it works to use it.&#160; You do need to know about a few things, though.&#160; First, you need to know how to use D-Bus Tubes to make objects in your Activity available for use by other instances of that Activity running elsewhere.
+</p>
+<p>An Activity that needs to use D-Bus Tubes needs to define what sorts of messages it is willing to act on, in effect what specific methods in in the program are available for this use.&#160; All Activities that use D-Bus Tubes have constants like this:
+</p>
+<pre>SERVICE = "org.randomink.sayamindu.Scribble"
+IFACE = SERVICE
+PATH = "/org/randomink/sayamindu/Scribble"
+</pre>
+<p>These are the constants used for the <strong>Scribble</strong> Activity.&#160; The first constant, named SERVICE, represents the <strong>bus name</strong> of the Activity.&#160; This is also called a <strong>well-known name</strong> because it uses a <strong>reversed domain name</strong> as part of the name.&#160; In this case Sayamindu Dasgupta has a website at <a href="http://sayamindu.randomink.org">http://sayamindu.randomink.org</a>&#160; so he reverses the dot-separated words of that URL to create the first part of his bus name.&#160; It is not necessary to own a domain name before you can create a bus name.&#160; You can use org.sugarlabs.ActivityName if you like.&#160; The point is that the bus name must be unique, and by convention this is made easier by starting with a reversed domain name.
+</p>
+<p>The PATH constant represents the <strong>object path</strong>.&#160; It looks like the bus name with slashes separating the words rather than periods.&#160; For most Activities that is exactly what it should be, but it is possible for an application to expose more than one object to D-Bus and in that case each object exposed would have its own unique name, by convention words separated by slashes.
+</p>
+<p>The third constant is IFACE, which is the <strong>interface name</strong>.&#160; An interface is a collection of related methods and <strong>signals</strong>, identified by a name that uses the same convention used by the bus name.&#160; In the example above, and probably in most Activities using a D-Bus Tube, the interface name and the bus name are identical.
+</p>
+<p>So what is a signal?&#160; A signal is like a method but instead of one running program calling a method in one other running program, a signal is <strong>broadcast</strong>.&#160; In other words, instead of executing a method in just one program it executes the same method in many running programs, in fact in every running program that has that method that it is connected to through the D-Bus.&#160; A signal can pass data into a method call but it can't receive anything back as a return value.&#160; It's like a radio station that broadcasts music to anyone that is tuned in.&#160; The flow of information is one way only.
+</p>
+<p>Of course a radio station often receives phone calls from its listeners.&#160; A disc jockey might play a new song and invite listeners to call the station and say what they thought about it.&#160; The phone call is two way communication between the disc jockey and the listener, but it was initiated by a request that was broadcast to all listeners.&#160; In the same way your Activity might use a signal to invite all listeners (Buddies) to use a method to call it back, and that method can both supply and receive information.
+</p>
+<p>In D-Bus methods and signals have <strong>signatures</strong>.&#160; A signature is a description of the parameters passed into a method or signal including its <strong>data types</strong>.&#160; Python is not a <strong>strongly typed</strong> language.&#160; In a strongly typed language every variable has a data type that limits what it can do.&#160; Data types include such things as <strong>strings</strong>, <strong>integers</strong>, <strong>long integers</strong>, <strong>floating point numbers</strong>, <strong>booleans</strong>, etc.&#160; Each one can be used for a specific purpose only.&#160; For instance a boolean can only hold the values <strong>True</strong> and <strong>False</strong>, nothing else.&#160; A string can be used to hold strings of characters, but even if those characters represent a number you cannot use a string for calculations.&#160; Instead you need to convert the string into one of the numeric data types.&#160; An integer can hold integers up to a certain size, and a long integer can hold much larger integers,&#160; A floating point number is a number with a decimal point in scientific notation.&#160; It is almost useless for business arithmetic, which requires rounded results.
+</p>
+<p>In Python you can put anything into any variable and the language itself will figure out how to deal with it.&#160; To make Python work with D-Bus, which requires strongly typed variables that Python doesn't have, you need a way to tell D-Bus what types the variables you pass into a method should have.&#160; You do this by using a signature string as an argument to the method or signal.&#160; Methods have two strings: an <strong>in_signature</strong> and an <strong>out_signature</strong>.&#160; Signals just have a <strong>signature</strong> parameter.&#160; Some examples of signature strings:
+</p>
+<p>
+ </p><table border="1" cellpadding="1" cellspacing="1"><tbody><tr><td>ii</td>
+ <td>Two parameters, both integers</td>
+ </tr><tr><td>sss</td>
+ <td>Three parameters, all strings</td>
+ </tr><tr><td>ixd</td>
+ <td>Three parameters, an integer, a long integer, and a double precision floating point number.</td>
+ </tr><tr><td>a(ssiii)</td>
+ <td>An array where each element of the array is a tuple containing two strings and three integers.</td>
+ </tr></tbody></table><p>There is more information on signature strings in the dbus-python tutorial at <a href="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html">http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html</a>.
+ <br/></p>
+<h2>Introducing Hello Mesh And Friends
+ <br/></h2>
+<p>If you study the source code of a few shared Activities you'll conclude that many of them contain nearly identical methods, as if they were all copied from the same source.&#160; In fact, more likely than not they were.&#160; The Activity <strong>Hello Mesh</strong> was created to be an example of how to use D-Bus Tubes in a shared Activity.&#160; It is traditional in programming textbooks to have the first example program be something that just prints the words "Hello World" to the console or displays the same words in a window.&#160; In that tradition <strong>Hello Mesh</strong> is a program that doesn't do all that much.&#160; You can find the code in Gitorious at <a href="http://git.sugarlabs.org/projects/hello-mesh">http://git.sugarlabs.org/projects/hello-mesh</a>.
+</p>
+<p><strong>Hello Mesh</strong> is widely copied because it demonstrates how to do things that all shared Activities need to do.&#160; When you have a shared Activity you need to be able to do two things:
+</p>
+<ul><li>Send information or commands to other instances of your Activity.</li>
+ <li>Give Buddies joining your Activity a copy of the current state of the Activity.</li>
+</ul><p>It does this using two signals and one method:
+</p>
+<ul><li>A signal called <em>Hello()</em> that someone joining the Activity sends to all participants.&#160; The <em>Hello()</em> method takes no parameters.</li>
+ <li>A method called <em>World()</em> which instances of the Activity receiving <em>Hello()</em> send back to the sender.&#160; This method takes a text string as an argument, which is meant to represent the current state of the Activity.</li>
+ <li>Another signal called <em>SendText()</em> which sends a text string to all participants.&#160; This represents updating the state of the shared Activity.&#160; In the case of <strong>Scribble</strong> this would be informing the others that this instance has just drawn a new shape.</li>
+</ul><p>Rather than study <strong>Hello Mesh</strong> itself I'd like to look at the code derived from it used in <strong>Batalla Naval</strong>.&#160; I have taken the liberty of running the comments, originally in Spanish, through <em>Google Translate</em> to make everything in English.&#160; I have also removed some commented-out lines of code.
+ <br/></p>
+<p>This Activity does something clever to make it possible to run it either as a Sugar Activity or as a standalone Python program.&#160; The standalone program does not support sharing at all, and it runs in a Window.&#160; The class <strong>Activity</strong> is a subclass of <strong>Window</strong>, so when the code is run standalone the <em>init()</em> function in <strong>BatallaNaval.py</strong> gets a Window, and when the same code is run as an Activity the instance of class <strong>BatallaNavalActivity</strong> is passed to <em>init()</em>:
+ <br/></p>
+<pre>from sugar.activity.activity import Activity, ActivityToolbox
+import BatallaNaval
+from Collaboration import CollaborationWrapper
+
+class BatallaNavalActivity(Activity):
+ ''' The Sugar class called when you run this
+ program as an Activity. The name of this
+ class file is marked in the
+ activity/activity.info file.'''
+
+ def __init__(self, handle):
+ Activity.__init__(self, handle)
+
+ self.gamename = 'BatallaNaval'
+
+ # Create the basic Sugar toolbar
+ toolbox = ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ # Create an instance of the CollaborationWrapper
+ # so you can share the activity.
+ self.colaboracion = CollaborationWrapper(self)
+
+ # The activity is a subclass of Window, so it
+ # passses itself to the init function
+ BatallaNaval.init(False, self)
+</pre>
+<p> The other clever thing going on here is that all the collaboration code is placed in its own <strong>CollaborationWrapper</strong> class which takes the instance of the <strong>BatallNavalActivity</strong> class in its constructor.&#160; This separates the collaboration code from the rest of the program.&#160; Here is the code in <strong>CollaborationWrapper.py</strong>:
+ <br/></p>
+<pre>import logging
+
+from sugar.presence import presenceservice
+import telepathy
+from dbus.service import method, signal
+# In build 656 Sugar lacks sugartubeconn
+try:
+ from sugar.presence.sugartubeconn import \
+ SugarTubeConnection
+except:
+ from sugar.presence.tubeconn import TubeConnection as \
+ SugarTubeConnection
+from dbus.gobject_service import ExportedGObject
+
+''' In all collaborative Activities in Sugar we are
+ made aware when a player enters or leaves. So that
+ everyone knows the state of the Activity we use
+ the methods Hello and World. When a participant
+ enters Hello sends a signal that reaches
+ all participants and the participants
+ respond directly using the method "World",
+ which retrieves the current state of the Activity.
+ After the updates are given then the signal
+ Play is used by each participant to make his move.
+ In short this module encapsulates the logic of
+ "collaboration" with the following effect:
+ - When someone enters the collaboration
+ the Hello signal is sent.
+ - Whoever receives the Hello signal responds
+ with World
+ - Every time someone makes a move he uses
+ the method Play giving a signal which
+ communicates to each participant
+ what his move was.
+'''
+
+SERVICE = "org.ceibaljam.BatallaNaval"
+IFACE = SERVICE
+PATH = "/org/ceibaljam/BatallaNaval"
+
+logger = logging.getLogger('BatallaNaval')
+logger.setLevel(logging.DEBUG)
+
+class CollaborationWrapper(ExportedGObject):
+ ''' A wrapper for the collaboration methods.
+ Get the activity and the necessary callbacks.
+ '''
+
+ def __init__(self, activity):
+ self.activity = activity
+ self.presence_service = \
+ presenceservice.get_instance()
+ self.owner = \
+ self.presence_service.get_owner()
+
+ def set_up(self, buddy_joined_cb, buddy_left_cb,
+ World_cb, Play_cb, my_boats):
+ self.activity.connect('shared',
+ self._shared_cb)
+ if self.activity._shared_activity:
+ # We are joining the activity
+ self.activity.connect('joined',
+ self._joined_cb)
+ if self.activity.get_shared():
+ # We've already joined
+ self._joined_cb()
+
+ self.buddy_joined = buddy_joined_cb
+ self.buddy_left = buddy_left_cb
+ self.World_cb = World_cb
+ # Called when someone passes the board state.
+ self.Play_cb = Play_cb
+ # Called when someone makes a move.
+
+ # Submitted by making World on a new partner
+ self.my_boats = [(b.nombre, b.orientacion,
+ b.largo, b.pos[0],
+ b.pos[1]) for b in my_boats]
+ self.world = False
+ self.entered = False
+
+ def _shared_cb(self, activity):
+ self._sharing_setup()
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ OfferDBusTube(
+ SERVICE, {})
+ self.is_initiator = True
+
+ def _joined_cb(self, activity):
+ self._sharing_setup()
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+ self.is_initiator = False
+
+ def _sharing_setup(self):
+ if self.activity._shared_activity is None:
+ logger.error(
+ 'Failed to share or join activity')
+ return
+
+ self.conn = \
+ self.activity._shared_activity.telepathy_conn
+ self.tubes_chan = \
+ self.activity._shared_activity.telepathy_tubes_chan
+ self.text_chan = \
+ self.activity._shared_activity.telepathy_text_chan
+
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ connect_to_signal(
+ 'NewTube', self._new_tube_cb)
+
+ self.activity._shared_activity.connect(
+ 'buddy-joined',
+ self._buddy_joined_cb)
+ self.activity._shared_activity.connect(
+ 'buddy-left',
+ self._buddy_left_cb)
+
+ # Optional - included for example:
+ # Find out who's already in the shared activity:
+ for buddy in \
+ self.activity._shared_activity.\
+ get_joined_buddies():
+ logger.debug(
+ 'Buddy %s is already in the activity',
+ buddy.props.nick)
+
+ def participant_change_cb(self, added, removed):
+ logger.debug(
+ 'Tube: Added participants: %r', added)
+ logger.debug(
+ 'Tube: Removed participants: %r', removed)
+ for handle, bus_name in added:
+ buddy = self._get_buddy(handle)
+ if buddy is not None:
+ logger.debug(
+ 'Tube: Handle %u (Buddy %s) was added',
+ handle, buddy.props.nick)
+ for handle in removed:
+ buddy = self._get_buddy(handle)
+ if buddy is not None:
+ logger.debug('Buddy %s was removed' %
+ buddy.props.nick)
+ if not self.entered:
+ if self.is_initiator:
+ logger.debug(
+ "I'm initiating the tube, "
+ "will watch for hellos.")
+ self.add_hello_handler()
+ else:
+ logger.debug(
+ 'Hello, everyone! What did I miss?')
+ self.Hello()
+ self.entered = True
+
+
+ # This is sent to all participants whenever we
+ # join an activity
+ @signal(dbus_interface=IFACE, signature='')
+ def Hello(self):
+ """Say Hello to whoever else is in the tube."""
+ logger.debug('I said Hello.')
+
+ # This is called by whoever receives our Hello signal
+ # This method receives the current game state and
+ # puts us in sync with the rest of the participants.
+ # The current game state is represented by the
+ # game object
+ @method(dbus_interface=IFACE, in_signature='a(ssiii)',
+ out_signature='a(ssiii)')
+ def World(self, boats):
+ """To be called on the incoming XO after
+ they Hello."""
+ if not self.world:
+ logger.debug('Somebody called World on me')
+ self.world = True # Instead of loading
+ # the world, I am
+ # receiving play by
+ # play.
+ self.World_cb(boats)
+ # now I can World others
+ self.add_hello_handler()
+ else:
+ self.world = True
+ logger.debug(
+ "I've already been welcomed, doing nothing")
+ return self.my_boats
+
+ @signal(dbus_interface=IFACE, signature='ii')
+ def Play(self, x, y):
+ """Say Hello to whoever else is in the tube."""
+ logger.debug('Running remote play:%s x %s.', x, y)
+
+ def add_hello_handler(self):
+ logger.debug('Adding hello handler.')
+ self.tube.add_signal_receiver(self.hello_signal_cb,
+ 'Hello', IFACE,
+ path=PATH, sender_keyword='sender')
+ self.tube.add_signal_receiver(self.play_signal_cb,
+ 'Play', IFACE,
+ path=PATH, sender_keyword='sender')
+
+ def hello_signal_cb(self, sender=None):
+ """Somebody Helloed me. World them."""
+ if sender == self.tube.get_unique_name():
+ # sender is my bus name, so ignore my own signal
+ return
+ logger.debug('Newcomer %s has joined', sender)
+ logger.debug(
+ 'Welcoming newcomer and sending them '
+ 'the game state')
+
+ self.other = sender
+
+ # I send my ships and I get theirs in return
+ enemy_boats = self.tube.get_object(self.other,
+ PATH).World(
+ self.my_boats, dbus_interface=IFACE)
+
+ # I call the callback World, to load the enemy ships
+ self.World_cb(enemy_boats)
+
+ def play_signal_cb(self, x, y, sender=None):
+ """Somebody placed a stone. """
+ if sender == self.tube.get_unique_name():
+ return # sender is my bus name,
+ # so ignore my own signal
+ logger.debug('Buddy %s placed a stone at %s x %s',
+ sender, x, y)
+ # Call our Play callback
+ self.Play_cb(x, y)
+ # In theory, no matter who sent him
+
+ def _list_tubes_error_cb(self, e):
+ logger.error('ListTubes() failed: %s', e)
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _new_tube_cb(self, id, initiator, type,
+ service, params, state):
+ logger.debug('New tube: ID=%d initator=%d '
+ 'type=%d service=%s '
+ 'params=%r state=%d', id, initiator, '
+ 'type, service, params, state)
+ if (type == telepathy.TUBE_TYPE_DBUS and
+ service == SERVICE):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES]
+ .AcceptDBusTube(id)
+ self.tube = SugarTubeConnection(self.conn,
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES],
+ id, group_iface=
+ self.text_chan[telepathy.\
+ CHANNEL_INTERFACE_GROUP])
+ super(CollaborationWrapper,
+ self).__init__(self.tube, PATH)
+ self.tube.watch_participants(
+ self.participant_change_cb)
+
+ def _buddy_joined_cb (self, activity, buddy):
+ """Called when a buddy joins the shared
+ activity. """
+ logger.debug(
+ 'Buddy %s joined', buddy.props.nick)
+ if self.buddy_joined:
+ self.buddy_joined(buddy)
+
+ def _buddy_left_cb (self, activity, buddy):
+ """Called when a buddy leaves the shared
+ activity. """
+ if self.buddy_left:
+ self.buddy_left(buddy)
+
+ def _get_buddy(self, cs_handle):
+ """Get a Buddy from a channel specific handle."""
+ logger.debug('Trying to find owner of handle %u...',
+ cs_handle)
+ group = self.text_chan[telepathy.\
+ CHANNEL_INTERFACE_GROUP]
+ my_csh = group.GetSelfHandle()
+ logger.debug(
+ 'My handle in that group is %u', my_csh)
+ if my_csh == cs_handle:
+ handle = self.conn.GetSelfHandle()
+ logger.debug('CS handle %u belongs to me, %u',
+ cs_handle, handle)
+ elif group.GetGroupFlags() &amp; \
+ telepathy.\
+ CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES:
+ handle = group.GetHandleOwners([cs_handle])[0]
+ logger.debug('CS handle %u belongs to %u',
+ cs_handle, handle)
+ else:
+ handle = cs_handle
+ logger.debug('non-CS handle %u belongs to itself',
+ handle)
+ # XXX: deal with failure to get the handle owner
+ assert handle != 0
+ return self.presence_service.\
+ get_buddy_by_telepathy_handle(
+ self.conn.service_name,
+ self.conn.object_path, handle)
+</pre>
+<p> Most of the code above is similar to what we've seen in the other examples, and most of it can be used as is in any Activity that needs to make D-Bus calls.&#160; For this reason we'll focus on the code that is specific to using D-Bus.&#160; The logical place to start is the <em>Hello()</em> method.&#160; There is of course nothing magic about the name "Hello".&#160; <strong>Hello Mesh</strong> is meant to be a "Hello World" program for using D-Bus Tubes, so by convention the words "Hello" and "World" had to be used for <em>something</em>.&#160; The <em>Hello()</em> method is broadcast to all instances of the Activity to inform them that a new instance is ready to receive information about the current state of the shared Activity.&#160; Your own Activity will probably need something similar, but you should feel free to name it something else, and if you're writing the code for a school assignment you should definitely name it something else:
+</p>
+<pre> # This is sent to all participants whenever we
+ # join an activity
+ @signal(dbus_interface=IFACE, signature='')
+ def Hello(self):
+ """Say Hello to whoever else is in the tube."""
+ logger.debug('I said Hello.')
+
+ def add_hello_handler(self):
+ logger.debug('Adding hello handler.')
+ self.tube.add_signal_receiver(
+ self.hello_signal_cb,
+ 'Hello', IFACE,
+ path=PATH, sender_keyword='sender')
+...
+
+ def hello_signal_cb(self, sender=None):
+ """Somebody Helloed me. World them."""
+ if sender == self.tube.get_unique_name():
+ # sender is my bus name,
+ # so ignore my own signal
+ return
+ logger.debug('Newcomer %s has joined', sender)
+ logger.debug(
+ 'Welcoming newcomer and sending them '
+ 'the game state')
+
+ self.other = sender
+
+ # I send my ships and I returned theirs
+ enemy_boats = self.tube.get_object(
+ self.other, PATH).World(
+ self.my_boats, dbus_interface=IFACE)
+
+ # I call the callback World, to load the enemy ships
+ self.World_cb(enemy_boats)
+</pre>
+<p>&#160;The most interesting thing about this code is this line, which Python calls a <strong>Decorator</strong>:
+</p>
+<pre> @signal(dbus_interface=IFACE, signature='')
+</pre>
+<p> When you put <strong>@signal</strong> in front of a method name it has the effect of adding the two parameters shown to the method call whenever it is invoked, in effect changing it from a normal method call to a D-Bus call for a signal.&#160; The <strong>signature</strong> parameter is an empty string, indicating that the method call has no parameters.&#160; The <em>Hello()</em> method does nothing at all locally but when it is received by the other instances of the Activity it causes them to execute the <em>World()</em> method, which sends back the location of their boats and gets the new participants boats in return.
+</p>
+<p><strong>Batalla Naval</strong> is apparently meant to be a demonstration program.&#160; <em>Battleship</em> is a game for two players, but there is nothing in the code to prevent more players from joining and no way to handle it if they do.&#160; Ideally you would want code to make only the first joiner an actual player and make the rest only spectators.
+</p>
+<p>Next we'll look at the <em>World()</em> method:
+</p>
+<pre> # This is called by whoever receives our Hello signal
+ # This method receives the current game state and
+ # puts us in sync with the rest of the participants.
+ # The current game state is represented by the game
+ # object
+ @method(dbus_interface=IFACE, in_signature='a(ssiii)',
+ out_signature='a(ssiii)')
+ def World(self, boats):
+ """To be called on the incoming XO after
+ they Hello."""
+ if not self.world:
+ logger.debug('Somebody called World on me')
+ self.world = True # Instead of loading the world,
+ # I am receiving play by play.
+ self.World_cb(boats)
+ # now I can World others
+ self.add_hello_handler()
+ else:
+ self.world = True
+ logger.debug("I've already been welcomed, "
+ "doing nothing")
+ return self.my_boats
+</pre>
+<p>There is another decorator here, this one converting the <em>World()</em> method to a D-Bus call for a method.&#160; The signature is more interesting than <em>Hello()</em> had.&#160; It means an array of tuples where each tuple contains two strings and three integers.&#160; Each element in the array represents one ship and its attributes.&#160; <em>World_cb</em> is set to point to a method in <strong>BatallaNaval.py</strong>, (and so is <em>Play_cb</em>).&#160; If you study the <em>init()</em> code in <strong>BatallaNaval.py</strong> you'll see how this happens.&#160; <em>World()</em> is called in the <em>hello_signal_cb()</em> method we just looked at.&#160; It is sent to the joiner who sent <em>Hello()</em> to us.
+ <br/></p>
+<p>Finally we'll look at the <em>Play()</em> signal:
+</p>
+<pre> @signal(dbus_interface=IFACE, signature='ii')
+ def Play(self, x, y):
+ """Say Hello to whoever else is in the tube."""
+ logger.debug('Running remote play:%s x %s.', x, y)
+
+ def add_hello_handler(self):
+...
+ self.tube.add_signal_receiver(self.play_signal_cb,
+ 'Play', IFACE,
+ path=PATH, sender_keyword='sender')
+...
+ def play_signal_cb(self, x, y, sender=None):
+ """Somebody placed a stone. """
+ if sender == self.tube.get_unique_name():
+ return # sender is my bus name, so
+ # ignore my own signal
+ logger.debug('Buddy %s placed a stone at %s x %s',
+ sender, x, y)
+ # Call our Play callback
+ self.Play_cb(x, y)
+</pre>
+<p>This is a signal so there is only one signature string, this one indicating that the input parameters are two integers.
+</p>
+<p>There are several ways you could improve this Activity.&#160; When playing against the computer in non-sharing mode the game just makes random moves.&#160; The game does not limit the players to two and make the rest of the joiners spectators.&#160; It does not make the players take turns.&#160; When a player succeeds in sinking all the other players ships nothing happens to mark the event.&#160; Finally, <em>gettext()</em> is not used for the text strings displayed by the Activity so it cannot be translated into languages other than Spanish.
+</p>
+<p>In the tradition of textbooks everywhere I will leave making these improvements as an exercise for the student.
+</p>
+<p>
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch018_adding-text-to-speech.xhtml b/studio/static/doc/myosa/ch018_adding-text-to-speech.xhtml
new file mode 100644
index 0000000..b56bbb7
--- /dev/null
+++ b/studio/static/doc/myosa/ch018_adding-text-to-speech.xhtml
@@ -0,0 +1,695 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Adding Text To Speech
+</h1>
+<h2>Introduction
+</h2>
+<p>Certainly one of the most popular Activities available is <strong>Speak</strong>, which takes the words you type in and speaks them out loud, at the same time displaying a cartoon face that seems to be speaking the words.&#160; You might be surprised to learn how little of the code in that Activity is used to get the words spoken.&#160;&#160; If your Activity could benefit from having words spoken out loud (the possibilities for educational Activities and games are definitely there) this chapter will teach you how to make it happen.
+</p>
+<h2><img alt="SpeakActivity.png" src="static/ActivitiesGuideSugar-SpeakActivity-en.png" height="450" width="600"/></h2>
+<h2>We Have Ways To Make You Talk
+</h2>
+<p>A couple of ways, actually, and neither one is that painful.&#160; They are:
+</p>
+<ul><li>Running the <strong>espeak</strong> program directly</li>
+ <li>Using the <strong>gstreamer espeak plugin</strong></li>
+</ul><p>Both approaches have their advantages.&#160; The first one is the one used by Speak.&#160; (Technically, Speak uses the gstreamer plugin if it is available, and otherwise executes espeak directly.&#160; For what Speak is doing using the gstreamer plugin isn't really needed).&#160; Executing espeak is definitely the simplest method, and may be suitable for your own Activity.&#160; Its big advantage is that you do not need to have the gstreamer plugin installed.&#160; If your Activity needs to run on something other than the latest version of Sugar this will be something to consider.
+ <br/></p>
+<p>The gstreamer plugin is what is used by <strong>Read Etexts</strong> to do text to speech with highlighting.&#160; For this application we needed to be able to do things that are not possible by just running <strong>espeak</strong>.&#160; For example:
+</p>
+<ul><li>We needed to be able to pause and resume speech, because the Activity needs to speak a whole page worth of text, not just simple phrases.</li>
+ <li>We needed to highlight the words being spoken as they are spoken.</li>
+</ul><p>You might think that you could achieve these objectives by running espeak on one word at a time.&#160; If you do, don't feel bad because I thought that too.&#160; On a fast computer it sounds really awful, like HAL 9000 developing a stutter towards the end of being deactivated.&#160; On the XO no sounds came out at all.
+</p>
+<p> Originally Read Etexts used <strong>speech-dispatcher</strong> to do what the gstreamer plugin does.&#160; The developers of that program were very helpful in getting the highlighting in Read Etexts working, but speech-dispatcher needed to be configured before you could use it which was an issue for us.&#160; (There is more than one kind of text to speech software available and speech-dispatcher supports most of them.&#160; This makes configuration files inevitable).&#160; Aleksey Lim of Sugar Labs came up with the idea of using a gstreamer plugin and was the one who wrote it.&#160; He also rewrote much of <strong>Read Etexts</strong> so it would use the plugin if it was available, use speech-dispatcher if not, and would not support speech if neither was available.
+</p>
+<h2> Running espeak Directly
+</h2>
+<p>You can run the <strong>espeak</strong> program from the terminal to try out its options.&#160; To see what options are available for espeak you can use the <strong>man</strong> command:
+</p>
+<pre>man espeak</pre>
+<p>This will give you a manual page describing how to run the program and what options are available.&#160; The parts of the man page that are most interesting to us are these:
+</p>
+<pre><strong>NAME</strong>
+ espeak - A multi-lingual software speech synthesizer.
+
+<strong>SYNOPSIS</strong>
+ espeak [options] [&lt;words&gt;]
+
+<strong>DESCRIPTION</strong>
+ espeak is a software speech synthesizer for English,
+ and some other languages.
+
+<strong>OPTIONS</strong>
+ -p &lt;integer&gt;
+ Pitch adjustment, 0 to 99, default is 50
+
+ -s &lt;integer&gt;
+ Speed in words per minute, default is 160
+
+ -v &lt;voice name&gt;
+ Use voice file of this name from
+ espeak-data/voices
+
+ --voices[=&lt;language code&gt;]
+ Lists the available voices. If =&lt;language code&gt;
+ is present then only those voices which are
+ suitable for that language are listed.
+</pre>
+<p>Let's try out some of these options. First let's get a list of <strong>Voices</strong>:
+</p>
+<pre><strong>espeak --voices</strong>
+Pty Language Age/Gender VoiceName File Other Langs
+ 5 af M afrikaans af
+ 5 bs M bosnian bs
+ 5 ca M catalan ca
+ 5 cs M czech cs
+ 5 cy M welsh-test cy
+ 5 de M german de
+ 5 el M greek el
+ 5 en M default default
+ 5 en-sc M en-scottish en/en-sc (en 4)
+ 2 en-uk M english en/en (en 2)
+<em>... and many more ...</em>
+</pre>
+<p>Now that we know what the names of the voices are we can try them out. How about English with a French accent?
+</p>
+<pre>espeak "Your mother was a hamster and your father \
+smelled of elderberries." -v fr
+</pre>
+<p>Let's try experimenting with rate and pitch:
+</p>
+<pre>espeak "I'm sorry, Dave. I'm afraid I can't \
+do that." -s 120 -p 30
+</pre>
+<p>The next thing to do is to write some Python code to run espeak.&#160; Here is a short program adapted from the code in <strong>Speak</strong>:
+</p>
+<pre>import re
+import subprocess
+
+PITCH_MAX = 99
+RATE_MAX = 99
+PITCH_DEFAULT = PITCH_MAX/2
+RATE_DEFAULT = RATE_MAX/3
+
+def speak(text, rate=RATE_DEFAULT, pitch=PITCH_DEFAULT,
+ voice="default"):
+
+ # espeak uses 80 to 370
+ rate = 80 + (370-80) * int(rate) / 100
+
+ subprocess.call(["espeak", "-p", str(pitch),
+ "-s", str(rate), "-v", voice, text],
+ stdout=subprocess.PIPE)
+
+def voices():
+ out = []
+ result = subprocess.Popen(["espeak", "--voices"],
+ stdout=subprocess.PIPE).communicate()[0]
+
+ for line in result.split('\n'):
+ m = re.match(
+ r'\s*\d+\s+([\w-]+)\s+([MF])\s+([\w_-]+)\s+(.+)',
+ line)
+ if not m:
+ continue
+ language, gender, name, stuff = m.groups()
+ if stuff.startswith('mb/') or \
+ name in ('en-rhotic','english_rp',
+ 'english_wmids'):
+ # these voices don't produce sound
+ continue
+ out.append((language, name))
+
+ return out
+
+def main():
+ print voices()
+ speak("I'm afraid I can't do that, Dave.")
+ speak("Your mother was a hamster, and your father "
+ + "smelled of elderberries!", 30, 60, "fr")
+
+if __name__ == "__main__":
+ main()
+</pre>
+<p>In the Git repository in the directory <strong>Adding_TTS</strong> this file is named <strong>espeak.py</strong>.&#160; Load this file into <strong>Eric</strong> and do <strong>Run Script</strong> from the <strong>Start</strong> menu to run it.&#160; In addition to hearing speech you should see this text:
+</p>
+<p><em>[('af', 'afrikaans'), ('bs', 'bosnian'), ('ca', 'catalan'), ('cs', 'czech'), ('cy', 'welsh-test'), ('de', 'german'), ('el', 'greek'), ('en', 'default'), ('en-sc', 'en-scottish'), ('en-uk', 'english'), ('en-uk-north', 'lancashire'), ('en-us', 'english-us'), ('en-wi', 'en-westindies'), ('eo', 'esperanto'), ('es', 'spanish'), ('es-la', 'spanish-latin-american'), ('fi', 'finnish'), ('fr', 'french'), ('fr-be', 'french'), ('grc', 'greek-ancient'), ('hi', 'hindi-test'), ('hr', 'croatian'), ('hu', 'hungarian'), ('hy', 'armenian'), ('hy', 'armenian-west'), ('id', 'indonesian-test'), ('is', 'icelandic-test'), ('it', 'italian'), ('ku', 'kurdish'), ('la', 'latin'), ('lv', 'latvian'), ('mk', 'macedonian-test'), ('nl', 'dutch-test'), ('no', 'norwegian-test'), ('pl', 'polish'), ('pt', 'brazil'), ('pt-pt', 'portugal'), ('ro', 'romanian'), ('ru', 'russian_test'), ('sk', 'slovak'), ('sq', 'albanian'), ('sr', 'serbian'), ('sv', 'swedish'), ('sw', 'swahihi-test'), ('ta', 'tamil'), ('tr', 'turkish'), ('vi', 'vietnam-test'), ('zh', 'Mandarin'), ('zh-yue', 'cantonese-test')] </em>
+</p>
+<p>The <em>voices()</em> function returns a list of voices as one tuple per voice, and eliminates voices from the list that espeak cannot produce on its own.&#160; This list of tuples can be used to populate a drop down list.
+ <br/></p>
+<p>The <em>speak()</em> function adjusts the value of <strong>rate</strong> so you can input a value between 0 and 99 rather than between 80 to 370.&#160; <em>speak()</em> is more complex in the Speak Activity than what we have here because in that Activity it monitors the spoken audio and generates mouth movements based on the amplitude of the voice.&#160; Making the face move is most of what the Speak Activity does, and since we aren't doing that we need very little code to make our Activity speak.
+</p>
+<p>You can use <strong>import espeak</strong> to include this file in your own Activities.
+</p>
+<h2>Using The gstreamer espeak Plugin
+</h2>
+<p>The gstreamer espeak plugin can be installed in <strong>Fedora 10</strong> or later using <strong>Add/Remove Software</strong>.
+</p>
+<p><img alt="Installing the plugin." src="static/ActivitiesGuideSugar-espeak-en.jpg" height="363" width="600"/></p>
+<p> When you have this done you should be able to download the <strong>Read Etexts</strong> Activity (the real one, not the simplified version we're using for the book) from ASLO and try out the <strong>Speech</strong> tab.&#160; You should do that now.&#160; It will look something like this:
+</p>
+<p><img alt="espeak2_1.jpg" src="static/ActivitiesGuideSugar-espeak2_1-en.jpg" height="415" width="600"/><br/></p>
+<p>The book used in the earlier screenshots of this manual was <em>Pride and Prejudice</em> by Jane Austen.&#160; To balance things out the rest of the screen shots will be using <em>The Innocents Abroad</em> by Mark Twain.
+</p>
+<p><strong>Gstreamer</strong> is a framework for multimedia.&#160; If you've watched videos on the web you are familiar with the concept of streaming media.&#160; Instead of downloading a whole song or a whole movie clip and then playing it, streaming means the downloading and the playing happen at the same time, with the downloading just a bit behind the playing.&#160; There are many different kinds of media files: MP3's, DivX, WMV, Real Media, and so on.&#160; For every kind of media file Gstreamer has a plugin.
+</p>
+<p>Gstreamer makes use of a concept called <strong>pipelining</strong>.&#160; The idea is that the output of one program can become the input to another.&#160; One way to handle that situation is to put the output of the first program into a temporary file and have the second program read it.&#160; This would mean that the first program would have to finish running before the second one could begin.&#160; What if you could have both programs run at the same time and have the second program read the data as the first one wrote it out?&#160; It's possible, and the mechanism for getting data from one program to the other is called a <strong>pipe</strong>.&#160; A collection of programs joined together in this way is called a <strong>pipeline</strong>.&#160; The program that feeds data into a pipe is called a <strong>source</strong>, and the data that takes the data out of the pipe is called a <strong>sink</strong>.
+ <br/></p>
+<p>The gstreamer espeak plugin uses a simple pipe: text goes into espeak at one end and sound comes out the other and is sent to your soundcard.&#160; You might think that doesn't sound much different from what we were doing before, but it is.&#160; When you just run espeak the program has to load itself into memory, speak the text you give it into the sound card, then unload itself.&#160; This is one of the reasons you can't just use espeak a word at a time to achieve speech with highlighted words.&#160; There is a short lag while the program is loading.&#160; It isn't that noticeable if you give espeak a complete phrase or sentence to speak, but if it happens for every word it is <em>very</em> noticeable.&#160; Using the gstreamer plugin we can have espeak loaded into memory all the time, just waiting for us to send some words into its input pipe.&#160; It will speak them and then wait for the next batch.
+</p>
+<p>Since we can control what goes into the pipe it is possible to pause and resume speech.
+</p>
+<p> The example we'll use here is a version of <strong>Read Etexts</strong> again, but instead of the Activity we're going to modify the standalone version.&#160; There is nothing special about the gstreamer plugin that makes it only work with Activities.&#160; Any Python program can use it.&#160; I'm only including Text to Speech as a topic in this manual because every Sugar installation includes espeak and many Activities could find it useful.
+</p>
+<p>There is a in our Git repository named <strong>speech.py</strong> which looks like this:
+</p>
+<pre>import gst
+
+voice = 'default'
+pitch = 0
+
+rate = -20
+highlight_cb = None
+
+def _create_pipe():
+ pipeline = 'espeak name=source ! autoaudiosink'
+ pipe = gst.parse_launch(pipeline)
+
+ def stop_cb(bus, message):
+ pipe.set_state(gst.STATE_NULL)
+
+ def mark_cb(bus, message):
+ if message.structure.get_name() == 'espeak-mark':
+ mark = message.structure['mark']
+ highlight_cb(int(mark))
+
+ bus = pipe.get_bus()
+ bus.add_signal_watch()
+ bus.connect('message::eos', stop_cb)
+ bus.connect('message::error', stop_cb)
+ bus.connect('message::element', mark_cb)
+
+ return (pipe.get_by_name('source'), pipe)
+
+def _speech(source, pipe, words):
+ source.props.pitch = pitch
+ source.props.rate = rate
+ source.props.voice = voice
+ source.props.text = words;
+ pipe.set_state(gst.STATE_PLAYING)
+
+info_source, info_pipe = _create_pipe()
+play_source, play_pipe = _create_pipe()
+
+# track for marks
+play_source.props.track = 2
+
+def voices():
+ return info_source.props.voices
+
+def say(words):
+ _speech(info_source, info_pipe, words)
+ print words
+
+def play(words):
+ _speech(play_source, play_pipe, words)
+
+def is_stopped():
+ for i in play_pipe.get_state():
+ if isinstance(i, gst.State) and \
+ i == gst.STATE_NULL:
+ return True
+ return False
+
+def stop():
+ play_pipe.set_state(gst.STATE_NULL)
+
+def is_paused():
+ for i in play_pipe.get_state():
+ if isinstance(i, gst.State) and \
+ i == gst.STATE_PAUSED:
+ return True
+ return False
+
+def pause():
+ play_pipe.set_state(gst.STATE_PAUSED)
+
+def rate_up():
+ global rate
+ rate = min(99, rate + 10)
+
+def rate_down():
+ global rate
+ rate = max(-99, rate - 10)
+
+def pitch_up():
+ global pitch
+ pitch = min(99, pitch + 10)
+
+def pitch_down():
+ global pitch
+ pitch = max(-99, pitch - 10)
+
+def prepare_highlighting(label_text):
+ i = 0
+ j = 0
+ word_begin = 0
+ word_end = 0
+ current_word = 0
+ word_tuples = []
+ omitted = [' ', '\n', u'\r', '_', '[', '{', ']',\
+ '}', '|', '&lt;', '&gt;', '*', '+', '/', '\\' ]
+ omitted_chars = set(omitted)
+ while i &lt; len(label_text):
+ if label_text[i] not in omitted_chars:
+ word_begin = i
+ j = i
+ while j &lt; len(label_text) and \
+ label_text[j] not in omitted_chars:
+ j = j + 1
+ word_end = j
+ i = j
+ word_t = (word_begin, word_end, \
+ label_text[word_begin: word_end].strip())
+ if word_t[2] != u'\r':
+ word_tuples.append(word_t)
+ i = i + 1
+ return word_tuples
+
+def add_word_marks(word_tuples):
+ "Adds a mark between each word of text."
+ i = 0
+ marked_up_text = '&lt;speak&gt; '
+ while i &lt; len(word_tuples):
+ word_t = word_tuples[i]
+ marked_up_text = marked_up_text + \
+ '&lt;mark name="' + str(i) + '"/&gt;' + word_t[2]
+ i = i + 1
+ return marked_up_text + '&lt;/speak&gt;'</pre>
+<p>There is another file named <strong>ReadEtextsTTS.py</strong> which looks like this:
+ <br/></p>
+<pre>import sys
+import os
+import zipfile
+import pygtk
+import gtk
+import getopt
+import pango
+import gobject
+import time
+import speech
+
+speech_supported = True
+
+try:
+ import gst
+ gst.element_factory_make('espeak')
+ print 'speech supported!'
+except Exception, e:
+ speech_supported = False
+ print 'speech not supported!'
+
+page=0
+PAGE_SIZE = 45
+
+class ReadEtextsActivity():
+ def __init__(self):
+ "The entry point to the Activity"
+ speech.highlight_cb = self.highlight_next_word
+ # print speech.voices()
+
+ def highlight_next_word(self, word_count):
+ if word_count &lt;&#8286; len(self.word_tuples):
+ word_tuple = self.word_tuples[word_count]
+ textbuffer = self.textview.get_buffer()
+ tag = textbuffer.create_tag()
+ tag.set_property('weight', pango.WEIGHT_BOLD)
+ tag.set_property( 'foreground', "white")
+ tag.set_property( 'background', "black")
+ iterStart = \
+ textbuffer.get_iter_at_offset(word_tuple[0])
+ iterEnd = \
+ textbuffer.get_iter_at_offset(word_tuple[1])
+ bounds = textbuffer.get_bounds()
+ textbuffer.remove_all_tags(bounds[0], bounds[1])
+ textbuffer.apply_tag(tag, iterStart, iterEnd)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ max = v_adjustment.upper - \
+ v_adjustment.page_size
+ max = max * word_count
+ max = max / len(self.word_tuples)
+ v_adjustment.value = max
+ return True
+
+ def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the arrow keys"
+ global done
+ global speech_supported
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ if keyname == 'KP_End' and speech_supported:
+ if speech.is_paused() or speech.is_stopped():
+ speech.play(self.words_on_page)
+ else:
+ speech.pause()
+ return True
+ if keyname == 'plus':
+ self.font_increase()
+ return True
+ if keyname == 'minus':
+ self.font_decrease()
+ return True
+ if speech_supported and speech.is_stopped() == False \
+ and speech.is_paused == False:
+ # If speech is in progress, ignore other keys.
+ return True
+ if keyname == '7':
+ speech.pitch_down()
+ speech.say('Pitch Adjusted')
+ return True
+ if keyname == '8':
+ speech.pitch_up()
+ speech.say('Pitch Adjusted')
+ return True
+ if keyname == '9':
+ speech.rate_down()
+ speech.say('Rate Adjusted')
+ return True
+ if keyname == '0':
+ speech.rate_up()
+ speech.say('Rate Adjusted')
+ return True
+ if keyname == 'KP_Right':
+ self.page_next()
+ return True
+ if keyname == 'Page_Up' or keyname == 'KP_Up':
+ self.page_previous()
+ return True
+ if keyname == 'KP_Left':
+ self.page_previous()
+ return True
+ if keyname == 'Page_Down' or keyname == 'KP_Down':
+ self.page_next()
+ return True
+ if keyname == 'Up':
+ self.scroll_up()
+ return True
+ if keyname == 'Down':
+ self.scroll_down()
+ return True
+ return False
+
+ def page_previous(self):
+ global page
+ page=page-1
+ if page &lt; 0: page=0
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.upper - \
+ v_adjustment.page_size
+
+ def page_next(self):
+ global page
+ page=page+1
+ if page &gt;= len(self.page_index): page=0
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.lower
+
+ def font_decrease(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size - 1
+ if font_size &lt; 1:
+ font_size = 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def font_increase(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size + 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def scroll_down(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.upper - \
+ v_adjustment.page_size:
+ self.page_next()
+ return
+ if v_adjustment.value &lt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.value + \
+ v_adjustment.step_increment
+ if new_value &gt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.upper - \
+ v_adjustment.page_size
+ v_adjustment.value = new_value
+
+ def scroll_up(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.lower:
+ self.page_previous()
+ return
+ if v_adjustment.value &gt; v_adjustment.lower:
+ new_value = v_adjustment.value - \
+ v_adjustment.step_increment
+ if new_value &lt; v_adjustment.lower:
+ new_value = v_adjustment.lower
+ v_adjustment.value = new_value
+
+ def show_page(self, page_number):
+ global PAGE_SIZE, current_word
+ position = self.page_index[page_number]
+ self.etext_file.seek(position)
+ linecount = 0
+ label_text = ''
+ textbuffer = self.textview.get_buffer()
+ while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + \
+ unicode(line, 'iso-8859-1')
+ linecount = linecount + 1
+ textbuffer.set_text(label_text)
+ self.textview.set_buffer(textbuffer)
+ self.word_tuples = \
+ speech.prepare_highlighting(label_text)
+ self.words_on_page = \
+ speech.add_word_marks(self.word_tuples)
+
+ def save_extracted_file(self, zipfile, filename):
+ "Extract the file to a temp directory for viewing"
+ filebytes = zipfile.read(filename)
+ f = open("/tmp/" + filename, 'w')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close()
+
+ def read_file(self, filename):
+ "Read the Etext file"
+ global PAGE_SIZE
+
+ if zipfile.is_zipfile(filename):
+ self.zf = zipfile.ZipFile(filename, 'r')
+ self.book_files = self.zf.namelist()
+ self.save_extracted_file(self.zf, \
+ self.book_files[0])
+ currentFileName = "/tmp/" + self.book_files[0]
+ else:
+ currentFileName = filename
+
+ self.etext_file = open(currentFileName,"r")
+ self.page_index = [ 0 ]
+ linecount = 0
+ while self.etext_file:
+ line = self.etext_file.readline()
+ if not line:
+ break
+ linecount = linecount + 1
+ if linecount &gt;= PAGE_SIZE:
+ position = self.etext_file.tell()
+ self.page_index.append(position)
+ linecount = 0
+ if filename.endswith(".zip"):
+ os.remove(currentFileName)
+
+ def delete_cb(self, widget, event, data=None):
+ speech.stop()
+ return False
+
+ def destroy_cb(self, widget, data=None):
+ speech.stop()
+ gtk.main_quit()
+
+ def main(self, file_path):
+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.window.connect("delete_event", self.delete_cb)
+ self.window.connect("destroy", self.destroy_cb)
+ self.window.set_title("Read Etexts Activity")
+ self.window.set_size_request(800, 600)
+ self.window.set_border_width(0)
+ self.read_file(file_path)
+ self.scrolled_window = gtk.ScrolledWindow(
+ hadjustment=None, vadjustment=None)
+ self.textview = gtk.TextView()
+ self.textview.set_editable(False)
+ self.textview.set_left_margin(50)
+ self.textview.set_cursor_visible(False)
+ self.textview.connect("key_press_event",
+ self.keypress_cb)
+ self.font_desc = pango.FontDescription("sans 12")
+ self.textview.modify_font(self.font_desc)
+ self.show_page(0)
+ self.scrolled_window.add(self.textview)
+ self.window.add(self.scrolled_window)
+ self.textview.show()
+ self.scrolled_window.show()
+ self.window.show()
+ gtk.main()
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "")
+ ReadEtextsActivity().main(args[0])
+ except getopt.error, msg:
+ print msg
+ print "This program has no options"
+ sys.exit(2)
+</pre>
+<p>The program <strong>ReadEtextsTTS</strong> has only a few changes to make it enabled for speech. The first one checks for the existence of the gstreamer plugin:
+</p>
+<pre>speech_supported = True
+
+try:
+ import gst
+ gst.element_factory_make('espeak')
+ print 'speech supported!'
+except Exception, e:
+ speech_supported = False
+ print 'speech not supported!'
+</pre>
+<p> This code detects whether the plugin is installed by attempting to import for the Python library associated with it named "gst". If the import fails it throws an <strong>Exception</strong> and we catch that Exception and use it to set a variable named <strong>speech_supported</strong> to <strong>False</strong>.&#160; We can check the value of this variable in other places in the program to make a program that works with Text To Speech if it is available and without it if it is not.&#160; Making a program work in different environments by doing these kinds of checks is called <em>degrading gracefully</em>.&#160; Catching exceptions on imports is a common technique in Python to achieve this.&#160; If you want your Activity to run on older versions of Sugar you may find yourself using it.
+</p>
+<p>The next bit of code we're going to look at highlights a word in the textview and scrolls the textview to keep the highlighted word visible.
+</p>
+<pre>class ReadEtextsActivity():
+ def __init__(self):
+ "The entry point to the Activity"
+ speech.highlight_cb = self.highlight_next_word
+ # print speech.voices()
+
+ def highlight_next_word(self, word_count):
+ if word_count &lt; len(self.word_tuples):
+ word_tuple = self.word_tuples[word_count]
+ textbuffer = self.textview.get_buffer()
+ tag = textbuffer.create_tag()
+ tag.set_property('weight', pango.WEIGHT_BOLD)
+ tag.set_property( 'foreground', "white")
+ tag.set_property( 'background', "black")
+ iterStart = \
+ textbuffer.get_iter_at_offset(word_tuple[0])
+ iterEnd = \
+ textbuffer.get_iter_at_offset(word_tuple[1])
+ bounds = textbuffer.get_bounds()
+ textbuffer.remove_all_tags(bounds[0], bounds[1])
+ textbuffer.apply_tag(tag, iterStart, iterEnd)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ max = v_adjustment.upper - v_adjustment.page_size
+ max = max * word_count
+ max = max / len(self.word_tuples)
+ v_adjustment.value = max
+ return True
+</pre>
+<p>In the <em>__init__()</em> method we assign a variable called <em>highlight_cb</em> in <strong>speech.py</strong> with a method called <em>highlight_next_word()</em>.&#160; This gives <strong>speech.py</strong> a way to call that method every time a new word in the textview needs to be highlighted.
+</p>
+<p>The next line will print the list of tuples containing Voice names to the terminal if you uncomment it.&#160; We aren't letting the user change voices in this application but it would not be difficult to add that feature.
+ <br/></p>
+<p>The code for the method that highlights the words follows.&#160; What it does is look in a list of tuples that contain the starting and ending offsets of every word in the textarea's text buffer.&#160; The caller of this method passes in a word number (for instance the first word in the buffer is word 0, the second is word 1, and so on).&#160; The method looks up that entry in the list, gets its starting and ending offsets, removes any previous highlighting, then highlights the new text.&#160; In addition to that it figures out what fraction of the total number of words the current word is and scrolls the textviewer enough to make sure that word is visible.
+</p>
+<p>Of course this method works best on pages without many blank lines, which fortunately is most pages.&#160; It does not work so well on title pages.&#160; An experienced programmer could probably come up with a more elegant and reliable way of doing this scrolling.&#160; Let me know what you come up with.
+</p>
+<p>Further down we see the code that gets the keystrokes the user enters and does speech-related things with them:
+</p>
+<pre> def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the arrow keys"
+ global done
+ global speech_supported
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ if keyname == 'KP_End' and speech_supported:
+ if speech.is_paused() or speech.is_stopped():
+ speech.play(self.words_on_page)
+ else:
+ speech.pause()
+ return True
+ if speech_supported and speech.is_stopped() == False \
+ and speech.is_paused == False:
+ # If speech is in progress, ignore other keys.
+ return True
+ if keyname == '7':
+ speech.pitch_down()
+ speech.say('Pitch Adjusted')
+ return True
+ if keyname == '8':
+ speech.pitch_up()
+ speech.say('Pitch Adjusted')
+ return True
+ if keyname == '9':
+ speech.rate_down()
+ speech.say('Rate Adjusted')
+ return True
+ if keyname == '0':
+ speech.rate_up()
+ speech.say('Rate Adjusted')
+ return True
+</pre>
+<p>As you can see, the functions we're calling are all in the file <strong>speech.py</strong> that we imported.&#160; You don't have to fully understand how these functions work to make use of them in your own Activities.&#160; Notice that the code as written prevents the user from changing pitch or rate while speech is in progress.&#160; Notice also that there are two different methods in speech.py for doing speech.&#160; <strong>play()</strong> is the method for doing text to speech with word highlighting.&#160; <strong>say()</strong> is for saying short phrases produced by the user interface, in this case "Pitch adjusted" and "Rate adjusted".&#160; Of course if you put code like this in your Activity you would use the _() function so these phrases could be translated into other languages.
+</p>
+<p>There is one more bit of code we need to do text to speech with highlighting: we need to prepare the words to be spoken to be highlighted in the textviewer.
+</p>
+<pre> def show_page(self, page_number):
+ global PAGE_SIZE, current_word
+ position = self.page_index[page_number]
+ self.etext_file.seek(position)
+ linecount = 0
+ label_text = ''
+ textbuffer = self.textview.get_buffer()
+ while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + unicode(line, \
+ 'iso-8859-1')
+ linecount = linecount + 1
+ textbuffer.set_text(label_text)
+ self.textview.set_buffer(textbuffer)
+ self.word_tuples = \
+ speech.prepare_highlighting(label_text)
+ self.words_on_page = \
+ speech.add_word_marks(self.word_tuples)
+</pre>
+<p>The beginning of this method reads a page's worth of text into a string called label_text and puts it into the textview's buffer.&#160; The last two lines splits the text into words, leaving in punctuation, and puts each word and its beginning and ending offsets into a tuple.&#160; The tuples are added to a List.
+</p>
+<p> <strong>speech.add_word_marks()</strong> converts the words in the List to a document in <em>SSML</em> (<em>Speech Synthesis Markup Language</em>) format.&#160; SSML is a standard for adding tags (sort of like the tags used to make web pages) to text to tell speech software what to do with the text.&#160; We're just using a very small part of this standard to produce a marked up document with a mark between each word, like this:
+</p>
+<pre>&lt;speak&gt;
+ &lt;mark name="0"/&gt;The&lt;mark name="1"/&gt;quick&lt;mark name-"2"/&gt;
+ brown&lt;mark name="3"/&gt;fox&lt;mark name="4"/&gt;jumps
+&lt;/speak&gt;</pre>
+<p>When espeak reads this file it will do a <em>callback</em> into our program every time it reads one of the mark tags.&#160; The callback will contain the number of the word in the <strong>word_tuples</strong> List which it will get from the <strong>name </strong>attribute of the <strong>mark </strong>tag.&#160; In this way the method being called will know which word to highlight.&#160; The advantage of using the mark name rather than just highlighting the next word in the textviewer is that if espeak should fail to do one of the callbacks the highlighting won't be thrown out of sync.&#160; This was a problem with speech-dispatcher.
+</p>
+<p>A callback is just what it sounds like.&#160; When one program calls another program it can pass in a function or method of its own that it wants the second program to call when something happens.
+ <br/></p>
+<p>To try out the new program run
+</p>
+<pre>./ReadEtextsTTS.py <em>bookfile</em></pre>
+<p>from the Terminal.&#160; You can adjust pitch and rate up and down using the keys <strong>7, 8, 9</strong>, and <strong>0</strong> on the top row of the keyboard.&#160; It should say "Pitch Adjusted" or "Rate Adjusted" when you do that.&#160; You can start, pause, and resume speech with highlighting by using the <strong>End</strong> key on the keypad.&#160; (On the XO laptop the "game" keys are mapped to what is the numeric keypad on a normal keyboard.&#160; This makes these keys handy for use when the XO is folded into tablet mode and the keyboard is not available).&#160; You cannot change pitch or rate while speech is in progress.&#160; Attempts to do that will be ignored.&#160; The program in action looks like this:
+</p>
+<p><img alt="espeak3.jpg" src="static/ActivitiesGuideSugar-espeak3-en.jpg" height="465" width="600"/></p>
+<p>That brings us to the end of the topic of Text to Speech.&#160; If you're like to see more, the Git repository for this book has a few more sample programs that use the gstreamer espeak plugin.&#160; These examples were created by the author of the plugin and demonstrate some other ways you can use it.&#160; There's even a "choir" program that demonstrates multiple voices speaking at the same time.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch019_fun-with-the-journal.xhtml b/studio/static/doc/myosa/ch019_fun-with-the-journal.xhtml
new file mode 100644
index 0000000..981884d
--- /dev/null
+++ b/studio/static/doc/myosa/ch019_fun-with-the-journal.xhtml
@@ -0,0 +1,1085 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Fun With The Journal
+</h1>
+<h2>Introduction
+</h2>
+<p>By default every Activity creates and reads one Journal entry.&#160; Most Activities don't need to do any more with the Journal than that, and if your Activity is like that you won't need the information&#160; in this chapter.&#160; Chances are that someday you will want to do more than that, so if you do keep reading.
+</p>
+<p> First let's review what the Journal is.&#160; The Journal is a collection of files that each have <strong>metadata</strong> (data about data) associated with them.&#160; Metadata is stored as text strings and includes such things as the <strong>Title</strong>, <strong>Description</strong>, <strong>Tags</strong>, <strong>MIME Type</strong>, and a screen shot of the Activity when it was last used.
+</p>
+<p>Your Activity cannot read and write these files directly.&#160; Instead Sugar provides an API (Application Programming Interface) that gives you an indirect way to add, delete and modify entries in the Journal, as well as a way to search Journal entries and make a list of entries that meet the search criteria.
+</p>
+<p>The API we'll use is in the <strong>datastore</strong> package.&#160; After version .82 of Sugar this API was rewritten, so we'll need to learn how to support both versions in the same Activity.
+</p>
+<p>If you've read this far you've seen several examples where Sugar started out doing one thing and then changed to do the same thing a better way but still provided a way to create Activities that would work with either the old or the new way.&#160; You may be wondering if it is normal for a project to do this.&#160; As a professional programmer I can tell you that doing tricks like this to maintain backward compatibility is extremely common, and Sugar does no more of this than any other project.&#160; There are decisions made by Herman Hollerith when he tabulated the 1890 census using punched cards that computer programmers must live with to this day.
+</p>
+<h2>Introducing Sugar Commander
+</h2>
+<p> I am a big fan of the concept of the Journal but not so much of the <strong>Journal Activity</strong> that Sugar uses to navigate through it and maintain it.&#160; My biggest gripe against it is that it represents the contents of thumb drives and SD cards as if the files on these were also Journal entries.&#160; My feeling is that files and directories are one thing and the Journal is another, and the user interface should recognize that.
+</p>
+<p>Strictly speaking the Journal Activity is and is not an Activity.&#160; It inherits code from the Activity class just like any other Activity, and it is written in Python and uses the same datastore API that other Activities use.&#160; However, it is run in a special way that gives it powers and abilities far beyond those of an ordinary Activity.&#160; In particular it can do two things:
+</p>
+<ul><li>It can write to files on external media like thumb drives and SD cards.</li>
+ <li>It alone can be used to resume Journal entries using other Activities.</li>
+</ul><p>While I would like to write a Journal Activity that does everything the original does but has a user interface more to my own taste the Sugar security model won't allow that.&#160; Recently I came to the conclusion that a more mild-mannered version of the Journal Activity might be useful.&#160; Just as Kal-El sometimes finds it more useful to be Clark Kent than Superman, my own Activity might be a worthy alternative to the built-in Journal Activity when super powers are not needed.
+</p>
+<p> My Activity, which I call <strong>Sugar Commander</strong>, has two tabs.&#160; One represents the Journal and looks like this:
+</p>
+<p><img alt="Sugar Commander Journal Tab" src="static/ActivitiesGuideSugar-SCommander2-en.jpg" height="450" width="600"/></p>
+<p>This tab lets you browse through the Journal sorted by Title or MIME Type, select entries and view their details, update Title, Description or Tags, and delete entries you no longer want.&#160; The other tab shows files and folders and looks like this:
+</p>
+<p><img alt="Sugar Commander Files Tab" src="static/ActivitiesGuideSugar-SCommander1-en.jpg" height="450" width="600"/></p>
+<p>This tab lets you browse through the files and folders or the regular file system, including thumb drives and SD cards.&#160; You can select a file and make a Journal entry out of it by pushing the button at the bottom of the screen.
+</p>
+<p>&#160;This Activity has very little code and still manages to do everything an ordinary Activity can do with the Journal.&#160; You can download the Git repository using this command:
+ <br/></p>
+<pre><code>git clone git://git.sugarlabs.org/sugar-commander/\
+mainline.git</code></pre>
+<p>There is only one source file, <strong>sugarcommander.py</strong>:
+</p>
+<pre>import logging
+import os
+import gtk
+import pango
+import zipfile
+from sugar import mime
+from sugar.activity import activity
+from sugar.datastore import datastore
+from sugar.graphics.alert import NotifyAlert
+from sugar.graphics import style
+from gettext import gettext as _
+import gobject
+import dbus
+
+COLUMN_TITLE = 0
+COLUMN_MIME = 1
+COLUMN_JOBJECT = 2
+
+DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
+DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
+DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
+
+_logger = logging.getLogger('sugar-commander')
+
+class SugarCommander(activity.Activity):
+ def __init__(self, handle, create_jobject=True):
+ "The entry point to the Activity"
+ activity.Activity.__init__(self, handle, False)
+ self.selected_journal_entry = None
+ self.selected_path = None
+
+ canvas = gtk.Notebook()
+ canvas.props.show_border = True
+ canvas.props.show_tabs = True
+ canvas.show()
+
+ self.ls_journal = gtk.ListStore(
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_PYOBJECT)
+ self.tv_journal = gtk.TreeView(self.ls_journal)
+ self.tv_journal.set_rules_hint(True)
+ self.tv_journal.set_search_column(COLUMN_TITLE)
+ self.selection_journal = \
+ self.tv_journal.get_selection()
+ self.selection_journal.set_mode(
+ gtk.SELECTION_SINGLE)
+ self.selection_journal.connect("changed",
+ self.selection_journal_cb)
+ renderer = gtk.CellRendererText()
+ renderer.set_property('wrap-mode', gtk.WRAP_WORD)
+ renderer.set_property('wrap-width', 500)
+ renderer.set_property('width', 500)
+ self.col_journal = gtk.TreeViewColumn(_('Title'),
+ renderer, text=COLUMN_TITLE)
+ self.col_journal.set_sort_column_id(COLUMN_TITLE)
+ self.tv_journal.append_column(self.col_journal)
+
+ mime_renderer = gtk.CellRendererText()
+ mime_renderer.set_property('width', 500)
+ self.col_mime = gtk.TreeViewColumn(_('MIME'),
+ mime_renderer, text=COLUMN_MIME)
+ self.col_mime.set_sort_column_id(COLUMN_MIME)
+ self.tv_journal.append_column(self.col_mime)
+
+ self.list_scroller_journal = gtk.ScrolledWindow(
+ hadjustment=None, vadjustment=None)
+ self.list_scroller_journal.set_policy(
+ gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ self.list_scroller_journal.add(self.tv_journal)
+
+ label_attributes = pango.AttrList()
+ label_attributes.insert(pango.AttrSize(
+ 14000, 0, -1))
+ label_attributes.insert(pango.AttrForeground(
+ 65535, 65535, 65535, 0, -1))
+
+ tab1_label = gtk.Label(_("Journal"))
+ tab1_label.set_attributes(label_attributes)
+ tab1_label.show()
+ self.tv_journal.show()
+ self.list_scroller_journal.show()
+
+ column_table = gtk.Table(rows=1, columns=2,
+ homogeneous = False)
+
+ image_table = gtk.Table(rows=2, columns=2,
+ homogeneous=False)
+ self.image = gtk.Image()
+ image_table.attach(self.image, 0, 2, 0, 1,
+ xoptions=gtk.FILL|gtk.SHRINK,
+ yoptions=gtk.FILL|gtk.SHRINK,
+ xpadding=10,
+ ypadding=10)
+
+ self.btn_save = gtk.Button(_("Save"))
+ self.btn_save.connect('button_press_event',
+ self.save_button_press_event_cb)
+ image_table.attach(self.btn_save, 0, 1, 1, 2,
+ xoptions=gtk.SHRINK,
+ yoptions=gtk.SHRINK, xpadding=10,
+ ypadding=10)
+ self.btn_save.props.sensitive = False
+ self.btn_save.show()
+
+ self.btn_delete = gtk.Button(_("Delete"))
+ self.btn_delete.connect('button_press_event',
+ self.delete_button_press_event_cb)
+ image_table.attach(self.btn_delete, 1, 2, 1, 2,
+ xoptions=gtk.SHRINK,
+ yoptions=gtk.SHRINK, xpadding=10,
+ ypadding=10)
+ self.btn_delete.props.sensitive = False
+ self.btn_delete.show()
+
+ column_table.attach(image_table, 0, 1, 0, 1,
+ xoptions=gtk.FILL|gtk.SHRINK,
+ yoptions=gtk.SHRINK, xpadding=10,
+ ypadding=10)
+
+ entry_table = gtk.Table(rows=3, columns=2,
+ homogeneous=False)
+
+ title_label = gtk.Label(_("Title"))
+ entry_table.attach(title_label, 0, 1, 0, 1,
+ xoptions=gtk.SHRINK,
+ yoptions=gtk.SHRINK,
+ xpadding=10, ypadding=10)
+ title_label.show()
+
+ self.title_entry = gtk.Entry(max=0)
+ entry_table.attach(self.title_entry, 1, 2, 0, 1,
+ xoptions=gtk.FILL|gtk.SHRINK,
+ yoptions=gtk.SHRINK, xpadding=10, ypadding=10)
+ self.title_entry.connect('key_press_event',
+ self.key_press_event_cb)
+ self.title_entry.show()
+
+ description_label = gtk.Label(_("Description"))
+ entry_table.attach(description_label, 0, 1, 1, 2,
+ xoptions=gtk.SHRINK,
+ yoptions=gtk.SHRINK,
+ xpadding=10, ypadding=10)
+ description_label.show()
+
+ self.description_textview = gtk.TextView()
+ self.description_textview.set_wrap_mode(
+ gtk.WRAP_WORD)
+ entry_table.attach(self.description_textview,
+ 1, 2, 1, 2,
+ xoptions=gtk.EXPAND|gtk.FILL|gtk.SHRINK,
+ yoptions=gtk.EXPAND|gtk.FILL|gtk.SHRINK,
+ xpadding=10, ypadding=10)
+ self.description_textview.props.accepts_tab = False
+ self.description_textview.connect('key_press_event',
+ self.key_press_event_cb)
+ self.description_textview.show()
+
+ tags_label = gtk.Label(_("Tags"))
+ entry_table.attach(tags_label, 0, 1, 2, 3,
+ xoptions=gtk.SHRINK,
+ yoptions=gtk.SHRINK,
+ xpadding=10, ypadding=10)
+ tags_label.show()
+
+ self.tags_textview = gtk.TextView()
+ self.tags_textview.set_wrap_mode(gtk.WRAP_WORD)
+ entry_table.attach(self.tags_textview, 1, 2, 2, 3,
+ xoptions=gtk.FILL,
+ yoptions=gtk.EXPAND|gtk.FILL,
+ xpadding=10, ypadding=10)
+ self.tags_textview.props.accepts_tab = False
+ self.tags_textview.connect('key_press_event',
+ self.key_press_event_cb)
+ self.tags_textview.show()
+
+ entry_table.show()
+
+ self.scroller_entry = gtk.ScrolledWindow(
+ hadjustment=None, vadjustment=None)
+ self.scroller_entry.set_policy(gtk.POLICY_NEVER,
+ gtk.POLICY_AUTOMATIC)
+ self.scroller_entry.add_with_viewport(entry_table)
+ self.scroller_entry.show()
+
+ column_table.attach(self.scroller_entry,
+ 1, 2, 0, 1,
+ xoptions=gtk.FILL|gtk.EXPAND|gtk.SHRINK,
+ yoptions=gtk.FILL|gtk.EXPAND|gtk.SHRINK,
+ xpadding=10, ypadding=10)
+ image_table.show()
+ column_table.show()
+
+ vbox = gtk.VBox(homogeneous=True, spacing=5)
+ vbox.pack_start(column_table)
+ vbox.pack_end(self.list_scroller_journal)
+
+ canvas.append_page(vbox, tab1_label)
+
+ self._filechooser = gtk.FileChooserWidget(
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ backend=None)
+ self._filechooser.set_current_folder("/media")
+ self.copy_button = gtk.Button(
+ _("Copy File To The Journal"))
+ self.copy_button.connect('clicked',
+ self.create_journal_entry)
+ self.copy_button.show()
+ self._filechooser.set_extra_widget(self.copy_button)
+ preview = gtk.Image()
+ self._filechooser.set_preview_widget(preview)
+ self._filechooser.connect("update-preview",
+ self.update_preview_cb, preview)
+ tab2_label = gtk.Label(_("Files"))
+ tab2_label.set_attributes(label_attributes)
+ tab2_label.show()
+ canvas.append_page(self._filechooser, tab2_label)
+
+ self.set_canvas(canvas)
+ self.show_all()
+
+ toolbox = activity.ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.keep.props.visible = False
+ activity_toolbar.share.props.visible = False
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ self.load_journal_table()
+
+ bus = dbus.SessionBus()
+ remote_object = bus.get_object(
+ DS_DBUS_SERVICE, DS_DBUS_PATH)
+ _datastore = dbus.Interface(remote_object,
+ DS_DBUS_INTERFACE)
+ _datastore.connect_to_signal('Created',
+ self.datastore_created_cb)
+ _datastore.connect_to_signal('Updated',
+ self.datastore_updated_cb)
+ _datastore.connect_to_signal('Deleted',
+ self.datastore_deleted_cb)
+
+ self.selected_journal_entry = None
+
+ def update_preview_cb(self, file_chooser, preview):
+ filename = file_chooser.get_preview_filename()
+ try:
+ file_mimetype = mime.get_for_file(filename)
+ if file_mimetype.startswith('image/'):
+ pixbuf = \
+ gtk.gdk.pixbuf_new_from_file_at_size(
+ filename,
+ style.zoom(320), style.zoom(240))
+ preview.set_from_pixbuf(pixbuf)
+ have_preview = True
+ elif file_mimetype == 'application/x-cbz':
+ fname = self.extract_image(filename)
+ pixbuf = \
+ gtk.gdk.pixbuf_new_from_file_at_size(
+ fname,
+ style.zoom(320), style.zoom(240))
+ preview.set_from_pixbuf(pixbuf)
+ have_preview = True
+ os.remove(fname)
+ else:
+ have_preview = False
+ except:
+ have_preview = False
+ file_chooser.set_preview_widget_active(
+ have_preview)
+ return
+
+ def key_press_event_cb(self, entry, event):
+ self.btn_save.props.sensitive = True
+
+ def save_button_press_event_cb(self, entry, event):
+ self.update_entry()
+
+ def delete_button_press_event_cb(self, entry, event):
+ datastore.delete(
+ self.selected_journal_entry.object_id)
+
+ def datastore_created_cb(self, uid):
+ new_jobject = datastore.get(uid)
+ iter = self.ls_journal.append()
+ title = new_jobject.metadata['title']
+ self.ls_journal.set(iter, COLUMN_TITLE, title)
+ mime = new_jobject.metadata['mime_type']
+ self.ls_journal.set(iter, COLUMN_MIME, mime)
+ self.ls_journal.set(iter, COLUMN_JOBJECT,
+ new_jobject)
+
+ def datastore_updated_cb(self, uid):
+ new_jobject = datastore.get(uid)
+ iter = self.ls_journal.get_iter_first()
+ for row in self.ls_journal:
+ jobject = row[COLUMN_JOBJECT]
+ if jobject.object_id == uid:
+ title = new_jobject.metadata['title']
+ self.ls_journal.set_value(iter,
+ COLUMN_TITLE, title)
+ break
+ iter = self.ls_journal.iter_next(iter)
+ object_id = self.selected_journal_entry.object_id
+ if object_id == uid:
+ self.set_form_fields(new_jobject)
+
+ def datastore_deleted_cb(self, uid):
+ save_path = self.selected_path
+ iter = self.ls_journal.get_iter_first()
+ for row in self.ls_journal:
+ jobject = row[COLUMN_JOBJECT]
+ if jobject.object_id == uid:
+ self.ls_journal.remove(iter)
+ break
+ iter = self.ls_journal.iter_next(iter)
+
+ try:
+ self.selection_journal.select_path(save_path)
+ self.tv_journal.grab_focus()
+ except:
+ self.title_entry.set_text('')
+ description_textbuffer = \
+ self.description_textview.get_buffer()
+ description_textbuffer.set_text('')
+ tags_textbuffer = \
+ self.tags_textview.get_buffer()
+ tags_textbuffer.set_text('')
+ self.btn_save.props.sensitive = False
+ self.btn_delete.props.sensitive = False
+ self.image.clear()
+ self.image.show()
+
+ def update_entry(self):
+ needs_update = False
+
+ if self.selected_journal_entry is None:
+ return
+
+ object_id = self.selected_journal_entry.object_id
+ jobject = datastore.get(object_id)
+
+ old_title = jobject.metadata.get('title', None)
+ if old_title != self.title_entry.props.text:
+ jobject.metadata['title'] = \
+ self.title_entry.props.text
+ jobject.metadata['title_set_by_user'] = '1'
+ needs_update = True
+
+ old_tags = jobject.metadata.get('tags', None)
+ new_tags = \
+ self.tags_textview.props.buffer.props.text
+ if old_tags != new_tags:
+ jobject.metadata['tags'] = new_tags
+ needs_update = True
+
+ old_description = jobject.metadata.get(
+ 'description', None)
+ new_description = \
+ self.description_textview.props.buffer.props.text
+ if old_description != new_description:
+ jobject.metadata['description'] = new_description
+ needs_update = True
+
+ if needs_update:
+ datastore.write(jobject, update_mtime=False,
+ reply_handler=self.datastore_write_cb,
+ error_handler=self.datastore_write_error_cb)
+ self.btn_save.props.sensitive = False
+
+ def datastore_write_cb(self):
+ pass
+
+ def datastore_write_error_cb(self, error):
+ logging.error(
+ 'sugarcommander.datastore_write_error_cb:'
+ ' %r' % error)
+
+ def close(self, skip_save=False):
+ "Override the close method so we don't try to
+ create a Journal entry."
+ activity.Activity.close(self, True)
+
+ def selection_journal_cb(self, selection):
+ self.btn_delete.props.sensitive = True
+ tv = selection.get_tree_view()
+ model = tv.get_model()
+ sel = selection.get_selected()
+ if sel:
+ model, iter = sel
+ jobject = model.get_value(iter,COLUMN_JOBJECT)
+ jobject = datastore.get(jobject.object_id)
+ self.selected_journal_entry = jobject
+ self.set_form_fields(jobject)
+ self.selected_path = model.get_path(iter)
+
+ def set_form_fields(self, jobject):
+ self.title_entry.set_text(jobject.metadata['title'])
+ description_textbuffer = \
+ self.description_textview.get_buffer()
+ if jobject.metadata.has_key('description'):
+ description_textbuffer.set_text(
+ jobject.metadata['description'])
+ else:
+ description_textbuffer.set_text('')
+ tags_textbuffer = self.tags_textview.get_buffer()
+ if jobject.metadata.has_key('tags'):
+ tags_textbuffer.set_text(jobject.metadata['tags'])
+ else:
+ tags_textbuffer.set_text('')
+ self.create_preview(jobject.object_id)
+
+ def create_preview(self, object_id):
+ jobject = datastore.get(object_id)
+
+ if jobject.metadata.has_key('preview'):
+ preview = jobject.metadata['preview']
+ if preview is None or preview == '' \
+ or preview == 'None':
+ if jobject.metadata['mime_type'].startswith(
+ 'image/'):
+ filename = jobject.get_file_path()
+ self.show_image(filename)
+ return
+ if jobject.metadata['mime_type'] == \
+ 'application/x-cbz':
+ filename = jobject.get_file_path()
+ fname = self.extract_image(filename)
+ self.show_image(fname)
+ os.remove(fname)
+ return
+
+ if jobject.metadata.has_key('preview') and \
+ len(jobject.metadata['preview']) &gt; 4:
+
+ if jobject.metadata['preview'][1:4] == 'PNG':
+ preview_data = jobject.metadata['preview']
+ else:
+ import base64
+ preview_data = \
+ base64.b64decode(
+ jobject.metadata['preview'])
+
+ loader = gtk.gdk.PixbufLoader()
+ loader.write(preview_data)
+ scaled_buf = loader.get_pixbuf()
+ loader.close()
+ self.image.set_from_pixbuf(scaled_buf)
+ self.image.show()
+ else:
+ self.image.clear()
+ self.image.show()
+
+ def load_journal_table(self):
+ self.btn_save.props.sensitive = False
+ self.btn_delete.props.sensitive = False
+ ds_mounts = datastore.mounts()
+ mountpoint_id = None
+ if len(ds_mounts) == 1 and \
+ ds_mounts[0]['id'] == 1:
+ pass
+ else:
+ for mountpoint in ds_mounts:
+ id = mountpoint['id']
+ uri = mountpoint['uri']
+ if uri.startswith('/home'):
+ mountpoint_id = id
+
+ query = {}
+ if mountpoint_id is not None:
+ query['mountpoints'] = [ mountpoint_id ]
+ ds_objects, num_objects = \
+ datastore.find(query, properties=['uid',
+ 'title', 'mime_type'])
+
+ self.ls_journal.clear()
+ for i in xrange (0, num_objects, 1):
+ iter = self.ls_journal.append()
+ title = ds_objects[i].metadata['title']
+ self.ls_journal.set(iter, COLUMN_TITLE, title)
+ mime = ds_objects[i].metadata['mime_type']
+ self.ls_journal.set(iter, COLUMN_MIME, mime)
+ self.ls_journal.set(iter, COLUMN_JOBJECT,
+ ds_objects[i])
+ if not self.selected_journal_entry is None and \
+ self.selected_journal_entry.object_id == \
+ ds_objects[i].object_id:
+ self.selection_journal.select_iter(iter)
+
+ self.ls_journal.set_sort_column_id(COLUMN_TITLE,
+ gtk.SORT_ASCENDING)
+ v_adjustment = \
+ self.list_scroller_journal.get_vadjustment()
+ v_adjustment.value = 0
+ return ds_objects[0]
+
+ def create_journal_entry(self, widget, data=None):
+ filename = self._filechooser.get_filename()
+ journal_entry = datastore.create()
+ journal_entry.metadata['title'] = \
+ self.make_new_filename(filename)
+ journal_entry.metadata['title_set_by_user'] = '1'
+ journal_entry.metadata['keep'] = '0'
+ file_mimetype = mime.get_for_file(filename)
+ if not file_mimetype is None:
+ journal_entry.metadata['mime_type'] = \
+ file_mimetype
+ journal_entry.metadata['buddies'] = ''
+ if file_mimetype.startswith('image/'):
+ preview = \
+ self.create_preview_metadata(filename)
+ elif file_mimetype == 'application/x-cbz':
+ fname = self.extract_image(filename)
+ preview = self.create_preview_metadata(fname)
+ os.remove(fname)
+ else:
+ preview = ''
+ if not preview == '':
+ journal_entry.metadata['preview'] = \
+ dbus.ByteArray(preview)
+ else:
+ journal_entry.metadata['preview'] = ''
+
+ journal_entry.file_path = filename
+ datastore.write(journal_entry)
+ self.alert(_('Success'), _('%s added to Journal.')
+ % self.make_new_filename(filename))
+
+ def alert(self, title, text=None):
+ alert = NotifyAlert(timeout=20)
+ alert.props.title = title
+ alert.props.msg = text
+ self.add_alert(alert)
+ alert.connect('response', self.alert_cancel_cb)
+ alert.show()
+
+ def alert_cancel_cb(self, alert, response_id):
+ self.remove_alert(alert)
+
+ def show_image(self, filename):
+ "display a resized image in a preview"
+ scaled_buf = gtk.gdk.pixbuf_new_from_file_at_size(
+ filename,
+ style.zoom(320), style.zoom(240))
+ self.image.set_from_pixbuf(scaled_buf)
+ self.image.show()
+
+ def extract_image(self, filename):
+ zf = zipfile.ZipFile(filename, 'r')
+ image_files = zf.namelist()
+ image_files.sort()
+ file_to_extract = image_files[0]
+ extract_new_filename = self.make_new_filename(
+ file_to_extract)
+ if extract_new_filename is None or \
+ extract_new_filename == '':
+ # skip over directory name if the images
+ # are in a subdirectory.
+ file_to_extract = image_files[1]
+ extract_new_filename = self.make_new_filename(
+ file_to_extract)
+
+ if len(image_files) &gt; 0:
+ if self.save_extracted_file(zf, file_to_extract):
+ fname = os.path.join(self.get_activity_root(),
+ 'instance',
+ extract_new_filename)
+ return fname
+
+ def save_extracted_file(self, zipfile, filename):
+ "Extract the file to a temp directory for viewing"
+ try:
+ filebytes = zipfile.read(filename)
+ except zipfile.BadZipfile, err:
+ print 'Error opening the zip file: %s' % (err)
+ return False
+ except KeyError, err:
+ self.alert('Key Error', 'Zipfile key not found: '
+ + str(filename))
+ return
+ outfn = self.make_new_filename(filename)
+ if (outfn == ''):
+ return False
+ fname = os.path.join(self.get_activity_root(),
+ 'instance', outfn)
+ f = open(fname, 'w')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close()
+ return True
+
+ def make_new_filename(self, filename):
+ partition_tuple = filename.rpartition('/')
+ return partition_tuple[2]
+
+ def create_preview_metadata(self, filename):
+
+ file_mimetype = mime.get_for_file(filename)
+ if not file_mimetype.startswith('image/'):
+ return ''
+
+ scaled_pixbuf = \
+ gtk.gdk.pixbuf_new_from_file_at_size(
+ filename,
+ style.zoom(320), style.zoom(240))
+ preview_data = []
+
+ def save_func(buf, data):
+ data.append(buf)
+
+ scaled_pixbuf.save_to_callback(save_func,
+ 'png',
+ user_data=preview_data)
+ preview_data = ''.join(preview_data)
+
+ return preview_data</pre>
+<p>Let's look at this code one method at a time.
+ <br/></p>
+<h2> Adding A Journal Entry
+ <br/></h2>
+<p>We add a Journal entry when someone pushes a button on the gtk.FileChooser.&#160; This is the code that gets run:
+</p>
+<pre> def create_journal_entry(self, widget, data=None):
+ filename = self._filechooser.get_filename()
+ journal_entry = datastore.create()
+ journal_entry.metadata['title'] = \
+ self.make_new_filename(
+ filename)
+ journal_entry.metadata['title_set_by_user'] = '1'
+ journal_entry.metadata['keep'] = '0'
+ file_mimetype = mime.get_for_file(filename)
+ if not file_mimetype is None:
+ journal_entry.metadata['mime_type'] = \
+ file_mimetype
+ journal_entry.metadata['buddies'] = ''
+ if file_mimetype.startswith('image/'):
+ preview = self.create_preview_metadata(filename)
+ elif file_mimetype == 'application/x-cbz':
+ fname = self.extract_image(filename)
+ preview = self.create_preview_metadata(fname)
+ os.remove(fname)
+ else:
+ preview = ''
+ if not preview == '':
+ journal_entry.metadata['preview'] = \
+ dbus.ByteArray(preview)
+ else:
+ journal_entry.metadata['preview'] = ''
+ journal_entry.file_path = filename
+ datastore.write(journal_entry)
+</pre>
+<p>The only thing worth commenting on here is the metadata.&#160; <strong>title</strong> is what appears as #3 in the picture below.&#160; <strong>title_set_by_user</strong> is set to 1 so that the Activity won't prompt the user to change the title when the Activity closes.&#160; <strong>keep</strong> refers to the little star that appears at the beginning of the Journal entry (see #1 in the picture below).&#160; Highlight it by setting this to 1, otherwise set to 0.&#160;&#160; <strong>buddies</strong> is a list of users that collaborated on the Journal entry, and in this case there aren't any (these show up as #4 in the picture below).&#160;
+ <br/></p><img alt="Journal Legend" src="static/ActivitiesGuideSugar-journal_main_screen-en.png" height="415" width="600"/><h2>
+</h2>
+<p><strong>preview</strong> is an image file in the PNG format that is a screenshot of the Activity in action.&#160; This is created by the Activity itself when it is run so there is no need to make one when you add a Journal entry.&#160; You can simply use an empty string ('') for this property.
+</p>
+<p>Because previews are much more visible in Sugar Commander than they are in the regular Journal Activity I decided that Sugar Commander should make a preview image for image files and comic books as soon as they are added to the Journal.&#160; To do this I made a pixbuf of the image that would fit within the scaled dimensions of 320x240 pixels and made a <strong>dbus.ByteArray</strong> out of it, which is the format that the Journal uses to store preview images.
+ <br/></p>
+<p><strong>mime_type</strong> describes the format of the file and is generally assigned based on the filename suffix.&#160; For instance, files ending in .html have a MIME type of 'text/html'.&#160; Python has a package called <strong>mimetypes </strong>that takes a file name and figures out what its MIME type should be, but Sugar provides its own package to do the same thing.&#160; For most files either one would give the correct answer, but Sugar has its own MIME types for things like Activity bundles, etc. so for best results you really should use Sugar's mime package.&#160; You can import it like this:
+</p>
+<pre>from sugar import mime</pre>
+<p>The rest of the metadata (icon, modified time) is created automatically.&#160;
+ <br/></p>
+<h2>NOT Adding A Journal Entry
+</h2>
+<p>Sugar Activities by default create a Journal entry using the <em>write_file()</em> method.&#160; There will be Activities that don't need to do this.&#160; For instance, <strong>Get Internet Archive Books</strong> downloads e-books to the Journal, but has no need for a Journal entry of its own.&#160; The same thing is true of <strong>Sugar Commander</strong>.&#160; You might make a game that keeps track of high scores.&#160; You could keep those scores in a Journal entry, but that would require players to resume the game from the Journal rather than just starting it up from the Activity Ring.&#160; For that reason you might prefer to store the high scores in a file in the <strong>data </strong>directory rather than the Journal, and not leave a Journal entry behind at all.
+</p>
+<p>Sugar gives you a way to do that.&#160; First you need to specify an extra argument in your Activity's <em>__init__()</em> method like this:
+</p>
+<pre>class SugarCommander(activity.Activity):
+ def __init__(self, handle, create_jobject=True):
+ "The entry point to the Activity"
+ activity.Activity.__init__(self, handle, False)
+</pre>
+<p>Second, you need to override the <em>close()</em> method like this:
+ <br/></p>
+<pre> def close(self, skip_save=False):
+ "Override the close method so we don't try to
+ create a Journal entry."
+ activity.Activity.close(self, True)
+</pre>
+<p>That's all there is to it.
+</p>
+<h2>Listing Out Journal Entries
+</h2>
+<p>If you need to list out Journal entries you can use the <em>find()</em> method of <strong>datastore</strong>.&#160; The find method takes an argument containing search criteria.&#160; If you want to search for image files you can search by mime-type using a statement like this:
+</p>
+<pre> ds_objects, num_objects = datastore.find(
+ {'mime_type':['image/jpeg',
+ 'image/gif', 'image/tiff', 'image/png']},
+ properties=['uid',
+ 'title', 'mime_type']))
+</pre>
+<p>You can use any metadata attribute to search on.&#160; If you want to list out everything in the Journal you can use an empty search criteria like this:
+</p>
+<pre> ds_objects, num_objects = datastore.find({},
+ properties=['uid',
+ 'title', 'mime_type']))
+</pre>
+<p>The properties argument specifies what metadata to return for each object in the list.&#160; You should limit these to what you plan to use, but always include <strong>uid</strong>.&#160; One thing you should <em>never</em> include in a list is <strong>preview</strong>.&#160; This is an image file showing what the Activity for the Journal object looked like when it was last used.&#160; If for some reason you need this there is a simple way to get it for an individual Journal object, but you never want to include it in a list because it will slow down your Activity enormously.
+ <br/></p>
+<p>Listing out what is in the Journal is complicated because of the datastore rewrite done for Sugar .84.&#160; Before .84 the <em>datastore.find()</em> method listed out both Journal entries and files on external media like thumb drives and SD cards and you need to figure out which is which.&#160; In .84 and later it only lists out Journal entries.&#160; Fortunately it is possible to write code that supports either behavior.&#160; Here is code in <strong>Sugar Commander</strong> that only lists Journal entries:
+ <br/></p>
+<pre> def load_journal_table(self):
+ self.btn_save.props.sensitive = False
+ self.btn_delete.props.sensitive = False
+ ds_mounts = datastore.mounts()
+ mountpoint_id = None
+ if len(ds_mounts) == 1 and ds_mounts[0]['id'] == 1:
+ pass
+ else:
+ for mountpoint in ds_mounts:
+ id = mountpoint['id']
+ uri = mountpoint['uri']
+ if uri.startswith('/home'):
+ mountpoint_id = id
+
+ query = {}
+ if mountpoint_id is not None:
+ query['mountpoints'] = [ mountpoint_id ]
+ ds_objects, num_objects = datastore.find(
+ query, properties=['uid',
+ 'title', 'mime_type'])
+
+ self.ls_journal.clear()
+ for i in xrange (0, num_objects, 1):
+ iter = self.ls_journal.append()
+ title = ds_objects[i].metadata['title']
+ self.ls_journal.set(iter,
+ COLUMN_TITLE, title)
+ mime = ds_objects[i].metadata['mime_type']
+ self.ls_journal.set(iter, COLUMN_MIME, mime)
+ self.ls_journal.set(iter, COLUMN_JOBJECT,
+ ds_objects[i])
+ if not self.selected_journal_entry is None and \
+ self.selected_journal_entry.object_id == \
+ ds_objects[i].object_id:
+ self.selection_journal.select_iter(iter)
+
+ self.ls_journal.set_sort_column_id(COLUMN_TITLE,
+ gtk.SORT_ASCENDING)
+ v_adjustment = \
+ self.list_scroller_journal.get_vadjustment()
+ v_adjustment.value = 0
+ return ds_objects[0]</pre>
+<p>We need to use the <em>datastore.mounts()</em> method for two purposes:
+</p>
+<ul><li>In Sugar .82 and below it will list out all mount points, including the place the Journal is mounted on and the places external media is mounted on.&#160; The mountpoint is a Python dictionary that contains a <strong>uri</strong> property (which is the path to the mount point) and an <strong>id</strong> property (which is a name given to the mount point).&#160; Every Journal entry has a metadata attribute named <strong>mountpoint</strong>.&#160; The Journal <strong>uri</strong> will be the only one starting with <strong>/home</strong>, so if we limit the search to Journal objects where the <strong>id</strong> of that mountpoint equals the <strong>mountpoint</strong> metadata in the Journal objects we can easily list only objects from the Journal.</li>
+ <li>In Sugar .84 and later the <em>datastore.mounts()</em> method still exists but doesn't tell you anything about mountpoints.&#160; However, you can use the code above to see if there is only one mountpoint and if its id is 1.&#160; If it is you know you're dealing with the rewritten datastore of .84 and later.&#160; The other difference is that the Journal objects no longer have metadata with a key of <strong>mountpoint</strong>.&#160; If you use the code above it will account for this difference and work with either version of Sugar.</li>
+</ul><p>What if you want the Sugar .82 behavior, listing both Journal entries and USB files as Journal objects, in both .82 and .84 and up?&#160; I wanted to do that for <strong>View Slides</strong> and ended up using this code:
+</p>
+<pre> def load_journal_table(self):
+ ds_objects, num_objects = datastore.find(
+ {'mime_type':['image/jpeg',
+ 'image/gif', 'image/tiff', 'image/png']},
+ properties=['uid', 'title', 'mime_type'])
+ self.ls_right.clear()
+ for i in xrange (0, num_objects, 1):
+ iter = self.ls_right.append()
+ title = ds_objects[i].metadata['title']
+ mime_type = ds_objects[i].metadata['mime_type']
+ if mime_type == 'image/jpeg' \
+ and not title.endswith('.jpg') \
+ and not title.endswith('.jpeg') \
+ and not title.endswith('.JPG') \
+ and not title.endswith('.JPEG') :
+ title = title + '.jpg'
+ if mime_type == 'image/png' \
+ and not title.endswith('.png') \
+ and not title.endswith('.PNG'):
+ title = title + '.png'
+ if mime_type == 'image/gif' \
+ and not title.endswith('.gif')\
+ and not title.endswith('.GIF'):
+ title = title + '.gif'
+ if mime_type == 'image/tiff' \
+ and not title.endswith('.tiff')\
+ and not title.endswith('.TIFF'):
+ title = title + '.tiff'
+ self.ls_right.set(iter, COLUMN_IMAGE, title)
+ jobject_wrapper = JobjectWrapper()
+ jobject_wrapper.set_jobject(ds_objects[i])
+ self.ls_right.set(iter, COLUMN_PATH,
+ jobject_wrapper)
+
+ valid_endings = ('.jpg', '.jpeg', '.JPEG',
+ '.JPG', '.gif', '.GIF', '.tiff',
+ '.TIFF', '.png', '.PNG')
+ ds_mounts = datastore.mounts()
+ if len(ds_mounts) == 1 and ds_mounts[0]['id'] == 1:
+ # datastore.mounts() is stubbed out,
+ # we're running .84 or better
+ for dirname, dirnames, filenames in os.walk(
+ '/media'):
+ if '.olpc.store' in dirnames:
+ dirnames.remove('.olpc.store')
+ # don't visit .olpc.store directories
+ for filename in filenames:
+ if filename.endswith(valid_endings):
+ iter = self.ls_right.append()
+ jobject_wrapper = JobjectWrapper()
+ jobject_wrapper.set_file_path(
+ os.path.join(dirname, filename))
+ self.ls_right.set(iter, COLUMN_IMAGE,
+ filename)
+ self.ls_right.set(iter, COLUMN_PATH,
+ jobject_wrapper)
+
+ self.ls_right.set_sort_column_id(COLUMN_IMAGE,
+ gtk.SORT_ASCENDING)
+</pre>
+<p>In this case I use the <em>datastore.mounts()</em> method to figure out what version of the datastore I have and then if I'm running .84 and later I use <em>os.walk()</em> to create a flat list of all files in all directories found under the directory <strong>/media</strong> (which is where USB and SD cards are always mounted).&#160; I can't make these files into directories, but what I can do is make a wrapper class that can contain either a Journal object or a file and use those objects where I would normally use Journal objects.&#160; The wrapper class looks like this:
+</p>
+<pre>class JobjectWrapper():
+ def __init__(self):
+ self.__jobject = None
+ self.__file_path = None
+
+ def set_jobject(self, jobject):
+ self.__jobject = jobject
+
+ def set_file_path(self, file_path):
+ self.__file_path = file_path
+
+ def get_file_path(self):
+ if self.__jobject != None:
+ return self.__jobject.get_file_path()
+ else:
+ return self.__file_path
+</pre>
+<h2> Using Journal Entries
+</h2>
+<p>When you're ready to read a file stored in a Journal object you can use the <em>get_file_path()</em> method of the Journal object to get a file path and open it for reading, like this:
+ <br/></p>
+<pre> fname = jobject.get_file_path()
+</pre>
+<p>One word of caution: be aware that this path does not exist until you call <em>get_file_path()</em> and will not exist long after.&#160; With the Journal you work with copies of files in the Journal, not the originals.&#160; For that reason you don't want to store the return value of <em>get_file_path()</em> for later use because later it may not be valid.&#160; Instead, store the Journal object itself and call the method right before you need the path.
+</p>
+<p>Metadata entries for Journal objects generally contain strings and work the way you would expect, with one exception, which is the <strong>preview</strong>.
+ <br/></p>
+<pre> def create_preview(self, object_id):
+ jobject = datastore.get(object_id)
+
+ if jobject.metadata.has_key('preview'):
+ preview = jobject.metadata['preview']
+ if preview is None or preview == '' or
+ preview == 'None':
+ if jobject.metadata['mime_type'].startswith(
+ 'image/'):
+ filename = jobject.get_file_path()
+ self.show_image(filename)
+ return
+ if jobject.metadata['mime_type'] == \
+ 'application/x-cbz':
+ filename = jobject.get_file_path()
+ fname = self.extract_image(filename)
+ self.show_image(fname)
+ os.remove(fname)
+ return
+
+ if jobject.metadata.has_key('preview') and \
+ len(jobject.metadata['preview']) &gt; 4:
+
+ if jobject.metadata['preview'][1:4] == 'PNG':
+ preview_data = jobject.metadata['preview']
+ else:
+ import base64
+ preview_data = base64.b64decode(
+ jobject.metadata['preview'])
+
+ loader = gtk.gdk.PixbufLoader()
+ loader.write(preview_data)
+ scaled_buf = loader.get_pixbuf()
+ loader.close()
+ self.image.set_from_pixbuf(scaled_buf)
+ self.image.show()
+ else:
+ self.image.clear()
+ self.image.show()
+</pre>
+<p> The <strong>preview </strong>metadata attribute is different in two ways:
+</p>
+<ul><li>We should never request <strong>preview</strong> as metadata to be returned in our list of Journal objects.&#160; We'll need to get a complete copy of the Journal object to get it.&#160; Since we already have a Journal object we can get the complete Journal object by getting its <strong>object id</strong> then requesting a new copy from the datastore using the id.</li>
+ <li>The preview image is a <strong>binary</strong> object (<strong>dbus.ByteArray</strong>) but in versions of Sugar older than .82 it will be stored as a text string.&#160; To accomplish this it is <strong>base 64 encoded</strong>.</li>
+</ul><p>The code you would use to get a complete copy of a Journal object looks like this:
+</p>
+<pre> object_id = jobject.object_id
+ jobject = datastore.get(object_id)
+</pre>
+<p>Now for an explanation of base 64 encoding.&#160; You've probably heard that computers use the base two numbering system, in which the only digits used are 1 and 0.&#160; A unit of data storage that can hold either a zero or a one is called a <strong>bit</strong>.&#160; Computers need to store information besides numbers, so to accomodate this we group bits into groups of 8 (usually) and these groups are called <strong>bytes</strong>.&#160; If you only use 7 of the 8 bits in a byte you can store a letter of the Roman alphabet, a punctuation mark, or a single digit, plus things like tabs and line feed characters.&#160; Any file that can be created using only 7 bits out of the 8 is called a <strong>text file</strong>.&#160; Everything that needs all 8 bits of each byte to make, including computer programs, movies, music, and pictures of Jessica Alba is a <strong>binary</strong>.&#160; In versions of Sugar before .82 Journal object metadata can only store text strings.&#160; Somehow we need to represent 8-bit bytes in 7 bits.&#160; We do this by grouping the bytes together into a larger collection of bits and then splitting them back out into groups of 7 bits.&#160; Python has the <strong>base64</strong> package to do this for us.
+</p>
+<p>Base 64 encoding is actually a pretty common technique.&#160; If you've ever sent an email with an attached file the file was base 64 encoded.
+</p>
+<p>The code above has a couple of ways of creating a preview image.&#160; If the preview metadata contains a PNG image it is loaded into a pixbuf and displayed.&#160; If there is no preview metadata but the MIME type is for an image file or a comic book zip file we create the preview from the Journal entry itself.
+ <br/></p>
+<p>The code checks the first three characters of the preview metadata to see if they are 'PNG'.&#160; If so, the file is a <strong>Portable Network Graphics</strong> image stored as a binary and does not need to be converted from base 64 encoding, otherwise it does.
+</p>
+<h2>Updating A Journal Object
+</h2>
+<p>The code to update a Journal object looks like this:
+</p>
+<pre> def update_entry(self):
+ needs_update = False
+
+ if self.selected_journal_entry is None:
+ return
+
+ object_id = self.selected_journal_entry.object_id
+ jobject = datastore.get(object_id)
+
+ old_title = jobject.metadata.get('title', None)
+ if old_title != self.title_entry.props.text:
+ jobject.metadata['title'] = \
+ self.title_entry.props.text
+ jobject.metadata['title_set_by_user'] = '1'
+ needs_update = True
+
+ old_tags = jobject.metadata.get('tags', None)
+ new_tags = \
+ self.tags_textview.props.buffer.props.text
+ if old_tags != new_tags:
+ jobject.metadata['tags'] = new_tags
+ needs_update = True
+
+ old_description = \
+ jobject.metadata.get('description', None)
+ new_description = \
+ self.description_textview.props.buffer.props.text
+ if old_description != new_description:
+ jobject.metadata['description'] = \
+ new_description
+ needs_update = True
+
+ if needs_update:
+ datastore.write(jobject, update_mtime=False,
+ reply_handler=self.datastore_write_cb,
+ error_handler=self.datastore_write_error_cb)
+ self.btn_save.props.sensitive = False
+
+ def datastore_write_cb(self):
+ pass
+
+ def datastore_write_error_cb(self, error):
+ logging.error(
+ 'sugarcommander.datastore_write_error_cb:'
+ ' %r' % error)
+</pre>
+<h2>Deleting A Journal Entry
+</h2>
+<p>The code to delete a Journal entry is this:
+</p>
+<pre> def delete_button_press_event_cb(self, entry, event):
+ datastore.delete(
+ self.selected_journal_entry.object_id)
+</pre>
+<h2>Getting Callbacks From The Journal Using D-Bus
+</h2>
+<p>In the chapter on <strong>Making Shared Activities</strong> we saw how D-Bus calls sent over Telepathy Tubes could be used to send messages from an Activity running on one computer to the same Activity running on a different computer.&#160;&#160; D-Bus is not normally used that way; typically it is used to send messages between programs running on the same computer.&#160;
+</p>
+<p>For example, if you're working with the Journal you can get callbacks whenever the Journal is updated.&#160; You get the callbacks whether the update was done by your Activity or elsewhere.&#160; If it is important for your Activity to know when the Journal has been updated you'll want to get these callbacks.
+</p>
+<p>The first thing you need to do is define some constants and import the dbus package:
+ <br/></p>
+<pre>DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
+DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
+DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
+import dbus
+</pre>
+<p>Next, in your __init__() method put code to connect to the signals and do the callbacks:
+ <br/></p>
+<pre> bus = dbus.SessionBus()
+ remote_object = bus.get_object(
+ DS_DBUS_SERVICE, DS_DBUS_PATH)
+ _datastore = dbus.Interface(remote_object,
+ DS_DBUS_INTERFACE)
+ _datastore.connect_to_signal('Created',
+ self._datastore_created_cb)
+ _datastore.connect_to_signal('Updated',
+ self._datastore_updated_cb)
+ _datastore.connect_to_signal('Deleted',
+ self._datastore_deleted_cb)
+</pre>
+<p>The methods being run by the callbacks might look something like this:
+</p>
+<pre> def datastore_created_cb(self, uid):
+ new_jobject = datastore.get(uid)
+ iter = self.ls_journal.append()
+ title = new_jobject.metadata['title']
+ self.ls_journal.set(iter,
+ COLUMN_TITLE, title)
+ mime = new_jobject.metadata['mime_type']
+ self.ls_journal.set(iter,
+ COLUMN_MIME, mime)
+ self.ls_journal.set(iter,
+ COLUMN_JOBJECT, new_jobject)
+
+ def datastore_updated_cb(self, uid):
+ new_jobject = datastore.get(uid)
+ iter = self.ls_journal.get_iter_first()
+ for row in self.ls_journal:
+ jobject = row[COLUMN_JOBJECT]
+ if jobject.object_id == uid:
+ title = new_jobject.metadata['title']
+ self.ls_journal.set_value(iter,
+ COLUMN_TITLE, title)
+ break
+ iter = self.ls_journal.iter_next(iter)
+ object_id = \
+ self.selected_journal_entry.object_id
+ if object_id == uid:
+ self.set_form_fields(new_jobject)
+
+ def datastore_deleted_cb(self, uid):
+ save_path = self.selected_path
+ iter = self.ls_journal.get_iter_first()
+ for row in self.ls_journal:
+ jobject = row[COLUMN_JOBJECT]
+ if jobject.object_id == uid:
+ self.ls_journal.remove(iter)
+ break
+ iter = self.ls_journal.iter_next(iter)
+
+ try:
+ self.selection_journal.select_path(
+ save_path)
+ self.tv_journal.grab_focus()
+ except:
+ self.title_entry.set_text('')
+ description_textbuffer = \
+ self.description_textview.get_buffer()
+ description_textbuffer.set_text('')
+ tags_textbuffer = \
+ self.tags_textview.get_buffer()
+ tags_textbuffer.set_text('')
+ self.btn_save.props.sensitive = False
+ self.btn_delete.props.sensitive = False
+ self.image.clear()
+ self.image.show()
+</pre>
+<p>The <strong>uid</strong> passed to each callback method is the <strong>object id</strong> of the Journal object that has been added, updated, or deleted.&#160; If an entry is added to the Journal I get the Journal object from the datastore by its uid, then add it to the gtk.ListStore for the gtk.TreeModel I'm using to list out Journal entries.&#160; If an entry is updated or deleted I need to account for the possibility that the Journal entry I am viewing or editing may have been updated or removed.&#160;&#160;&#160; I use the uid to figure out which row in the gtk.ListStore needs to be removed or modified by looping through the entries in the gtk.ListStore looking for a match.
+ <br/></p>
+<p>Now you know everything you'll ever need to know to work with the Journal.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch020_making-activities-using-pygame.xhtml b/studio/static/doc/myosa/ch020_making-activities-using-pygame.xhtml
new file mode 100644
index 0000000..f4ad6ee
--- /dev/null
+++ b/studio/static/doc/myosa/ch020_making-activities-using-pygame.xhtml
@@ -0,0 +1,536 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Making Activities Using PyGame
+</h1>
+<h2>Introduction
+</h2>
+<p><strong>PyGame</strong> and <strong>PyGTK</strong> are two different ways to make a Python program with a graphical user interface.&#160; Normally you would not use both in the same program.&#160; Each of them has its own way of creating a window and each has its own way of handling events.
+</p>
+<p>The base class Activity we have been using is an extension of the PyGTK Window class and uses PyGTK event handling.&#160; The toolbars all Activities use are PyGTK components.&#160; In short, any Activity written in Python must use PyGTK. &#160; Putting a PyGame program in the middle of a PyGTK program is a bit like putting a model ship in a bottle.&#160; Fortunately there is some Python code called <strong>SugarGame</strong> that will make it possible to do that.
+</p>
+<p>Before we figure out how we'll get it in the bottle, let's have a look at our ship.
+</p>
+<h2>Making A Standalone Game Using PyGame
+</h2>
+<p>As you might expect, it's a good idea to make a standalone Python game using PyGame before you make an Activity out of it.&#160; I am not an experienced PyGame developer, but using the tutorial <em>Rapid Game Development with Python</em> by Richard Jones at this URL:
+</p>
+<p><a href="http://richard.cgpublisher.com/product/pub.84/prod.11">http://richard.cgpublisher.com/product/pub.84/prod.11</a>
+ <br/></p>
+<p>I was able to put together a modest game in about a day.&#160; It would have been sooner but the tutorial examples had bugs in them and I had to spend a fair amount of time using <strong>The GIMP</strong> to create image files for the sprites in the game.&#160;
+</p>
+<p><strong>Sprites</strong> are small images, often animated, that represent objects in a game.&#160; They generally have a transparent background so they can be drawn on top of a background image.&#160; I used the <strong>PNG</strong> format for my sprite files because it supports having an <strong>alpha channel</strong> (another term that indicates that part of the image is transparent).
+</p>
+<p>PyGame has code to display background images, to create sprites and move them around on the background, and to detect when sprites collide with one another and do something when that happens.&#160; This is the basis for making a lot of 2D games.&#160; There are lots of games written with PyGame that could be easily adapted to be Sugar Activities.
+ <br/></p>
+<p>My game is similar to the car game in the tutorial, but instead of a car I have an airplane.&#160; The airplane is the <em>Demoiselle</em> created by Alberto Santos-Dumont in 1909.&#160; Instead of having "pads" to collide with I have four students of Otto Lilienthal hovering motionless in their hang gliders.&#160; The hang gliders pitch downwards when Santos-Dumont collides with them.&#160; The controls used for the game have been modified too.&#160; I use the Plus and Minus keys on both the main keyboard and the keypad, plus the keypad 9 and 3 keys, to open and close the throttle and the Up and Down arrows on both the main keyboard and the keypad to move the joystick forward and back.&#160; Using the keypad keys is useful for a couple of reasons.&#160; First, some versions of <strong>sugar-emulator</strong> don't recognize the arrow keys on the main keyboard.&#160; Second, the arrow keys on the keypad map to the game controller on the XO laptop, and the non-arrow keys on the keypad map to the other buttons on the XO laptop screen.&#160; These buttons can be used to play the game when the XO is in tablet mode.
+</p>
+<p>As a flight simulator it isn't much, but it does demonstrate at least some of the things PyGame can do.&#160; Here is the code for the game, which I'm calling <strong>Demoiselle</strong>:
+</p>
+<pre>#! /usr/bin/env python
+import pygame
+import math
+import sys
+
+class Demoiselle:
+ "This is a simple demonstration of using PyGame \
+ sprites and collision detection."
+ def __init__(self):
+ self.background = pygame.image.load('sky.jpg')
+ self.screen = pygame.display.get_surface()
+ self.screen.blit(self.background, (0, 0))
+ self.clock = pygame.time.Clock()
+ self.running = True
+
+ gliders = [
+ GliderSprite((200, 200)),
+ GliderSprite((800, 200)),
+ GliderSprite((200, 600)),
+ GliderSprite((800, 600)),
+ ]
+ self. glider_group = pygame.sprite.RenderPlain(
+ gliders)
+
+ def run(self):
+ "This method processes PyGame messages"
+ rect = self.screen.get_rect()
+ airplane = AirplaneSprite('demoiselle.png',
+ rect.center)
+ airplane_sprite = pygame.sprite.RenderPlain(
+ airplane)
+
+ while self.running:
+ self.clock.tick(30)
+
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ self.running = False
+ return
+ elif event.type == pygame.VIDEORESIZE:
+ pygame.display.set_mode(event.size,
+ pygame.RESIZABLE)
+ self.screen.blit(self.background,
+ (0, 0))
+
+ if not hasattr(event, 'key'):
+ continue
+ down = event.type == pygame.KEYDOWN
+ if event.key == pygame.K_DOWN or \
+ event.key == pygame.K_KP2:
+ airplane.joystick_back = down * 5
+ elif event.key == pygame.K_UP or \
+ event.key == pygame.K_KP8:
+ airplane.joystick_forward = down * -5
+ elif event.key == pygame.K_EQUALS or \
+ event.key == pygame.K_KP_PLUS or \
+ event.key == pygame.K_KP9:
+ airplane.throttle_up = down * 2
+ elif event.key == pygame.K_MINUS or \
+ event.key == pygame.K_KP_MINUS or \
+ event.key == pygame.K_KP3:
+ airplane.throttle_down = down * -2
+
+ self.glider_group.clear(self.screen,
+ self.background)
+ airplane_sprite.clear(self.screen,
+ self.background)
+ collisions = pygame.sprite.spritecollide(
+ airplane,
+ self.glider_group, False)
+ self.glider_group.update(collisions)
+ self.glider_group.draw(self.screen)
+ airplane_sprite.update()
+ airplane_sprite.draw(self.screen)
+ pygame.display.flip()
+
+class AirplaneSprite(pygame.sprite.Sprite):
+ "This class represents an airplane, the Demoiselle \
+ created by Alberto Santos-Dumont"
+ MAX_FORWARD_SPEED = 10
+ MIN_FORWARD_SPEED = 1
+ ACCELERATION = 2
+ TURN_SPEED = 5
+ def __init__(self, image, position):
+ pygame.sprite.Sprite.__init__(self)
+ self.src_image = pygame.image.load(image)
+ self.rect = pygame.Rect(
+ self.src_image.get_rect())
+ self.position = position
+ self.rect.center = self.position
+ self.speed = 1
+ self.direction = 0
+ self.joystick_back = self.joystick_forward = \
+ self.throttle_down = self.throttle_up = 0
+
+ def update(self):
+ "This method redraws the airplane in response\
+ to events."
+ self.speed += (self.throttle_up +
+ self.throttle_down)
+ if self.speed &gt; self.MAX_FORWARD_SPEED:
+ self.speed = self.MAX_FORWARD_SPEED
+ if self.speed &lt; self.MIN_FORWARD_SPEED:
+ self.speed = self.MIN_FORWARD_SPEED
+ self.direction += (self.joystick_forward + \
+ self.joystick_back)
+ x_coord, y_coord = self.position
+ rad = self.direction * math.pi / 180
+ x_coord += -self.speed * math.cos(rad)
+ y_coord += -self.speed * math.sin(rad)
+ screen = pygame.display.get_surface()
+ if y_coord &lt; 0:
+ y_coord = screen.get_height()
+
+ if x_coord &lt; 0:
+ x_coord = screen.get_width()
+
+ if x_coord &gt; screen.get_width():
+ x_coord = 0
+
+ if y_coord &gt; screen.get_height():
+ y_coord = 0
+ self.position = (x_coord, y_coord)
+ self.image = pygame.transform.rotate(
+ self.src_image, -self.direction)
+ self.rect = self.image.get_rect()
+ self.rect.center = self.position
+
+class GliderSprite(pygame.sprite.Sprite):
+ "This class represents an individual hang \
+ glider as developed by Otto Lilienthal."
+ def __init__(self, position):
+ pygame.sprite.Sprite.__init__(self)
+ self.normal = pygame.image.load(
+ 'glider_normal.png')
+ self.rect = pygame.Rect(self.normal.get_rect())
+ self.rect.center = position
+ self.image = self.normal
+ self.hit = pygame.image.load('glider_hit.png')
+ def update(self, hit_list):
+ "This method redraws the glider when it collides\
+ with the airplane and when it is no longer \
+ colliding with the airplane."
+ if self in hit_list:
+ self.image = self.hit
+ else:
+ self.image = self.normal
+
+def main():
+ "This function is called when the game is run \
+ from the command line"
+ pygame.init()
+ pygame.display.set_mode((0, 0), pygame.RESIZABLE)
+ game = Demoiselle()
+ game.run()
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
+</pre>
+<p>And here is the game in action:
+</p>
+<p><img alt="The Demoiselle standalone game." src="static/ActivitiesGuideSugar-demoiselle1-en.jpg" height="454" width="600"/></p>
+<p>You'll find the code for this game in the file <strong>demoiselle.py</strong> in the book examples project in Git.
+</p>
+<h2>Introducing SugarGame
+</h2>
+<p><strong>SugarGame</strong> is not part of Sugar proper.&#160; If you want to use it you'll need to include the Python code for SugarGame inside your Activity bundle.&#160; I've included the version of SugarGame I'm using in the book examples project in the <strong>sugargame</strong> directory, but when you make your own games you'll want to be sure and get the latest code to include.&#160; You can do that by downloading the project from Gitorious using these commands:
+</p>
+<pre><code>mkdir sugargame
+cd sugargame
+git clone git://git.sugarlabs.org/sugargame/mainline.git</code></pre>
+<p>You'll see two subdirectories in this project: <strong>sugargame</strong> and <strong>test</strong>, plus a <strong>README.txt</strong> file that contains information on using sugargame in your own Activities.&#160; The test directory contains a simple PyGame program that can be run either standalone or as an Activity.&#160; The standalone program is in the file named <strong>TestGame.py</strong>.&#160; The Activity, which is a sort of wrapper around the standalone version, is in file <strong>TestActivity.py</strong>.
+ <br/></p>
+<p>If you run <strong>TestGame.py</strong> from the command line you'll see it displays a bouncing ball on a white background.&#160; To try running the Activity version you'll need to run
+</p>
+<pre>./setup.py dev</pre>
+<p>from the command line first.&#160; I was not able to get the Activity to work under sugar-emulator until I made two changes to it:
+</p>
+<ul><li>I made a copy of the <strong>sugargame</strong> directory within the <strong>test</strong> directory.</li>
+ <li>I removed the line reading "<strong>sys.path.append(<span class="String"><span class="String">'</span>..<span class="String">'</span></span>) </strong><span class="Comment"><strong><span class="Comment">#</span> Import sugargame package from top directory.</strong>" from <strong>TestActivity.py</strong>.&#160; Obviously this line is supposed to help the program find the <strong>sugargame</strong> directory in the project but it didn't work in Fedora 10.&#160; Your own experience may be different.</span>
+ <br/></li>
+</ul><p><span class="Comment">The Activity looks like this:</span>
+</p>
+<p><span class="Comment"><img alt="The SugarGame demo Activity" src="static/ActivitiesGuideSugar-sugargame_1-en.jpg" height="453" width="600"/></span>
+</p>
+<p><span class="Comment">The <strong>PyGame</strong> toolbar has a single button that lets you make the bouncing ball pause and resume bouncing.</span>
+</p>
+<h2><span class="Comment">Making A Sugar Activity Out Of A PyGame Program</span>
+</h2>
+<p><span class="Comment">Now it's time to put our ship in that bottle.&#160; The first thing we need to do is make a copy of the <strong>sugargame</strong> directory of the SugarGame project into the mainline directory of our own project.</span>
+</p>
+<p><span class="Comment">The <strong>README.txt</strong> file in the SugarGame project is worth reading.&#160; It tells us to make an Activity based on the <strong>TestActivity.py</strong> example in the SugarGame project.&#160; This will be our bottle.&#160; Here is the code for mine, which is named <strong>DemoiselleActivity.py</strong>:</span>
+</p>
+<pre># DemoiselleActivity.py
+
+from gettext import gettext as _
+
+import gtk
+import pygame
+from sugar.activity import activity
+from sugar.graphics.toolbutton import ToolButton
+import gobject
+import sugargame.canvas
+import demoiselle2
+
+class DemoiselleActivity(activity.Activity):
+ def __init__(self, handle):
+ super(DemoiselleActivity, self).__init__(handle)
+
+ # Build the activity toolbar.
+ self.build_toolbar()
+
+ # Create the game instance.
+ self.game = demoiselle2.Demoiselle()
+
+ # Build the Pygame canvas.
+ self._pygamecanvas = \
+ sugargame.canvas.PygameCanvas(self)
+ # Note that set_canvas implicitly calls
+ # read_file when resuming from the Journal.
+ self.set_canvas(self._pygamecanvas)
+ self.score = ''
+
+ # Start the game running.
+ self._pygamecanvas.run_pygame(self.game.run)
+
+ def build_toolbar(self):
+ toolbox = activity.ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.keep.props.visible = False
+ activity_toolbar.share.props.visible = False
+
+ self.view_toolbar = ViewToolbar()
+ toolbox.add_toolbar(_('View'), self.view_toolbar)
+ self.view_toolbar.connect('go-fullscreen',
+ self.view_toolbar_go_fullscreen_cb)
+ self.view_toolbar.show()
+
+ toolbox.show()
+ self.set_toolbox(toolbox)
+
+ def view_toolbar_go_fullscreen_cb(self, view_toolbar):
+ self.fullscreen()
+
+ def read_file(self, file_path):
+ score_file = open(file_path, "r")
+ while score_file:
+ self.score = score_file.readline()
+ self.game.set_score(int(self.score))
+ score_file.close()
+
+ def write_file(self, file_path):
+ score = self.game.get_score()
+ f = open(file_path, 'wb')
+ try:
+ f.write(str(score))
+ finally:
+ f.close
+
+class ViewToolbar(gtk.Toolbar):
+ __gtype_name__ = 'ViewToolbar'
+
+ __gsignals__ = {
+ 'needs-update-size': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([])),
+ 'go-fullscreen': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([]))
+ }
+
+ def __init__(self):
+ gtk.Toolbar.__init__(self)
+ self.fullscreen = ToolButton('view-fullscreen')
+ self.fullscreen.set_tooltip(_('Fullscreen'))
+ self.fullscreen.connect('clicked',
+ self.fullscreen_cb)
+ self.insert(self.fullscreen, -1)
+ self.fullscreen.show()
+
+ def fullscreen_cb(self, button):
+ self.emit('go-fullscreen')
+</pre>
+<p><span class="Comment">This is a bit fancier than <strong>TestActivity.py</strong>.&#160; I decided that my game didn't really need to be paused and resumed, so I replaced the <strong>PyGame</strong> toolbar with a <strong>View</strong> toolbar that lets the user hide the toolbar when it is not needed.&#160; I use the <em>read_file()</em> and <em>write_file()</em> methods to save and restore the game score.&#160; (Actually this is faked, because I never put in any scoring logic in the game).&#160; I also hide the <strong>Keep</strong> and <strong>Share</strong> controls in the main toolbar.</span>
+</p>
+<p><span class="Comment">As you would expect, getting a ship in a bottle does require the ship to be modified.&#160; Here is <strong>demoiselle2.py</strong>, which has the modifications:</span>
+</p>
+<pre>#! /usr/bin/env python
+import pygame
+import gtk
+import math
+import sys
+
+class Demoiselle:
+ "This is a simple demonstration of using PyGame \
+ sprites and collision detection."
+ def __init__(self):
+ self.clock = pygame.time.Clock()
+ self.running = True
+ self.background = pygame.image.load('sky.jpg')
+
+ def get_score(self):
+ return '99'
+
+ def run(self):
+ "This method processes PyGame messages"
+
+ screen = pygame.display.get_surface()
+ screen.blit(self.background, (0, 0))
+
+ gliders = [
+ GliderSprite((200, 200)),
+ GliderSprite((800, 200)),
+ GliderSprite((200, 600)),
+ GliderSprite((800, 600)),
+ ]
+ glider_group = pygame.sprite.RenderPlain(gliders)
+
+ rect = screen.get_rect()
+ airplane = AirplaneSprite('demoiselle.png',
+ rect.center)
+ airplane_sprite = pygame.sprite.RenderPlain(
+ airplane)
+
+ while self.running:
+ self.clock.tick(30)
+
+ # Pump GTK messages.
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ # Pump PyGame messages.
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ self.running = False
+ return
+ elif event.type == pygame.VIDEORESIZE:
+ pygame.display.set_mode(event.size,
+ pygame.RESIZABLE)
+ screen.blit(self.background, (0, 0))
+
+ if not hasattr(event, 'key'):
+ continue
+ down = event.type == pygame.KEYDOWN
+ if event.key == pygame.K_DOWN or \
+ event.key == pygame.K_KP2:
+ airplane.joystick_back = down * 5
+ elif event.key == pygame.K_UP or \
+ event.key == pygame.K_KP8:
+ airplane.joystick_forward = down * -5
+ elif event.key == pygame.K_EQUALS or \
+ event.key == pygame.K_KP_PLUS or \
+ event.key == pygame.K_KP9:
+ airplane.throttle_up = down * 2
+ elif event.key == pygame.K_MINUS or \
+ event.key == pygame.K_KP_MINUS or \
+ event.key == pygame.K_KP3:
+ airplane.throttle_down = down * -2
+
+ glider_group.clear(screen, self.background)
+ airplane_sprite.clear(screen, self.background)
+ collisions = pygame.sprite.spritecollide(
+ airplane,
+ glider_group, False)
+ glider_group.update(collisions)
+ glider_group.draw(screen)
+ airplane_sprite.update()
+ airplane_sprite.draw(screen)
+ pygame.display.flip()
+
+class AirplaneSprite(pygame.sprite.Sprite):
+ "This class represents an airplane, the Demoiselle \
+ created by Alberto Santos-Dumont"
+ MAX_FORWARD_SPEED = 10
+ MIN_FORWARD_SPEED = 1
+ ACCELERATION = 2
+ TURN_SPEED = 5
+ def __init__(self, image, position):
+ pygame.sprite.Sprite.__init__(self)
+ self.src_image = pygame.image.load(image)
+ self.rect = pygame.Rect(self.src_image.get_rect())
+ self.position = position
+ self.rect.center = self.position
+ self.speed = 1
+ self.direction = 0
+ self.joystick_back = self.joystick_forward = \
+ self.throttle_down = self.throttle_up = 0
+
+ def update(self):
+ "This method redraws the airplane in response\
+ to events."
+ self.speed += (self.throttle_up +
+ self.throttle_down)
+ if self.speed &gt; self.MAX_FORWARD_SPEED:
+ self.speed = self.MAX_FORWARD_SPEED
+ if self.speed &lt; self.MIN_FORWARD_SPEED:
+ self.speed = self.MIN_FORWARD_SPEED
+ self.direction += (self.joystick_forward +
+ self.joystick_back)
+ x_coord, y_coord = self.position
+ rad = self.direction * math.pi / 180
+ x_coord += -self.speed * math.cos(rad)
+ y_coord += -self.speed * math.sin(rad)
+ screen = pygame.display.get_surface()
+ if y_coord &lt; 0:
+ y_coord = screen.get_height()
+
+ if x_coord &lt; 0:
+ x_coord = screen.get_width()
+
+ if x_coord &gt; screen.get_width():
+ x_coord = 0
+
+ if y_coord &gt; screen.get_height():
+ y_coord = 0
+ self.position = (x_coord, y_coord)
+ self.image = pygame.transform.rotate(
+ self.src_image, -self.direction)
+ self.rect = self.image.get_rect()
+ self.rect.center = self.position
+
+class GliderSprite(pygame.sprite.Sprite):
+ "This class represents an individual hang \
+ glider as developed by Otto Lilienthal."
+ def __init__(self, position):
+ pygame.sprite.Sprite.__init__(self)
+ self.normal = pygame.image.load(
+ 'glider_normal.png')
+ self.rect = pygame.Rect(self.normal.get_rect())
+ self.rect.center = position
+ self.image = self.normal
+ self.hit = pygame.image.load('glider_hit.png')
+ def update(self, hit_list):
+ "This method redraws the glider when it collides\
+ with the airplane and when it is no longer \
+ colliding with the airplane."
+ if self in hit_list:
+ self.image = self.hit
+ else:
+ self.image = self.normal
+
+def main():
+ "This function is called when the game is run \
+ from the command line"
+ pygame.init()
+ pygame.display.set_mode((0, 0), pygame.RESIZABLE)
+ game = Demoiselle()
+ game.run()
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
+</pre>
+<p><span class="Comment">Why not load both <strong>demoiselle.py</strong> and <strong>demoiselle2.py</strong> in Eric and take a few minutes to see if you can figure out what changed between the two versions?</span>
+</p>
+<p><span class="Comment">Surprisingly little is different.&#160; I added some code to the PyGame main loop to check for PyGTK events and deal with them:</span>
+</p>
+<pre> while self.running:
+ self.clock.tick(30)
+
+ <strong># Pump GTK messages.
+ while gtk.events_pending():
+ gtk.main_iteration()</strong>
+
+ # Pump PyGame messages.
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ self.running = False
+ return
+ elif event.type == pygame.VIDEORESIZE:
+ pygame.display.set_mode(event.size,
+ pygame.RESIZABLE)
+ screen.blit(self.background, (0, 0))
+
+ if not hasattr(event, 'key'):
+ continue
+ down = event.type == pygame.KEYDOWN
+ if event.key == pygame.K_DOWN or \
+
+<em>... continue dealing with PyGame events ...</em>
+</pre>
+<p><span class="Comment">This has the effect of making PyGame and PyGTK take turns handling events.&#160; If this code was not present GTK events would be ignored and you'd have no way to close the Activity, hide the toolbar, etc.&#160; You need to add <strong>import gtk</strong> at the top of the file so these methods can be found.</span>
+</p>
+<p><span class="Comment">Of course I also added the methods to set and return scores:</span>
+</p>
+<pre>&#160; def get_score(self):
+ return self.score
+
+ def set_score(self, score):
+ self.score = score
+</pre>
+<p><span class="Comment">The biggest change is in the <em>__init__()</em> method of the <strong>Demoiselle</strong> class.&#160; Originally I had code to display the background image on the screen:</span>
+</p>
+<pre> def __init__(self):
+ self.background = pygame.image.load('sky.jpg')
+ self.screen = pygame.display.get_surface()
+ self.screen.blit(self.background, (0, 0))
+</pre>
+<p><span class="Comment">The problem with this is that sugargame is going to create a special PyGTK Canvas object to replace the PyGame display and the DemoiselleActivity code hasn't done that yet, so <strong>self.screen</strong> will have a value of None.&#160; The only way to get around that is to move any code that refers to the <strong>display</strong> out of the <em>__init__()</em> method of the class and into the beginning of the method that contains the event loop.&#160; This may leave you with an <em>__init__()</em> method that does little or nothing.&#160; About the only thing you'll want there is code to create instance variables.</span>
+</p>
+<p><span class="Comment">Nothing we have done to <strong>demoiselle2.py</strong> will prevent it from being run as a standalone Python program.</span>
+</p>To try out the game <span class="Comment">run <strong>./setup.py dev</strong> from within the <strong>Making_Activities_Using_PyGame</strong> directory.&#160; When you try out the Activity it should look like this:</span>
+<p>
+</p>
+<p><span class="Comment"><img alt="The Demoiselle Activity." src="static/ActivitiesGuideSugar-demoiselle2_1-en.jpg" height="448" width="600"/><br/></span>
+</p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch021_making-new-style-toolbars.xhtml b/studio/static/doc/myosa/ch021_making-new-style-toolbars.xhtml
new file mode 100644
index 0000000..73e662e
--- /dev/null
+++ b/studio/static/doc/myosa/ch021_making-new-style-toolbars.xhtml
@@ -0,0 +1,1150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Making New Style Toolbars
+</h1>
+<h2>Introduction
+</h2>
+<p>They say "There's no Toolbar like an old Toolbar" and if your users are not running the very latest version of Sugar they're right.&#160; Activities will need to support the original style toolbars for some time to come.&#160; However, it is possible to make an Activity that supports both and that is what we'll do in this chapter.
+</p>
+<p>The new style toolbars came about because of problems with the old toolbars.&#160; Activity users were having a hard time figuring out how to quit an Activity because the <strong>Close</strong> button is only on the Activity toolbar.&#160; If the Activity starts on a different toolbar, as many do, it is not obvious that you need to switch to the Activity toolbar to quit the Activity.&#160; Another issue brought up was that the Tabs for the toolbars took up screen real estate that could be better used elsewhere.&#160; Let's compare toolbars for similar Activities.&#160; First, the old style toolbar for <strong>Read Etexts</strong>:
+</p>
+<p><img alt="Old style toolbar, Read Etexts" src="static/ActivitiesGuideSugar-oldtoolbar-en.jpg" width="600" height="80"/></p>
+<p>Now compare it with the new style toolbar for the <strong>Read</strong> Activity:
+</p>
+<p><img alt="New style toolbar, closed." src="static/ActivitiesGuideSugar-newtoolbar1_1-en.jpg" width="600" height="48"/></p>
+<p>This is thinner than the older version and the <strong>Close</strong> button is always visible.&#160; Some functions are on the main toolbar and others are attached to toolbars that drop down when you click on their icon.&#160; First, the new Activity drop down toolbar:
+</p>
+<p><img alt="The Activity drop down menu." src="static/ActivitiesGuideSugar-newtoolbar2-en.jpg" width="600" height="87"/></p>
+<p>Next the <strong>Edit</strong> toolbar:
+</p>
+<p><img alt="New Edit Toolbar" src="static/ActivitiesGuideSugar-newtoolbar3-en.jpg" width="598" height="90"/></p>
+<p>Finally, the <strong>View</strong> toolbar:
+</p>
+<p><img alt="The new View Toolbar." src="static/ActivitiesGuideSugar-newtoolbar4-en.jpg" width="600" height="100"/></p>
+<h2> Adding New Style Toolbars to Read Etexts II
+</h2>
+<p>When working on the original <strong>Read Etexts</strong> Activity I borrowed a lot of user interface code from the original <strong>Read</strong> Activity and I see no reason to stop doing that now.&#160; One complication to doing this is that <strong>Read</strong> has some dependencies that prevent the latest version of <strong>Read</strong> from working with older versions of Sugar, and that being the case there is no need at all for Read to support both old and new toolbars.&#160; <strong>Read Etexts IV</strong> will not be so fortunate; it will need to figure out at runtime what kind of toolbar is supported and use that.
+</p>
+<p>I am able to test the Activity with both old and new style toolbars on the same box because I'm running Fedora 11, which has an installed Sugar environment that supports the old toolbars, plus I have downloaded and run <strong>sugar-jhbuild</strong>, which supports the new toolbars in its version of Sugar.
+</p>
+<p>Here is the code for <strong>ReadEtextsActivity4.py</strong>:
+</p>
+<pre>import os
+import re
+import logging
+import time
+import zipfile
+import gtk
+import pango
+import dbus
+import gobject
+import telepathy
+from sugar.activity import activity
+
+from sugar.graphics.toolbutton import ToolButton
+
+_NEW_TOOLBAR_SUPPORT = True
+try:
+ from sugar.graphics.toolbarbox import ToolbarBox
+ from sugar.graphics.toolbarbox import ToolbarButton
+ from sugar.activity.widgets import StopButton
+ from toolbar import ViewToolbar
+ from mybutton import MyActivityToolbarButton
+except:
+ _NEW_TOOLBAR_SUPPORT = False
+ from toolbar import ReadToolbar, ViewToolbar
+
+from sugar.graphics.toggletoolbutton import ToggleToolButton
+from sugar.graphics.menuitem import MenuItem
+
+from sugar.graphics import style
+from sugar import network
+from sugar.datastore import datastore
+from sugar.graphics.alert import NotifyAlert
+from gettext import gettext as _
+
+page=0
+PAGE_SIZE = 45
+TOOLBAR_READ = 2
+
+logger = logging.getLogger('read-etexts2-activity')
+
+class ReadHTTPRequestHandler(
+ network.ChunkedGlibHTTPRequestHandler):
+ """HTTP Request Handler for transferring document while
+ collaborating.
+
+ RequestHandler class that integrates with Glib mainloop.
+ It writes the specified file to the client in chunks,
+ returning control to the mainloop between chunks.
+
+ """
+ def translate_path(self, path):
+ """Return the filepath to the shared document."""
+ return self.server.filepath
+
+
+class ReadHTTPServer(network.GlibTCPServer):
+ """HTTP Server for transferring document while
+ collaborating."""
+ def __init__(self, server_address, filepath):
+ """Set up the GlibTCPServer with the
+ ReadHTTPRequestHandler.
+
+ filepath -- path to shared document to be served.
+ """
+ self.filepath = filepath
+ network.GlibTCPServer.__init__(self,
+ server_address,
+ ReadHTTPRequestHandler)
+
+
+class ReadURLDownloader(network.GlibURLDownloader):
+ """URLDownloader that provides content-length
+ and content-type."""
+
+ def get_content_length(self):
+ """Return the content-length of the download."""
+ if self._info is not None:
+ return int(self._info.headers.get(
+ 'Content-Length'))
+
+ def get_content_type(self):
+ """Return the content-type of the download."""
+ if self._info is not None:
+ return self._info.headers.get('Content-type')
+ return None
+
+READ_STREAM_SERVICE = 'read-etexts-activity-http'
+
+class ReadEtextsActivity(activity.Activity):
+ def __init__(self, handle):
+ "The entry point to the Activity"
+ global page
+ activity.Activity.__init__(self, handle)
+
+ self.fileserver = None
+ self.object_id = handle.object_id
+
+ if _NEW_TOOLBAR_SUPPORT:
+ self.create_new_toolbar()
+ else:
+ self.create_old_toolbar()
+
+ self.scrolled_window = gtk.ScrolledWindow()
+ self.scrolled_window.set_policy(gtk.POLICY_NEVER,
+ gtk.POLICY_AUTOMATIC)
+ self.scrolled_window.props.shadow_type = \
+ gtk.SHADOW_NONE
+
+ self.textview = gtk.TextView()
+ self.textview.set_editable(False)
+ self.textview.set_cursor_visible(False)
+ self.textview.set_left_margin(50)
+ self.textview.connect("key_press_event",
+ self.keypress_cb)
+
+ self.progressbar = gtk.ProgressBar()
+ self.progressbar.set_orientation(
+ gtk.PROGRESS_LEFT_TO_RIGHT)
+ self.progressbar.set_fraction(0.0)
+
+ self.scrolled_window.add(self.textview)
+ self.textview.show()
+ self.scrolled_window.show()
+
+ vbox = gtk.VBox()
+ vbox.pack_start(self.progressbar, False,
+ False, 10)
+ vbox.pack_start(self.scrolled_window)
+ self.set_canvas(vbox)
+ vbox.show()
+
+ page = 0
+ self.clipboard = gtk.Clipboard(
+ display=gtk.gdk.display_get_default(),
+ selection="CLIPBOARD")
+ self.textview.grab_focus()
+ self.font_desc = pango.FontDescription(
+ "sans %d" % style.zoom(10))
+ self.textview.modify_font(self.font_desc)
+
+ buffer = self.textview.get_buffer()
+ self.markset_id = buffer.connect("mark-set",
+ self.mark_set_cb)
+
+ self.unused_download_tubes = set()
+ self.want_document = True
+ self.download_content_length = 0
+ self.download_content_type = None
+ # Status of temp file used for write_file:
+ self.tempfile = None
+ self.close_requested = False
+ self.connect("shared", self.shared_cb)
+
+ self.is_received_document = False
+
+ if self._shared_activity and \
+ handle.object_id == None:
+ # We're joining, and we don't already have
+ # the document.
+ if self.get_shared():
+ # Already joined for some reason,
+ # just get the document
+ self.joined_cb(self)
+ else:
+ # Wait for a successful join before
+ # trying to get the document
+ self.connect("joined", self.joined_cb)
+
+ def create_old_toolbar(self):
+ toolbox = activity.ActivityToolbox(self)
+ activity_toolbar = toolbox.get_activity_toolbar()
+ activity_toolbar.keep.props.visible = False
+
+ self.edit_toolbar = activity.EditToolbar()
+ self.edit_toolbar.undo.props.visible = False
+ self.edit_toolbar.redo.props.visible = False
+ self.edit_toolbar.separator.props.visible = False
+ self.edit_toolbar.copy.set_sensitive(False)
+ self.edit_toolbar.copy.connect('clicked',
+ self.edit_toolbar_copy_cb)
+ self.edit_toolbar.paste.props.visible = False
+ toolbox.add_toolbar(_('Edit'), self.edit_toolbar)
+ self.edit_toolbar.show()
+
+ self.read_toolbar = ReadToolbar()
+ toolbox.add_toolbar(_('Read'), self.read_toolbar)
+ self.read_toolbar.back.connect('clicked',
+ self.go_back_cb)
+ self.read_toolbar.forward.connect('clicked',
+ self.go_forward_cb)
+ self.read_toolbar.num_page_entry.connect('activate',
+ self.num_page_entry_activate_cb)
+ self.read_toolbar.show()
+
+ self.view_toolbar = ViewToolbar()
+ toolbox.add_toolbar(_('View'), self.view_toolbar)
+ self.view_toolbar.connect('go-fullscreen',
+ self.view_toolbar_go_fullscreen_cb)
+ self.view_toolbar.zoom_in.connect('clicked',
+ self.zoom_in_cb)
+ self.view_toolbar.zoom_out.connect('clicked',
+ self.zoom_out_cb)
+ self.view_toolbar.show()
+
+ self.set_toolbox(toolbox)
+ toolbox.show()
+ self.toolbox.set_current_toolbar(TOOLBAR_READ)
+
+ def create_new_toolbar(self):
+ toolbar_box = ToolbarBox()
+
+ activity_button = MyActivityToolbarButton(self)
+ toolbar_box.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ self.edit_toolbar = activity.EditToolbar()
+ self.edit_toolbar.undo.props.visible = False
+ self.edit_toolbar.redo.props.visible = False
+ self.edit_toolbar.separator.props.visible = False
+ self.edit_toolbar.copy.set_sensitive(False)
+ self.edit_toolbar.copy.connect('clicked',
+ self.edit_toolbar_copy_cb)
+ self.edit_toolbar.paste.props.visible = False
+
+ edit_toolbar_button = ToolbarButton(
+ page=self.edit_toolbar,
+ icon_name='toolbar-edit')
+ self.edit_toolbar.show()
+ toolbar_box.toolbar.insert(edit_toolbar_button, -1)
+ edit_toolbar_button.show()
+
+ self.view_toolbar = ViewToolbar()
+ self.view_toolbar.connect('go-fullscreen',
+ self.view_toolbar_go_fullscreen_cb)
+ self.view_toolbar.zoom_in.connect('clicked',
+ self.zoom_in_cb)
+ self.view_toolbar.zoom_out.connect('clicked',
+ self.zoom_out_cb)
+ self.view_toolbar.show()
+ view_toolbar_button = ToolbarButton(
+ page=self.view_toolbar,
+ icon_name='toolbar-view')
+ toolbar_box.toolbar.insert(view_toolbar_button, -1)
+ view_toolbar_button.show()
+
+ self.back = ToolButton('go-previous')
+ self.back.set_tooltip(_('Back'))
+ self.back.props.sensitive = False
+ self.back.connect('clicked', self.go_back_cb)
+ toolbar_box.toolbar.insert(self.back, -1)
+ self.back.show()
+
+ self.forward = ToolButton('go-next')
+ self.forward.set_tooltip(_('Forward'))
+ self.forward.props.sensitive = False
+ self.forward.connect('clicked',
+ self.go_forward_cb)
+ toolbar_box.toolbar.insert(self.forward, -1)
+ self.forward.show()
+
+ num_page_item = gtk.ToolItem()
+ self.num_page_entry = gtk.Entry()
+ self.num_page_entry.set_text('0')
+ self.num_page_entry.set_alignment(1)
+ self.num_page_entry.connect('insert-text',
+ self.__new_num_page_entry_insert_text_cb)
+ self.num_page_entry.connect('activate',
+ self.__new_num_page_entry_activate_cb)
+ self.num_page_entry.set_width_chars(4)
+ num_page_item.add(self.num_page_entry)
+ self.num_page_entry.show()
+ toolbar_box.toolbar.insert(num_page_item, -1)
+ num_page_item.show()
+
+ total_page_item = gtk.ToolItem()
+ self.total_page_label = gtk.Label()
+
+ label_attributes = pango.AttrList()
+ label_attributes.insert(pango.AttrSize(
+ 14000, 0, -1))
+ label_attributes.insert(pango.AttrForeground(
+ 65535, 65535, 65535, 0, -1))
+ self.total_page_label.set_attributes(
+ label_attributes)
+
+ self.total_page_label.set_text(' / 0')
+ total_page_item.add(self.total_page_label)
+ self.total_page_label.show()
+ toolbar_box.toolbar.insert(total_page_item, -1)
+ total_page_item.show()
+
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ toolbar_box.toolbar.insert(separator, -1)
+ separator.show()
+
+ stop_button = StopButton(self)
+ stop_button.props.accelerator = '&lt;Ctrl&gt;&lt;Shift&gt;Q'
+ toolbar_box.toolbar.insert(stop_button, -1)
+ stop_button.show()
+
+ self.set_toolbar_box(toolbar_box)
+ toolbar_box.show()
+
+ def __new_num_page_entry_insert_text_cb(self, entry,
+ text, length, position):
+ if not re.match('[0-9]', text):
+ entry.emit_stop_by_name('insert-text')
+ return True
+ return False
+
+ def __new_num_page_entry_activate_cb(self, entry):
+ global page
+ if entry.props.text:
+ new_page = int(entry.props.text) - 1
+ else:
+ new_page = 0
+
+ if new_page &gt;= self.total_pages:
+ new_page = self.total_pages - 1
+ elif new_page &lt; 0:
+ new_page = 0
+
+ self.current_page = new_page
+ self.set_current_page(new_page)
+ self.show_page(new_page)
+ entry.props.text = str(new_page + 1)
+ self.update_nav_buttons()
+ page = new_page
+
+ def update_nav_buttons(self):
+ current_page = self.current_page
+ self.back.props.sensitive = current_page &gt; 0
+ self.forward.props.sensitive = \
+ current_page &lt; self.total_pages - 1
+
+ self.num_page_entry.props.text = str(
+ current_page + 1)
+ self.total_page_label.props.label = \
+ ' / ' + str(self.total_pages)
+
+ def set_total_pages(self, pages):
+ self.total_pages = pages
+
+ def set_current_page(self, page):
+ self.current_page = page
+ self.update_nav_buttons()
+
+ def keypress_cb(self, widget, event):
+ "Respond when the user presses one of the \
+ arrow keys"
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ print keyname
+ if keyname == 'plus':
+ self.font_increase()
+ return True
+ if keyname == 'minus':
+ self.font_decrease()
+ return True
+ if keyname == 'Page_Up' :
+ self.page_previous()
+ return True
+ if keyname == 'Page_Down':
+ self.page_next()
+ return True
+ if keyname == 'Up' or keyname == 'KP_Up' \
+ or keyname == 'KP_Left':
+ self.scroll_up()
+ return True
+ if keyname == 'Down' or keyname == 'KP_Down' \
+ or keyname == 'KP_Right':
+ self.scroll_down()
+ return True
+ return False
+
+ def num_page_entry_activate_cb(self, entry):
+ global page
+ if entry.props.text:
+ new_page = int(entry.props.text) - 1
+ else:
+ new_page = 0
+
+ if new_page &gt;= self.read_toolbar.total_pages:
+ new_page = self.read_toolbar.total_pages - 1
+ elif new_page &lt; 0:
+ new_page = 0
+
+ self.read_toolbar.current_page = new_page
+ self.read_toolbar.set_current_page(new_page)
+ self.show_page(new_page)
+ entry.props.text = str(new_page + 1)
+ self.read_toolbar.update_nav_buttons()
+ page = new_page
+
+ def go_back_cb(self, button):
+ self.page_previous()
+
+ def go_forward_cb(self, button):
+ self.page_next()
+
+ def page_previous(self):
+ global page
+ page=page-1
+ if page &lt; 0: page=0
+ if _NEW_TOOLBAR_SUPPORT:
+ self.set_current_page(page)
+ else:
+ self.read_toolbar.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.upper - \
+ v_adjustment.page_size
+
+ def page_next(self):
+ global page
+ page=page+1
+ if page &gt;= len(self.page_index): page=0
+ if _NEW_TOOLBAR_SUPPORT:
+ self.set_current_page(page)
+ else:
+ self.read_toolbar.set_current_page(page)
+ self.show_page(page)
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ v_adjustment.value = v_adjustment.lower
+
+ def zoom_in_cb(self, button):
+ self.font_increase()
+
+ def zoom_out_cb(self, button):
+ self.font_decrease()
+
+ def font_decrease(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size - 1
+ if font_size &lt; 1:
+ font_size = 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def font_increase(self):
+ font_size = self.font_desc.get_size() / 1024
+ font_size = font_size + 1
+ self.font_desc.set_size(font_size * 1024)
+ self.textview.modify_font(self.font_desc)
+
+ def mark_set_cb(self, textbuffer, iter, textmark):
+
+ if textbuffer.get_has_selection():
+ begin, end = textbuffer.get_selection_bounds()
+ self.edit_toolbar.copy.set_sensitive(True)
+ else:
+ self.edit_toolbar.copy.set_sensitive(False)
+
+ def edit_toolbar_copy_cb(self, button):
+ textbuffer = self.textview.get_buffer()
+ begin, end = textbuffer.get_selection_bounds()
+ copy_text = textbuffer.get_text(begin, end)
+ self.clipboard.set_text(copy_text)
+
+ def view_toolbar_go_fullscreen_cb(self, view_toolbar):
+ self.fullscreen()
+
+ def scroll_down(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.upper - \
+ v_adjustment.page_size:
+ self.page_next()
+ return
+ if v_adjustment.value &lt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.value + \
+ v_adjustment.step_increment
+ if new_value &gt; v_adjustment.upper - \
+ v_adjustment.page_size:
+ new_value = v_adjustment.upper - \
+ v_adjustment.page_size
+ v_adjustment.value = new_value
+
+ def scroll_up(self):
+ v_adjustment = \
+ self.scrolled_window.get_vadjustment()
+ if v_adjustment.value == v_adjustment.lower:
+ self.page_previous()
+ return
+ if v_adjustment.value &gt; v_adjustment.lower:
+ new_value = v_adjustment.value - \
+ v_adjustment.step_increment
+ if new_value &lt; v_adjustment.lower:
+ new_value = v_adjustment.lower
+ v_adjustment.value = new_value
+
+ def show_page(self, page_number):
+ global PAGE_SIZE, current_word
+ position = self.page_index[page_number]
+ self.etext_file.seek(position)
+ linecount = 0
+ label_text = '\n\n\n'
+ textbuffer = self.textview.get_buffer()
+ while linecount &lt; PAGE_SIZE:
+ line = self.etext_file.readline()
+ label_text = label_text + unicode(line,
+ 'iso-8859-1')
+ linecount = linecount + 1
+ label_text = label_text + '\n\n\n'
+ textbuffer.set_text(label_text)
+ self.textview.set_buffer(textbuffer)
+
+ def save_extracted_file(self, zipfile, filename):
+ "Extract the file to a temp directory for viewing"
+ filebytes = zipfile.read(filename)
+ outfn = self.make_new_filename(filename)
+ if (outfn == ''):
+ return False
+ f = open(os.path.join(self.get_activity_root(),
+ 'tmp', outfn), 'w')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close()
+
+ def get_saved_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not title[len(title)-1].isdigit():
+ page = 0
+ else:
+ i = len(title) - 1
+ newPage = ''
+ while (title[i].isdigit() and i &gt; 0):
+ newPage = title[i] + newPage
+ i = i - 1
+ if title[i] == 'P':
+ page = int(newPage) - 1
+ else:
+ # not a page number; maybe a volume number.
+ page = 0
+
+ def save_page_number(self):
+ global page
+ title = self.metadata.get('title', '')
+ if title == '' or not title[len(title)-1].isdigit():
+ title = title + ' P' + str(page + 1)
+ else:
+ i = len(title) - 1
+ while (title[i].isdigit() and i &gt; 0):
+ i = i - 1
+ if title[i] == 'P':
+ title = title[0:i] + 'P' + str(page + 1)
+ else:
+ title = title + ' P' + str(page + 1)
+ self.metadata['title'] = title
+
+ def read_file(self, filename):
+ "Read the Etext file"
+ global PAGE_SIZE, page
+
+ tempfile = os.path.join(self.get_activity_root(),
+ 'instance', 'tmp%i' % time.time())
+ os.link(filename, tempfile)
+ self.tempfile = tempfile
+
+ if zipfile.is_zipfile(filename):
+ self.zf = zipfile.ZipFile(filename, 'r')
+ self.book_files = self.zf.namelist()
+ self.save_extracted_file(self.zf,
+ self.book_files[0])
+ currentFileName = os.path.join(
+ self.get_activity_root(),
+ 'tmp', self.book_files[0])
+ else:
+ currentFileName = filename
+
+ self.etext_file = open(currentFileName,"r")
+ self.page_index = [ 0 ]
+ pagecount = 0
+ linecount = 0
+ while self.etext_file:
+ line = self.etext_file.readline()
+ if not line:
+ break
+ linecount = linecount + 1
+ if linecount &gt;= PAGE_SIZE:
+ position = self.etext_file.tell()
+ self.page_index.append(position)
+ linecount = 0
+ pagecount = pagecount + 1
+ if filename.endswith(".zip"):
+ os.remove(currentFileName)
+ self.get_saved_page_number()
+ self.show_page(page)
+ if _NEW_TOOLBAR_SUPPORT:
+ self.set_total_pages(pagecount + 1)
+ self.set_current_page(page)
+ else:
+ self.read_toolbar.set_total_pages(
+ pagecount + 1)
+ self.read_toolbar.set_current_page(page)
+
+ # We've got the document, so if we're a shared
+ # activity, offer it
+ if self.get_shared():
+ self.watch_for_tubes()
+ self.share_document()
+
+ def make_new_filename(self, filename):
+ partition_tuple = filename.rpartition('/')
+ return partition_tuple[2]
+
+ def write_file(self, filename):
+ "Save meta data for the file."
+ if self.is_received_document:
+ # This document was given to us by someone,
+ # so we have to save it to the Journal.
+ self.etext_file.seek(0)
+ filebytes = self.etext_file.read()
+ print 'saving shared document'
+ f = open(filename, 'wb')
+ try:
+ f.write(filebytes)
+ finally:
+ f.close()
+ elif self.tempfile:
+ if self.close_requested:
+ os.link(self.tempfile, filename)
+ logger.debug(
+ "Removing temp file %s because "
+ "we will close",
+ self.tempfile)
+ os.unlink(self.tempfile)
+ self.tempfile = None
+ else:
+ # skip saving empty file
+ raise NotImplementedError
+
+ self.metadata['activity'] = self.get_bundle_id()
+ self.save_page_number()
+
+ def can_close(self):
+ self.close_requested = True
+ return True
+
+ def joined_cb(self, also_self):
+ """Callback for when a shared activity is joined.
+
+ Get the shared document from another participant.
+ """
+ self.watch_for_tubes()
+ gobject.idle_add(self.get_document)
+
+ def get_document(self):
+ if not self.want_document:
+ return False
+
+ # Assign a file path to download if one
+ # doesn't exist yet
+ if not self._jobject.file_path:
+ path = os.path.join(self.get_activity_root(),
+ 'instance', 'tmp%i' % time.time())
+ else:
+ path = self._jobject.file_path
+
+ # Pick an arbitrary tube we can try to download
+ # the document from
+ try:
+ tube_id = self.unused_download_tubes.pop()
+ except (ValueError, KeyError), e:
+ logger.debug(
+ 'No tubes to get the document from '
+ 'right now: %s', e)
+ return False
+
+ # Avoid trying to download the document
+ # multiple times at once
+ self.want_document = False
+ gobject.idle_add(self.download_document,
+ tube_id, path)
+ return False
+
+ def download_document(self, tube_id, path):
+ chan = self._shared_activity.telepathy_tubes_chan
+ iface = chan[telepathy.CHANNEL_TYPE_TUBES]
+ addr = iface.AcceptStreamTube(tube_id,
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST,
+ 0,
+ utf8_strings=True)
+ logger.debug(
+ 'Accepted stream tube: '
+ 'listening address is %r',
+ addr)
+ assert isinstance(addr, dbus.Struct)
+ assert len(addr) == 2
+ assert isinstance(addr[0], str)
+ assert isinstance(addr[1], (int, long))
+ assert addr[1] &gt; 0 and addr[1] &lt; 65536
+ port = int(addr[1])
+
+ self.progressbar.show()
+ getter = ReadURLDownloader(
+ "http://%s:%d/document"
+ % (addr[0], port))
+ getter.connect("finished",
+ self.download_result_cb, tube_id)
+ getter.connect("progress",
+ self.download_progress_cb, tube_id)
+ getter.connect("error",
+ self.download_error_cb, tube_id)
+ logger.debug("Starting download to %s...", path)
+ getter.start(path)
+ self.download_content_length = \
+ getter.get_content_length()
+ self.download_content_type = \
+ getter.get_content_type()
+ return False
+
+ def download_progress_cb(self, getter,
+ bytes_downloaded, tube_id):
+ if self.download_content_length &gt; 0:
+ logger.debug(
+ "Downloaded %u of %u bytes from tube %u...",
+ bytes_downloaded,
+ self.download_content_length,
+ tube_id)
+ else:
+ logger.debug(
+ "Downloaded %u bytes from tube %u...",
+ bytes_downloaded, tube_id)
+ total = self.download_content_length
+ self.set_downloaded_bytes(bytes_downloaded,
+ total)
+ gtk.gdk.threads_enter()
+ while gtk.events_pending():
+ gtk.main_iteration()
+ gtk.gdk.threads_leave()
+
+ def set_downloaded_bytes(self, bytes, total):
+ fraction = float(bytes) / float(total)
+ self.progressbar.set_fraction(fraction)
+ logger.debug("Downloaded percent", fraction)
+
+ def clear_downloaded_bytes(self):
+ self.progressbar.set_fraction(0.0)
+ logger.debug("Cleared download bytes")
+
+ def download_error_cb(self, getter, err, tube_id):
+ self.progressbar.hide()
+ logger.debug(
+ "Error getting document from tube %u: %s",
+ tube_id, err)
+ self.alert(_('Failure'),
+ _('Error getting document from tube'))
+ self.want_document = True
+ self.download_content_length = 0
+ self.download_content_type = None
+ gobject.idle_add(self.get_document)
+
+ def download_result_cb(self, getter, tempfile,
+ suggested_name, tube_id):
+ if self.download_content_type.startswith(
+ 'text/html'):
+ # got an error page instead
+ self.download_error_cb(getter,
+ 'HTTP Error', tube_id)
+ return
+
+ del self.unused_download_tubes
+
+ self.tempfile = tempfile
+ file_path = os.path.join(
+ self.get_activity_root(),
+ 'instance', '%i' % time.time())
+ logger.debug(
+ "Saving file %s to datastore...", file_path)
+ os.link(tempfile, file_path)
+ self._jobject.file_path = file_path
+ datastore.write(self._jobject,
+ transfer_ownership=True)
+
+ logger.debug("Got document %s (%s) from tube %u",
+ tempfile, suggested_name, tube_id)
+ self.is_received_document = True
+ self.read_file(tempfile)
+ self.save()
+ self.progressbar.hide()
+
+ def shared_cb(self, activityid):
+ """Callback when activity shared.
+
+ Set up to share the document.
+
+ """
+ # We initiated this activity and have now
+ # shared it, so by definition we have the file.
+ logger.debug('Activity became shared')
+ self.watch_for_tubes()
+ self.share_document()
+
+ def share_document(self):
+ """Share the document."""
+ h = hash(self._activity_id)
+ port = 1024 + (h % 64511)
+ logger.debug(
+ 'Starting HTTP server on port %d', port)
+ self.fileserver = ReadHTTPServer(("", port),
+ self.tempfile)
+
+ # Make a tube for it
+ chan = self._shared_activity.telepathy_tubes_chan
+ iface = chan[telepathy.CHANNEL_TYPE_TUBES]
+ self.fileserver_tube_id = iface.OfferStreamTube(
+ READ_STREAM_SERVICE,
+ {},
+ telepathy.SOCKET_ADDRESS_TYPE_IPV4,
+ ('127.0.0.1', dbus.UInt16(port)),
+ telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0)
+
+ def watch_for_tubes(self):
+ """Watch for new tubes."""
+ tubes_chan = \
+ self._shared_activity.telepathy_tubes_chan
+
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
+ connect_to_signal(
+ 'NewTube',
+ self.new_tube_cb)
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self.list_tubes_reply_cb,
+ error_handler=self.list_tubes_error_cb)
+
+ def new_tube_cb(self, tube_id, initiator, tube_type,
+ service, params, state):
+ """Callback when a new tube becomes available."""
+ logger.debug(
+ 'New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d', tube_id,
+ initiator, tube_type,
+ service, params, state)
+ if service == READ_STREAM_SERVICE:
+ logger.debug('I could download from that tube')
+ self.unused_download_tubes.add(tube_id)
+ # if no download is in progress, let's
+ # fetch the document
+ if self.want_document:
+ gobject.idle_add(self.get_document)
+
+ def list_tubes_reply_cb(self, tubes):
+ """Callback when new tubes are available."""
+ for tube_info in tubes:
+ self.new_tube_cb(*tube_info)
+
+ def list_tubes_error_cb(self, e):
+ """Handle ListTubes error by logging."""
+ logger.error('ListTubes() failed: %s', e)
+
+ def alert(self, title, text=None):
+ alert = NotifyAlert(timeout=20)
+ alert.props.title = title
+ alert.props.msg = text
+ self.add_alert(alert)
+ alert.connect('response', self.alert_cancel_cb)
+ alert.show()
+
+ def alert_cancel_cb(self, alert, response_id):
+ self.remove_alert(alert)
+ self.textview.grab_focus()
+</pre>
+<div class="objavi-forcebreak">
+</div>
+<p>Here is what it looks like running under <strong>sugar-jhbuild</strong>:
+</p>
+<p><img alt="Read Etexts II, New Toolbar" src="static/ActivitiesGuideSugar-newtoolbar5-en.jpg" width="600" height="450"/></p>
+<p>Let's have a look at how it works.&#160;&#8286; If you've paid attention to other chapters when I've talked about the idea of "degrading gracefully" the imports in this code will be about what you would expect:
+</p>
+<pre>_NEW_TOOLBAR_SUPPORT = True
+try:
+ from sugar.graphics.toolbarbox import ToolbarBox
+ from sugar.graphics.toolbarbox import ToolbarButton
+ from sugar.activity.widgets import StopButton
+ from toolbar import ViewToolbar
+ from mybutton import MyActivityToolbarButton
+except:
+ _NEW_TOOLBAR_SUPPORT = False
+ from toolbar import ReadToolbar, ViewToolbar
+</pre>
+<p>Here we try to import a bunch of stuff that only exists in versions of Sugar that support the new toolbars.&#160; If we succeed, then _NEW_TOOLBAR_SUPPORT will remain set to True.&#160; If any of the imports fail then the variable is set to False.&#160; Note that a couple of imports that should always succeed are placed after the three that might fail.&#160; If any of the first three fail I don't want these imports to be done.
+</p>
+<p>This next bit of code in the <em>__init__()</em> method should not be surprising:
+</p>
+<pre> if _NEW_TOOLBAR_SUPPORT:
+ self.create_new_toolbar()
+ else:
+ self.create_old_toolbar()
+</pre>
+<p>I moved creating the toolbars into their own methods to make it easier to compare how the two different toolbars are created.&#160; The old toolbar code is unchanged.&#160; Here is the new toolbar code:
+</p>
+<pre> def create_new_toolbar(self):
+ toolbar_box = ToolbarBox()
+
+ activity_button = MyActivityToolbarButton(self)
+ toolbar_box.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ self.edit_toolbar = activity.EditToolbar()
+ self.edit_toolbar.undo.props.visible = False
+ self.edit_toolbar.redo.props.visible = False
+ self.edit_toolbar.separator.props.visible = False
+ self.edit_toolbar.copy.set_sensitive(False)
+ self.edit_toolbar.copy.connect('clicked',
+ self.edit_toolbar_copy_cb)
+ self.edit_toolbar.paste.props.visible = False
+
+ edit_toolbar_button = ToolbarButton(
+ page=self.edit_toolbar,
+ icon_name='toolbar-edit')
+ self.edit_toolbar.show()
+ toolbar_box.toolbar.insert(edit_toolbar_button, -1)
+ edit_toolbar_button.show()
+
+ self.view_toolbar = ViewToolbar()
+ self.view_toolbar.connect('go-fullscreen',
+ self.view_toolbar_go_fullscreen_cb)
+ self.view_toolbar.zoom_in.connect('clicked',
+ self.zoom_in_cb)
+ self.view_toolbar.zoom_out.connect('clicked',
+ self.zoom_out_cb)
+ self.view_toolbar.show()
+ view_toolbar_button = ToolbarButton(
+ page=self.view_toolbar,
+ icon_name='toolbar-view')
+ toolbar_box.toolbar.insert(
+ view_toolbar_button, -1)
+ view_toolbar_button.show()
+
+ self.back = ToolButton('go-previous')
+ self.back.set_tooltip(_('Back'))
+ self.back.props.sensitive = False
+ self.back.connect('clicked', self.go_back_cb)
+ toolbar_box.toolbar.insert(self.back, -1)
+ self.back.show()
+
+ self.forward = ToolButton('go-next')
+ self.forward.set_tooltip(_('Forward'))
+ self.forward.props.sensitive = False
+ self.forward.connect('clicked',
+ self.go_forward_cb)
+ toolbar_box.toolbar.insert(self.forward, -1)
+ self.forward.show()
+
+ num_page_item = gtk.ToolItem()
+ self.num_page_entry = gtk.Entry()
+ self.num_page_entry.set_text('0')
+ self.num_page_entry.set_alignment(1)
+ self.num_page_entry.connect('insert-text',
+ self.__new_num_page_entry_insert_text_cb)
+ self.num_page_entry.connect('activate',
+ self.__new_num_page_entry_activate_cb)
+ self.num_page_entry.set_width_chars(4)
+ num_page_item.add(self.num_page_entry)
+ self.num_page_entry.show()
+ toolbar_box.toolbar.insert(num_page_item, -1)
+ num_page_item.show()
+
+ total_page_item = gtk.ToolItem()
+ self.total_page_label = gtk.Label()
+
+ label_attributes = pango.AttrList()
+ label_attributes.insert(pango.AttrSize(
+ 14000, 0, -1))
+ label_attributes.insert(pango.AttrForeground(
+ 65535, 65535, 65535, 0, -1))
+ self.total_page_label.set_attributes(
+ label_attributes)
+
+ self.total_page_label.set_text(' / 0')
+ total_page_item.add(self.total_page_label)
+ self.total_page_label.show()
+ toolbar_box.toolbar.insert(total_page_item, -1)
+ total_page_item.show()
+
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ toolbar_box.toolbar.insert(separator, -1)
+ separator.show()
+
+ stop_button = StopButton(self)
+ stop_button.props.accelerator = '&lt;Ctrl&gt;&lt;Shift&gt;Q'
+ toolbar_box.toolbar.insert(stop_button, -1)
+ stop_button.show()
+
+ self.set_toolbar_box(toolbar_box)
+ toolbar_box.show()
+
+ def __new_num_page_entry_insert_text_cb(self, entry,
+ text, length, position):
+ if not re.match('[0-9]', text):
+ entry.emit_stop_by_name('insert-text')
+ return True
+ return False
+
+ def __new_num_page_entry_activate_cb(self, entry):
+ global page
+ if entry.props.text:
+ new_page = int(entry.props.text) - 1
+ else:
+ new_page = 0
+
+ if new_page &gt;= self.total_pages:
+ new_page = self.total_pages - 1
+ elif new_page &lt; 0:
+ new_page = 0
+
+ self.current_page = new_page
+ self.set_current_page(new_page)
+ self.show_page(new_page)
+ entry.props.text = str(new_page + 1)
+ self.update_nav_buttons()
+ page = new_page
+
+ def update_nav_buttons(self):
+ current_page = self.current_page
+ self.back.props.sensitive = current_page &gt; 0
+ self.forward.props.sensitive = \
+ current_page &lt; self.total_pages - 1
+
+ self.num_page_entry.props.text = str(
+ current_page + 1)
+ self.total_page_label.props.label = \
+ ' / ' + str(self.total_pages)
+
+ def set_total_pages(self, pages):
+ self.total_pages = pages
+
+ def set_current_page(self, page):
+ self.current_page = page
+ self.update_nav_buttons()
+</pre>
+<p>Much of the code in the two methods is the same.&#160; In particular, the <strong>View</strong> toolbar and the <strong>Edit</strong> toolbar are exactly the same in both.&#160; Instead of becoming the active toolbar they drop down from the toolbar to become sub toolbars.&#160; If we had done the Read toolbar the same way we could have implemented both old and new toolbars with very little code.&#160; However, the <strong>Read</strong> toolbar contains controls that are important enough to the Activity that they should be available at all times, so we put them in the main toolbar instead.&#160; Because of this every place where the code refers to the <strong>Read</strong> toolbar has to have two ways it can be performed, like this:
+</p>
+<pre> if _NEW_TOOLBAR_SUPPORT:
+ self.set_total_pages(pagecount + 1)
+ self.set_current_page(page)
+ else:
+ self.read_toolbar.set_total_pages(
+ pagecount + 1)
+ self.read_toolbar.set_current_page(page)
+</pre>
+<p>There is one more point of interest when it comes to the main toolbar.&#160; When you have an old style toolbar you get the stop button as part of the <strong>Activity</strong> toolbar.&#160; With the new style toolbar you need to add it to the end of the main toolbar yourself:
+</p>
+<pre> separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ toolbar_box.toolbar.insert(separator, -1)
+ separator.show()
+
+ stop_button = StopButton(self)
+ stop_button.props.accelerator = '&lt;Ctrl&gt;&lt;Shift&gt;Q'
+ toolbar_box.toolbar.insert(stop_button, -1)
+ stop_button.show()
+</pre>
+<p>Note that you must put a <strong>gtk.SeparatorToolItem</strong> with <em>set_expand()</em> equal to True before the <strong>StopButton</strong>.&#160; This will push the button all the way to the right of the toolbar, where it belongs.
+ <br/></p>
+<p>That just leaves the <strong>Activity</strong> toolbar to discuss:
+</p>
+<pre> toolbar_box = ToolbarBox()
+
+ activity_button = MyActivityToolbarButton(self)
+ toolbar_box.toolbar.insert(activity_button, 0)
+ activity_button.show()
+</pre>
+<p>Normally you would use the class <strong>ActivityToolbarButton</strong> to create the default drop down Activity toolbar.&#160; The problem I have with that is if I do that there is no way to hide the <strong>Keep</strong> button or the <strong>Share</strong> control.&#160; This version of the Activity needs the <strong>Share</strong> control, but has no use at all for the <strong>Keep</strong> button.
+</p>
+<p>There have been some spirited discussions about the <strong>Keep</strong> button on the mailing lists.&#160; New computer users don't know what it's for, and experienced computer users expect it to be like a <strong>Save Game</strong> button or a <strong>Save As...</strong> menu option in a regular application.&#160; It isn't quite like either one, and that can lead to confusion.&#160; For these reasons I've decided that no Activity of mine will leave the <strong>Keep</strong> button unhidden.&#160; To hide the button I copied a bit of the code for the original <strong>ActivityToolbarButton</strong> in a file named <strong>mybutton.py</strong>:
+</p>
+<pre>import gtk
+import gconf
+
+from sugar.graphics.toolbarbox import ToolbarButton
+from sugar.activity.widgets import ActivityToolbar
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.icon import Icon
+from sugar.bundle.activitybundle import ActivityBundle
+
+def _create_activity_icon(metadata):
+ if metadata.get('icon-color', ''):
+ color = XoColor(metadata['icon-color'])
+ else:
+ client = gconf.client_get_default()
+ color = XoColor(client.get_string(
+ '/desktop/sugar/user/color'))
+
+ from sugar.activity.activity import get_bundle_path
+ bundle = ActivityBundle(get_bundle_path())
+ icon = Icon(file=bundle.get_icon(), xo_color=color)
+
+ return icon
+
+class MyActivityToolbarButton(ToolbarButton):
+
+ def __init__(self, activity, **kwargs):
+ toolbar = ActivityToolbar(activity,
+ orientation_left=True)
+ toolbar.stop.hide()
+ <strong>toolbar.keep.hide()</strong>
+
+ ToolbarButton.__init__(self, page=toolbar,
+ **kwargs)
+
+ icon = _create_activity_icon(activity.metadata)
+ self.set_icon_widget(icon)
+ icon.show()
+</pre>
+<p>The line in <strong>bold</strong> is the one difference between mine and the original.&#160; If <strong>toolbar</strong> had been made an instance variable (<strong>self.toolbar</strong>) I could have used the original class.
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch023_where-to-go-from-here.xhtml b/studio/static/doc/myosa/ch023_where-to-go-from-here.xhtml
new file mode 100644
index 0000000..aa718e2
--- /dev/null
+++ b/studio/static/doc/myosa/ch023_where-to-go-from-here.xhtml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>Where To Go From Here?
+</h1>
+<p>This book attempts to give a beginning programmer the information she needs to develop and publish her own Sugar Activities.&#160; It already contains many URL's of websites containing information not covered in the book.&#160; This chapter will contain URL's and pointers to still more resources that will be useful to any Sugar developer.
+</p>
+<h2>PyGTK Book by Peter Gill
+ <br/></h2>
+<p>Much of the work you will do writing Activities involves PyGTK.&#160; Peter Gill is working on a PyGTK book that covers the subject in great detail.&#160; You can download the book here:
+</p>
+<p><a href="http://www.majorsilence.com/PyGTK_Book">http://www.majorsilence.com/PyGTK_Book</a>&#160;
+ <br/></p>
+<h2>OLPC Austria Activity Handbook
+</h2>
+<p>This is the first attempt to write a manual on creating Sugar Activities.&#160; It is aimed at experienced programmers and covers topics that this book does not, like how to write Activities using languages other than Python.&#160; The book was written in 2008 and as a result some of the advice is a bit dated.&#160; It's still an excellent source of information.&#160; The authors are Christoph Derndorfer and Daniel Jahre.
+</p>
+<p><a href="http://wiki.sugarlabs.org/images/5/51/Activity_Handbook_200805_online.pdf">http://wiki.sugarlabs.org/images/5/51/Activity_Handbook_200805_online.pdf</a>
+ <br/></p>
+<p><a href="http://www.olpcaustria.org">http://www.olpcaustria.org</a>
+ <br/></p>
+<h2>The Sugar Almanac
+</h2>
+<p>This is a series of Wiki articles covering the Sugar <strong>API</strong> (<strong>Application Programming Interface</strong>).&#160; It's a good source of information that I have referred to many times.
+</p>
+<p> <a href="http://wiki.sugarlabs.org/go/Development_Team/Almanac">http://wiki.sugarlabs.org/go/Development_Team/Almanac</a>&#160;
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2> Sugar Labs Mailing Lists
+</h2>
+<p>Sugar Labs has several email mailing lists that might be worth subscribing to.&#160; The ones I follow most are the <strong>IAEP</strong> (<strong>It's An Education Proiject</strong>) list and <strong>Sugar-Devel</strong>.&#160; Sugar-Devel is a good place to ask questions about developing Sugar Activities and learn about the latest work being done on Sugar itself.&#160; IAEP is a good place to get ideas on what kinds of Activities teachers and students want and to get feedback on your own Activities.&#160; Anyone can sign up to these mailing lists here:
+</p>
+<p><a href="http://lists.sugarlabs.org">http://lists.sugarlabs.org/</a>&#160;
+</p>
+<h2>PyDoc
+</h2>
+<p><strong>PyDoc</strong> is a utility for viewing documentation generated from the Python libraries on your computer, including the Sugar libraries.&#160; To run it use this command from a terminal:
+</p>
+<pre>pydoc -p <em>1234</em></pre>
+<p>This command will not finish.&#160; It runs a kind of web server on your system where <em>1234</em> is a port number.&#160; You can access the website it serves at <strong>http://localhost:1234</strong>.&#160; There is nothing magic about the number 1234.&#160; You can use any number you like.
+</p>
+<p>The website lets you follow links to documentation on all the Python libraries you have installed.&#160; When you are done browsing the documentation you can stop the pydoc command bt returning to the terminal and hitting Ctrl-C (hold down the Ctrl key and hit the "c" key).
+</p>
+<p>
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch024_about-the-authors.xhtml b/studio/static/doc/myosa/ch024_about-the-authors.xhtml
new file mode 100644
index 0000000..6f6b90d
--- /dev/null
+++ b/studio/static/doc/myosa/ch024_about-the-authors.xhtml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>About The Authors
+ <br/></h1>
+<h2>James Simmons
+ <br/></h2>
+<p><strong>James Simmons</strong> has programmed professionally since 1978.&#160; Back then computer programs were made using a special machine that punched holes into cards, reels of tape were the most common data storage medium, and hard disks were so expensive and exotic that the hard disk inventory of a Fortune 500 company would today be considered barely large enough to hold a nice picture of Jessica Alba.
+</p>
+<p>The industry has come a long way since then, and to a lesser extent so has James.
+ <br/></p>
+<p>James learned to program at Oakton Community College in Morton Grove, Illinois and Western Illinois University in Macomb, Illinois.&#160; Times were hard back then and a young man's best chance of being employed after graduation was to become an Accountant or a Computer Programmer.&#160; It was while he attended OCC that James saw a Monty Python sketch about an Accountant who wished to become a Lion Tamer.&#160; This convinced James that he should become a Computer Programmer.
+</p>
+<p>James' studies at WIU got off to a rough start when he signed up for Basic Assembly Language as his first real computer class, erroneously thinking that the word "Basic" meant "for beginners". &#160; From the computer's point of view it was basic, but for students not so much.&#160; He barely passed the course with a "D" but in the process learned that he enjoyed programming computers.&#160; He decided to continue his computer studies and graduated with a Bachelor's Degree in Information Science.
+</p>
+<p>James was born in 1956, the year before Sputnik went up.&#160; He was a nerdy kid.&#160; At various times he fooled around with Erector sets, chemistry sets, microscopes, dissecting kits, model cars, model planes, model rockets, amateur radio, film making, and writing science fiction stories.&#160; He achieved no real success with any of these activities.
+</p>
+<p>James participated in the first <em>Give One Get One</em> promotion of the <em>One Laptop Per Child</em> project and started developing Activities for the Sugar platform soon after.&#160; He has written the Activities <strong>Read Etexts</strong>, <strong>View Slides</strong>, <strong>Sugar Commander</strong> and <strong>Get Internet Archive Books</strong>.
+</p>
+<div class="objavi-forcebreak">
+</div>
+<h2>Oceana Rain Fields
+</h2>
+<h2>
+</h2>
+<p> <strong><a href="http://sixes.net/rdcHQ/about/meet-the-rdc/oceana-rain-fields/">Oceana Rain Fields</a></strong> &#8211; Oceana is a visual artist and creative spirit with a flair for the unexpected and the desire to support worthy causes with her art. She graduated in 2010 from Pacific High School, earning several notable scholarships. In 2010, her painting &#8220;Malaria&#8221; won first in show in the Vision 2010 high school art competition at the Coos Art Museum in Coos Bay, Oregon.&#160; Oceana plans to continue her art education at Southwestern Oregon Community College in Fall 2010.&#160;
+</p>
+<p>Oceana is responsible for the cover art of the bound and printed version of this book.&#160; As a "mentee" of the Rural Design Collective, she also did cover and interior illustrations for another FLOSS Manual: <em>An E-Book Revolution: Reading and Leading with One Laptop Per Child</em>.
+</p>
+<p>
+ <br/></p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/ch025_credits.xhtml b/studio/static/doc/myosa/ch025_credits.xhtml
new file mode 100644
index 0000000..c88beaa
--- /dev/null
+++ b/studio/static/doc/myosa/ch025_credits.xhtml
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><body><h1>License
+</h1>
+<p>All chapters copyright of the authors (see below). Unless otherwise stated all chapters in this manual licensed with <strong>GNU General Public License version 2</strong>
+</p> This documentation is free documentation; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+<p> This documentation is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+</p>
+<p> You should have received a copy of the GNU General Public License along with this documentation; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+</p>
+<h2>Acknowledgements
+</h2>
+<p>Many people contributed to this book besides the authors listed.&#160; They offered advice, technical support, corrections, and much code.&#160; If I tried to list all of their names I might leave someone out, so let me just thank all the members of the Sugar-Devel mailing list.
+</p>
+<p>Cover art of the printed version&#160; Copyright (C) 2010 by Oceana Rain Fields.
+ <br/></p>
+<h2>Authors
+</h2>
+<p>
+</p><em>ABOUT THE AUTHORS</em>
+<br/>&#169; James Simmons 2010
+<hr/><em>MAKING ACTIVITIES USING PYGAME</em>
+<br/>&#169; James Simmons 2010
+<hr/><em>ADD REFINEMENTS</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>CREATING YOUR FIRST ACTIVITY</em>
+<br/>&#169; James Simmons 2009
+<br/><br/><hr/><em>CREDITS</em>
+<br/>&#169; James Simmons 2010
+<br/><br/><hr/><em>DISTRIBUTE YOUR ACTIVITY</em>
+<br/>&#169; James Simmons 2010
+<br/><br/><hr/><em>FUN WITH THE JOURNAL</em>
+<br/>&#169; James Simmons 2010
+<hr/><em>GOING INTERNATIONAL WITH POOTLE</em>
+<br/>&#169; James Simmons 2010
+<br/><br/><hr/><em>INHERIT FROM SUGAR.ACTIVITY.ACTIVITY</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>INTRODUCTION</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>WHERE TO GO FROM HERE?</em>
+<br/>&#169; James Simmons 2010
+<hr/><em>WHAT DO I NEED TO KNOW TO WRITE A SUGAR ACTIVITY?</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>MAKING NEW STYLE TOOLBARS</em>
+<br/>&#169; James Simmons 2010
+<hr/><em>PACKAGE THE ACTIVITY</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>SETTING UP A DEVELOPMENT ENVIRONMENT</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>A STANDALONE PYTHON PROGRAM FOR READING ETEXTS</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>MAKING SHARED ACTIVITIES</em>
+<br/>&#169; James Simmons 2010
+<br/><br/><hr/><em>DEBUGGING SUGAR ACTIVITIES</em>
+<br/>&#169; James Simmons 2010
+<hr/><em>ADDING TEXT TO SPEECH</em>
+<br/>&#169; James Simmons 2010
+<br/><br/><hr/><em>ADD YOUR ACTIVITY CODE TO VERSION CONTROL</em>
+<br/>&#169; James Simmons 2010
+<br/><br/><hr/><em>WHAT IS SUGAR?</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/><em>WHAT IS A SUGAR ACTIVITY?</em>
+<br/>&#169; James Simmons 2009, 2010
+<br/><br/><hr/>&#160;
+<p> <a href="http://www.flossmanuals.net/"><img alt="100.gif" src="static/Floss-100-en.gif" height="54" width="110" border="0"/></a>
+ <br/><strong/>
+</p>
+<p><sub>Free manuals for free software</sub></p>
+<p>
+</p>
+<p>&#160;
+</p>
+<p>&#160;
+</p>
+<h2>General Public License
+</h2>
+<p>Version 2, June 1991
+</p>
+<p>Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ <br/>51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ <br/><br/>Everyone is permitted to copy and distribute verbatim copies
+ <br/>of this license document, but changing it is not allowed.
+ <br/></p>
+<p><strong>Preamble</strong>
+</p>
+<p> The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
+</p>
+<p> When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+</p>
+<p> To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+</p>
+<p> For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+</p>
+<p> We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+</p>
+<p> Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+</p>
+<p> Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+</p>
+<p> The precise terms and conditions for copying, distribution and modification follow.
+</p>
+<p><strong>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</strong>
+</p>
+<p> <strong>0.</strong> This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+</p>
+<p> Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+</p>
+<p> <strong>1.</strong> You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+</p>
+<p> You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+</p>
+<p> <strong>2.</strong> You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+</p>
+<dl><dt>
+ <br/></dt>
+ <dd> <strong>a)</strong> You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.</dd>
+ <dt>
+ <br/></dt>
+ <dd> <strong>b)</strong> You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.</dd>
+ <dt>
+ <br/></dt>
+ <dd> <strong>c)</strong> If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)</dd>
+</dl><p> These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+</p>
+<p> Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+</p>
+<p> In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+</p>
+<p> <strong>3.</strong> You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+</p>
+<dl><dt>
+ <br/></dt>
+ <dd> <strong>a)</strong> Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,</dd>
+ <dt>
+ <br/></dt>
+ <dd> <strong>b)</strong> Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,</dd>
+ <dt>
+ <br/></dt>
+ <dd> <strong>c)</strong> Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)</dd>
+</dl><p> The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+</p>
+<p> If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+</p>
+<p> <strong>4.</strong> You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+</p>
+<p> <strong>5.</strong> You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+</p>
+<p> <strong>6.</strong> Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+</p>
+<p> <strong>7.</strong> If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+</p>
+<p> If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+</p>
+<p> It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+</p>
+<p> This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+</p>
+<p> <strong>8.</strong> If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+</p>
+<p> <strong>9.</strong> The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+</p>
+<p> Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+</p>
+<p> <strong>10.</strong> If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+</p>
+<p><strong>NO WARRANTY</strong>
+</p>
+<p> <strong>11.</strong> BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+</p>
+<p> <strong>12.</strong> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+</p>
+<p><strong>END OF TERMS AND CONDITIONS</strong>
+</p>
+<p>
+</p></body></html> \ No newline at end of file
diff --git a/studio/static/doc/myosa/content.opf b/studio/static/doc/myosa/content.opf
new file mode 100644
index 0000000..3d6af9f
--- /dev/null
+++ b/studio/static/doc/myosa/content.opf
@@ -0,0 +1,126 @@
+<package xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="primary_id">
+ <metadata>
+ <dc:publisher>FLOSS Manuals http://flossmanuals.net</dc:publisher>
+ <dc:rights scheme="License">GPLv2+</dc:rights>
+ <dc:language>en</dc:language>
+ <dc:title>Make Your Own Sugar Activities!</dc:title>
+ <dc:creator>James D. Simmons</dc:creator>
+ <dc:date>2010-11-28</dc:date>
+ <dc:date scheme="start">2010.12.09-07.20</dc:date>
+ <dc:date scheme="last-modified">2011.05.13-00.00</dc:date>
+ <dc:date scheme="published">2011.05.13-18.25</dc:date>
+ <dc:identifier id="primary_id">http://en.flossmanuals.net/epub/ActivitiesGuideSugar/2010.11.28-14.18.35</dc:identifier>
+ <dc:identifier scheme="booki.cc">http://booki.flossmanuals.net/make-your-own-sugar-activities/2011.05.13-18.25</dc:identifier>
+ <meta name="cover" content="att000_MYOSA_Cover" />
+ </metadata>
+ <guide>
+ <reference type="toc" title="Table Of Contents" href="ch000_table_of_contents.xhtml" />
+ </guide>
+ <manifest>
+ <item href="static/ActivitiesGuideSugar-collab6-en.jpg" media-type="image/jpeg" id="att014_ActivitiesGuideSugar-collab6-en"/>
+ <item href="ch015_debugging-sugar-activities.xhtml" media-type="application/xhtml+xml" id="ch015_debugging-sugar-activities"/>
+ <item href="static/ActivitiesGuideSugar-pootle1-en.jpg" media-type="image/jpeg" id="att023_ActivitiesGuideSugar-pootle1-en"/>
+ <item href="static/ActivitiesGuideSugar-collab5-en.jpg" media-type="image/jpeg" id="att007_ActivitiesGuideSugar-collab5-en"/>
+ <item href="ch007_creating-your-first-activity.xhtml" media-type="application/xhtml+xml" id="ch007_creating-your-first-activity"/>
+ <item href="static/ActivitiesGuideSugar-git5-en.jpg" media-type="image/jpeg" id="att002_ActivitiesGuideSugar-git5-en"/>
+ <item href="ch003_what-is-a-sugar-activity.xhtml" media-type="application/xhtml+xml" id="ch003_what-is-a-sugar-activity"/>
+ <item href="static/ActivitiesGuideSugar-inkscape-en.jpg" media-type="image/jpeg" id="att048_ActivitiesGuideSugar-inkscape-en"/>
+ <item href="static/ActivitiesGuideSugar-collab1_1-en.jpg" media-type="image/jpeg" id="att005_ActivitiesGuideSugar-collab1_1-en"/>
+ <item href="static/Floss-100-en.gif" media-type="image/gif" id="att039_Floss-100-en"/>
+ <item href="static/ActivitiesGuideSugar-ReadEtexts_02-en.jpg" media-type="image/jpeg" id="att017_ActivitiesGuideSugar-ReadEtexts_02-en"/>
+ <item href="static/ActivitiesGuideSugar-gimp1-en.jpg" media-type="image/jpeg" id="att011_ActivitiesGuideSugar-gimp1-en"/>
+ <item href="static/ActivitiesGuideSugar-ReadEtexts_01_1-en.jpg" media-type="image/jpeg" id="att050_ActivitiesGuideSugar-ReadEtexts_01_1-en"/>
+ <item href="static/ActivitiesGuideSugar-thegimp-en.jpg" media-type="image/jpeg" id="att052_ActivitiesGuideSugar-thegimp-en"/>
+ <item href="ch008_a-standalone-python-program-for-reading-etexts.xhtml" media-type="application/xhtml+xml" id="ch008_a-standalone-python-program-for-reading-etexts"/>
+ <item href="ch018_adding-text-to-speech.xhtml" media-type="application/xhtml+xml" id="ch018_adding-text-to-speech"/>
+ <item href="static/ActivitiesGuideSugar-journal_main_screen-en.png" media-type="image/png" id="att036_ActivitiesGuideSugar-journal_main_screen-en"/>
+ <item href="static/ActivitiesGuideSugar-SpeakActivity-en.png" media-type="image/png" id="att035_ActivitiesGuideSugar-SpeakActivity-en"/>
+ <item href="static/ActivitiesGuideSugar-git3-en.jpg" media-type="image/jpeg" id="att001_ActivitiesGuideSugar-git3-en"/>
+ <item href="ch025_credits.xhtml" media-type="application/xhtml+xml" id="ch025_credits"/>
+ <item href="static/ActivitiesGuideSugar-collab2_1-en.jpg" media-type="image/jpeg" id="att055_ActivitiesGuideSugar-collab2_1-en"/>
+ <item href="ch012_add-your-activity-code-to-version-control.xhtml" media-type="application/xhtml+xml" id="ch012_add-your-activity-code-to-version-control"/>
+ <item href="ch000_table_of_contents.xhtml" media-type="application/xhtml+xml" id="ch000_table_of_contents"/>
+ <item href="ch001_introduction.xhtml" media-type="application/xhtml+xml" id="ch001_introduction"/>
+ <item href="static/ActivitiesGuideSugar-git10-en.jpg" media-type="image/jpeg" id="att032_ActivitiesGuideSugar-git10-en"/>
+ <item href="ch023_where-to-go-from-here.xhtml" media-type="application/xhtml+xml" id="ch023_where-to-go-from-here"/>
+ <item href="ch021_making-new-style-toolbars.xhtml" media-type="application/xhtml+xml" id="ch021_making-new-style-toolbars"/>
+ <item href="static/ActivitiesGuideSugar-logactivity2-en.jpg" media-type="image/jpeg" id="att054_ActivitiesGuideSugar-logactivity2-en"/>
+ <item href="static/ActivitiesGuideSugar-newtoolbar1_1-en.jpg" media-type="image/jpeg" id="att056_ActivitiesGuideSugar-newtoolbar1_1-en"/>
+ <item href="static/ActivitiesGuideSugar-logactivity1-en.jpg" media-type="image/jpeg" id="att053_ActivitiesGuideSugar-logactivity1-en"/>
+ <item href="ch014_distribute-your-activity.xhtml" media-type="application/xhtml+xml" id="ch014_distribute-your-activity"/>
+ <item href="static/ActivitiesGuideSugar-git1-en.jpg" media-type="image/jpeg" id="att031_ActivitiesGuideSugar-git1-en"/>
+ <item href="ch006_setting-up-a-development-environment.xhtml" media-type="application/xhtml+xml" id="ch006_setting-up-a-development-environment"/>
+ <item href="static/ActivitiesGuideSugar-git13-en.jpg" media-type="image/jpeg" id="att028_ActivitiesGuideSugar-git13-en"/>
+ <item href="static/ActivitiesGuideSugar-git2-en.jpg" media-type="image/jpeg" id="att008_ActivitiesGuideSugar-git2-en"/>
+ <item href="static/ActivitiesGuideSugar-git7-en.jpg" media-type="image/jpeg" id="att033_ActivitiesGuideSugar-git7-en"/>
+ <item href="ch013_going-international-with-pootle.xhtml" media-type="application/xhtml+xml" id="ch013_going-international-with-pootle"/>
+ <item href="ch004_what-do-i-need-to-know-to-write-a-sugar-activity.xhtml" media-type="application/xhtml+xml" id="ch004_what-do-i-need-to-know-to-write-a-sugar-activity"/>
+ <item href="static/ActivitiesGuideSugar-SCommander2-en.jpg" media-type="image/jpeg" id="att013_ActivitiesGuideSugar-SCommander2-en"/>
+ <item href="toc.ncx" media-type="application/x-dtbncx+xml" id="ncx"/>
+ <item href="static/ActivitiesGuideSugar-ReadEtexts_04-en.jpg" media-type="image/jpeg" id="att045_ActivitiesGuideSugar-ReadEtexts_04-en"/>
+ <item href="static/ActivitiesGuideSugar-ReadEtexts_03-en.jpg" media-type="image/jpeg" id="att020_ActivitiesGuideSugar-ReadEtexts_03-en"/>
+ <item href="static/ActivitiesGuideSugar-git8-en.jpg" media-type="image/jpeg" id="att046_ActivitiesGuideSugar-git8-en"/>
+ <item href="static/ActivitiesGuideSugar-espeak-en.jpg" media-type="image/jpeg" id="att030_ActivitiesGuideSugar-espeak-en"/>
+ <item href="static/ActivitiesGuideSugar-CoverImage600x450-en.png" media-type="image/png" id="att034_ActivitiesGuideSugar-CoverImage600x450-en"/>
+ <item href="static/ActivitiesGuideSugar-newtoolbar5-en.jpg" media-type="image/jpeg" id="att041_ActivitiesGuideSugar-newtoolbar5-en"/>
+ <item href="static/MYOSA_Cover.png" media-type="image/png" id="att000_MYOSA_Cover"/>
+ <item href="static/ActivitiesGuideSugar-newtoolbar4-en.jpg" media-type="image/jpeg" id="att010_ActivitiesGuideSugar-newtoolbar4-en"/>
+ <item href="static/ActivitiesGuideSugar-newtoolbar3-en.jpg" media-type="image/jpeg" id="att047_ActivitiesGuideSugar-newtoolbar3-en"/>
+ <item href="static/ActivitiesGuideSugar-pootle2-en.jpg" media-type="image/jpeg" id="att042_ActivitiesGuideSugar-pootle2-en"/>
+ <item href="ch017_making-shared-activities.xhtml" media-type="application/xhtml+xml" id="ch017_making-shared-activities"/>
+ <item href="static/ActivitiesGuideSugar-eric-en.jpg" media-type="image/jpeg" id="att004_ActivitiesGuideSugar-eric-en"/>
+ <item href="static/ActivitiesGuideSugar-git14-en.jpg" media-type="image/jpeg" id="att051_ActivitiesGuideSugar-git14-en"/>
+ <item href="static/ActivitiesGuideSugar-demoiselle1-en.jpg" media-type="image/jpeg" id="att019_ActivitiesGuideSugar-demoiselle1-en"/>
+ <item href="static/ActivitiesGuideSugar-sugargame_1-en.jpg" media-type="image/jpeg" id="att037_ActivitiesGuideSugar-sugargame_1-en"/>
+ <item href="static/ActivitiesGuideSugar-SCommander1-en.jpg" media-type="image/jpeg" id="att009_ActivitiesGuideSugar-SCommander1-en"/>
+ <item href="static/ActivitiesGuideSugar-analyzeactivity_1-en.jpg" media-type="image/jpeg" id="att029_ActivitiesGuideSugar-analyzeactivity_1-en"/>
+ <item href="ch009_inherit-from-sugaractivityactivity.xhtml" media-type="application/xhtml+xml" id="ch009_inherit-from-sugaractivityactivity"/>
+ <item href="static/ActivitiesGuideSugar-espeak3-en.jpg" media-type="image/jpeg" id="att027_ActivitiesGuideSugar-espeak3-en"/>
+ <item href="ch011_add-refinements.xhtml" media-type="application/xhtml+xml" id="ch011_add-refinements"/>
+ <item href="static/ActivitiesGuideSugar-espeak2_1-en.jpg" media-type="image/jpeg" id="att022_ActivitiesGuideSugar-espeak2_1-en"/>
+ <item href="static/ActivitiesGuideSugar-demoiselle2_1-en.jpg" media-type="image/jpeg" id="att049_ActivitiesGuideSugar-demoiselle2_1-en"/>
+ <item href="static/ActivitiesGuideSugar-collab4-en.jpg" media-type="image/jpeg" id="att015_ActivitiesGuideSugar-collab4-en"/>
+ <item href="static/ActivitiesGuideSugar-newtoolbar2-en.jpg" media-type="image/jpeg" id="att026_ActivitiesGuideSugar-newtoolbar2-en"/>
+ <item href="ch010_package-the-activity.xhtml" media-type="application/xhtml+xml" id="ch010_package-the-activity"/>
+ <item href="ch020_making-activities-using-pygame.xhtml" media-type="application/xhtml+xml" id="ch020_making-activities-using-pygame"/>
+ <item href="static/ActivitiesGuideSugar-batallanaval-en.jpg" media-type="image/jpeg" id="att016_ActivitiesGuideSugar-batallanaval-en"/>
+ <item href="static/ActivitiesGuideSugar-ReadEtexts_05-en.jpg" media-type="image/jpeg" id="att038_ActivitiesGuideSugar-ReadEtexts_05-en"/>
+ <item href="static/ActivitiesGuideSugar-pootle3-en.jpg" media-type="image/jpeg" id="att044_ActivitiesGuideSugar-pootle3-en"/>
+ <item href="static/ActivitiesGuideSugar-git12-en.jpg" media-type="image/jpeg" id="att012_ActivitiesGuideSugar-git12-en"/>
+ <item href="static/ActivitiesGuideSugar-git11_1-en.jpg" media-type="image/jpeg" id="att018_ActivitiesGuideSugar-git11_1-en"/>
+ <item href="ch002_what-is-sugar.xhtml" media-type="application/xhtml+xml" id="ch002_what-is-sugar"/>
+ <item href="static/ActivitiesGuideSugar-collab3_1-en.jpg" media-type="image/jpeg" id="att040_ActivitiesGuideSugar-collab3_1-en"/>
+ <item href="static/ActivitiesGuideSugar-git4-en.jpg" media-type="image/jpeg" id="att024_ActivitiesGuideSugar-git4-en"/>
+ <item href="static/ActivitiesGuideSugar-oldtoolbar-en.jpg" media-type="image/jpeg" id="att025_ActivitiesGuideSugar-oldtoolbar-en"/>
+ <item href="ch024_about-the-authors.xhtml" media-type="application/xhtml+xml" id="ch024_about-the-authors"/>
+ <item href="ch019_fun-with-the-journal.xhtml" media-type="application/xhtml+xml" id="ch019_fun-with-the-journal"/>
+ <item href="static/ActivitiesGuideSugar-git6-en.jpg" media-type="image/jpeg" id="att006_ActivitiesGuideSugar-git6-en"/>
+ <item href="static/ActivitiesGuideSugar-git9-en.jpg" media-type="image/jpeg" id="att021_ActivitiesGuideSugar-git9-en"/>
+ <item href="static/ActivitiesGuideSugar-scribble-en.jpg" media-type="image/jpeg" id="att003_ActivitiesGuideSugar-scribble-en"/>
+ <item href="static/ActivitiesGuideSugar-spe-en.jpg" media-type="image/jpeg" id="att043_ActivitiesGuideSugar-spe-en"/>
+ </manifest>
+ <spine toc="ncx">
+ <itemref idref="ch001_introduction"/>
+ <itemref idref="ch002_what-is-sugar"/>
+ <itemref idref="ch003_what-is-a-sugar-activity"/>
+ <itemref idref="ch004_what-do-i-need-to-know-to-write-a-sugar-activity"/>
+ <itemref idref="ch006_setting-up-a-development-environment"/>
+ <itemref idref="ch007_creating-your-first-activity"/>
+ <itemref idref="ch008_a-standalone-python-program-for-reading-etexts"/>
+ <itemref idref="ch009_inherit-from-sugaractivityactivity"/>
+ <itemref idref="ch010_package-the-activity"/>
+ <itemref idref="ch011_add-refinements"/>
+ <itemref idref="ch012_add-your-activity-code-to-version-control"/>
+ <itemref idref="ch013_going-international-with-pootle"/>
+ <itemref idref="ch014_distribute-your-activity"/>
+ <itemref idref="ch015_debugging-sugar-activities"/>
+ <itemref idref="ch017_making-shared-activities"/>
+ <itemref idref="ch018_adding-text-to-speech"/>
+ <itemref idref="ch019_fun-with-the-journal"/>
+ <itemref idref="ch020_making-activities-using-pygame"/>
+ <itemref idref="ch021_making-new-style-toolbars"/>
+ <itemref idref="ch023_where-to-go-from-here"/>
+ <itemref idref="ch024_about-the-authors"/>
+ <itemref idref="ch025_credits"/>
+ </spine>
+</package>
diff --git a/studio/static/doc/myosa/mimetype b/studio/static/doc/myosa/mimetype
new file mode 100644
index 0000000..bc3a3ea
--- /dev/null
+++ b/studio/static/doc/myosa/mimetype
@@ -0,0 +1 @@
+application/x-booki+zip \ No newline at end of file
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-CoverImage600x450-en.png b/studio/static/doc/myosa/static/ActivitiesGuideSugar-CoverImage600x450-en.png
new file mode 100644
index 0000000..2734e7b
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-CoverImage600x450-en.png
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_01_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_01_1-en.jpg
new file mode 100644
index 0000000..352effb
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_01_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_02-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_02-en.jpg
new file mode 100644
index 0000000..dcf22b5
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_02-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_03-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_03-en.jpg
new file mode 100644
index 0000000..9302035
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_03-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_04-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_04-en.jpg
new file mode 100644
index 0000000..a24fd89
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_04-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_05-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_05-en.jpg
new file mode 100644
index 0000000..e0bbe0c
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-ReadEtexts_05-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander1-en.jpg
new file mode 100644
index 0000000..e68af82
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander2-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander2-en.jpg
new file mode 100644
index 0000000..4f5c422
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-SCommander2-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-SpeakActivity-en.png b/studio/static/doc/myosa/static/ActivitiesGuideSugar-SpeakActivity-en.png
new file mode 100644
index 0000000..d4b8701
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-SpeakActivity-en.png
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-analyzeactivity_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-analyzeactivity_1-en.jpg
new file mode 100644
index 0000000..bbd3d97
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-analyzeactivity_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-batallanaval-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-batallanaval-en.jpg
new file mode 100644
index 0000000..6e29bc8
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-batallanaval-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab1_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab1_1-en.jpg
new file mode 100644
index 0000000..9411746
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab1_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab2_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab2_1-en.jpg
new file mode 100644
index 0000000..e8fdf36
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab2_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab3_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab3_1-en.jpg
new file mode 100644
index 0000000..3c0a319
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab3_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab4-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab4-en.jpg
new file mode 100644
index 0000000..c36d275
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab4-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab5-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab5-en.jpg
new file mode 100644
index 0000000..ceebd85
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab5-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab6-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab6-en.jpg
new file mode 100644
index 0000000..43b42d0
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-collab6-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle1-en.jpg
new file mode 100644
index 0000000..363910e
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle2_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle2_1-en.jpg
new file mode 100644
index 0000000..29ec5f7
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-demoiselle2_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-eric-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-eric-en.jpg
new file mode 100644
index 0000000..2a95c5c
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-eric-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak-en.jpg
new file mode 100644
index 0000000..9a144ad
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak2_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak2_1-en.jpg
new file mode 100644
index 0000000..5e00959
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak2_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak3-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak3-en.jpg
new file mode 100644
index 0000000..59e4140
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-espeak3-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-gimp1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-gimp1-en.jpg
new file mode 100644
index 0000000..38f7478
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-gimp1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git1-en.jpg
new file mode 100644
index 0000000..cae8fba
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git10-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git10-en.jpg
new file mode 100644
index 0000000..47c3278
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git10-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git11_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git11_1-en.jpg
new file mode 100644
index 0000000..23572f2
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git11_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git12-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git12-en.jpg
new file mode 100644
index 0000000..bb3aef1
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git12-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git13-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git13-en.jpg
new file mode 100644
index 0000000..7d8a902
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git13-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git14-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git14-en.jpg
new file mode 100644
index 0000000..ca3e795
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git14-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git2-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git2-en.jpg
new file mode 100644
index 0000000..bd80440
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git2-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git3-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git3-en.jpg
new file mode 100644
index 0000000..e9df4a7
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git3-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git4-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git4-en.jpg
new file mode 100644
index 0000000..3eff680
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git4-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git5-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git5-en.jpg
new file mode 100644
index 0000000..cb82bfe
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git5-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git6-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git6-en.jpg
new file mode 100644
index 0000000..d5ba0da
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git6-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git7-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git7-en.jpg
new file mode 100644
index 0000000..5c383bf
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git7-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git8-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git8-en.jpg
new file mode 100644
index 0000000..0940192
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git8-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-git9-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git9-en.jpg
new file mode 100644
index 0000000..80cf84f
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-git9-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-inkscape-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-inkscape-en.jpg
new file mode 100644
index 0000000..4128ee7
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-inkscape-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-journal_main_screen-en.png b/studio/static/doc/myosa/static/ActivitiesGuideSugar-journal_main_screen-en.png
new file mode 100644
index 0000000..5173660
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-journal_main_screen-en.png
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity1-en.jpg
new file mode 100644
index 0000000..22362de
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity2-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity2-en.jpg
new file mode 100644
index 0000000..aabacce
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-logactivity2-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar1_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar1_1-en.jpg
new file mode 100644
index 0000000..972fb49
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar1_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar2-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar2-en.jpg
new file mode 100644
index 0000000..cc2f900
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar2-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar3-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar3-en.jpg
new file mode 100644
index 0000000..e7f441b
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar3-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar4-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar4-en.jpg
new file mode 100644
index 0000000..6ecf697
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar4-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar5-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar5-en.jpg
new file mode 100644
index 0000000..d1d6b15
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-newtoolbar5-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-oldtoolbar-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-oldtoolbar-en.jpg
new file mode 100644
index 0000000..03e8509
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-oldtoolbar-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle1-en.jpg
new file mode 100644
index 0000000..80845e1
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle2-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle2-en.jpg
new file mode 100644
index 0000000..42e6695
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle2-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle3-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle3-en.jpg
new file mode 100644
index 0000000..8bdf69e
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-pootle3-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-scribble-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-scribble-en.jpg
new file mode 100644
index 0000000..d1627da
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-scribble-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-spe-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-spe-en.jpg
new file mode 100644
index 0000000..18e47ed
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-spe-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-sugargame_1-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-sugargame_1-en.jpg
new file mode 100644
index 0000000..844029f
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-sugargame_1-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/ActivitiesGuideSugar-thegimp-en.jpg b/studio/static/doc/myosa/static/ActivitiesGuideSugar-thegimp-en.jpg
new file mode 100644
index 0000000..c090c83
--- /dev/null
+++ b/studio/static/doc/myosa/static/ActivitiesGuideSugar-thegimp-en.jpg
Binary files differ
diff --git a/studio/static/doc/myosa/static/Floss-100-en.gif b/studio/static/doc/myosa/static/Floss-100-en.gif
new file mode 100644
index 0000000..de26325
--- /dev/null
+++ b/studio/static/doc/myosa/static/Floss-100-en.gif
Binary files differ
diff --git a/studio/static/doc/myosa/static/MYOSA_Cover.png b/studio/static/doc/myosa/static/MYOSA_Cover.png
new file mode 100644
index 0000000..1bce943
--- /dev/null
+++ b/studio/static/doc/myosa/static/MYOSA_Cover.png
Binary files differ
diff --git a/studio/static/doc/myosa/toc.ncx b/studio/static/doc/myosa/toc.ncx
new file mode 100644
index 0000000..a5054b8
--- /dev/null
+++ b/studio/static/doc/myosa/toc.ncx
@@ -0,0 +1,170 @@
+<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
+<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
+ <head>
+ <meta content="http://en.flossmanuals.net/epub/ActivitiesGuideSugar/2010.11.28-14.18.35" name="dtb:uid"/>
+ <meta content="2" name="dtb:depth"/>
+ <meta content="0" name="dtb:totalPageCount"/>
+ <meta content="0" name="dtb:maxPageNumber"/>
+ </head>
+ <docTitle>
+ <text>Make Your Own Sugar Activities!</text>
+ </docTitle>
+ <navMap>
+ <navPoint id="chapter0" playOrder="1">
+ <navLabel>
+ <text>SUGAR ACTIVITIES</text>
+ </navLabel>
+ <content src="ch001_introduction.xhtml"/>
+ <navPoint id="chapter1" playOrder="2">
+ <navLabel>
+ <text>INTRODUCTION</text>
+ </navLabel>
+ <content src="ch001_introduction.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter2" playOrder="3">
+ <navLabel>
+ <text>WHAT IS SUGAR?</text>
+ </navLabel>
+ <content src="ch002_what-is-sugar.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter3" playOrder="4">
+ <navLabel>
+ <text>WHAT IS A SUGAR ACTIVITY?</text>
+ </navLabel>
+ <content src="ch003_what-is-a-sugar-activity.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter4" playOrder="5">
+ <navLabel>
+ <text>WHAT DO I NEED TO KNOW TO WRITE A SUGAR ACTIVITY?</text>
+ </navLabel>
+ <content src="ch004_what-do-i-need-to-know-to-write-a-sugar-activity.xhtml"/>
+ </navPoint>
+ </navPoint>
+ <navPoint id="chapter5" playOrder="6">
+ <navLabel>
+ <text>PROGRAMMING</text>
+ </navLabel>
+ <content src="ch006_setting-up-a-development-environment.xhtml"/>
+ <navPoint id="chapter6" playOrder="7">
+ <navLabel>
+ <text>SETTING UP A DEVELOPMENT ENVIRONMENT</text>
+ </navLabel>
+ <content src="ch006_setting-up-a-development-environment.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter7" playOrder="8">
+ <navLabel>
+ <text>CREATING YOUR FIRST ACTIVITY</text>
+ </navLabel>
+ <content src="ch007_creating-your-first-activity.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter8" playOrder="9">
+ <navLabel>
+ <text>A STANDALONE PYTHON PROGRAM FOR READING ETEXTS</text>
+ </navLabel>
+ <content src="ch008_a-standalone-python-program-for-reading-etexts.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter9" playOrder="10">
+ <navLabel>
+ <text>INHERIT FROM SUGAR.ACTIVITY.ACTIVITY</text>
+ </navLabel>
+ <content src="ch009_inherit-from-sugaractivityactivity.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter10" playOrder="11">
+ <navLabel>
+ <text>PACKAGE THE ACTIVITY</text>
+ </navLabel>
+ <content src="ch010_package-the-activity.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter11" playOrder="12">
+ <navLabel>
+ <text>ADD REFINEMENTS</text>
+ </navLabel>
+ <content src="ch011_add-refinements.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter12" playOrder="13">
+ <navLabel>
+ <text>ADD YOUR ACTIVITY CODE TO VERSION CONTROL</text>
+ </navLabel>
+ <content src="ch012_add-your-activity-code-to-version-control.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter13" playOrder="14">
+ <navLabel>
+ <text>GOING INTERNATIONAL WITH POOTLE</text>
+ </navLabel>
+ <content src="ch013_going-international-with-pootle.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter14" playOrder="15">
+ <navLabel>
+ <text>DISTRIBUTE YOUR ACTIVITY</text>
+ </navLabel>
+ <content src="ch014_distribute-your-activity.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter15" playOrder="16">
+ <navLabel>
+ <text>DEBUGGING SUGAR ACTIVITIES</text>
+ </navLabel>
+ <content src="ch015_debugging-sugar-activities.xhtml"/>
+ </navPoint>
+ </navPoint>
+ <navPoint id="chapter16" playOrder="17">
+ <navLabel>
+ <text>ADVANCED TOPICS</text>
+ </navLabel>
+ <content src="ch017_making-shared-activities.xhtml"/>
+ <navPoint id="chapter17" playOrder="18">
+ <navLabel>
+ <text>MAKING SHARED ACTIVITIES</text>
+ </navLabel>
+ <content src="ch017_making-shared-activities.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter18" playOrder="19">
+ <navLabel>
+ <text>ADDING TEXT TO SPEECH</text>
+ </navLabel>
+ <content src="ch018_adding-text-to-speech.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter19" playOrder="20">
+ <navLabel>
+ <text>FUN WITH THE JOURNAL</text>
+ </navLabel>
+ <content src="ch019_fun-with-the-journal.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter20" playOrder="21">
+ <navLabel>
+ <text>MAKING ACTIVITIES USING PYGAME</text>
+ </navLabel>
+ <content src="ch020_making-activities-using-pygame.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter21" playOrder="22">
+ <navLabel>
+ <text>MAKING NEW STYLE TOOLBARS</text>
+ </navLabel>
+ <content src="ch021_making-new-style-toolbars.xhtml"/>
+ </navPoint>
+ </navPoint>
+ <navPoint id="chapter22" playOrder="23">
+ <navLabel>
+ <text>APPENDIX</text>
+ </navLabel>
+ <content src="ch023_where-to-go-from-here.xhtml"/>
+ <navPoint id="chapter23" playOrder="24">
+ <navLabel>
+ <text>WHERE TO GO FROM HERE?</text>
+ </navLabel>
+ <content src="ch023_where-to-go-from-here.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter24" playOrder="25">
+ <navLabel>
+ <text>ABOUT THE AUTHORS</text>
+ </navLabel>
+ <content src="ch024_about-the-authors.xhtml"/>
+ </navPoint>
+ <navPoint id="chapter25" playOrder="26">
+ <navLabel>
+ <text>CREDITS</text>
+ </navLabel>
+ <content src="ch025_credits.xhtml"/>
+ </navPoint>
+ </navPoint>
+ </navMap>
+</ncx>
diff --git a/studio/static/doc/sugar-theme.html b/studio/static/doc/sugar-theme.html
new file mode 100644
index 0000000..ac09754
--- /dev/null
+++ b/studio/static/doc/sugar-theme.html
@@ -0,0 +1,369 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <title>jQuery UI Example Page</title>
+ <link rel="stylesheet" href="/static/css/main.css" type="text/css" />
+ <link type="text/css" href="/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css" rel="stylesheet" />
+ <script type="text/javascript" src="/static/js/jquery-1.6.2.js"></script>
+ <script type="text/javascript" src="/static/js/jquery-ui-1.8.16.sugar.min.js"></script>
+ <script type="text/javascript">
+ $(function(){
+
+ // Accordion
+ $("#accordion").accordion({ header: "h3" });
+
+ // Tabs
+ $('#tabs').tabs();
+
+
+ // Dialog
+ $('#dialog').dialog({
+ autoOpen: false,
+ width: 600,
+ buttons: {
+ "Ok": function() {
+ $(this).dialog("close");
+ },
+ "Cancel": function() {
+ $(this).dialog("close");
+ }
+ }
+ });
+
+ // Dialog Link
+ $('#dialog_link').click(function(){
+ $('#dialog').dialog('open');
+ return false;
+ });
+
+ // Datepicker
+ $('#datepicker').datepicker({
+ inline: true
+ });
+
+ // Slider
+ $('#slider').slider({
+ range: true,
+ values: [17, 67]
+ });
+
+ // Progressbar
+ $("#progressbar").progressbar({
+ value: 20
+ });
+
+ //hover states on the static widgets
+ $('#dialog_link, ul#icons li').hover(
+ function() { $(this).addClass('ui-state-hover'); },
+ function() { $(this).removeClass('ui-state-hover'); }
+ );
+
+ });
+ </script>
+ <style type="text/css">
+ /*demo page css*/
+ body { margin: 40px; }
+ .demoHeaders { margin-top: 2em; }
+ #dialog_link {padding: .4em 1em .4em 20px;text-decoration: none;position: relative;}
+ #dialog_link span.ui-icon {margin: 0 5px 0 0;position: absolute;left: .2em;top: 50%;margin-top: -8px;}
+ ul#icons {margin: 0; padding: 0;}
+ ul#icons li {margin: 2px; position: relative; padding: 4px 0; cursor: pointer; float: left; list-style: none;}
+ ul#icons span.ui-icon {float: left; margin: 0 4px;}
+ </style>
+ </head>
+ <body>
+ <h1>Welcome to jQuery UI!</h1>
+ <p style="font-size: 1.3em; line-height: 1.5; margin: 1em 0; width: 50%;">
+ <a href="/edit/websdk/static/doc/sugar-theme.html">View the source of this document.</a>
+ </p>
+
+ <p style="font-weight: bold; margin: 2em 0 1em; font-size: 1.3em;">YOUR COMPONENTS:</p>
+
+ <!-- Accordion -->
+ <h2 class="demoHeaders">Accordion</h2>
+ <div id="accordion">
+ <div>
+ <h3><a href="#">First</a></h3>
+ <div>Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.</div>
+ </div>
+ <div>
+ <h3><a href="#">Second</a></h3>
+ <div>Phasellus mattis tincidunt nibh.</div>
+ </div>
+ <div>
+ <h3><a href="#">Third</a></h3>
+ <div>Nam dui erat, auctor a, dignissim quis.</div>
+ </div>
+ </div>
+
+ <!-- Tabs -->
+ <h2 class="demoHeaders">Tabs</h2>
+ <div id="tabs">
+ <ul>
+ <li><a href="#tabs-1">First</a></li>
+ <li><a href="#tabs-2">Second</a></li>
+ <li><a href="#tabs-3">Third</a></li>
+ </ul>
+ <div id="tabs-1">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
+ <div id="tabs-2">Phasellus mattis tincidunt nibh. Cras orci urna, blandit id, pretium vel, aliquet ornare, felis. Maecenas scelerisque sem non nisl. Fusce sed lorem in enim dictum bibendum.</div>
+ <div id="tabs-3">Nam dui erat, auctor a, dignissim quis, sollicitudin eu, felis. Pellentesque nisi urna, interdum eget, sagittis et, consequat vestibulum, lacus. Mauris porttitor ullamcorper augue.</div>
+ </div>
+
+ <!-- Dialog NOTE: Dialog is not generated by UI in this demo so it can be visually styled in themeroller-->
+ <h2 class="demoHeaders">Dialog</h2>
+ <p><a href="#" id="dialog_link" class="ui-state-default ui-corner-all"><span class="ui-icon ui-icon-newwin"></span>Open Dialog</a></p>
+
+
+ <h2 class="demoHeaders">Overlay and Shadow Classes <em>(not currently used in UI widgets)</em></h2>
+ <div style="position: relative; width: 96%; height: 200px; padding:1% 4%; overflow:hidden;" class="fakewindowcontain">
+ <p>Lorem ipsum dolor sit amet, Nulla nec tortor. Donec id elit quis purus consectetur consequat. </p><p>Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. </p><p>Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. </p><p>Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. </p><p>Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. </p><p>Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. </p>
+
+ <!-- ui-dialog -->
+ <div class="ui-overlay"><div class="ui-widget-overlay"></div><div class="ui-widget-shadow ui-corner-all" style="width: 302px; height: 152px; position: absolute; left: 50px; top: 30px;"></div></div>
+ <div style="position: absolute; width: 280px; height: 130px;left: 50px; top: 30px; padding: 10px;" class="ui-widget ui-widget-content ui-corner-all">
+ <div class="ui-dialog-content ui-widget-content" style="background: none; border: 0;">
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
+ </div>
+ </div>
+
+ </div>
+
+
+ <!-- ui-dialog -->
+ <div id="dialog" title="Dialog Title">
+ <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
+ </div>
+
+
+
+ <h2 class="demoHeaders">Framework Icons (content color preview)</h2>
+ <ul id="icons" class="ui-widget ui-helper-clearfix">
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-n"><span class="ui-icon ui-icon-carat-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-ne"><span class="ui-icon ui-icon-carat-1-ne"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-e"><span class="ui-icon ui-icon-carat-1-e"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-se"><span class="ui-icon ui-icon-carat-1-se"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-s"><span class="ui-icon ui-icon-carat-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-sw"><span class="ui-icon ui-icon-carat-1-sw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-w"><span class="ui-icon ui-icon-carat-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-1-nw"><span class="ui-icon ui-icon-carat-1-nw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-2-n-s"><span class="ui-icon ui-icon-carat-2-n-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-carat-2-e-w"><span class="ui-icon ui-icon-carat-2-e-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-n"><span class="ui-icon ui-icon-triangle-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-ne"><span class="ui-icon ui-icon-triangle-1-ne"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-e"><span class="ui-icon ui-icon-triangle-1-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-se"><span class="ui-icon ui-icon-triangle-1-se"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-s"><span class="ui-icon ui-icon-triangle-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-sw"><span class="ui-icon ui-icon-triangle-1-sw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-w"><span class="ui-icon ui-icon-triangle-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-nw"><span class="ui-icon ui-icon-triangle-1-nw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-2-n-s"><span class="ui-icon ui-icon-triangle-2-n-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-2-e-w"><span class="ui-icon ui-icon-triangle-2-e-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-n"><span class="ui-icon ui-icon-arrow-1-n"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-ne"><span class="ui-icon ui-icon-arrow-1-ne"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-e"><span class="ui-icon ui-icon-arrow-1-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-se"><span class="ui-icon ui-icon-arrow-1-se"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-s"><span class="ui-icon ui-icon-arrow-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-sw"><span class="ui-icon ui-icon-arrow-1-sw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-w"><span class="ui-icon ui-icon-arrow-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-nw"><span class="ui-icon ui-icon-arrow-1-nw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-n-s"><span class="ui-icon ui-icon-arrow-2-n-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-ne-sw"><span class="ui-icon ui-icon-arrow-2-ne-sw"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-e-w"><span class="ui-icon ui-icon-arrow-2-e-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-se-nw"><span class="ui-icon ui-icon-arrow-2-se-nw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-n"><span class="ui-icon ui-icon-arrowstop-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-e"><span class="ui-icon ui-icon-arrowstop-1-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-s"><span class="ui-icon ui-icon-arrowstop-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-w"><span class="ui-icon ui-icon-arrowstop-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-n"><span class="ui-icon ui-icon-arrowthick-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-ne"><span class="ui-icon ui-icon-arrowthick-1-ne"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-e"><span class="ui-icon ui-icon-arrowthick-1-e"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-se"><span class="ui-icon ui-icon-arrowthick-1-se"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-s"><span class="ui-icon ui-icon-arrowthick-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-sw"><span class="ui-icon ui-icon-arrowthick-1-sw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-w"><span class="ui-icon ui-icon-arrowthick-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-nw"><span class="ui-icon ui-icon-arrowthick-1-nw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-n-s"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-ne-sw"><span class="ui-icon ui-icon-arrowthick-2-ne-sw"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-e-w"><span class="ui-icon ui-icon-arrowthick-2-e-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-se-nw"><span class="ui-icon ui-icon-arrowthick-2-se-nw"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-n"><span class="ui-icon ui-icon-arrowthickstop-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-e"><span class="ui-icon ui-icon-arrowthickstop-1-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-s"><span class="ui-icon ui-icon-arrowthickstop-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-w"><span class="ui-icon ui-icon-arrowthickstop-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-w"><span class="ui-icon ui-icon-arrowreturnthick-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-n"><span class="ui-icon ui-icon-arrowreturnthick-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-e"><span class="ui-icon ui-icon-arrowreturnthick-1-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-s"><span class="ui-icon ui-icon-arrowreturnthick-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-w"><span class="ui-icon ui-icon-arrowreturn-1-w"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-n"><span class="ui-icon ui-icon-arrowreturn-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-e"><span class="ui-icon ui-icon-arrowreturn-1-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-s"><span class="ui-icon ui-icon-arrowreturn-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-w"><span class="ui-icon ui-icon-arrowrefresh-1-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-n"><span class="ui-icon ui-icon-arrowrefresh-1-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-e"><span class="ui-icon ui-icon-arrowrefresh-1-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-s"><span class="ui-icon ui-icon-arrowrefresh-1-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-4"><span class="ui-icon ui-icon-arrow-4"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-4-diag"><span class="ui-icon ui-icon-arrow-4-diag"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-extlink"><span class="ui-icon ui-icon-extlink"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-newwin"><span class="ui-icon ui-icon-newwin"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-refresh"><span class="ui-icon ui-icon-refresh"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-shuffle"><span class="ui-icon ui-icon-shuffle"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-transfer-e-w"><span class="ui-icon ui-icon-transfer-e-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-transferthick-e-w"><span class="ui-icon ui-icon-transferthick-e-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-folder-collapsed"><span class="ui-icon ui-icon-folder-collapsed"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-folder-open"><span class="ui-icon ui-icon-folder-open"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-document"><span class="ui-icon ui-icon-document"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-document-b"><span class="ui-icon ui-icon-document-b"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-note"><span class="ui-icon ui-icon-note"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-mail-closed"><span class="ui-icon ui-icon-mail-closed"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-mail-open"><span class="ui-icon ui-icon-mail-open"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-suitcase"><span class="ui-icon ui-icon-suitcase"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-comment"><span class="ui-icon ui-icon-comment"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-person"><span class="ui-icon ui-icon-person"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-print"><span class="ui-icon ui-icon-print"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-trash"><span class="ui-icon ui-icon-trash"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-locked"><span class="ui-icon ui-icon-locked"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-unlocked"><span class="ui-icon ui-icon-unlocked"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-bookmark"><span class="ui-icon ui-icon-bookmark"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-tag"><span class="ui-icon ui-icon-tag"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-home"><span class="ui-icon ui-icon-home"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-flag"><span class="ui-icon ui-icon-flag"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-calculator"><span class="ui-icon ui-icon-calculator"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-cart"><span class="ui-icon ui-icon-cart"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-pencil"><span class="ui-icon ui-icon-pencil"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-clock"><span class="ui-icon ui-icon-clock"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-disk"><span class="ui-icon ui-icon-disk"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-calendar"><span class="ui-icon ui-icon-calendar"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-zoomin"><span class="ui-icon ui-icon-zoomin"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-zoomout"><span class="ui-icon ui-icon-zoomout"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-search"><span class="ui-icon ui-icon-search"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-wrench"><span class="ui-icon ui-icon-wrench"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-gear"><span class="ui-icon ui-icon-gear"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-heart"><span class="ui-icon ui-icon-heart"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-star"><span class="ui-icon ui-icon-star"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-link"><span class="ui-icon ui-icon-link"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-cancel"><span class="ui-icon ui-icon-cancel"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-plus"><span class="ui-icon ui-icon-plus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-plusthick"><span class="ui-icon ui-icon-plusthick"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-minus"><span class="ui-icon ui-icon-minus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-minusthick"><span class="ui-icon ui-icon-minusthick"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-close"><span class="ui-icon ui-icon-close"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-closethick"><span class="ui-icon ui-icon-closethick"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-key"><span class="ui-icon ui-icon-key"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-lightbulb"><span class="ui-icon ui-icon-lightbulb"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-scissors"><span class="ui-icon ui-icon-scissors"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-clipboard"><span class="ui-icon ui-icon-clipboard"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-copy"><span class="ui-icon ui-icon-copy"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-contact"><span class="ui-icon ui-icon-contact"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-image"><span class="ui-icon ui-icon-image"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-video"><span class="ui-icon ui-icon-video"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-script"><span class="ui-icon ui-icon-script"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-alert"><span class="ui-icon ui-icon-alert"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-info"><span class="ui-icon ui-icon-info"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-notice"><span class="ui-icon ui-icon-notice"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-help"><span class="ui-icon ui-icon-help"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-check"><span class="ui-icon ui-icon-check"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-bullet"><span class="ui-icon ui-icon-bullet"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-radio-off"><span class="ui-icon ui-icon-radio-off"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-radio-on"><span class="ui-icon ui-icon-radio-on"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-pin-w"><span class="ui-icon ui-icon-pin-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-pin-s"><span class="ui-icon ui-icon-pin-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-play"><span class="ui-icon ui-icon-play"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-pause"><span class="ui-icon ui-icon-pause"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-seek-next"><span class="ui-icon ui-icon-seek-next"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-seek-prev"><span class="ui-icon ui-icon-seek-prev"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-seek-end"><span class="ui-icon ui-icon-seek-end"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-seek-first"><span class="ui-icon ui-icon-seek-first"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-stop"><span class="ui-icon ui-icon-stop"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-eject"><span class="ui-icon ui-icon-eject"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-volume-off"><span class="ui-icon ui-icon-volume-off"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-volume-on"><span class="ui-icon ui-icon-volume-on"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-power"><span class="ui-icon ui-icon-power"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-signal-diag"><span class="ui-icon ui-icon-signal-diag"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-signal"><span class="ui-icon ui-icon-signal"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-battery-0"><span class="ui-icon ui-icon-battery-0"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-battery-1"><span class="ui-icon ui-icon-battery-1"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-battery-2"><span class="ui-icon ui-icon-battery-2"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-battery-3"><span class="ui-icon ui-icon-battery-3"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-plus"><span class="ui-icon ui-icon-circle-plus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-minus"><span class="ui-icon ui-icon-circle-minus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-close"><span class="ui-icon ui-icon-circle-close"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-e"><span class="ui-icon ui-icon-circle-triangle-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-s"><span class="ui-icon ui-icon-circle-triangle-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-w"><span class="ui-icon ui-icon-circle-triangle-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-n"><span class="ui-icon ui-icon-circle-triangle-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-e"><span class="ui-icon ui-icon-circle-arrow-e"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-s"><span class="ui-icon ui-icon-circle-arrow-s"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-w"><span class="ui-icon ui-icon-circle-arrow-w"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-n"><span class="ui-icon ui-icon-circle-arrow-n"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-zoomin"><span class="ui-icon ui-icon-circle-zoomin"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-zoomout"><span class="ui-icon ui-icon-circle-zoomout"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circle-check"><span class="ui-icon ui-icon-circle-check"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-plus"><span class="ui-icon ui-icon-circlesmall-plus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-minus"><span class="ui-icon ui-icon-circlesmall-minus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-close"><span class="ui-icon ui-icon-circlesmall-close"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-plus"><span class="ui-icon ui-icon-squaresmall-plus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-minus"><span class="ui-icon ui-icon-squaresmall-minus"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-close"><span class="ui-icon ui-icon-squaresmall-close"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-grip-dotted-vertical"><span class="ui-icon ui-icon-grip-dotted-vertical"></span></li>
+
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-grip-dotted-horizontal"><span class="ui-icon ui-icon-grip-dotted-horizontal"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-grip-solid-vertical"><span class="ui-icon ui-icon-grip-solid-vertical"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-grip-solid-horizontal"><span class="ui-icon ui-icon-grip-solid-horizontal"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-gripsmall-diagonal-se"><span class="ui-icon ui-icon-gripsmall-diagonal-se"></span></li>
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-grip-diagonal-se"><span class="ui-icon ui-icon-grip-diagonal-se"></span></li>
+ </ul>
+
+
+ <!-- Slider -->
+ <h2 class="demoHeaders">Slider</h2>
+ <div id="slider"></div>
+
+ <!-- Datepicker -->
+ <h2 class="demoHeaders">Datepicker</h2>
+ <div id="datepicker"></div>
+
+ <!-- Progressbar -->
+ <h2 class="demoHeaders">Progressbar</h2>
+ <div id="progressbar"></div>
+
+ <!-- Highlight / Error -->
+ <h2 class="demoHeaders">Highlight / Error</h2>
+ <div class="ui-widget">
+ <div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;">
+ <p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
+ <strong>Hey!</strong> Sample ui-state-highlight style.</p>
+ </div>
+ </div>
+ <br/>
+ <div class="ui-widget">
+ <div class="ui-state-error ui-corner-all" style="padding: 0 .7em;">
+ <p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>
+ <strong>Alert:</strong> Sample ui-state-error style.</p>
+ </div>
+ </div>
+
+ </body>
+</html>
+
+
diff --git a/studio/static/icons b/studio/static/icons
new file mode 120000
index 0000000..9a5906b
--- /dev/null
+++ b/studio/static/icons
@@ -0,0 +1 @@
+../../icons/ \ No newline at end of file
diff --git a/studio/static/images/logo-flask-s.png b/studio/static/images/logo-flask-s.png
new file mode 100644
index 0000000..9436c2a
--- /dev/null
+++ b/studio/static/images/logo-flask-s.png
Binary files differ
diff --git a/studio/static/images/logo-jqueryui.gif b/studio/static/images/logo-jqueryui.gif
new file mode 100644
index 0000000..a6cfb97
--- /dev/null
+++ b/studio/static/images/logo-jqueryui.gif
Binary files differ
diff --git a/studio/static/images/logo-sugar-trans.png b/studio/static/images/logo-sugar-trans.png
new file mode 100644
index 0000000..5e0ce26
--- /dev/null
+++ b/studio/static/images/logo-sugar-trans.png
Binary files differ
diff --git a/studio/static/images/throbber.gif b/studio/static/images/throbber.gif
new file mode 100644
index 0000000..a7d0fc8
--- /dev/null
+++ b/studio/static/images/throbber.gif
Binary files differ
diff --git a/studio/static/init.html b/studio/static/init.html
new file mode 100644
index 0000000..069b021
--- /dev/null
+++ b/studio/static/init.html
@@ -0,0 +1,26 @@
+ <!DOCTYPE HTML>
+ <html lang="en-US">
+ <head>
+ <meta charset="UTF-8" />
+ <title></title>
+ <link rel="stylesheet" href="static/css/main.css" type="text/css" />
+ <link rel="stylesheet" href="static/css/sugar-theme/jquery-ui-1.8.15.custom.css" />
+ <script src="static/js/jquery-1.6.2.js" type="text/javascript"></script>
+ <script src="static/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
+ <!--script src="js/websdk-1.js" type="text/javascript"></script-->
+ </head>
+ <body>
+ <img id="throbber" class="bling" src="images/throbber.gif"/>
+ <script>
+ jQuery.fn.center = function () {
+ this.css("position","absolute");
+ this.css("top", (($(window).height() - this.outerHeight()) / 2) + $(window).scrollTop() + "px");
+ this.css("left", (($(window).width() - this.outerWidth()) / 2) + $(window).scrollLeft() + "px");
+ return this;
+ }
+ window.onload = function() {
+ $("#throbber").center().fadeIn(1000);
+ }
+ </script>
+</body>
+</html>
diff --git a/studio/static/js/ace/ace-uncompressed.js b/studio/static/js/ace/ace-uncompressed.js
new file mode 100644
index 0000000..389daf2
--- /dev/null
+++ b/studio/static/js/ace/ace-uncompressed.js
@@ -0,0 +1,15284 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Define a module along with a payload
+ * @param module a name for the payload
+ * @param payload a function to call with (require, exports, module) params
+ */
+
+(function() {
+
+var global = (function() {
+ return this;
+})();
+
+// if we find an existing require function use it.
+if (global.require && global.define) {
+ require.packaged = true;
+ return;
+}
+
+var _define = function(module, deps, payload) {
+ if (typeof module !== 'string') {
+ if (_define.original)
+ _define.original.apply(window, arguments);
+ else {
+ console.error('dropping module because define wasn\'t a string.');
+ console.trace();
+ }
+ return;
+ }
+
+ if (arguments.length == 2)
+ payload = deps;
+
+ if (!define.modules)
+ define.modules = {};
+
+ define.modules[module] = payload;
+};
+if (global.define)
+ _define.original = global.define;
+
+global.define = _define;
+
+
+/**
+ * Get at functionality define()ed using the function above
+ */
+var _require = function(module, callback) {
+ if (Object.prototype.toString.call(module) === "[object Array]") {
+ var params = [];
+ for (var i = 0, l = module.length; i < l; ++i) {
+ var dep = lookup(module[i]);
+ if (!dep && _require.original)
+ return _require.original.apply(window, arguments);
+ params.push(dep);
+ }
+ if (callback) {
+ callback.apply(null, params);
+ }
+ }
+ else if (typeof module === 'string') {
+ var payload = lookup(module);
+ if (!payload && _require.original)
+ return _require.original.apply(window, arguments);
+
+ if (callback) {
+ callback();
+ }
+
+ return payload;
+ }
+ else {
+ if (_require.original)
+ return _require.original.apply(window, arguments);
+ }
+};
+
+if (global.require)
+ _require.original = global.require;
+
+global.require = _require;
+require.packaged = true;
+
+/**
+ * Internal function to lookup moduleNames and resolve them by calling the
+ * definition function if needed.
+ */
+var lookup = function(moduleName) {
+ var module = define.modules[moduleName];
+ if (module == null) {
+ console.error('Missing module: ' + moduleName);
+ return null;
+ }
+
+ if (typeof module === 'function') {
+ var exports = {};
+ module(require, exports, { id: moduleName, uri: '' });
+ // cache the resulting module object for next time
+ define.modules[moduleName] = exports;
+ return exports;
+ }
+
+ return module;
+};
+
+})();// vim:set ts=4 sts=4 sw=4 st:
+// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
+// -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)
+// -- dantman Daniel Friesen Copyright(C) 2010 XXX No License Specified
+// -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License
+// -- Irakli Gozalishvili Copyright (C) 2010 MIT License
+
+/*!
+ Copyright (c) 2009, 280 North Inc. http://280north.com/
+ MIT License. http://github.com/280north/narwhal/blob/master/README.md
+*/
+
+define('pilot/fixoldbrowsers', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+/**
+ * Brings an environment as close to ECMAScript 5 compliance
+ * as is possible with the facilities of erstwhile engines.
+ *
+ * ES5 Draft
+ * http://www.ecma-international.org/publications/files/drafts/tc39-2009-050.pdf
+ *
+ * NOTE: this is a draft, and as such, the URL is subject to change. If the
+ * link is broken, check in the parent directory for the latest TC39 PDF.
+ * http://www.ecma-international.org/publications/files/drafts/
+ *
+ * Previous ES5 Draft
+ * http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf
+ * This is a broken link to the previous draft of ES5 on which most of the
+ * numbered specification references and quotes herein were taken. Updating
+ * these references and quotes to reflect the new document would be a welcome
+ * volunteer project.
+ *
+ * @module
+ */
+
+/*whatsupdoc*/
+
+//
+// Function
+// ========
+//
+
+// ES-5 15.3.4.5
+// http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf
+
+if (!Function.prototype.bind) {
+ var slice = Array.prototype.slice;
+ Function.prototype.bind = function bind(that) { // .length is 1
+ // 1. Let Target be the this value.
+ var target = this;
+ // 2. If IsCallable(Target) is false, throw a TypeError exception.
+ // XXX this gets pretty close, for all intents and purposes, letting
+ // some duck-types slide
+ if (typeof target.apply !== "function" || typeof target.call !== "function")
+ return new TypeError();
+ // 3. Let A be a new (possibly empty) internal list of all of the
+ // argument values provided after thisArg (arg1, arg2 etc), in order.
+ var args = slice.call(arguments);
+ // 4. Let F be a new native ECMAScript object.
+ // 9. Set the [[Prototype]] internal property of F to the standard
+ // built-in Function prototype object as specified in 15.3.3.1.
+ // 10. Set the [[Call]] internal property of F as described in
+ // 15.3.4.5.1.
+ // 11. Set the [[Construct]] internal property of F as described in
+ // 15.3.4.5.2.
+ // 12. Set the [[HasInstance]] internal property of F as described in
+ // 15.3.4.5.3.
+ // 13. The [[Scope]] internal property of F is unused and need not
+ // exist.
+ var bound = function bound() {
+
+ if (this instanceof bound) {
+ // 15.3.4.5.2 [[Construct]]
+ // When the [[Construct]] internal method of a function object,
+ // F that was created using the bind function is called with a
+ // list of arguments ExtraArgs the following steps are taken:
+ // 1. Let target be the value of F's [[TargetFunction]]
+ // internal property.
+ // 2. If target has no [[Construct]] internal method, a
+ // TypeError exception is thrown.
+ // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
+ // property.
+ // 4. Let args be a new list containing the same values as the
+ // list boundArgs in the same order followed by the same
+ // values as the list ExtraArgs in the same order.
+
+ var self = Object.create(target.prototype);
+ target.apply(self, args.concat(slice.call(arguments)));
+ return self;
+
+ } else {
+ // 15.3.4.5.1 [[Call]]
+ // When the [[Call]] internal method of a function object, F,
+ // which was created using the bind function is called with a
+ // this value and a list of arguments ExtraArgs the following
+ // steps are taken:
+ // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
+ // property.
+ // 2. Let boundThis be the value of F's [[BoundThis]] internal
+ // property.
+ // 3. Let target be the value of F's [[TargetFunction]] internal
+ // property.
+ // 4. Let args be a new list containing the same values as the list
+ // boundArgs in the same order followed by the same values as
+ // the list ExtraArgs in the same order. 5. Return the
+ // result of calling the [[Call]] internal method of target
+ // providing boundThis as the this value and providing args
+ // as the arguments.
+
+ // equiv: target.call(this, ...boundArgs, ...args)
+ return target.call.apply(
+ target,
+ args.concat(slice.call(arguments))
+ );
+
+ }
+
+ };
+ bound.length = (
+ // 14. If the [[Class]] internal property of Target is "Function", then
+ typeof target === "function" ?
+ // a. Let L be the length property of Target minus the length of A.
+ // b. Set the length own property of F to either 0 or L, whichever is larger.
+ Math.max(target.length - args.length, 0) :
+ // 15. Else set the length own property of F to 0.
+ 0
+ )
+ // 16. The length own property of F is given attributes as specified in
+ // 15.3.5.1.
+ // TODO
+ // 17. Set the [[Extensible]] internal property of F to true.
+ // TODO
+ // 18. Call the [[DefineOwnProperty]] internal method of F with
+ // arguments "caller", PropertyDescriptor {[[Value]]: null,
+ // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]:
+ // false}, and false.
+ // TODO
+ // 19. Call the [[DefineOwnProperty]] internal method of F with
+ // arguments "arguments", PropertyDescriptor {[[Value]]: null,
+ // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]:
+ // false}, and false.
+ // TODO
+ // NOTE Function objects created using Function.prototype.bind do not
+ // have a prototype property.
+ // XXX can't delete it in pure-js.
+ return bound;
+ };
+}
+
+// Shortcut to an often accessed properties, in order to avoid multiple
+// dereference that costs universally.
+// _Please note: Shortcuts are defined after `Function.prototype.bind` as we
+// us it in defining shortcuts.
+var call = Function.prototype.call;
+var prototypeOfArray = Array.prototype;
+var prototypeOfObject = Object.prototype;
+var owns = call.bind(prototypeOfObject.hasOwnProperty);
+
+var defineGetter, defineSetter, lookupGetter, lookupSetter, supportsAccessors;
+// If JS engine supports accessors creating shortcuts.
+if ((supportsAccessors = owns(prototypeOfObject, '__defineGetter__'))) {
+ defineGetter = call.bind(prototypeOfObject.__defineGetter__);
+ defineSetter = call.bind(prototypeOfObject.__defineSetter__);
+ lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
+ lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
+}
+
+
+//
+// Array
+// =====
+//
+
+// ES5 15.4.3.2
+if (!Array.isArray) {
+ Array.isArray = function isArray(obj) {
+ return Object.prototype.toString.call(obj) === "[object Array]";
+ };
+}
+
+// ES5 15.4.4.18
+if (!Array.prototype.forEach) {
+ Array.prototype.forEach = function forEach(block, thisObject) {
+ var len = +this.length;
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ block.call(thisObject, this[i], i, this);
+ }
+ }
+ };
+}
+
+// ES5 15.4.4.19
+// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
+if (!Array.prototype.map) {
+ Array.prototype.map = function map(fun /*, thisp*/) {
+ var len = +this.length;
+ if (typeof fun !== "function")
+ throw new TypeError();
+
+ var res = new Array(len);
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this)
+ res[i] = fun.call(thisp, this[i], i, this);
+ }
+
+ return res;
+ };
+}
+
+// ES5 15.4.4.20
+if (!Array.prototype.filter) {
+ Array.prototype.filter = function filter(block /*, thisp */) {
+ var values = [];
+ var thisp = arguments[1];
+ for (var i = 0; i < this.length; i++)
+ if (block.call(thisp, this[i]))
+ values.push(this[i]);
+ return values;
+ };
+}
+
+// ES5 15.4.4.16
+if (!Array.prototype.every) {
+ Array.prototype.every = function every(block /*, thisp */) {
+ var thisp = arguments[1];
+ for (var i = 0; i < this.length; i++)
+ if (!block.call(thisp, this[i]))
+ return false;
+ return true;
+ };
+}
+
+// ES5 15.4.4.17
+if (!Array.prototype.some) {
+ Array.prototype.some = function some(block /*, thisp */) {
+ var thisp = arguments[1];
+ for (var i = 0; i < this.length; i++)
+ if (block.call(thisp, this[i]))
+ return true;
+ return false;
+ };
+}
+
+// ES5 15.4.4.21
+// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
+if (!Array.prototype.reduce) {
+ Array.prototype.reduce = function reduce(fun /*, initial*/) {
+ var len = +this.length;
+ if (typeof fun !== "function")
+ throw new TypeError();
+
+ // no value to return if no initial value and an empty array
+ if (len === 0 && arguments.length === 1)
+ throw new TypeError();
+
+ var i = 0;
+ if (arguments.length >= 2) {
+ var rv = arguments[1];
+ } else {
+ do {
+ if (i in this) {
+ rv = this[i++];
+ break;
+ }
+
+ // if array contains no values, no initial value to return
+ if (++i >= len)
+ throw new TypeError();
+ } while (true);
+ }
+
+ for (; i < len; i++) {
+ if (i in this)
+ rv = fun.call(null, rv, this[i], i, this);
+ }
+
+ return rv;
+ };
+}
+
+// ES5 15.4.4.22
+// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
+if (!Array.prototype.reduceRight) {
+ Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) {
+ var len = +this.length;
+ if (typeof fun !== "function")
+ throw new TypeError();
+
+ // no value to return if no initial value, empty array
+ if (len === 0 && arguments.length === 1)
+ throw new TypeError();
+
+ var i = len - 1;
+ if (arguments.length >= 2) {
+ var rv = arguments[1];
+ } else {
+ do {
+ if (i in this) {
+ rv = this[i--];
+ break;
+ }
+
+ // if array contains no values, no initial value to return
+ if (--i < 0)
+ throw new TypeError();
+ } while (true);
+ }
+
+ for (; i >= 0; i--) {
+ if (i in this)
+ rv = fun.call(null, rv, this[i], i, this);
+ }
+
+ return rv;
+ };
+}
+
+// ES5 15.4.4.14
+if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function indexOf(value /*, fromIndex */ ) {
+ var length = this.length;
+ if (!length)
+ return -1;
+ var i = arguments[1] || 0;
+ if (i >= length)
+ return -1;
+ if (i < 0)
+ i += length;
+ for (; i < length; i++) {
+ if (!owns(this, i))
+ continue;
+ if (value === this[i])
+ return i;
+ }
+ return -1;
+ };
+}
+
+// ES5 15.4.4.15
+if (!Array.prototype.lastIndexOf) {
+ Array.prototype.lastIndexOf = function lastIndexOf(value /*, fromIndex */) {
+ var length = this.length;
+ if (!length)
+ return -1;
+ var i = arguments[1] || length;
+ if (i < 0)
+ i += length;
+ i = Math.min(i, length - 1);
+ for (; i >= 0; i--) {
+ if (!owns(this, i))
+ continue;
+ if (value === this[i])
+ return i;
+ }
+ return -1;
+ };
+}
+
+//
+// Object
+// ======
+//
+
+// ES5 15.2.3.2
+if (!Object.getPrototypeOf) {
+ // https://github.com/kriskowal/es5-shim/issues#issue/2
+ // http://ejohn.org/blog/objectgetprototypeof/
+ // recommended by fschaefer on github
+ Object.getPrototypeOf = function getPrototypeOf(object) {
+ return object.__proto__ || object.constructor.prototype;
+ // or undefined if not available in this engine
+ };
+}
+
+// ES5 15.2.3.3
+if (!Object.getOwnPropertyDescriptor) {
+ var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a " +
+ "non-object: ";
+ Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
+ if ((typeof object !== "object" && typeof object !== "function") || object === null)
+ throw new TypeError(ERR_NON_OBJECT + object);
+ // If object does not owns property return undefined immediately.
+ if (!owns(object, property))
+ return undefined;
+
+ var despriptor, getter, setter;
+
+ // If object has a property then it's for sure both `enumerable` and
+ // `configurable`.
+ despriptor = { enumerable: true, configurable: true };
+
+ // If JS engine supports accessor properties then property may be a
+ // getter or setter.
+ if (supportsAccessors) {
+ // Unfortunately `__lookupGetter__` will return a getter even
+ // if object has own non getter property along with a same named
+ // inherited getter. To avoid misbehavior we temporary remove
+ // `__proto__` so that `__lookupGetter__` will return getter only
+ // if it's owned by an object.
+ var prototype = object.__proto__;
+ object.__proto__ = prototypeOfObject;
+
+ var getter = lookupGetter(object, property);
+ var setter = lookupSetter(object, property);
+
+ // Once we have getter and setter we can put values back.
+ object.__proto__ = prototype;
+
+ if (getter || setter) {
+ if (getter) descriptor.get = getter;
+ if (setter) descriptor.set = setter;
+
+ // If it was accessor property we're done and return here
+ // in order to avoid adding `value` to the descriptor.
+ return descriptor;
+ }
+ }
+
+ // If we got this far we know that object has an own property that is
+ // not an accessor so we set it as a value and return descriptor.
+ descriptor.value = object[property];
+ return descriptor;
+ };
+}
+
+// ES5 15.2.3.4
+if (!Object.getOwnPropertyNames) {
+ Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
+ return Object.keys(object);
+ };
+}
+
+// ES5 15.2.3.5
+if (!Object.create) {
+ Object.create = function create(prototype, properties) {
+ var object;
+ if (prototype === null) {
+ object = { "__proto__": null };
+ } else {
+ if (typeof prototype !== "object")
+ throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'");
+ var Type = function () {};
+ Type.prototype = prototype;
+ object = new Type();
+ // IE has no built-in implementation of `Object.getPrototypeOf`
+ // neither `__proto__`, but this manually setting `__proto__` will
+ // guarantee that `Object.getPrototypeOf` will work as expected with
+ // objects created using `Object.create`
+ object.__proto__ = prototype;
+ }
+ if (typeof properties !== "undefined")
+ Object.defineProperties(object, properties);
+ return object;
+ };
+}
+
+// ES5 15.2.3.6
+if (!Object.defineProperty) {
+ var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: ";
+ var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "
+ var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " +
+ "on this javascript engine";
+
+ Object.defineProperty = function defineProperty(object, property, descriptor) {
+ if (typeof object !== "object" && typeof object !== "function")
+ throw new TypeError(ERR_NON_OBJECT_TARGET + object);
+ if (typeof object !== "object" || object === null)
+ throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor);
+
+ // If it's a data property.
+ if (owns(descriptor, "value")) {
+ // fail silently if "writable", "enumerable", or "configurable"
+ // are requested but not supported
+ /*
+ // alternate approach:
+ if ( // can't implement these features; allow false but not true
+ !(owns(descriptor, "writable") ? descriptor.writable : true) ||
+ !(owns(descriptor, "enumerable") ? descriptor.enumerable : true) ||
+ !(owns(descriptor, "configurable") ? descriptor.configurable : true)
+ )
+ throw new RangeError(
+ "This implementation of Object.defineProperty does not " +
+ "support configurable, enumerable, or writable."
+ );
+ */
+
+ if (supportsAccessors && (lookupGetter(object, property) ||
+ lookupSetter(object, property)))
+ {
+ // As accessors are supported only on engines implementing
+ // `__proto__` we can safely override `__proto__` while defining
+ // a property to make sure that we don't hit an inherited
+ // accessor.
+ var prototype = object.__proto__;
+ object.__proto__ = prototypeOfObject;
+ // Deleting a property anyway since getter / setter may be
+ // defined on object itself.
+ delete object[property];
+ object[property] = descriptor.value;
+ // Setting original `__proto__` back now.
+ object.prototype;
+ } else {
+ object[property] = descriptor.value;
+ }
+ } else {
+ if (!supportsAccessors)
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
+ // If we got that far then getters and setters can be defined !!
+ if (owns(descriptor, "get"))
+ defineGetter(object, property, descriptor.get);
+ if (owns(descriptor, "set"))
+ defineSetter(object, property, descriptor.set);
+ }
+
+ return object;
+ };
+}
+
+// ES5 15.2.3.7
+if (!Object.defineProperties) {
+ Object.defineProperties = function defineProperties(object, properties) {
+ for (var property in properties) {
+ if (owns(properties, property))
+ Object.defineProperty(object, property, properties[property]);
+ }
+ return object;
+ };
+}
+
+// ES5 15.2.3.8
+if (!Object.seal) {
+ Object.seal = function seal(object) {
+ // this is misleading and breaks feature-detection, but
+ // allows "securable" code to "gracefully" degrade to working
+ // but insecure code.
+ return object;
+ };
+}
+
+// ES5 15.2.3.9
+if (!Object.freeze) {
+ Object.freeze = function freeze(object) {
+ // this is misleading and breaks feature-detection, but
+ // allows "securable" code to "gracefully" degrade to working
+ // but insecure code.
+ return object;
+ };
+}
+
+// detect a Rhino bug and patch it
+try {
+ Object.freeze(function () {});
+} catch (exception) {
+ Object.freeze = (function freeze(freezeObject) {
+ return function freeze(object) {
+ if (typeof object === "function") {
+ return object;
+ } else {
+ return freezeObject(object);
+ }
+ };
+ })(Object.freeze);
+}
+
+// ES5 15.2.3.10
+if (!Object.preventExtensions) {
+ Object.preventExtensions = function preventExtensions(object) {
+ // this is misleading and breaks feature-detection, but
+ // allows "securable" code to "gracefully" degrade to working
+ // but insecure code.
+ return object;
+ };
+}
+
+// ES5 15.2.3.11
+if (!Object.isSealed) {
+ Object.isSealed = function isSealed(object) {
+ return false;
+ };
+}
+
+// ES5 15.2.3.12
+if (!Object.isFrozen) {
+ Object.isFrozen = function isFrozen(object) {
+ return false;
+ };
+}
+
+// ES5 15.2.3.13
+if (!Object.isExtensible) {
+ Object.isExtensible = function isExtensible(object) {
+ return true;
+ };
+}
+
+// ES5 15.2.3.14
+// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
+if (!Object.keys) {
+
+ var hasDontEnumBug = true,
+ dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+ ],
+ dontEnumsLength = dontEnums.length;
+
+ for (var key in {"toString": null})
+ hasDontEnumBug = false;
+
+ Object.keys = function keys(object) {
+
+ if (
+ typeof object !== "object" && typeof object !== "function"
+ || object === null
+ )
+ throw new TypeError("Object.keys called on a non-object");
+
+ var keys = [];
+ for (var name in object) {
+ if (owns(object, name)) {
+ keys.push(name);
+ }
+ }
+
+ if (hasDontEnumBug) {
+ for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
+ var dontEnum = dontEnums[i];
+ if (owns(object, dontEnum)) {
+ keys.push(dontEnum);
+ }
+ }
+ }
+
+ return keys;
+ };
+
+}
+
+//
+// Date
+// ====
+//
+
+// ES5 15.9.5.43
+// Format a Date object as a string according to a subset of the ISO-8601 standard.
+// Useful in Atom, among other things.
+if (!Date.prototype.toISOString) {
+ Date.prototype.toISOString = function toISOString() {
+ return (
+ this.getUTCFullYear() + "-" +
+ (this.getUTCMonth() + 1) + "-" +
+ this.getUTCDate() + "T" +
+ this.getUTCHours() + ":" +
+ this.getUTCMinutes() + ":" +
+ this.getUTCSeconds() + "Z"
+ );
+ }
+}
+
+// ES5 15.9.4.4
+if (!Date.now) {
+ Date.now = function now() {
+ return new Date().getTime();
+ };
+}
+
+// ES5 15.9.5.44
+if (!Date.prototype.toJSON) {
+ Date.prototype.toJSON = function toJSON(key) {
+ // This function provides a String representation of a Date object for
+ // use by JSON.stringify (15.12.3). When the toJSON method is called
+ // with argument key, the following steps are taken:
+
+ // 1. Let O be the result of calling ToObject, giving it the this
+ // value as its argument.
+ // 2. Let tv be ToPrimitive(O, hint Number).
+ // 3. If tv is a Number and is not finite, return null.
+ // XXX
+ // 4. Let toISO be the result of calling the [[Get]] internal method of
+ // O with argument "toISOString".
+ // 5. If IsCallable(toISO) is false, throw a TypeError exception.
+ if (typeof this.toISOString !== "function")
+ throw new TypeError();
+ // 6. Return the result of calling the [[Call]] internal method of
+ // toISO with O as the this value and an empty argument list.
+ return this.toISOString();
+
+ // NOTE 1 The argument is ignored.
+
+ // NOTE 2 The toJSON function is intentionally generic; it does not
+ // require that its this value be a Date object. Therefore, it can be
+ // transferred to other kinds of objects for use as a method. However,
+ // it does require that any such object have a toISOString method. An
+ // object is free to use the argument key to filter its
+ // stringification.
+ };
+}
+
+// 15.9.4.2 Date.parse (string)
+// 15.9.1.15 Date Time String Format
+// Date.parse
+// based on work shared by Daniel Friesen (dantman)
+// http://gist.github.com/303249
+if (isNaN(Date.parse("T00:00"))) {
+ // XXX global assignment won't work in embeddings that use
+ // an alternate object for the context.
+ Date = (function(NativeDate) {
+
+ // Date.length === 7
+ var Date = function(Y, M, D, h, m, s, ms) {
+ var length = arguments.length;
+ if (this instanceof NativeDate) {
+ var date = length === 1 && String(Y) === Y ? // isString(Y)
+ // We explicitly pass it through parse:
+ new NativeDate(Date.parse(Y)) :
+ // We have to manually make calls depending on argument
+ // length here
+ length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
+ length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
+ length >= 5 ? new NativeDate(Y, M, D, h, m) :
+ length >= 4 ? new NativeDate(Y, M, D, h) :
+ length >= 3 ? new NativeDate(Y, M, D) :
+ length >= 2 ? new NativeDate(Y, M) :
+ length >= 1 ? new NativeDate(Y) :
+ new NativeDate();
+ // Prevent mixups with unfixed Date object
+ date.constructor = Date;
+ return date;
+ }
+ return NativeDate.apply(this, arguments);
+ };
+
+ // 15.9.1.15 Date Time String Format
+ var isoDateExpression = new RegExp("^" +
+ "(?:" + // optional year-month-day
+ "(" + // year capture
+ "(?:[+-]\\d\\d)?" + // 15.9.1.15.1 Extended years
+ "\\d\\d\\d\\d" + // four-digit year
+ ")" +
+ "(?:-" + // optional month-day
+ "(\\d\\d)" + // month capture
+ "(?:-" + // optional day
+ "(\\d\\d)" + // day capture
+ ")?" +
+ ")?" +
+ ")?" +
+ "(?:T" + // hour:minute:second.subsecond
+ "(\\d\\d)" + // hour capture
+ ":(\\d\\d)" + // minute capture
+ "(?::" + // optional :second.subsecond
+ "(\\d\\d)" + // second capture
+ "(?:\\.(\\d\\d\\d))?" + // milisecond capture
+ ")?" +
+ ")?" +
+ "(?:" + // time zone
+ "Z|" + // UTC capture
+ "([+-])(\\d\\d):(\\d\\d)" + // timezone offset
+ // capture sign, hour, minute
+ ")?" +
+ "$");
+
+ // Copy any custom methods a 3rd party library may have added
+ for (var key in NativeDate)
+ Date[key] = NativeDate[key];
+
+ // Copy "native" methods explicitly; they may be non-enumerable
+ Date.now = NativeDate.now;
+ Date.UTC = NativeDate.UTC;
+ Date.prototype = NativeDate.prototype;
+ Date.prototype.constructor = Date;
+
+ // Upgrade Date.parse to handle the ISO dates we use
+ // TODO review specification to ascertain whether it is
+ // necessary to implement partial ISO date strings.
+ Date.parse = function parse(string) {
+ var match = isoDateExpression.exec(string);
+ if (match) {
+ match.shift(); // kill match[0], the full match
+ // recognize times without dates before normalizing the
+ // numeric values, for later use
+ var timeOnly = match[0] === undefined;
+ // parse numerics
+ for (var i = 0; i < 10; i++) {
+ // skip + or - for the timezone offset
+ if (i === 7)
+ continue;
+ // Note: parseInt would read 0-prefix numbers as
+ // octal. Number constructor or unary + work better
+ // here:
+ match[i] = +(match[i] || (i < 3 ? 1 : 0));
+ // match[1] is the month. Months are 0-11 in JavaScript
+ // Date objects, but 1-12 in ISO notation, so we
+ // decrement.
+ if (i === 1)
+ match[i]--;
+ }
+ // if no year-month-date is provided, return a milisecond
+ // quantity instead of a UTC date number value.
+ if (timeOnly)
+ return ((match[3] * 60 + match[4]) * 60 + match[5]) * 1000 + match[6];
+
+ // account for an explicit time zone offset if provided
+ var offset = (match[8] * 60 + match[9]) * 60 * 1000;
+ if (match[6] === "-")
+ offset = -offset;
+
+ return NativeDate.UTC.apply(this, match.slice(0, 7)) + offset;
+ }
+ return NativeDate.parse.apply(this, arguments);
+ };
+
+ return Date;
+ })(Date);
+}
+
+//
+// String
+// ======
+//
+
+// ES5 15.5.4.20
+if (!String.prototype.trim) {
+ // http://blog.stevenlevithan.com/archives/faster-trim-javascript
+ var trimBeginRegexp = /^\s\s*/;
+ var trimEndRegexp = /\s\s*$/;
+ String.prototype.trim = function trim() {
+ return String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, '');
+ };
+}
+
+});/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/ace', ['require', 'exports', 'module' , 'pilot/index', 'pilot/fixoldbrowsers', 'pilot/plugin_manager', 'pilot/dom', 'pilot/event', 'ace/editor', 'ace/edit_session', 'ace/undomanager', 'ace/virtual_renderer', 'ace/theme/textmate', 'pilot/environment'], function(require, exports, module) {
+
+ require("pilot/index");
+ require("pilot/fixoldbrowsers");
+ var catalog = require("pilot/plugin_manager").catalog;
+ catalog.registerPlugins([ "pilot/index" ]);
+
+ var Dom = require("pilot/dom");
+ var Event = require("pilot/event");
+
+ var Editor = require("ace/editor").Editor;
+ var EditSession = require("ace/edit_session").EditSession;
+ var UndoManager = require("ace/undomanager").UndoManager;
+ var Renderer = require("ace/virtual_renderer").VirtualRenderer;
+
+ exports.edit = function(el) {
+ if (typeof(el) == "string") {
+ el = document.getElementById(el);
+ }
+
+ var doc = new EditSession(Dom.getInnerText(el));
+ doc.setUndoManager(new UndoManager());
+ el.innerHTML = '';
+
+ var editor = new Editor(new Renderer(el, require("ace/theme/textmate")));
+ editor.setSession(doc);
+
+ var env = require("pilot/environment").create();
+ catalog.startupPlugins({ env: env }).then(function() {
+ env.document = doc;
+ env.editor = editor;
+ editor.resize();
+ Event.addListener(window, "resize", function() {
+ editor.resize();
+ });
+ el.env = env;
+ });
+ // Store env on editor such that it can be accessed later on from
+ // the returned object.
+ editor.env = env;
+ return editor;
+ };
+});/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/index', ['require', 'exports', 'module' , 'pilot/fixoldbrowsers', 'pilot/types/basic', 'pilot/types/command', 'pilot/types/settings', 'pilot/commands/settings', 'pilot/commands/basic', 'pilot/settings/canon', 'pilot/canon'], function(require, exports, module) {
+
+exports.startup = function(data, reason) {
+ require('pilot/fixoldbrowsers');
+
+ require('pilot/types/basic').startup(data, reason);
+ require('pilot/types/command').startup(data, reason);
+ require('pilot/types/settings').startup(data, reason);
+ require('pilot/commands/settings').startup(data, reason);
+ require('pilot/commands/basic').startup(data, reason);
+ // require('pilot/commands/history').startup(data, reason);
+ require('pilot/settings/canon').startup(data, reason);
+ require('pilot/canon').startup(data, reason);
+};
+
+exports.shutdown = function(data, reason) {
+ require('pilot/types/basic').shutdown(data, reason);
+ require('pilot/types/command').shutdown(data, reason);
+ require('pilot/types/settings').shutdown(data, reason);
+ require('pilot/commands/settings').shutdown(data, reason);
+ require('pilot/commands/basic').shutdown(data, reason);
+ // require('pilot/commands/history').shutdown(data, reason);
+ require('pilot/settings/canon').shutdown(data, reason);
+ require('pilot/canon').shutdown(data, reason);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/types/basic', ['require', 'exports', 'module' , 'pilot/types'], function(require, exports, module) {
+
+var types = require("pilot/types");
+var Type = types.Type;
+var Conversion = types.Conversion;
+var Status = types.Status;
+
+/**
+ * These are the basic types that we accept. They are vaguely based on the
+ * Jetpack settings system (https://wiki.mozilla.org/Labs/Jetpack/JEP/24)
+ * although clearly more restricted.
+ *
+ * <p>In addition to these types, Jetpack also accepts range, member, password
+ * that we are thinking of adding.
+ *
+ * <p>This module probably should not be accessed directly, but instead used
+ * through types.js
+ */
+
+/**
+ * 'text' is the default if no type is given.
+ */
+var text = new Type();
+
+text.stringify = function(value) {
+ return value;
+};
+
+text.parse = function(value) {
+ if (typeof value != 'string') {
+ throw new Error('non-string passed to text.parse()');
+ }
+ return new Conversion(value);
+};
+
+text.name = 'text';
+
+/**
+ * We don't currently plan to distinguish between integers and floats
+ */
+var number = new Type();
+
+number.stringify = function(value) {
+ if (!value) {
+ return null;
+ }
+ return '' + value;
+};
+
+number.parse = function(value) {
+ if (typeof value != 'string') {
+ throw new Error('non-string passed to number.parse()');
+ }
+
+ if (value.replace(/\s/g, '').length === 0) {
+ return new Conversion(null, Status.INCOMPLETE, '');
+ }
+
+ var reply = new Conversion(parseInt(value, 10));
+ if (isNaN(reply.value)) {
+ reply.status = Status.INVALID;
+ reply.message = 'Can\'t convert "' + value + '" to a number.';
+ }
+
+ return reply;
+};
+
+number.decrement = function(value) {
+ return value - 1;
+};
+
+number.increment = function(value) {
+ return value + 1;
+};
+
+number.name = 'number';
+
+/**
+ * One of a known set of options
+ */
+function SelectionType(typeSpec) {
+ if (!Array.isArray(typeSpec.data) && typeof typeSpec.data !== 'function') {
+ throw new Error('instances of SelectionType need typeSpec.data to be an array or function that returns an array:' + JSON.stringify(typeSpec));
+ }
+ Object.keys(typeSpec).forEach(function(key) {
+ this[key] = typeSpec[key];
+ }, this);
+};
+
+SelectionType.prototype = new Type();
+
+SelectionType.prototype.stringify = function(value) {
+ return value;
+};
+
+SelectionType.prototype.parse = function(str) {
+ if (typeof str != 'string') {
+ throw new Error('non-string passed to parse()');
+ }
+ if (!this.data) {
+ throw new Error('Missing data on selection type extension.');
+ }
+ var data = (typeof(this.data) === 'function') ? this.data() : this.data;
+
+ // The matchedValue could be the boolean value false
+ var hasMatched = false;
+ var matchedValue;
+ var completions = [];
+ data.forEach(function(option) {
+ if (str == option) {
+ matchedValue = this.fromString(option);
+ hasMatched = true;
+ }
+ else if (option.indexOf(str) === 0) {
+ completions.push(this.fromString(option));
+ }
+ }, this);
+
+ if (hasMatched) {
+ return new Conversion(matchedValue);
+ }
+ else {
+ // This is something of a hack it basically allows us to tell the
+ // setting type to forget its last setting hack.
+ if (this.noMatch) {
+ this.noMatch();
+ }
+
+ if (completions.length > 0) {
+ var msg = 'Possibilities' +
+ (str.length === 0 ? '' : ' for \'' + str + '\'');
+ return new Conversion(null, Status.INCOMPLETE, msg, completions);
+ }
+ else {
+ var msg = 'Can\'t use \'' + str + '\'.';
+ return new Conversion(null, Status.INVALID, msg, completions);
+ }
+ }
+};
+
+SelectionType.prototype.fromString = function(str) {
+ return str;
+};
+
+SelectionType.prototype.decrement = function(value) {
+ var data = (typeof this.data === 'function') ? this.data() : this.data;
+ var index;
+ if (value == null) {
+ index = data.length - 1;
+ }
+ else {
+ var name = this.stringify(value);
+ var index = data.indexOf(name);
+ index = (index === 0 ? data.length - 1 : index - 1);
+ }
+ return this.fromString(data[index]);
+};
+
+SelectionType.prototype.increment = function(value) {
+ var data = (typeof this.data === 'function') ? this.data() : this.data;
+ var index;
+ if (value == null) {
+ index = 0;
+ }
+ else {
+ var name = this.stringify(value);
+ var index = data.indexOf(name);
+ index = (index === data.length - 1 ? 0 : index + 1);
+ }
+ return this.fromString(data[index]);
+};
+
+SelectionType.prototype.name = 'selection';
+
+/**
+ * SelectionType is a base class for other types
+ */
+exports.SelectionType = SelectionType;
+
+/**
+ * true/false values
+ */
+var bool = new SelectionType({
+ name: 'bool',
+ data: [ 'true', 'false' ],
+ stringify: function(value) {
+ return '' + value;
+ },
+ fromString: function(str) {
+ return str === 'true' ? true : false;
+ }
+});
+
+
+/**
+ * A we don't know right now, but hope to soon.
+ */
+function DeferredType(typeSpec) {
+ if (typeof typeSpec.defer !== 'function') {
+ throw new Error('Instances of DeferredType need typeSpec.defer to be a function that returns a type');
+ }
+ Object.keys(typeSpec).forEach(function(key) {
+ this[key] = typeSpec[key];
+ }, this);
+};
+
+DeferredType.prototype = new Type();
+
+DeferredType.prototype.stringify = function(value) {
+ return this.defer().stringify(value);
+};
+
+DeferredType.prototype.parse = function(value) {
+ return this.defer().parse(value);
+};
+
+DeferredType.prototype.decrement = function(value) {
+ var deferred = this.defer();
+ return (deferred.decrement ? deferred.decrement(value) : undefined);
+};
+
+DeferredType.prototype.increment = function(value) {
+ var deferred = this.defer();
+ return (deferred.increment ? deferred.increment(value) : undefined);
+};
+
+DeferredType.prototype.name = 'deferred';
+
+/**
+ * DeferredType is a base class for other types
+ */
+exports.DeferredType = DeferredType;
+
+
+/**
+ * A set of objects of the same type
+ */
+function ArrayType(typeSpec) {
+ if (typeSpec instanceof Type) {
+ this.subtype = typeSpec;
+ }
+ else if (typeof typeSpec === 'string') {
+ this.subtype = types.getType(typeSpec);
+ if (this.subtype == null) {
+ throw new Error('Unknown array subtype: ' + typeSpec);
+ }
+ }
+ else {
+ throw new Error('Can\' handle array subtype');
+ }
+};
+
+ArrayType.prototype = new Type();
+
+ArrayType.prototype.stringify = function(values) {
+ // TODO: Check for strings with spaces and add quotes
+ return values.join(' ');
+};
+
+ArrayType.prototype.parse = function(value) {
+ return this.defer().parse(value);
+};
+
+ArrayType.prototype.name = 'array';
+
+/**
+ * Registration and de-registration.
+ */
+var isStarted = false;
+exports.startup = function() {
+ if (isStarted) {
+ return;
+ }
+ isStarted = true;
+ types.registerType(text);
+ types.registerType(number);
+ types.registerType(bool);
+ types.registerType(SelectionType);
+ types.registerType(DeferredType);
+ types.registerType(ArrayType);
+};
+
+exports.shutdown = function() {
+ isStarted = false;
+ types.unregisterType(text);
+ types.unregisterType(number);
+ types.unregisterType(bool);
+ types.unregisterType(SelectionType);
+ types.unregisterType(DeferredType);
+ types.unregisterType(ArrayType);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/types', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+/**
+ * Some types can detect validity, that is to say they can distinguish between
+ * valid and invalid values.
+ * TODO: Change these constants to be numbers for more performance?
+ */
+var Status = {
+ /**
+ * The conversion process worked without any problem, and the value is
+ * valid. There are a number of failure states, so the best way to check
+ * for failure is (x !== Status.VALID)
+ */
+ VALID: {
+ toString: function() { return 'VALID'; },
+ valueOf: function() { return 0; }
+ },
+
+ /**
+ * A conversion process failed, however it was noted that the string
+ * provided to 'parse()' could be VALID by the addition of more characters,
+ * so the typing may not be actually incorrect yet, just unfinished.
+ * @see Status.INVALID
+ */
+ INCOMPLETE: {
+ toString: function() { return 'INCOMPLETE'; },
+ valueOf: function() { return 1; }
+ },
+
+ /**
+ * The conversion process did not work, the value should be null and a
+ * reason for failure should have been provided. In addition some completion
+ * values may be available.
+ * @see Status.INCOMPLETE
+ */
+ INVALID: {
+ toString: function() { return 'INVALID'; },
+ valueOf: function() { return 2; }
+ },
+
+ /**
+ * A combined status is the worser of the provided statuses
+ */
+ combine: function(statuses) {
+ var combined = Status.VALID;
+ for (var i = 0; i < statuses.length; i++) {
+ if (statuses[i].valueOf() > combined.valueOf()) {
+ combined = statuses[i];
+ }
+ }
+ return combined;
+ }
+};
+exports.Status = Status;
+
+/**
+ * The type.parse() method returns a Conversion to inform the user about not
+ * only the result of a Conversion but also about what went wrong.
+ * We could use an exception, and throw if the conversion failed, but that
+ * seems to violate the idea that exceptions should be exceptional. Typos are
+ * not. Also in order to store both a status and a message we'd still need
+ * some sort of exception type...
+ */
+function Conversion(value, status, message, predictions) {
+ /**
+ * The result of the conversion process. Will be null if status != VALID
+ */
+ this.value = value;
+
+ /**
+ * The status of the conversion.
+ * @see Status
+ */
+ this.status = status || Status.VALID;
+
+ /**
+ * A message to go with the conversion. This could be present for any status
+ * including VALID in the case where we want to note a warning for example.
+ * I18N: On the one hand this nasty and un-internationalized, however with
+ * a command line it is hard to know where to start.
+ */
+ this.message = message;
+
+ /**
+ * A array of strings which are the systems best guess at better inputs than
+ * the one presented.
+ * We generally expect there to be about 7 predictions (to match human list
+ * comprehension ability) however it is valid to provide up to about 20,
+ * or less. It is the job of the predictor to decide a smart cut-off.
+ * For example if there are 4 very good matches and 4 very poor ones,
+ * probably only the 4 very good matches should be presented.
+ */
+ this.predictions = predictions || [];
+}
+exports.Conversion = Conversion;
+
+/**
+ * Most of our types are 'static' e.g. there is only one type of 'text', however
+ * some types like 'selection' and 'deferred' are customizable. The basic
+ * Type type isn't useful, but does provide documentation about what types do.
+ */
+function Type() {
+};
+Type.prototype = {
+ /**
+ * Convert the given <tt>value</tt> to a string representation.
+ * Where possible, there should be round-tripping between values and their
+ * string representations.
+ */
+ stringify: function(value) { throw new Error("not implemented"); },
+
+ /**
+ * Convert the given <tt>str</tt> to an instance of this type.
+ * Where possible, there should be round-tripping between values and their
+ * string representations.
+ * @return Conversion
+ */
+ parse: function(str) { throw new Error("not implemented"); },
+
+ /**
+ * The plug-in system, and other things need to know what this type is
+ * called. The name alone is not enough to fully specify a type. Types like
+ * 'selection' and 'deferred' need extra data, however this function returns
+ * only the name, not the extra data.
+ * <p>In old bespin, equality was based on the name. This may turn out to be
+ * important in Ace too.
+ */
+ name: undefined,
+
+ /**
+ * If there is some concept of a higher value, return it,
+ * otherwise return undefined.
+ */
+ increment: function(value) {
+ return undefined;
+ },
+
+ /**
+ * If there is some concept of a lower value, return it,
+ * otherwise return undefined.
+ */
+ decrement: function(value) {
+ return undefined;
+ },
+
+ /**
+ * There is interesting information (like predictions) in a conversion of
+ * nothing, the output of this can sometimes be customized.
+ * @return Conversion
+ */
+ getDefault: function() {
+ return this.parse('');
+ }
+};
+exports.Type = Type;
+
+/**
+ * Private registry of types
+ * Invariant: types[name] = type.name
+ */
+var types = {};
+
+/**
+ * Add a new type to the list available to the system.
+ * You can pass 2 things to this function - either an instance of Type, in
+ * which case we return this instance when #getType() is called with a 'name'
+ * that matches type.name.
+ * Also you can pass in a constructor (i.e. function) in which case when
+ * #getType() is called with a 'name' that matches Type.prototype.name we will
+ * pass the typeSpec into this constructor. See #reconstituteType().
+ */
+exports.registerType = function(type) {
+ if (typeof type === 'object') {
+ if (type instanceof Type) {
+ if (!type.name) {
+ throw new Error('All registered types must have a name');
+ }
+ types[type.name] = type;
+ }
+ else {
+ throw new Error('Can\'t registerType using: ' + type);
+ }
+ }
+ else if (typeof type === 'function') {
+ if (!type.prototype.name) {
+ throw new Error('All registered types must have a name');
+ }
+ types[type.prototype.name] = type;
+ }
+ else {
+ throw new Error('Unknown type: ' + type);
+ }
+};
+
+exports.registerTypes = function registerTypes(types) {
+ Object.keys(types).forEach(function (name) {
+ var type = types[name];
+ type.name = name;
+ exports.registerType(type);
+ });
+};
+
+/**
+ * Remove a type from the list available to the system
+ */
+exports.deregisterType = function(type) {
+ delete types[type.name];
+};
+
+/**
+ * See description of #exports.registerType()
+ */
+function reconstituteType(name, typeSpec) {
+ if (name.substr(-2) === '[]') { // i.e. endsWith('[]')
+ var subtypeName = name.slice(0, -2);
+ return new types['array'](subtypeName);
+ }
+
+ var type = types[name];
+ if (typeof type === 'function') {
+ type = new type(typeSpec);
+ }
+ return type;
+}
+
+/**
+ * Find a type, previously registered using #registerType()
+ */
+exports.getType = function(typeSpec) {
+ if (typeof typeSpec === 'string') {
+ return reconstituteType(typeSpec);
+ }
+
+ if (typeof typeSpec === 'object') {
+ if (!typeSpec.name) {
+ throw new Error('Missing \'name\' member to typeSpec');
+ }
+ return reconstituteType(typeSpec.name, typeSpec);
+ }
+
+ throw new Error('Can\'t extract type from ' + typeSpec);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/types/command', ['require', 'exports', 'module' , 'pilot/canon', 'pilot/types/basic', 'pilot/types'], function(require, exports, module) {
+
+var canon = require("pilot/canon");
+var SelectionType = require("pilot/types/basic").SelectionType;
+var types = require("pilot/types");
+
+
+/**
+ * Select from the available commands
+ */
+var command = new SelectionType({
+ name: 'command',
+ data: function() {
+ return canon.getCommandNames();
+ },
+ stringify: function(command) {
+ return command.name;
+ },
+ fromString: function(str) {
+ return canon.getCommand(str);
+ }
+});
+
+
+/**
+ * Registration and de-registration.
+ */
+exports.startup = function() {
+ types.registerType(command);
+};
+
+exports.shutdown = function() {
+ types.unregisterType(command);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/canon', ['require', 'exports', 'module' , 'pilot/console', 'pilot/stacktrace', 'pilot/oop', 'pilot/useragent', 'pilot/keys', 'pilot/event_emitter', 'pilot/typecheck', 'pilot/catalog', 'pilot/types', 'pilot/lang'], function(require, exports, module) {
+
+var console = require('pilot/console');
+var Trace = require('pilot/stacktrace').Trace;
+var oop = require('pilot/oop');
+var useragent = require('pilot/useragent');
+var keyUtil = require('pilot/keys');
+var EventEmitter = require('pilot/event_emitter').EventEmitter;
+var typecheck = require('pilot/typecheck');
+var catalog = require('pilot/catalog');
+var Status = require('pilot/types').Status;
+var types = require('pilot/types');
+var lang = require('pilot/lang');
+
+/*
+// TODO: this doesn't belong here - or maybe anywhere?
+var dimensionsChangedExtensionSpec = {
+ name: 'dimensionsChanged',
+ description: 'A dimensionsChanged is a way to be notified of ' +
+ 'changes to the dimension of Skywriter'
+};
+exports.startup = function(data, reason) {
+ catalog.addExtensionSpec(commandExtensionSpec);
+};
+exports.shutdown = function(data, reason) {
+ catalog.removeExtensionSpec(commandExtensionSpec);
+};
+*/
+
+var commandExtensionSpec = {
+ name: 'command',
+ description: 'A command is a bit of functionality with optional ' +
+ 'typed arguments which can do something small like moving ' +
+ 'the cursor around the screen, or large like cloning a ' +
+ 'project from VCS.',
+ indexOn: 'name'
+};
+
+exports.startup = function(data, reason) {
+ // TODO: this is probably all kinds of evil, but we need something working
+ catalog.addExtensionSpec(commandExtensionSpec);
+};
+
+exports.shutdown = function(data, reason) {
+ catalog.removeExtensionSpec(commandExtensionSpec);
+};
+
+/**
+ * Manage a list of commands in the current canon
+ */
+
+/**
+ * A Command is a discrete action optionally with a set of ways to customize
+ * how it happens. This is here for documentation purposes.
+ * TODO: Document better
+ */
+var thingCommand = {
+ name: 'thing',
+ description: 'thing is an example command',
+ params: [{
+ name: 'param1',
+ description: 'an example parameter',
+ type: 'text',
+ defaultValue: null
+ }],
+ exec: function(env, args, request) {
+ thing();
+ }
+};
+
+/**
+ * A lookup hash of our registered commands
+ */
+var commands = {};
+
+/**
+ * A lookup has for command key bindings that use a string as sender.
+ */
+var commmandKeyBinding = {};
+
+/**
+ * Array with command key bindings that use a function to determ the sender.
+ */
+var commandKeyBindingFunc = { };
+
+function splitSafe(s, separator, limit, bLowerCase) {
+ return (bLowerCase && s.toLowerCase() || s)
+ .replace(/(?:^\s+|\n|\s+$)/g, "")
+ .split(new RegExp("[\\s ]*" + separator + "[\\s ]*", "g"), limit || 999);
+}
+
+function parseKeys(keys, val, ret) {
+ var key,
+ hashId = 0,
+ parts = splitSafe(keys, "\\-", null, true),
+ i = 0,
+ l = parts.length;
+
+ for (; i < l; ++i) {
+ if (keyUtil.KEY_MODS[parts[i]])
+ hashId = hashId | keyUtil.KEY_MODS[parts[i]];
+ else
+ key = parts[i] || "-"; //when empty, the splitSafe removed a '-'
+ }
+
+ if (ret == null) {
+ return {
+ key: key,
+ hashId: hashId
+ }
+ } else {
+ (ret[hashId] || (ret[hashId] = {}))[key] = val;
+ }
+}
+
+var platform = useragent.isMac ? "mac" : "win";
+function buildKeyHash(command) {
+ var binding = command.bindKey,
+ key = binding[platform],
+ ckb = commmandKeyBinding,
+ ckbf = commandKeyBindingFunc
+
+ if (!binding.sender) {
+ throw new Error('All key bindings must have a sender');
+ }
+ if (!binding.mac && binding.mac !== null) {
+ throw new Error('All key bindings must have a mac key binding');
+ }
+ if (!binding.win && binding.win !== null) {
+ throw new Error('All key bindings must have a windows key binding');
+ }
+ if(!binding[platform]) {
+ // No keymapping for this platform.
+ return;
+ }
+ if (typeof binding.sender == 'string') {
+ var targets = splitSafe(binding.sender, "\\|", null, true);
+ targets.forEach(function(target) {
+ if (!ckb[target]) {
+ ckb[target] = { };
+ }
+ key.split("|").forEach(function(keyPart) {
+ parseKeys(keyPart, command, ckb[target]);
+ });
+ });
+ } else if (typecheck.isFunction(binding.sender)) {
+ var val = {
+ command: command,
+ sender: binding.sender
+ };
+
+ keyData = parseKeys(key);
+ if (!ckbf[keyData.hashId]) {
+ ckbf[keyData.hashId] = { };
+ }
+ if (!ckbf[keyData.hashId][keyData.key]) {
+ ckbf[keyData.hashId][keyData.key] = [ val ];
+ } else {
+ ckbf[keyData.hashId][keyData.key].push(val);
+ }
+ } else {
+ throw new Error('Key binding must have a sender that is a string or function');
+ }
+}
+
+function findKeyCommand(env, sender, hashId, textOrKey) {
+ // Convert keyCode to the string representation.
+ if (typecheck.isNumber(textOrKey)) {
+ textOrKey = keyUtil.keyCodeToString(textOrKey);
+ }
+
+ // Check bindings with functions as sender first.
+ var bindFuncs = (commandKeyBindingFunc[hashId] || {})[textOrKey] || [];
+ for (var i = 0; i < bindFuncs.length; i++) {
+ if (bindFuncs[i].sender(env, sender, hashId, textOrKey)) {
+ return bindFuncs[i].command;
+ }
+ }
+
+ var ckbr = commmandKeyBinding[sender]
+ return ckbr && ckbr[hashId] && ckbr[hashId][textOrKey];
+}
+
+function execKeyCommand(env, sender, hashId, textOrKey) {
+ var command = findKeyCommand(env, sender, hashId, textOrKey);
+ if (command) {
+ return exec(command, env, sender, { });
+ } else {
+ return false;
+ }
+}
+
+/**
+ * A sorted list of command names, we regularly want them in order, so pre-sort
+ */
+var commandNames = [];
+
+/**
+ * This registration method isn't like other Ace registration methods because
+ * it doesn't return a decorated command because there is no functional
+ * decoration to be done.
+ * TODO: Are we sure that in the future there will be no such decoration?
+ */
+function addCommand(command) {
+ if (!command.name) {
+ throw new Error('All registered commands must have a name');
+ }
+ if (command.params == null) {
+ command.params = [];
+ }
+ if (!Array.isArray(command.params)) {
+ throw new Error('command.params must be an array in ' + command.name);
+ }
+ // Replace the type
+ command.params.forEach(function(param) {
+ if (!param.name) {
+ throw new Error('In ' + command.name + ': all params must have a name');
+ }
+ upgradeType(command.name, param);
+ }, this);
+ commands[command.name] = command;
+
+ if (command.bindKey) {
+ buildKeyHash(command);
+ }
+
+ commandNames.push(command.name);
+ commandNames.sort();
+};
+
+function upgradeType(name, param) {
+ var lookup = param.type;
+ param.type = types.getType(lookup);
+ if (param.type == null) {
+ throw new Error('In ' + name + '/' + param.name +
+ ': can\'t find type for: ' + JSON.stringify(lookup));
+ }
+}
+
+function removeCommand(command) {
+ var name = (typeof command === 'string' ? command : command.name);
+ delete commands[name];
+ lang.arrayRemove(commandNames, name);
+};
+
+function getCommand(name) {
+ return commands[name];
+};
+
+function getCommandNames() {
+ return commandNames;
+};
+
+/**
+ * Default ArgumentProvider that is used if no ArgumentProvider is provided
+ * by the command's sender.
+ */
+function defaultArgsProvider(request, callback) {
+ var args = request.args,
+ params = request.command.params;
+
+ for (var i = 0; i < params.length; i++) {
+ var param = params[i];
+
+ // If the parameter is already valid, then don't ask for it anymore.
+ if (request.getParamStatus(param) != Status.VALID ||
+ // Ask for optional parameters as well.
+ param.defaultValue === null)
+ {
+ var paramPrompt = param.description;
+ if (param.defaultValue === null) {
+ paramPrompt += " (optional)";
+ }
+ var value = prompt(paramPrompt, param.defaultValue || "");
+ // No value but required -> nope.
+ if (!value) {
+ callback();
+ return;
+ } else {
+ args[param.name] = value;
+ }
+ }
+ }
+ callback();
+}
+
+/**
+ * Entry point for keyboard accelerators or anything else that wants to execute
+ * a command. A new request object is created and a check performed, if the
+ * passed in arguments are VALID/INVALID or INCOMPLETE. If they are INCOMPLETE
+ * the ArgumentProvider on the sender is called or otherwise the default
+ * ArgumentProvider to get the still required arguments.
+ * If they are valid (or valid after the ArgumentProvider is done), the command
+ * is executed.
+ *
+ * @param command Either a command, or the name of one
+ * @param env Current environment to execute the command in
+ * @param sender String that should be the same as the senderObject stored on
+ * the environment in env[sender]
+ * @param args Arguments for the command
+ * @param typed (Optional)
+ */
+function exec(command, env, sender, args, typed) {
+ if (typeof command === 'string') {
+ command = commands[command];
+ }
+ if (!command) {
+ // TODO: Should we complain more than returning false?
+ return false;
+ }
+
+ var request = new Request({
+ sender: sender,
+ command: command,
+ args: args || {},
+ typed: typed
+ });
+
+ /**
+ * Executes the command and ensures request.done is called on the request in
+ * case it's not marked to be done already or async.
+ */
+ function execute() {
+ command.exec(env, request.args, request);
+
+ // If the request isn't asnync and isn't done, then make it done.
+ if (!request.isAsync && !request.isDone) {
+ request.done();
+ }
+ }
+
+
+ if (request.getStatus() == Status.INVALID) {
+ console.error("Canon.exec: Invalid parameter(s) passed to " +
+ command.name);
+ return false;
+ }
+ // If the request isn't complete yet, try to complete it.
+ else if (request.getStatus() == Status.INCOMPLETE) {
+ // Check if the sender has a ArgsProvider, otherwise use the default
+ // build in one.
+ var argsProvider;
+ var senderObj = env[sender];
+ if (!senderObj || !senderObj.getArgsProvider ||
+ !(argsProvider = senderObj.getArgsProvider()))
+ {
+ argsProvider = defaultArgsProvider;
+ }
+
+ // Ask the paramProvider to complete the request.
+ argsProvider(request, function() {
+ if (request.getStatus() == Status.VALID) {
+ execute();
+ }
+ });
+ return true;
+ } else {
+ execute();
+ return true;
+ }
+};
+
+exports.removeCommand = removeCommand;
+exports.addCommand = addCommand;
+exports.getCommand = getCommand;
+exports.getCommandNames = getCommandNames;
+exports.findKeyCommand = findKeyCommand;
+exports.exec = exec;
+exports.execKeyCommand = execKeyCommand;
+exports.upgradeType = upgradeType;
+
+
+/**
+ * We publish a 'output' event whenever new command begins output
+ * TODO: make this more obvious
+ */
+oop.implement(exports, EventEmitter);
+
+
+/**
+ * Current requirements are around displaying the command line, and provision
+ * of a 'history' command and cursor up|down navigation of history.
+ * <p>Future requirements could include:
+ * <ul>
+ * <li>Multiple command lines
+ * <li>The ability to recall key presses (i.e. requests with no output) which
+ * will likely be needed for macro recording or similar
+ * <li>The ability to store the command history either on the server or in the
+ * browser local storage.
+ * </ul>
+ * <p>The execute() command doesn't really live here, except as part of that
+ * last future requirement, and because it doesn't really have anywhere else to
+ * live.
+ */
+
+/**
+ * The array of requests that wish to announce their presence
+ */
+var requests = [];
+
+/**
+ * How many requests do we store?
+ */
+var maxRequestLength = 100;
+
+/**
+ * To create an invocation, you need to do something like this (all the ctor
+ * args are optional):
+ * <pre>
+ * var request = new Request({
+ * command: command,
+ * args: args,
+ * typed: typed
+ * });
+ * </pre>
+ * @constructor
+ */
+function Request(options) {
+ options = options || {};
+
+ // Will be used in the keyboard case and the cli case
+ this.command = options.command;
+
+ // Will be used only in the cli case
+ this.args = options.args;
+ this.typed = options.typed;
+
+ // Have we been initialized?
+ this._begunOutput = false;
+
+ this.start = new Date();
+ this.end = null;
+ this.completed = false;
+ this.error = false;
+};
+
+oop.implement(Request.prototype, EventEmitter);
+
+/**
+ * Return the status of a parameter on the request object.
+ */
+Request.prototype.getParamStatus = function(param) {
+ var args = this.args || {};
+
+ // Check if there is already a value for this parameter.
+ if (param.name in args) {
+ // If there is no value set and then the value is VALID if it's not
+ // required or INCOMPLETE if not set yet.
+ if (args[param.name] == null) {
+ if (param.defaultValue === null) {
+ return Status.VALID;
+ } else {
+ return Status.INCOMPLETE;
+ }
+ }
+
+ // Check if the parameter value is valid.
+ var reply,
+ // The passed in value when parsing a type is a string.
+ argsValue = args[param.name].toString();
+
+ // Type.parse can throw errors.
+ try {
+ reply = param.type.parse(argsValue);
+ } catch (e) {
+ return Status.INVALID;
+ }
+
+ if (reply.status != Status.VALID) {
+ return reply.status;
+ }
+ }
+ // Check if the param is marked as required.
+ else if (param.defaultValue === undefined) {
+ // The parameter is not set on the args object but it's required,
+ // which means, things are invalid.
+ return Status.INCOMPLETE;
+ }
+
+ return Status.VALID;
+}
+
+/**
+ * Return the status of a parameter name on the request object.
+ */
+Request.prototype.getParamNameStatus = function(paramName) {
+ var params = this.command.params || [];
+
+ for (var i = 0; i < params.length; i++) {
+ if (params[i].name == paramName) {
+ return this.getParamStatus(params[i]);
+ }
+ }
+
+ throw "Parameter '" + paramName +
+ "' not defined on command '" + this.command.name + "'";
+}
+
+/**
+ * Checks if all required arguments are set on the request such that it can
+ * get executed.
+ */
+Request.prototype.getStatus = function() {
+ var args = this.args || {},
+ params = this.command.params;
+
+ // If there are not parameters, then it's valid.
+ if (!params || params.length == 0) {
+ return Status.VALID;
+ }
+
+ var status = [];
+ for (var i = 0; i < params.length; i++) {
+ status.push(this.getParamStatus(params[i]));
+ }
+
+ return Status.combine(status);
+}
+
+/**
+ * Lazy init to register with the history should only be done on output.
+ * init() is expensive, and won't be used in the majority of cases
+ */
+Request.prototype._beginOutput = function() {
+ this._begunOutput = true;
+ this.outputs = [];
+
+ requests.push(this);
+ // This could probably be optimized with some maths, but 99.99% of the
+ // time we will only be off by one, and I'm feeling lazy.
+ while (requests.length > maxRequestLength) {
+ requests.shiftObject();
+ }
+
+ exports._dispatchEvent('output', { requests: requests, request: this });
+};
+
+/**
+ * Sugar for:
+ * <pre>request.error = true; request.done(output);</pre>
+ */
+Request.prototype.doneWithError = function(content) {
+ this.error = true;
+ this.done(content);
+};
+
+/**
+ * Declares that this function will not be automatically done when
+ * the command exits
+ */
+Request.prototype.async = function() {
+ this.isAsync = true;
+ if (!this._begunOutput) {
+ this._beginOutput();
+ }
+};
+
+/**
+ * Complete the currently executing command with successful output.
+ * @param output Either DOM node, an SproutCore element or something that
+ * can be used in the content of a DIV to create a DOM node.
+ */
+Request.prototype.output = function(content) {
+ if (!this._begunOutput) {
+ this._beginOutput();
+ }
+
+ if (typeof content !== 'string' && !(content instanceof Node)) {
+ content = content.toString();
+ }
+
+ this.outputs.push(content);
+ this.isDone = true;
+ this._dispatchEvent('output', {});
+
+ return this;
+};
+
+/**
+ * All commands that do output must call this to indicate that the command
+ * has finished execution.
+ */
+Request.prototype.done = function(content) {
+ this.completed = true;
+ this.end = new Date();
+ this.duration = this.end.getTime() - this.start.getTime();
+
+ if (content) {
+ this.output(content);
+ }
+
+ // Ensure to finish the request only once.
+ if (!this.isDone) {
+ this.isDone = true;
+ this._dispatchEvent('output', {});
+ }
+};
+exports.Request = Request;
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ * Patrick Walton (pwalton@mozilla.com)
+ * Julian Viereck (jviereck@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+define('pilot/console', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+/**
+ * This object represents a "safe console" object that forwards debugging
+ * messages appropriately without creating a dependency on Firebug in Firefox.
+ */
+
+var noop = function() {};
+
+// These are the functions that are available in Chrome 4/5, Safari 4
+// and Firefox 3.6. Don't add to this list without checking browser support
+var NAMES = [
+ "assert", "count", "debug", "dir", "dirxml", "error", "group", "groupEnd",
+ "info", "log", "profile", "profileEnd", "time", "timeEnd", "trace", "warn"
+];
+
+if (typeof(window) === 'undefined') {
+ // We're in a web worker. Forward to the main thread so the messages
+ // will show up.
+ NAMES.forEach(function(name) {
+ exports[name] = function() {
+ var args = Array.prototype.slice.call(arguments);
+ var msg = { op: 'log', method: name, args: args };
+ postMessage(JSON.stringify(msg));
+ };
+ });
+} else {
+ // For each of the console functions, copy them if they exist, stub if not
+ NAMES.forEach(function(name) {
+ if (window.console && window.console[name]) {
+ exports[name] = Function.prototype.bind.call(window.console[name], window.console);
+ } else {
+ exports[name] = noop;
+ }
+ });
+}
+
+});
+define('pilot/stacktrace', ['require', 'exports', 'module' , 'pilot/useragent', 'pilot/console'], function(require, exports, module) {
+
+var ua = require("pilot/useragent");
+var console = require('pilot/console');
+
+// Changed to suit the specific needs of running within Skywriter
+
+// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
+// Luke Smith http://lucassmith.name/ (2008)
+// Loic Dachary <loic@dachary.org> (2008)
+// Johan Euphrosine <proppy@aminche.com> (2008)
+// Øyvind Sean Kinsey http://kinsey.no/blog
+//
+// Information and discussions
+// http://jspoker.pokersource.info/skin/test-printstacktrace.html
+// http://eriwen.com/javascript/js-stack-trace/
+// http://eriwen.com/javascript/stacktrace-update/
+// http://pastie.org/253058
+// http://browsershots.org/http://jspoker.pokersource.info/skin/test-printstacktrace.html
+//
+
+//
+// guessFunctionNameFromLines comes from firebug
+//
+// Software License Agreement (BSD License)
+//
+// Copyright (c) 2007, Parakey Inc.
+// All rights reserved.
+//
+// Redistribution and use of this software in source and binary forms, 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.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the
+// following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// * Neither the name of Parakey Inc. nor the names of its
+// contributors may be used to endorse or promote products
+// derived from this software without specific prior
+// written permission of Parakey Inc.
+//
+// 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.
+
+
+
+/**
+ * Different browsers create stack traces in different ways.
+ * <strike>Feature</strike> Browser detection baby ;).
+ */
+var mode = (function() {
+
+ // We use SC's browser detection here to avoid the "break on error"
+ // functionality provided by Firebug. Firebug tries to do the right
+ // thing here and break, but it happens every time you load the page.
+ // bug 554105
+ if (ua.isGecko) {
+ return 'firefox';
+ } else if (ua.isOpera) {
+ return 'opera';
+ } else {
+ return 'other';
+ }
+
+ // SC doesn't do any detection of Chrome at this time.
+
+ // this is the original feature detection code that is used as a
+ // fallback.
+ try {
+ (0)();
+ } catch (e) {
+ if (e.arguments) {
+ return 'chrome';
+ }
+ if (e.stack) {
+ return 'firefox';
+ }
+ if (window.opera && !('stacktrace' in e)) { //Opera 9-
+ return 'opera';
+ }
+ }
+ return 'other';
+})();
+
+/**
+ *
+ */
+function stringifyArguments(args) {
+ for (var i = 0; i < args.length; ++i) {
+ var argument = args[i];
+ if (typeof argument == 'object') {
+ args[i] = '#object';
+ } else if (typeof argument == 'function') {
+ args[i] = '#function';
+ } else if (typeof argument == 'string') {
+ args[i] = '"' + argument + '"';
+ }
+ }
+ return args.join(',');
+}
+
+/**
+ * Extract a stack trace from the format emitted by each browser.
+ */
+var decoders = {
+ chrome: function(e) {
+ var stack = e.stack;
+ if (!stack) {
+ console.log(e);
+ return [];
+ }
+ return stack.replace(/^.*?\n/, '').
+ replace(/^.*?\n/, '').
+ replace(/^.*?\n/, '').
+ replace(/^[^\(]+?[\n$]/gm, '').
+ replace(/^\s+at\s+/gm, '').
+ replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@').
+ split('\n');
+ },
+
+ firefox: function(e) {
+ var stack = e.stack;
+ if (!stack) {
+ console.log(e);
+ return [];
+ }
+ // stack = stack.replace(/^.*?\n/, '');
+ stack = stack.replace(/(?:\n@:0)?\s+$/m, '');
+ stack = stack.replace(/^\(/gm, '{anonymous}(');
+ return stack.split('\n');
+ },
+
+ // Opera 7.x and 8.x only!
+ opera: function(e) {
+ var lines = e.message.split('\n'), ANON = '{anonymous}',
+ lineRE = /Line\s+(\d+).*?script\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i, i, j, len;
+
+ for (i = 4, j = 0, len = lines.length; i < len; i += 2) {
+ if (lineRE.test(lines[i])) {
+ lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) +
+ ' -- ' +
+ lines[i + 1].replace(/^\s+/, '');
+ }
+ }
+
+ lines.splice(j, lines.length - j);
+ return lines;
+ },
+
+ // Safari, Opera 9+, IE, and others
+ other: function(curr) {
+ var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], j = 0, fn, args;
+
+ var maxStackSize = 10;
+ while (curr && stack.length < maxStackSize) {
+ fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
+ args = Array.prototype.slice.call(curr['arguments']);
+ stack[j++] = fn + '(' + stringifyArguments(args) + ')';
+
+ //Opera bug: if curr.caller does not exist, Opera returns curr (WTF)
+ if (curr === curr.caller && window.opera) {
+ //TODO: check for same arguments if possible
+ break;
+ }
+ curr = curr.caller;
+ }
+ return stack;
+ }
+};
+
+/**
+ *
+ */
+function NameGuesser() {
+}
+
+NameGuesser.prototype = {
+
+ sourceCache: {},
+
+ ajax: function(url) {
+ var req = this.createXMLHTTPObject();
+ if (!req) {
+ return;
+ }
+ req.open('GET', url, false);
+ req.setRequestHeader('User-Agent', 'XMLHTTP/1.0');
+ req.send('');
+ return req.responseText;
+ },
+
+ createXMLHTTPObject: function() {
+ // Try XHR methods in order and store XHR factory
+ var xmlhttp, XMLHttpFactories = [
+ function() {
+ return new XMLHttpRequest();
+ }, function() {
+ return new ActiveXObject('Msxml2.XMLHTTP');
+ }, function() {
+ return new ActiveXObject('Msxml3.XMLHTTP');
+ }, function() {
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ }
+ ];
+ for (var i = 0; i < XMLHttpFactories.length; i++) {
+ try {
+ xmlhttp = XMLHttpFactories[i]();
+ // Use memoization to cache the factory
+ this.createXMLHTTPObject = XMLHttpFactories[i];
+ return xmlhttp;
+ } catch (e) {}
+ }
+ },
+
+ getSource: function(url) {
+ if (!(url in this.sourceCache)) {
+ this.sourceCache[url] = this.ajax(url).split('\n');
+ }
+ return this.sourceCache[url];
+ },
+
+ guessFunctions: function(stack) {
+ for (var i = 0; i < stack.length; ++i) {
+ var reStack = /{anonymous}\(.*\)@(\w+:\/\/([-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/;
+ var frame = stack[i], m = reStack.exec(frame);
+ if (m) {
+ var file = m[1], lineno = m[4]; //m[7] is character position in Chrome
+ if (file && lineno) {
+ var functionName = this.guessFunctionName(file, lineno);
+ stack[i] = frame.replace('{anonymous}', functionName);
+ }
+ }
+ }
+ return stack;
+ },
+
+ guessFunctionName: function(url, lineNo) {
+ try {
+ return this.guessFunctionNameFromLines(lineNo, this.getSource(url));
+ } catch (e) {
+ return 'getSource failed with url: ' + url + ', exception: ' + e.toString();
+ }
+ },
+
+ guessFunctionNameFromLines: function(lineNo, source) {
+ var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
+ var reGuessFunction = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
+ // Walk backwards from the first line in the function until we find the line which
+ // matches the pattern above, which is the function definition
+ var line = '', maxLines = 10;
+ for (var i = 0; i < maxLines; ++i) {
+ line = source[lineNo - i] + line;
+ if (line !== undefined) {
+ var m = reGuessFunction.exec(line);
+ if (m) {
+ return m[1];
+ }
+ else {
+ m = reFunctionArgNames.exec(line);
+ }
+ if (m && m[1]) {
+ return m[1];
+ }
+ }
+ }
+ return '(?)';
+ }
+};
+
+var guesser = new NameGuesser();
+
+var frameIgnorePatterns = [
+ /http:\/\/localhost:4020\/sproutcore.js:/
+];
+
+exports.ignoreFramesMatching = function(regex) {
+ frameIgnorePatterns.push(regex);
+};
+
+/**
+ * Create a stack trace from an exception
+ * @param ex {Error} The error to create a stacktrace from (optional)
+ * @param guess {Boolean} If we should try to resolve the names of anonymous functions
+ */
+exports.Trace = function Trace(ex, guess) {
+ this._ex = ex;
+ this._stack = decoders[mode](ex);
+
+ if (guess) {
+ this._stack = guesser.guessFunctions(this._stack);
+ }
+};
+
+/**
+ * Log to the console a number of lines (default all of them)
+ * @param lines {number} Maximum number of lines to wrote to console
+ */
+exports.Trace.prototype.log = function(lines) {
+ if (lines <= 0) {
+ // You aren't going to have more lines in your stack trace than this
+ // and it still fits in a 32bit integer
+ lines = 999999999;
+ }
+
+ var printed = 0;
+ for (var i = 0; i < this._stack.length && printed < lines; i++) {
+ var frame = this._stack[i];
+ var display = true;
+ frameIgnorePatterns.forEach(function(regex) {
+ if (regex.test(frame)) {
+ display = false;
+ }
+ });
+ if (display) {
+ console.debug(frame);
+ printed++;
+ }
+ }
+};
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/useragent', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var os = (navigator.platform.match(/mac|win|linux/i) || ["other"])[0].toLowerCase();
+var ua = navigator.userAgent;
+var av = navigator.appVersion;
+
+/** Is the user using a browser that identifies itself as Windows */
+exports.isWin = (os == "win");
+
+/** Is the user using a browser that identifies itself as Mac OS */
+exports.isMac = (os == "mac");
+
+/** Is the user using a browser that identifies itself as Linux */
+exports.isLinux = (os == "linux");
+
+exports.isIE = ! + "\v1";
+
+/** Is this Firefox or related? */
+exports.isGecko = exports.isMozilla = window.controllers && window.navigator.product === "Gecko";
+
+/** oldGecko == rev < 2.0 **/
+exports.isOldGecko = exports.isGecko && /rv\:1/.test(navigator.userAgent);
+
+/** Is this Opera */
+exports.isOpera = window.opera && Object.prototype.toString.call(window.opera) == "[object Opera]";
+
+/** Is the user using a browser that identifies itself as WebKit */
+exports.isWebKit = parseFloat(ua.split("WebKit/")[1]) || undefined;
+
+exports.isAIR = ua.indexOf("AdobeAIR") >= 0;
+
+exports.isIPad = ua.indexOf("iPad") >= 0;
+
+/**
+ * I hate doing this, but we need some way to determine if the user is on a Mac
+ * The reason is that users have different expectations of their key combinations.
+ *
+ * Take copy as an example, Mac people expect to use CMD or APPLE + C
+ * Windows folks expect to use CTRL + C
+ */
+exports.OS = {
+ LINUX: 'LINUX',
+ MAC: 'MAC',
+ WINDOWS: 'WINDOWS'
+};
+
+/**
+ * Return an exports.OS constant
+ */
+exports.getOS = function() {
+ if (exports.isMac) {
+ return exports.OS['MAC'];
+ } else if (exports.isLinux) {
+ return exports.OS['LINUX'];
+ } else {
+ return exports.OS['WINDOWS'];
+ }
+};
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/oop', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+exports.inherits = (function() {
+ var tempCtor = function() {};
+ return function(ctor, superCtor) {
+ tempCtor.prototype = superCtor.prototype;
+ ctor.super_ = superCtor.prototype;
+ ctor.prototype = new tempCtor();
+ ctor.prototype.constructor = ctor;
+ }
+}());
+
+exports.mixin = function(obj, mixin) {
+ for (var key in mixin) {
+ obj[key] = mixin[key];
+ }
+};
+
+exports.implement = function(proto, mixin) {
+ exports.mixin(proto, mixin);
+};
+
+});
+/*! @license
+==========================================================================
+SproutCore -- JavaScript Application Framework
+copyright 2006-2009, Sprout Systems Inc., Apple Inc. and contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+SproutCore and the SproutCore logo are trademarks of Sprout Systems, Inc.
+
+For more information about SproutCore, visit http://www.sproutcore.com
+
+
+==========================================================================
+@license */
+
+// Most of the following code is taken from SproutCore with a few changes.
+
+define('pilot/keys', ['require', 'exports', 'module' , 'pilot/oop'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+
+/**
+ * Helper functions and hashes for key handling.
+ */
+var Keys = (function() {
+ var ret = {
+ MODIFIER_KEYS: {
+ 16: 'Shift', 17: 'Ctrl', 18: 'Alt', 224: 'Meta'
+ },
+
+ KEY_MODS: {
+ "ctrl": 1, "alt": 2, "option" : 2,
+ "shift": 4, "meta": 8, "command": 8
+ },
+
+ FUNCTION_KEYS : {
+ 8 : "Backspace",
+ 9 : "Tab",
+ 13 : "Return",
+ 19 : "Pause",
+ 27 : "Esc",
+ 32 : "Space",
+ 33 : "PageUp",
+ 34 : "PageDown",
+ 35 : "End",
+ 36 : "Home",
+ 37 : "Left",
+ 38 : "Up",
+ 39 : "Right",
+ 40 : "Down",
+ 44 : "Print",
+ 45 : "Insert",
+ 46 : "Delete",
+ 112: "F1",
+ 113: "F2",
+ 114: "F3",
+ 115: "F4",
+ 116: "F5",
+ 117: "F6",
+ 118: "F7",
+ 119: "F8",
+ 120: "F9",
+ 121: "F10",
+ 122: "F11",
+ 123: "F12",
+ 144: "Numlock",
+ 145: "Scrolllock"
+ },
+
+ PRINTABLE_KEYS: {
+ 32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5',
+ 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 61: '=', 65: 'a',
+ 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h',
+ 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o',
+ 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v',
+ 87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.',
+ 188: ',', 190: '.', 191: '/', 192: '`', 219: '[', 220: '\\',
+ 221: ']', 222: '\"'
+ }
+ };
+
+ // A reverse map of FUNCTION_KEYS
+ for (i in ret.FUNCTION_KEYS) {
+ var name = ret.FUNCTION_KEYS[i].toUpperCase();
+ ret[name] = parseInt(i, 10);
+ }
+
+ // Add the MODIFIER_KEYS, FUNCTION_KEYS and PRINTABLE_KEYS to the KEY
+ // variables as well.
+ oop.mixin(ret, ret.MODIFIER_KEYS);
+ oop.mixin(ret, ret.PRINTABLE_KEYS);
+ oop.mixin(ret, ret.FUNCTION_KEYS);
+
+ return ret;
+})();
+oop.mixin(exports, Keys);
+
+exports.keyCodeToString = function(keyCode) {
+ return (Keys[keyCode] || String.fromCharCode(keyCode)).toLowerCase();
+}
+
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var EventEmitter = {};
+
+EventEmitter._emit =
+EventEmitter._dispatchEvent = function(eventName, e) {
+ this._eventRegistry = this._eventRegistry || {};
+
+ var listeners = this._eventRegistry[eventName];
+ if (!listeners || !listeners.length) return;
+
+ var e = e || {};
+ e.type = eventName;
+
+ for (var i=0; i<listeners.length; i++) {
+ listeners[i](e);
+ }
+};
+
+EventEmitter.on =
+EventEmitter.addEventListener = function(eventName, callback) {
+ this._eventRegistry = this._eventRegistry || {};
+
+ var listeners = this._eventRegistry[eventName];
+ if (!listeners) {
+ var listeners = this._eventRegistry[eventName] = [];
+ }
+ if (listeners.indexOf(callback) == -1) {
+ listeners.push(callback);
+ }
+};
+
+EventEmitter.removeListener =
+EventEmitter.removeEventListener = function(eventName, callback) {
+ this._eventRegistry = this._eventRegistry || {};
+
+ var listeners = this._eventRegistry[eventName];
+ if (!listeners) {
+ return;
+ }
+ var index = listeners.indexOf(callback);
+ if (index !== -1) {
+ listeners.splice(index, 1);
+ }
+};
+
+EventEmitter.removeAllListeners = function(eventName) {
+ if (this._eventRegistry) this._eventRegistry[eventName] = [];
+}
+
+exports.EventEmitter = EventEmitter;
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/typecheck', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var objectToString = Object.prototype.toString;
+
+/**
+ * Return true if it is a String
+ */
+exports.isString = function(it) {
+ return it && objectToString.call(it) === "[object String]";
+};
+
+/**
+ * Returns true if it is a Boolean.
+ */
+exports.isBoolean = function(it) {
+ return it && objectToString.call(it) === "[object Boolean]";
+};
+
+/**
+ * Returns true if it is a Number.
+ */
+exports.isNumber = function(it) {
+ return it && objectToString.call(it) === "[object Number]" && isFinite(it);
+};
+
+/**
+ * Hack copied from dojo.
+ */
+exports.isObject = function(it) {
+ return it !== undefined &&
+ (it === null || typeof it == "object" ||
+ Array.isArray(it) || exports.isFunction(it));
+};
+
+/**
+ * Is the passed object a function?
+ * From dojo.isFunction()
+ */
+exports.isFunction = function(it) {
+ return it && objectToString.call(it) === "[object Function]";
+};
+
+});/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Julian Viereck (jviereck@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/catalog', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+
+var extensionSpecs = {};
+
+exports.addExtensionSpec = function(extensionSpec) {
+ extensionSpecs[extensionSpec.name] = extensionSpec;
+};
+
+exports.removeExtensionSpec = function(extensionSpec) {
+ if (typeof extensionSpec === "string") {
+ delete extensionSpecs[extensionSpec];
+ }
+ else {
+ delete extensionSpecs[extensionSpec.name];
+ }
+};
+
+exports.getExtensionSpec = function(name) {
+ return extensionSpecs[name];
+};
+
+exports.getExtensionSpecs = function() {
+ return Object.keys(extensionSpecs);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/lang', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+exports.stringReverse = function(string) {
+ return string.split("").reverse().join("");
+};
+
+exports.stringRepeat = function (string, count) {
+ return new Array(count + 1).join(string);
+};
+
+var trimBeginRegexp = /^\s\s*/;
+var trimEndRegexp = /\s\s*$/;
+
+exports.stringTrimLeft = function (string) {
+ return string.replace(trimBeginRegexp, '')
+};
+
+exports.stringTrimRight = function (string) {
+ return string.replace(trimEndRegexp, '');
+};
+
+exports.copyObject = function(obj) {
+ var copy = {};
+ for (var key in obj) {
+ copy[key] = obj[key];
+ }
+ return copy;
+};
+
+exports.copyArray = function(array){
+ var copy = [];
+ for (i=0, l=array.length; i<l; i++) {
+ if (array[i] && typeof array[i] == "object")
+ copy[i] = this.copyObject( array[i] );
+ else
+ copy[i] = array[i]
+ }
+ return copy;
+};
+
+exports.deepCopy = function (obj) {
+ if (typeof obj != "object") {
+ return obj;
+ }
+
+ var copy = obj.constructor();
+ for (var key in obj) {
+ if (typeof obj[key] == "object") {
+ copy[key] = this.deepCopy(obj[key]);
+ } else {
+ copy[key] = obj[key];
+ }
+ }
+ return copy;
+}
+
+exports.arrayToMap = function(arr) {
+ var map = {};
+ for (var i=0; i<arr.length; i++) {
+ map[arr[i]] = 1;
+ }
+ return map;
+
+};
+
+/**
+ * splice out of 'array' anything that === 'value'
+ */
+exports.arrayRemove = function(array, value) {
+ for (var i = 0; i <= array.length; i++) {
+ if (value === array[i]) {
+ array.splice(i, 1);
+ }
+ }
+};
+
+exports.escapeRegExp = function(str) {
+ return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
+};
+
+exports.deferredCall = function(fcn) {
+
+ var timer = null;
+ var callback = function() {
+ timer = null;
+ fcn();
+ };
+
+ var deferred = function(timeout) {
+ if (!timer) {
+ timer = setTimeout(callback, timeout || 0);
+ }
+ return deferred;
+ }
+
+ deferred.schedule = deferred;
+
+ deferred.call = function() {
+ this.cancel();
+ fcn();
+ return deferred;
+ };
+
+ deferred.cancel = function() {
+ clearTimeout(timer);
+ timer = null;
+ return deferred;
+ };
+
+ return deferred;
+};
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/types/settings', ['require', 'exports', 'module' , 'pilot/types/basic', 'pilot/types', 'pilot/settings'], function(require, exports, module) {
+
+var SelectionType = require('pilot/types/basic').SelectionType;
+var DeferredType = require('pilot/types/basic').DeferredType;
+var types = require('pilot/types');
+var settings = require('pilot/settings').settings;
+
+
+/**
+ * EVIL: This relies on us using settingValue in the same event as setting
+ * The alternative is to have some central place where we store the current
+ * command line, but this might be a lesser evil for now.
+ */
+var lastSetting;
+
+/**
+ * Select from the available settings
+ */
+var setting = new SelectionType({
+ name: 'setting',
+ data: function() {
+ return env.settings.getSettingNames();
+ },
+ stringify: function(setting) {
+ lastSetting = setting;
+ return setting.name;
+ },
+ fromString: function(str) {
+ lastSetting = settings.getSetting(str);
+ return lastSetting;
+ },
+ noMatch: function() {
+ lastSetting = null;
+ }
+});
+
+/**
+ * Something of a hack to allow the set command to give a clearer definition
+ * of the type to the command line.
+ */
+var settingValue = new DeferredType({
+ name: 'settingValue',
+ defer: function() {
+ if (lastSetting) {
+ return lastSetting.type;
+ }
+ else {
+ return types.getType('text');
+ }
+ },
+ /**
+ * Promote the current value in any list of predictions, and add it if
+ * there are none.
+ */
+ getDefault: function() {
+ var conversion = this.parse('');
+ if (lastSetting) {
+ var current = lastSetting.get();
+ if (conversion.predictions.length === 0) {
+ conversion.predictions.push(current);
+ }
+ else {
+ // Remove current from predictions
+ var removed = false;
+ while (true) {
+ var index = conversion.predictions.indexOf(current);
+ if (index === -1) {
+ break;
+ }
+ conversion.predictions.splice(index, 1);
+ removed = true;
+ }
+ // If the current value wasn't something we would predict, leave it
+ if (removed) {
+ conversion.predictions.push(current);
+ }
+ }
+ }
+ return conversion;
+ }
+});
+
+var env;
+
+/**
+ * Registration and de-registration.
+ */
+exports.startup = function(data, reason) {
+ // TODO: this is probably all kinds of evil, but we need something working
+ env = data.env;
+ types.registerType(setting);
+ types.registerType(settingValue);
+};
+
+exports.shutdown = function(data, reason) {
+ types.unregisterType(setting);
+ types.unregisterType(settingValue);
+};
+
+
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ * Julian Viereck (jviereck@mozilla.com)
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ * Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/settings', ['require', 'exports', 'module' , 'pilot/console', 'pilot/oop', 'pilot/types', 'pilot/event_emitter', 'pilot/catalog'], function(require, exports, module) {
+
+/**
+ * This plug-in manages settings.
+ */
+
+var console = require('pilot/console');
+var oop = require('pilot/oop');
+var types = require('pilot/types');
+var EventEmitter = require('pilot/event_emitter').EventEmitter;
+var catalog = require('pilot/catalog');
+
+var settingExtensionSpec = {
+ name: 'setting',
+ description: 'A setting is something that the application offers as a ' +
+ 'way to customize how it works',
+ register: 'env.settings.addSetting',
+ indexOn: 'name'
+};
+
+exports.startup = function(data, reason) {
+ catalog.addExtensionSpec(settingExtensionSpec);
+};
+
+exports.shutdown = function(data, reason) {
+ catalog.removeExtensionSpec(settingExtensionSpec);
+};
+
+
+/**
+ * Create a new setting.
+ * @param settingSpec An object literal that looks like this:
+ * {
+ * name: 'thing',
+ * description: 'Thing is an example setting',
+ * type: 'string',
+ * defaultValue: 'something'
+ * }
+ */
+function Setting(settingSpec, settings) {
+ this._settings = settings;
+
+ Object.keys(settingSpec).forEach(function(key) {
+ this[key] = settingSpec[key];
+ }, this);
+
+ this.type = types.getType(this.type);
+ if (this.type == null) {
+ throw new Error('In ' + this.name +
+ ': can\'t find type for: ' + JSON.stringify(settingSpec.type));
+ }
+
+ if (!this.name) {
+ throw new Error('Setting.name == undefined. Ignoring.', this);
+ }
+
+ if (!this.defaultValue === undefined) {
+ throw new Error('Setting.defaultValue == undefined', this);
+ }
+
+ if (this.onChange) {
+ this.on('change', this.onChange.bind(this))
+ }
+
+ this.set(this.defaultValue);
+}
+Setting.prototype = {
+ get: function() {
+ return this.value;
+ },
+
+ set: function(value) {
+ if (this.value === value) {
+ return;
+ }
+
+ this.value = value;
+ if (this._settings.persister) {
+ this._settings.persister.persistValue(this._settings, this.name, value);
+ }
+
+ this._dispatchEvent('change', { setting: this, value: value });
+ },
+
+ /**
+ * Reset the value of the <code>key</code> setting to it's default
+ */
+ resetValue: function() {
+ this.set(this.defaultValue);
+ }
+};
+oop.implement(Setting.prototype, EventEmitter);
+
+
+/**
+ * A base class for all the various methods of storing settings.
+ * <p>Usage:
+ * <pre>
+ * // Create manually, or require 'settings' from the container.
+ * // This is the manual version:
+ * var settings = plugins.catalog.getObject('settings');
+ * // Add a new setting
+ * settings.addSetting({ name:'foo', ... });
+ * // Display the default value
+ * alert(settings.get('foo'));
+ * // Alter the value, which also publishes the change etc.
+ * settings.set('foo', 'bar');
+ * // Reset the value to the default
+ * settings.resetValue('foo');
+ * </pre>
+ * @constructor
+ */
+function Settings(persister) {
+ // Storage for deactivated values
+ this._deactivated = {};
+
+ // Storage for the active settings
+ this._settings = {};
+ // We often want sorted setting names. Cache
+ this._settingNames = [];
+
+ if (persister) {
+ this.setPersister(persister);
+ }
+};
+
+Settings.prototype = {
+ /**
+ * Function to add to the list of available settings.
+ * <p>Example usage:
+ * <pre>
+ * var settings = plugins.catalog.getObject('settings');
+ * settings.addSetting({
+ * name: 'tabsize', // For use in settings.get('X')
+ * type: 'number', // To allow value checking.
+ * defaultValue: 4 // Default value for use when none is directly set
+ * });
+ * </pre>
+ * @param {object} settingSpec Object containing name/type/defaultValue members.
+ */
+ addSetting: function(settingSpec) {
+ var setting = new Setting(settingSpec, this);
+ this._settings[setting.name] = setting;
+ this._settingNames.push(setting.name);
+ this._settingNames.sort();
+ },
+
+ addSettings: function addSettings(settings) {
+ Object.keys(settings).forEach(function (name) {
+ var setting = settings[name];
+ if (!('name' in setting)) setting.name = name;
+ this.addSetting(setting);
+ }, this);
+ },
+
+ removeSetting: function(setting) {
+ var name = (typeof setting === 'string' ? setting : setting.name);
+ setting = this._settings[name];
+ delete this._settings[name];
+ util.arrayRemove(this._settingNames, name);
+ settings.removeAllListeners('change');
+ },
+
+ removeSettings: function removeSettings(settings) {
+ Object.keys(settings).forEach(function(name) {
+ var setting = settings[name];
+ if (!('name' in setting)) setting.name = name;
+ this.removeSettings(setting);
+ }, this);
+ },
+
+ getSettingNames: function() {
+ return this._settingNames;
+ },
+
+ getSetting: function(name) {
+ return this._settings[name];
+ },
+
+ /**
+ * A Persister is able to store settings. It is an object that defines
+ * two functions:
+ * loadInitialValues(settings) and persistValue(settings, key, value).
+ */
+ setPersister: function(persister) {
+ this._persister = persister;
+ if (persister) {
+ persister.loadInitialValues(this);
+ }
+ },
+
+ resetAll: function() {
+ this.getSettingNames().forEach(function(key) {
+ this.resetValue(key);
+ }, this);
+ },
+
+ /**
+ * Retrieve a list of the known settings and their values
+ */
+ _list: function() {
+ var reply = [];
+ this.getSettingNames().forEach(function(setting) {
+ reply.push({
+ 'key': setting,
+ 'value': this.getSetting(setting).get()
+ });
+ }, this);
+ return reply;
+ },
+
+ /**
+ * Prime the local cache with the defaults.
+ */
+ _loadDefaultValues: function() {
+ this._loadFromObject(this._getDefaultValues());
+ },
+
+ /**
+ * Utility to load settings from an object
+ */
+ _loadFromObject: function(data) {
+ // We iterate over data rather than keys so we don't forget values
+ // which don't have a setting yet.
+ for (var key in data) {
+ if (data.hasOwnProperty(key)) {
+ var setting = this._settings[key];
+ if (setting) {
+ var value = setting.type.parse(data[key]);
+ this.set(key, value);
+ } else {
+ this.set(key, data[key]);
+ }
+ }
+ }
+ },
+
+ /**
+ * Utility to grab all the settings and export them into an object
+ */
+ _saveToObject: function() {
+ return this.getSettingNames().map(function(key) {
+ return this._settings[key].type.stringify(this.get(key));
+ }.bind(this));
+ },
+
+ /**
+ * The default initial settings
+ */
+ _getDefaultValues: function() {
+ return this.getSettingNames().map(function(key) {
+ return this._settings[key].spec.defaultValue;
+ }.bind(this));
+ }
+};
+exports.settings = new Settings();
+
+/**
+ * Save the settings in a cookie
+ * This code has not been tested since reboot
+ * @constructor
+ */
+function CookiePersister() {
+};
+
+CookiePersister.prototype = {
+ loadInitialValues: function(settings) {
+ settings._loadDefaultValues();
+ var data = cookie.get('settings');
+ settings._loadFromObject(JSON.parse(data));
+ },
+
+ persistValue: function(settings, key, value) {
+ try {
+ var stringData = JSON.stringify(settings._saveToObject());
+ cookie.set('settings', stringData);
+ } catch (ex) {
+ console.error('Unable to JSONify the settings! ' + ex);
+ return;
+ }
+ }
+};
+
+exports.CookiePersister = CookiePersister;
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Skywriter Team (skywriter@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/commands/settings', ['require', 'exports', 'module' , 'pilot/canon'], function(require, exports, module) {
+
+
+var setCommandSpec = {
+ name: 'set',
+ params: [
+ {
+ name: 'setting',
+ type: 'setting',
+ description: 'The name of the setting to display or alter',
+ defaultValue: null
+ },
+ {
+ name: 'value',
+ type: 'settingValue',
+ description: 'The new value for the chosen setting',
+ defaultValue: null
+ }
+ ],
+ description: 'define and show settings',
+ exec: function(env, args, request) {
+ var html;
+ if (!args.setting) {
+ // 'set' by itself lists all the settings
+ var names = env.settings.getSettingNames();
+ html = '';
+ // first sort the settingsList based on the name
+ names.sort(function(name1, name2) {
+ return name1.localeCompare(name2);
+ });
+
+ names.forEach(function(name) {
+ var setting = env.settings.getSetting(name);
+ var url = 'https://wiki.mozilla.org/Labs/Skywriter/Settings#' +
+ setting.name;
+ html += '<a class="setting" href="' + url +
+ '" title="View external documentation on setting: ' +
+ setting.name +
+ '" target="_blank">' +
+ setting.name +
+ '</a> = ' +
+ setting.value +
+ '<br/>';
+ });
+ } else {
+ // set with only a setting, shows the value for that setting
+ if (args.value === undefined) {
+ html = '<strong>' + setting.name + '</strong> = ' +
+ setting.get();
+ } else {
+ // Actually change the setting
+ args.setting.set(args.value);
+ html = 'Setting: <strong>' + args.setting.name + '</strong> = ' +
+ args.setting.get();
+ }
+ }
+ request.done(html);
+ }
+};
+
+var unsetCommandSpec = {
+ name: 'unset',
+ params: [
+ {
+ name: 'setting',
+ type: 'setting',
+ description: 'The name of the setting to return to defaults'
+ }
+ ],
+ description: 'unset a setting entirely',
+ exec: function(env, args, request) {
+ var setting = env.settings.get(args.setting);
+ if (!setting) {
+ request.doneWithError('No setting with the name <strong>' +
+ args.setting + '</strong>.');
+ return;
+ }
+
+ setting.reset();
+ request.done('Reset ' + setting.name + ' to default: ' +
+ env.settings.get(args.setting));
+ }
+};
+
+var canon = require('pilot/canon');
+
+exports.startup = function(data, reason) {
+ canon.addCommand(setCommandSpec);
+ canon.addCommand(unsetCommandSpec);
+};
+
+exports.shutdown = function(data, reason) {
+ canon.removeCommand(setCommandSpec);
+ canon.removeCommand(unsetCommandSpec);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Skywriter Team (skywriter@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/commands/basic', ['require', 'exports', 'module' , 'pilot/typecheck', 'pilot/canon'], function(require, exports, module) {
+
+
+var checks = require("pilot/typecheck");
+var canon = require('pilot/canon');
+
+/**
+ *
+ */
+var helpMessages = {
+ plainPrefix:
+ '<h2>Welcome to Skywriter - Code in the Cloud</h2><ul>' +
+ '<li><a href="http://labs.mozilla.com/projects/skywriter" target="_blank">Home Page</a></li>' +
+ '<li><a href="https://wiki.mozilla.org/Labs/Skywriter" target="_blank">Wiki</a></li>' +
+ '<li><a href="https://wiki.mozilla.org/Labs/Skywriter/UserGuide" target="_blank">User Guide</a></li>' +
+ '<li><a href="https://wiki.mozilla.org/Labs/Skywriter/Tips" target="_blank">Tips and Tricks</a></li>' +
+ '<li><a href="https://wiki.mozilla.org/Labs/Skywriter/FAQ" target="_blank">FAQ</a></li>' +
+ '<li><a href="https://wiki.mozilla.org/Labs/Skywriter/DeveloperGuide" target="_blank">Developers Guide</a></li>' +
+ '</ul>',
+ plainSuffix:
+ 'For more information, see the <a href="https://wiki.mozilla.org/Labs/Skywriter">Skywriter Wiki</a>.'
+};
+
+/**
+ * 'help' command
+ */
+var helpCommandSpec = {
+ name: 'help',
+ params: [
+ {
+ name: 'search',
+ type: 'text',
+ description: 'Search string to narrow the output.',
+ defaultValue: null
+ }
+ ],
+ description: 'Get help on the available commands.',
+ exec: function(env, args, request) {
+ var output = [];
+
+ var command = canon.getCommand(args.search);
+ if (command && command.exec) {
+ // caught a real command
+ output.push(command.description ?
+ command.description :
+ 'No description for ' + args.search);
+ } else {
+ var showHidden = false;
+
+ if (!args.search && helpMessages.plainPrefix) {
+ output.push(helpMessages.plainPrefix);
+ }
+
+ if (command) {
+ // We must be looking at sub-commands
+ output.push('<h2>Sub-Commands of ' + command.name + '</h2>');
+ output.push('<p>' + command.description + '</p>');
+ }
+ else if (args.search) {
+ if (args.search == 'hidden') { // sneaky, sneaky.
+ args.search = '';
+ showHidden = true;
+ }
+ output.push('<h2>Commands starting with \'' + args.search + '\':</h2>');
+ }
+ else {
+ output.push('<h2>Available Commands:</h2>');
+ }
+
+ var commandNames = canon.getCommandNames();
+ commandNames.sort();
+
+ output.push('<table>');
+ for (var i = 0; i < commandNames.length; i++) {
+ command = canon.getCommand(commandNames[i]);
+ if (!showHidden && command.hidden) {
+ continue;
+ }
+ if (command.description === undefined) {
+ // Ignore editor actions
+ continue;
+ }
+ if (args.search && command.name.indexOf(args.search) !== 0) {
+ // Filtered out by the user
+ continue;
+ }
+ if (!args.search && command.name.indexOf(' ') != -1) {
+ // sub command
+ continue;
+ }
+ if (command && command.name == args.search) {
+ // sub command, and we've already given that help
+ continue;
+ }
+
+ // todo add back a column with parameter information, perhaps?
+
+ output.push('<tr>');
+ output.push('<th class="right">' + command.name + '</th>');
+ output.push('<td>' + command.description + '</td>');
+ output.push('</tr>');
+ }
+ output.push('</table>');
+
+ if (!args.search && helpMessages.plainSuffix) {
+ output.push(helpMessages.plainSuffix);
+ }
+ }
+
+ request.done(output.join(''));
+ }
+};
+
+/**
+ * 'eval' command
+ */
+var evalCommandSpec = {
+ name: 'eval',
+ params: [
+ {
+ name: 'javascript',
+ type: 'text',
+ description: 'The JavaScript to evaluate'
+ }
+ ],
+ description: 'evals given js code and show the result',
+ hidden: true,
+ exec: function(env, args, request) {
+ var result;
+ var javascript = args.javascript;
+ try {
+ result = eval(javascript);
+ } catch (e) {
+ result = '<b>Error: ' + e.message + '</b>';
+ }
+
+ var msg = '';
+ var type = '';
+ var x;
+
+ if (checks.isFunction(result)) {
+ // converts the function to a well formated string
+ msg = (result + '').replace(/\n/g, '<br>').replace(/ /g, '&#160');
+ type = 'function';
+ } else if (checks.isObject(result)) {
+ if (Array.isArray(result)) {
+ type = 'array';
+ } else {
+ type = 'object';
+ }
+
+ var items = [];
+ var value;
+
+ for (x in result) {
+ if (result.hasOwnProperty(x)) {
+ if (checks.isFunction(result[x])) {
+ value = '[function]';
+ } else if (checks.isObject(result[x])) {
+ value = '[object]';
+ } else {
+ value = result[x];
+ }
+
+ items.push({name: x, value: value});
+ }
+ }
+
+ items.sort(function(a,b) {
+ return (a.name.toLowerCase() < b.name.toLowerCase()) ? -1 : 1;
+ });
+
+ for (x = 0; x < items.length; x++) {
+ msg += '<b>' + items[x].name + '</b>: ' + items[x].value + '<br>';
+ }
+
+ } else {
+ msg = result;
+ type = typeof result;
+ }
+
+ request.done('Result for eval <b>\'' + javascript + '\'</b>' +
+ ' (type: '+ type+'): <br><br>'+ msg);
+ }
+};
+
+/**
+ * 'version' command
+ */
+var versionCommandSpec = {
+ name: 'version',
+ description: 'show the Skywriter version',
+ hidden: true,
+ exec: function(env, args, request) {
+ var version = 'Skywriter ' + skywriter.versionNumber + ' (' +
+ skywriter.versionCodename + ')';
+ request.done(version);
+ }
+};
+
+/**
+ * 'skywriter' command
+ */
+var skywriterCommandSpec = {
+ name: 'skywriter',
+ hidden: true,
+ exec: function(env, args, request) {
+ var index = Math.floor(Math.random() * messages.length);
+ request.done('Skywriter ' + messages[index]);
+ }
+};
+var messages = [
+ 'really wants you to trick it out in some way.',
+ 'is your Web editor.',
+ 'would love to be like Emacs on the Web.',
+ 'is written on the Web platform, so you can tweak it.'
+];
+
+
+var canon = require('pilot/canon');
+
+exports.startup = function(data, reason) {
+ canon.addCommand(helpCommandSpec);
+ canon.addCommand(evalCommandSpec);
+ // canon.addCommand(versionCommandSpec);
+ canon.addCommand(skywriterCommandSpec);
+};
+
+exports.shutdown = function(data, reason) {
+ canon.removeCommand(helpCommandSpec);
+ canon.removeCommand(evalCommandSpec);
+ // canon.removeCommand(versionCommandSpec);
+ canon.removeCommand(skywriterCommandSpec);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/settings/canon', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+
+var historyLengthSetting = {
+ name: "historyLength",
+ description: "How many typed commands do we recall for reference?",
+ type: "number",
+ defaultValue: 50
+};
+
+exports.startup = function(data, reason) {
+ data.env.settings.addSetting(historyLengthSetting);
+};
+
+exports.shutdown = function(data, reason) {
+ data.env.settings.removeSetting(historyLengthSetting);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/plugin_manager', ['require', 'exports', 'module' , 'pilot/promise'], function(require, exports, module) {
+
+var Promise = require("pilot/promise").Promise;
+
+exports.REASONS = {
+ APP_STARTUP: 1,
+ APP_SHUTDOWN: 2,
+ PLUGIN_ENABLE: 3,
+ PLUGIN_DISABLE: 4,
+ PLUGIN_INSTALL: 5,
+ PLUGIN_UNINSTALL: 6,
+ PLUGIN_UPGRADE: 7,
+ PLUGIN_DOWNGRADE: 8
+};
+
+exports.Plugin = function(name) {
+ this.name = name;
+ this.status = this.INSTALLED;
+};
+
+exports.Plugin.prototype = {
+ /**
+ * constants for the state
+ */
+ NEW: 0,
+ INSTALLED: 1,
+ REGISTERED: 2,
+ STARTED: 3,
+ UNREGISTERED: 4,
+ SHUTDOWN: 5,
+
+ install: function(data, reason) {
+ var pr = new Promise();
+ if (this.status > this.NEW) {
+ pr.resolve(this);
+ return pr;
+ }
+ require([this.name], function(pluginModule) {
+ if (pluginModule.install) {
+ pluginModule.install(data, reason);
+ }
+ this.status = this.INSTALLED;
+ pr.resolve(this);
+ }.bind(this));
+ return pr;
+ },
+
+ register: function(data, reason) {
+ var pr = new Promise();
+ if (this.status != this.INSTALLED) {
+ pr.resolve(this);
+ return pr;
+ }
+ require([this.name], function(pluginModule) {
+ if (pluginModule.register) {
+ pluginModule.register(data, reason);
+ }
+ this.status = this.REGISTERED;
+ pr.resolve(this);
+ }.bind(this));
+ return pr;
+ },
+
+ startup: function(data, reason) {
+ reason = reason || exports.REASONS.APP_STARTUP;
+ var pr = new Promise();
+ if (this.status != this.REGISTERED) {
+ pr.resolve(this);
+ return pr;
+ }
+ require([this.name], function(pluginModule) {
+ if (pluginModule.startup) {
+ pluginModule.startup(data, reason);
+ }
+ this.status = this.STARTED;
+ pr.resolve(this);
+ }.bind(this));
+ return pr;
+ },
+
+ shutdown: function(data, reason) {
+ if (this.status != this.STARTED) {
+ return;
+ }
+ pluginModule = require(this.name);
+ if (pluginModule.shutdown) {
+ pluginModule.shutdown(data, reason);
+ }
+ }
+};
+
+exports.PluginCatalog = function() {
+ this.plugins = {};
+};
+
+exports.PluginCatalog.prototype = {
+ registerPlugins: function(pluginList, data, reason) {
+ var registrationPromises = [];
+ pluginList.forEach(function(pluginName) {
+ var plugin = this.plugins[pluginName];
+ if (plugin === undefined) {
+ plugin = new exports.Plugin(pluginName);
+ this.plugins[pluginName] = plugin;
+ registrationPromises.push(plugin.register(data, reason));
+ }
+ }.bind(this));
+ return Promise.group(registrationPromises);
+ },
+
+ startupPlugins: function(data, reason) {
+ var startupPromises = [];
+ for (var pluginName in this.plugins) {
+ var plugin = this.plugins[pluginName];
+ startupPromises.push(plugin.startup(data, reason));
+ }
+ return Promise.group(startupPromises);
+ }
+};
+
+exports.catalog = new exports.PluginCatalog();
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/promise', ['require', 'exports', 'module' , 'pilot/console', 'pilot/stacktrace'], function(require, exports, module) {
+
+var console = require("pilot/console");
+var Trace = require('pilot/stacktrace').Trace;
+
+/**
+ * A promise can be in one of 2 states.
+ * The ERROR and SUCCESS states are terminal, the PENDING state is the only
+ * start state.
+ */
+var ERROR = -1;
+var PENDING = 0;
+var SUCCESS = 1;
+
+/**
+ * We give promises and ID so we can track which are outstanding
+ */
+var _nextId = 0;
+
+/**
+ * Debugging help if 2 things try to complete the same promise.
+ * This can be slow (especially on chrome due to the stack trace unwinding) so
+ * we should leave this turned off in normal use.
+ */
+var _traceCompletion = false;
+
+/**
+ * Outstanding promises. Handy list for debugging only.
+ */
+var _outstanding = [];
+
+/**
+ * Recently resolved promises. Also for debugging only.
+ */
+var _recent = [];
+
+/**
+ * Create an unfulfilled promise
+ */
+Promise = function () {
+ this._status = PENDING;
+ this._value = undefined;
+ this._onSuccessHandlers = [];
+ this._onErrorHandlers = [];
+
+ // Debugging help
+ this._id = _nextId++;
+ //this._createTrace = new Trace(new Error());
+ _outstanding[this._id] = this;
+};
+
+/**
+ * Yeay for RTTI.
+ */
+Promise.prototype.isPromise = true;
+
+/**
+ * Have we either been resolve()ed or reject()ed?
+ */
+Promise.prototype.isComplete = function() {
+ return this._status != PENDING;
+};
+
+/**
+ * Have we resolve()ed?
+ */
+Promise.prototype.isResolved = function() {
+ return this._status == SUCCESS;
+};
+
+/**
+ * Have we reject()ed?
+ */
+Promise.prototype.isRejected = function() {
+ return this._status == ERROR;
+};
+
+/**
+ * Take the specified action of fulfillment of a promise, and (optionally)
+ * a different action on promise rejection.
+ */
+Promise.prototype.then = function(onSuccess, onError) {
+ if (typeof onSuccess === 'function') {
+ if (this._status === SUCCESS) {
+ onSuccess.call(null, this._value);
+ } else if (this._status === PENDING) {
+ this._onSuccessHandlers.push(onSuccess);
+ }
+ }
+
+ if (typeof onError === 'function') {
+ if (this._status === ERROR) {
+ onError.call(null, this._value);
+ } else if (this._status === PENDING) {
+ this._onErrorHandlers.push(onError);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Like then() except that rather than returning <tt>this</tt> we return
+ * a promise which
+ */
+Promise.prototype.chainPromise = function(onSuccess) {
+ var chain = new Promise();
+ chain._chainedFrom = this;
+ this.then(function(data) {
+ try {
+ chain.resolve(onSuccess(data));
+ } catch (ex) {
+ chain.reject(ex);
+ }
+ }, function(ex) {
+ chain.reject(ex);
+ });
+ return chain;
+};
+
+/**
+ * Supply the fulfillment of a promise
+ */
+Promise.prototype.resolve = function(data) {
+ return this._complete(this._onSuccessHandlers, SUCCESS, data, 'resolve');
+};
+
+/**
+ * Renege on a promise
+ */
+Promise.prototype.reject = function(data) {
+ return this._complete(this._onErrorHandlers, ERROR, data, 'reject');
+};
+
+/**
+ * Internal method to be called on resolve() or reject().
+ * @private
+ */
+Promise.prototype._complete = function(list, status, data, name) {
+ // Complain if we've already been completed
+ if (this._status != PENDING) {
+ console.group('Promise already closed');
+ console.error('Attempted ' + name + '() with ', data);
+ console.error('Previous status = ', this._status,
+ ', previous value = ', this._value);
+ console.trace();
+
+ if (this._completeTrace) {
+ console.error('Trace of previous completion:');
+ this._completeTrace.log(5);
+ }
+ console.groupEnd();
+ return this;
+ }
+
+ if (_traceCompletion) {
+ this._completeTrace = new Trace(new Error());
+ }
+
+ this._status = status;
+ this._value = data;
+
+ // Call all the handlers, and then delete them
+ list.forEach(function(handler) {
+ handler.call(null, this._value);
+ }, this);
+ this._onSuccessHandlers.length = 0;
+ this._onErrorHandlers.length = 0;
+
+ // Remove the given {promise} from the _outstanding list, and add it to the
+ // _recent list, pruning more than 20 recent promises from that list.
+ delete _outstanding[this._id];
+ _recent.push(this);
+ while (_recent.length > 20) {
+ _recent.shift();
+ }
+
+ return this;
+};
+
+/**
+ * Takes an array of promises and returns a promise that that is fulfilled once
+ * all the promises in the array are fulfilled
+ * @param group The array of promises
+ * @return the promise that is fulfilled when all the array is fulfilled
+ */
+Promise.group = function(promiseList) {
+ if (!(promiseList instanceof Array)) {
+ promiseList = Array.prototype.slice.call(arguments);
+ }
+
+ // If the original array has nothing in it, return now to avoid waiting
+ if (promiseList.length === 0) {
+ return new Promise().resolve([]);
+ }
+
+ var groupPromise = new Promise();
+ var results = [];
+ var fulfilled = 0;
+
+ var onSuccessFactory = function(index) {
+ return function(data) {
+ results[index] = data;
+ fulfilled++;
+ // If the group has already failed, silently drop extra results
+ if (groupPromise._status !== ERROR) {
+ if (fulfilled === promiseList.length) {
+ groupPromise.resolve(results);
+ }
+ }
+ };
+ };
+
+ promiseList.forEach(function(promise, index) {
+ var onSuccess = onSuccessFactory(index);
+ var onError = groupPromise.reject.bind(groupPromise);
+ promise.then(onSuccess, onError);
+ });
+
+ return groupPromise;
+};
+
+exports.Promise = Promise;
+exports._outstanding = _outstanding;
+exports._recent = _recent;
+
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Mihai Sucan <mihai AT sucan AT gmail ODT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/dom', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+exports.createElement = function(tag, ns) {
+ return document.createElementNS ?
+ document.createElementNS(ns || XHTML_NS, tag) :
+ document.createElement(tag);
+};
+
+exports.setText = function(elem, text) {
+ if (elem.innerText !== undefined) {
+ elem.innerText = text;
+ }
+ if (elem.textContent !== undefined) {
+ elem.textContent = text;
+ }
+};
+
+if (!document.documentElement.classList) {
+ exports.hasCssClass = function(el, name) {
+ var classes = el.className.split(/\s+/g);
+ return classes.indexOf(name) !== -1;
+ };
+
+ /**
+ * Add a CSS class to the list of classes on the given node
+ */
+ exports.addCssClass = function(el, name) {
+ if (!exports.hasCssClass(el, name)) {
+ el.className += " " + name;
+ }
+ };
+
+ /**
+ * Remove a CSS class from the list of classes on the given node
+ */
+ exports.removeCssClass = function(el, name) {
+ var classes = el.className.split(/\s+/g);
+ while (true) {
+ var index = classes.indexOf(name);
+ if (index == -1) {
+ break;
+ }
+ classes.splice(index, 1);
+ }
+ el.className = classes.join(" ");
+ };
+
+ exports.toggleCssClass = function(el, name) {
+ var classes = el.className.split(/\s+/g), add = true;
+ while (true) {
+ var index = classes.indexOf(name);
+ if (index == -1) {
+ break;
+ }
+ add = false;
+ classes.splice(index, 1);
+ }
+ if(add)
+ classes.push(name);
+
+ el.className = classes.join(" ");
+ return add;
+ };
+} else {
+ exports.hasCssClass = function(el, name) {
+ return el.classList.contains(name);
+ };
+
+ exports.addCssClass = function(el, name) {
+ el.classList.add(name);
+ };
+
+ exports.removeCssClass = function(el, name) {
+ el.classList.remove(name);
+ };
+
+ exports.toggleCssClass = function(el, name) {
+ return el.classList.toggle(name);
+ };
+}
+
+/**
+ * Add or remove a CSS class from the list of classes on the given node
+ * depending on the value of <tt>include</tt>
+ */
+exports.setCssClass = function(node, className, include) {
+ if (include) {
+ exports.addCssClass(node, className);
+ } else {
+ exports.removeCssClass(node, className);
+ }
+};
+
+exports.importCssString = function(cssText, doc){
+ doc = doc || document;
+
+ if (doc.createStyleSheet) {
+ var sheet = doc.createStyleSheet();
+ sheet.cssText = cssText;
+ }
+ else {
+ var style = doc.createElementNS ?
+ doc.createElementNS(XHTML_NS, "style") :
+ doc.createElement("style");
+
+ style.appendChild(doc.createTextNode(cssText));
+
+ var head = doc.getElementsByTagName("head")[0] || doc.documentElement;
+ head.appendChild(style);
+ }
+};
+
+exports.getInnerWidth = function(element) {
+ return (parseInt(exports.computedStyle(element, "paddingLeft"))
+ + parseInt(exports.computedStyle(element, "paddingRight")) + element.clientWidth);
+};
+
+exports.getInnerHeight = function(element) {
+ return (parseInt(exports.computedStyle(element, "paddingTop"))
+ + parseInt(exports.computedStyle(element, "paddingBottom")) + element.clientHeight);
+};
+
+if (window.pageYOffset !== undefined) {
+ exports.getPageScrollTop = function() {
+ return window.pageYOffset;
+ };
+
+ exports.getPageScrollLeft = function() {
+ return window.pageXOffset;
+ };
+}
+else {
+ exports.getPageScrollTop = function() {
+ return document.body.scrollTop;
+ };
+
+ exports.getPageScrollLeft = function() {
+ return document.body.scrollLeft;
+ };
+}
+
+if (window.getComputedStyle)
+ exports.computedStyle = function(element, style) {
+ if (style)
+ return (window.getComputedStyle(element, "") || {})[style] || "";
+ return window.getComputedStyle(element, "") || {}
+ };
+else
+ exports.computedStyle = function(element, style) {
+ if (style)
+ return element.currentStyle[style];
+ return element.currentStyle
+ };
+
+exports.scrollbarWidth = function() {
+
+ var inner = exports.createElement("p");
+ inner.style.width = "100%";
+ inner.style.height = "200px";
+
+ var outer = exports.createElement("div");
+ var style = outer.style;
+
+ style.position = "absolute";
+ style.left = "-10000px";
+ style.overflow = "hidden";
+ style.width = "200px";
+ style.height = "150px";
+
+ outer.appendChild(inner);
+
+ var body = document.body || document.documentElement;
+ body.appendChild(outer);
+
+ var noScrollbar = inner.offsetWidth;
+
+ style.overflow = "scroll";
+ var withScrollbar = inner.offsetWidth;
+
+ if (noScrollbar == withScrollbar) {
+ withScrollbar = outer.clientWidth;
+ }
+
+ body.removeChild(outer);
+
+ return noScrollbar-withScrollbar;
+};
+
+/**
+ * Optimized set innerHTML. This is faster than plain innerHTML if the element
+ * already contains a lot of child elements.
+ *
+ * See http://blog.stevenlevithan.com/archives/faster-than-innerhtml for details
+ */
+exports.setInnerHtml = function(el, innerHtml) {
+ var element = el.cloneNode(false);//document.createElement("div");
+ element.innerHTML = innerHtml;
+ el.parentNode.replaceChild(element, el);
+ return element;
+};
+
+exports.setInnerText = function(el, innerText) {
+ if (document.body && "textContent" in document.body)
+ el.textContent = innerText;
+ else
+ el.innerText = innerText;
+
+};
+
+exports.getInnerText = function(el) {
+ if (document.body && "textContent" in document.body)
+ return el.textContent;
+ else
+ return el.innerText || el.textContent || "";
+};
+
+exports.getParentWindow = function(document) {
+ return document.defaultView || document.parentWindow;
+};
+
+exports.getSelectionStart = function(textarea) {
+ // TODO IE
+ var start;
+ try {
+ start = textarea.selectionStart || 0;
+ } catch (e) {
+ start = 0;
+ }
+ return start;
+};
+
+exports.setSelectionStart = function(textarea, start) {
+ // TODO IE
+ return textarea.selectionStart = start;
+};
+
+exports.getSelectionEnd = function(textarea) {
+ // TODO IE
+ var end;
+ try {
+ end = textarea.selectionEnd || 0;
+ } catch (e) {
+ end = 0;
+ }
+ return end;
+};
+
+exports.setSelectionEnd = function(textarea, end) {
+ // TODO IE
+ return textarea.selectionEnd = end;
+};
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/event', ['require', 'exports', 'module' , 'pilot/keys', 'pilot/useragent', 'pilot/dom'], function(require, exports, module) {
+
+var keys = require("pilot/keys");
+var useragent = require("pilot/useragent");
+var dom = require("pilot/dom");
+
+exports.addListener = function(elem, type, callback) {
+ if (elem.addEventListener) {
+ return elem.addEventListener(type, callback, false);
+ }
+ if (elem.attachEvent) {
+ var wrapper = function() {
+ callback(window.event);
+ };
+ callback._wrapper = wrapper;
+ elem.attachEvent("on" + type, wrapper);
+ }
+};
+
+exports.removeListener = function(elem, type, callback) {
+ if (elem.removeEventListener) {
+ return elem.removeEventListener(type, callback, false);
+ }
+ if (elem.detachEvent) {
+ elem.detachEvent("on" + type, callback._wrapper || callback);
+ }
+};
+
+/**
+* Prevents propagation and clobbers the default action of the passed event
+*/
+exports.stopEvent = function(e) {
+ exports.stopPropagation(e);
+ exports.preventDefault(e);
+ return false;
+};
+
+exports.stopPropagation = function(e) {
+ if (e.stopPropagation)
+ e.stopPropagation();
+ else
+ e.cancelBubble = true;
+};
+
+exports.preventDefault = function(e) {
+ if (e.preventDefault)
+ e.preventDefault();
+ else
+ e.returnValue = false;
+};
+
+exports.getDocumentX = function(e) {
+ if (e.clientX) {
+ return e.clientX + dom.getPageScrollLeft();
+ } else {
+ return e.pageX;
+ }
+};
+
+exports.getDocumentY = function(e) {
+ if (e.clientY) {
+ return e.clientY + dom.getPageScrollTop();
+ } else {
+ return e.pageY;
+ }
+};
+
+/**
+ * @return {Number} 0 for left button, 1 for middle button, 2 for right button
+ */
+exports.getButton = function(e) {
+ if (e.type == "dblclick")
+ return 0;
+ else if (e.type == "contextmenu")
+ return 2;
+
+ // DOM Event
+ if (e.preventDefault) {
+ return e.button;
+ }
+ // old IE
+ else {
+ return {1:0, 2:2, 4:1}[e.button];
+ }
+};
+
+if (document.documentElement.setCapture) {
+ exports.capture = function(el, eventHandler, releaseCaptureHandler) {
+ function onMouseMove(e) {
+ eventHandler(e);
+ return exports.stopPropagation(e);
+ }
+
+ function onReleaseCapture(e) {
+ eventHandler && eventHandler(e);
+ releaseCaptureHandler && releaseCaptureHandler();
+
+ exports.removeListener(el, "mousemove", eventHandler);
+ exports.removeListener(el, "mouseup", onReleaseCapture);
+ exports.removeListener(el, "losecapture", onReleaseCapture);
+
+ el.releaseCapture();
+ }
+
+ exports.addListener(el, "mousemove", eventHandler);
+ exports.addListener(el, "mouseup", onReleaseCapture);
+ exports.addListener(el, "losecapture", onReleaseCapture);
+ el.setCapture();
+ };
+}
+else {
+ exports.capture = function(el, eventHandler, releaseCaptureHandler) {
+ function onMouseMove(e) {
+ eventHandler(e);
+ e.stopPropagation();
+ }
+
+ function onMouseUp(e) {
+ eventHandler && eventHandler(e);
+ releaseCaptureHandler && releaseCaptureHandler();
+
+ document.removeEventListener("mousemove", onMouseMove, true);
+ document.removeEventListener("mouseup", onMouseUp, true);
+
+ e.stopPropagation();
+ }
+
+ document.addEventListener("mousemove", onMouseMove, true);
+ document.addEventListener("mouseup", onMouseUp, true);
+ };
+}
+
+exports.addMouseWheelListener = function(el, callback) {
+ var listener = function(e) {
+ if (e.wheelDelta !== undefined) {
+ if (e.wheelDeltaX !== undefined) {
+ e.wheelX = -e.wheelDeltaX / 8;
+ e.wheelY = -e.wheelDeltaY / 8;
+ } else {
+ e.wheelX = 0;
+ e.wheelY = -e.wheelDelta / 8;
+ }
+ }
+ else {
+ if (e.axis && e.axis == e.HORIZONTAL_AXIS) {
+ e.wheelX = (e.detail || 0) * 5;
+ e.wheelY = 0;
+ } else {
+ e.wheelX = 0;
+ e.wheelY = (e.detail || 0) * 5;
+ }
+ }
+ callback(e);
+ };
+ exports.addListener(el, "DOMMouseScroll", listener);
+ exports.addListener(el, "mousewheel", listener);
+};
+
+exports.addMultiMouseDownListener = function(el, button, count, timeout, callback) {
+ var clicks = 0;
+ var startX, startY;
+
+ var listener = function(e) {
+ clicks += 1;
+ if (clicks == 1) {
+ startX = e.clientX;
+ startY = e.clientY;
+
+ setTimeout(function() {
+ clicks = 0;
+ }, timeout || 600);
+ }
+
+ var isButton = exports.getButton(e) == button;
+ if (!isButton || Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5)
+ clicks = 0;
+
+ if (clicks == count) {
+ clicks = 0;
+ callback(e);
+ }
+
+ if (isButton)
+ return exports.preventDefault(e);
+ };
+
+ exports.addListener(el, "mousedown", listener);
+ useragent.isIE && exports.addListener(el, "dblclick", listener);
+};
+
+function normalizeCommandKeys(callback, e, keyCode) {
+ var hashId = 0;
+ if (useragent.isOpera && useragent.isMac) {
+ hashId = 0 | (e.metaKey ? 1 : 0) | (e.altKey ? 2 : 0)
+ | (e.shiftKey ? 4 : 0) | (e.ctrlKey ? 8 : 0);
+ } else {
+ hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0)
+ | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);
+ }
+
+ if (keyCode in keys.MODIFIER_KEYS) {
+ switch (keys.MODIFIER_KEYS[keyCode]) {
+ case "Alt":
+ hashId = 2;
+ break;
+ case "Shift":
+ hashId = 4;
+ break
+ case "Ctrl":
+ hashId = 1;
+ break;
+ default:
+ hashId = 8;
+ break;
+ }
+ keyCode = 0;
+ }
+
+ if (hashId & 8 && (keyCode == 91 || keyCode == 93)) {
+ keyCode = 0;
+ }
+
+ // If there is no hashID and the keyCode is not a function key, then
+ // we don't call the callback as we don't handle a command key here
+ // (it's a normal key/character input).
+ if (hashId == 0 && !(keyCode in keys.FUNCTION_KEYS)) {
+ return false;
+ }
+
+ return callback(e, hashId, keyCode);
+}
+
+exports.addCommandKeyListener = function(el, callback) {
+ var addListener = exports.addListener;
+ if (useragent.isOldGecko) {
+ // Old versions of Gecko aka. Firefox < 4.0 didn't repeat the keydown
+ // event if the user pressed the key for a longer time. Instead, the
+ // keydown event was fired once and later on only the keypress event.
+ // To emulate the 'right' keydown behavior, the keyCode of the initial
+ // keyDown event is stored and in the following keypress events the
+ // stores keyCode is used to emulate a keyDown event.
+ var lastKeyDownKeyCode = null;
+ addListener(el, "keydown", function(e) {
+ lastKeyDownKeyCode = e.keyCode;
+ });
+ addListener(el, "keypress", function(e) {
+ return normalizeCommandKeys(callback, e, lastKeyDownKeyCode);
+ });
+ } else {
+ var lastDown = null;
+
+ addListener(el, "keydown", function(e) {
+ lastDown = e.keyIdentifier || e.keyCode;
+ return normalizeCommandKeys(callback, e, e.keyCode);
+ });
+
+ // repeated keys are fired as keypress and not keydown events
+ if (useragent.isMac && useragent.isOpera) {
+ addListener(el, "keypress", function(e) {
+ var keyId = e.keyIdentifier || e.keyCode;
+ if (lastDown !== keyId) {
+ return normalizeCommandKeys(callback, e, e.keyCode);
+ } else {
+ lastDown = null;
+ }
+ });
+ }
+ }
+};
+
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
+ * Julian Viereck <julian.viereck@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/editor', ['require', 'exports', 'module' , 'pilot/fixoldbrowsers', 'pilot/oop', 'pilot/event', 'pilot/lang', 'pilot/useragent', 'ace/keyboard/textinput', 'ace/mouse_handler', 'ace/keyboard/keybinding', 'ace/edit_session', 'ace/search', 'ace/range', 'pilot/event_emitter'], function(require, exports, module) {
+
+require("pilot/fixoldbrowsers");
+
+var oop = require("pilot/oop");
+var event = require("pilot/event");
+var lang = require("pilot/lang");
+var useragent = require("pilot/useragent");
+var TextInput = require("ace/keyboard/textinput").TextInput;
+var MouseHandler = require("ace/mouse_handler").MouseHandler;
+//var TouchHandler = require("ace/touch_handler").TouchHandler;
+var KeyBinding = require("ace/keyboard/keybinding").KeyBinding;
+var EditSession = require("ace/edit_session").EditSession;
+var Search = require("ace/search").Search;
+var Range = require("ace/range").Range;
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+
+var Editor =function(renderer, session) {
+ var container = renderer.getContainerElement();
+ this.container = container;
+ this.renderer = renderer;
+
+ this.textInput = new TextInput(renderer.getTextAreaContainer(), this);
+ this.keyBinding = new KeyBinding(this);
+
+ // TODO detect touch event support
+ if (useragent.isIPad) {
+ //this.$mouseHandler = new TouchHandler(this);
+ } else {
+ this.$mouseHandler = new MouseHandler(this);
+ }
+
+ this.$blockScrolling = 0;
+ this.$search = new Search().set({
+ wrap: true
+ });
+
+ this.setSession(session || new EditSession(""));
+};
+
+(function(){
+
+ oop.implement(this, EventEmitter);
+
+ this.$forwardEvents = {
+ gutterclick: 1,
+ gutterdblclick: 1
+ };
+
+ this.$originalAddEventListener = this.addEventListener;
+ this.$originalRemoveEventListener = this.removeEventListener;
+
+ this.addEventListener = function(eventName, callback) {
+ if (this.$forwardEvents[eventName]) {
+ return this.renderer.addEventListener(eventName, callback);
+ } else {
+ return this.$originalAddEventListener(eventName, callback);
+ }
+ };
+
+ this.removeEventListener = function(eventName, callback) {
+ if (this.$forwardEvents[eventName]) {
+ return this.renderer.removeEventListener(eventName, callback);
+ } else {
+ return this.$originalRemoveEventListener(eventName, callback);
+ }
+ };
+
+ this.setKeyboardHandler = function(keyboardHandler) {
+ this.keyBinding.setKeyboardHandler(keyboardHandler);
+ };
+
+ this.getKeyboardHandler = function() {
+ return this.keyBinding.getKeyboardHandler();
+ };
+
+ this.setSession = function(session) {
+ if (this.session == session) return;
+
+ if (this.session) {
+ var oldSession = this.session;
+ this.session.removeEventListener("change", this.$onDocumentChange);
+ this.session.removeEventListener("changeMode", this.$onChangeMode);
+ this.session.removeEventListener("tokenizerUpdate", this.$onTokenizerUpdate);
+ this.session.removeEventListener("changeTabSize", this.$onChangeTabSize);
+ this.session.removeEventListener("changeWrapLimit", this.$onChangeWrapLimit);
+ this.session.removeEventListener("changeWrapMode", this.$onChangeWrapMode);
+ this.session.removeEventListener("onChangeFold", this.$onChangeFold);
+ this.session.removeEventListener("changeFrontMarker", this.$onChangeFrontMarker);
+ this.session.removeEventListener("changeBackMarker", this.$onChangeBackMarker);
+ this.session.removeEventListener("changeBreakpoint", this.$onChangeBreakpoint);
+ this.session.removeEventListener("changeAnnotation", this.$onChangeAnnotation);
+ this.session.removeEventListener("changeOverwrite", this.$onCursorChange);
+
+ var selection = this.session.getSelection();
+ selection.removeEventListener("changeCursor", this.$onCursorChange);
+ selection.removeEventListener("changeSelection", this.$onSelectionChange);
+
+ this.session.setScrollTopRow(this.renderer.getScrollTopRow());
+ }
+
+ this.session = session;
+
+ this.$onDocumentChange = this.onDocumentChange.bind(this);
+ session.addEventListener("change", this.$onDocumentChange);
+ this.renderer.setSession(session);
+
+ this.$onChangeMode = this.onChangeMode.bind(this);
+ session.addEventListener("changeMode", this.$onChangeMode);
+
+ this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this);
+ session.addEventListener("tokenizerUpdate", this.$onTokenizerUpdate);
+
+ this.$onChangeTabSize = this.renderer.updateText.bind(this.renderer);
+ session.addEventListener("changeTabSize", this.$onChangeTabSize);
+
+ this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this);
+ session.addEventListener("changeWrapLimit", this.$onChangeWrapLimit);
+
+ this.$onChangeWrapMode = this.onChangeWrapMode.bind(this);
+ session.addEventListener("changeWrapMode", this.$onChangeWrapMode);
+
+ this.$onChangeFold = this.onChangeFold.bind(this);
+ session.addEventListener("changeFold", this.$onChangeFold);
+
+ this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this);
+ this.session.addEventListener("changeFrontMarker", this.$onChangeFrontMarker);
+
+ this.$onChangeBackMarker = this.onChangeBackMarker.bind(this);
+ this.session.addEventListener("changeBackMarker", this.$onChangeBackMarker);
+
+ this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this);
+ this.session.addEventListener("changeBreakpoint", this.$onChangeBreakpoint);
+
+ this.$onChangeAnnotation = this.onChangeAnnotation.bind(this);
+ this.session.addEventListener("changeAnnotation", this.$onChangeAnnotation);
+
+ this.$onCursorChange = this.onCursorChange.bind(this);
+ this.session.addEventListener("changeOverwrite", this.$onCursorChange);
+
+ this.selection = session.getSelection();
+ this.selection.addEventListener("changeCursor", this.$onCursorChange);
+
+ this.$onSelectionChange = this.onSelectionChange.bind(this);
+ this.selection.addEventListener("changeSelection", this.$onSelectionChange);
+
+ this.onChangeMode();
+
+ this.onCursorChange();
+ this.onSelectionChange();
+ this.onChangeFrontMarker();
+ this.onChangeBackMarker();
+ this.onChangeBreakpoint();
+ this.onChangeAnnotation();
+ this.renderer.scrollToRow(session.getScrollTopRow());
+ this.renderer.updateFull();
+
+ this._dispatchEvent("changeSession", {
+ session: session,
+ oldSession: oldSession
+ });
+ };
+
+ this.getSession = function() {
+ return this.session;
+ };
+
+ this.getSelection = function() {
+ return this.selection;
+ };
+
+ this.resize = function() {
+ this.renderer.onResize();
+ };
+
+ this.setTheme = function(theme) {
+ this.renderer.setTheme(theme);
+ };
+
+ this.getTheme = function() {
+ return this.renderer.getTheme();
+ }
+
+ this.setStyle = function(style) {
+ this.renderer.setStyle(style)
+ };
+
+ this.unsetStyle = function(style) {
+ this.renderer.unsetStyle(style)
+ }
+
+ this.$highlightBrackets = function() {
+ if (this.session.$bracketHighlight) {
+ this.session.removeMarker(this.session.$bracketHighlight);
+ this.session.$bracketHighlight = null;
+ }
+
+ if (this.$highlightPending) {
+ return;
+ }
+
+ // perform highlight async to not block the browser during navigation
+ var self = this;
+ this.$highlightPending = true;
+ setTimeout(function() {
+ self.$highlightPending = false;
+
+ var pos = self.session.findMatchingBracket(self.getCursorPosition());
+ if (pos) {
+ var range = new Range(pos.row, pos.column, pos.row, pos.column+1);
+ self.session.$bracketHighlight = self.session.addMarker(range, "ace_bracket");
+ }
+ }, 10);
+ };
+
+ this.focus = function() {
+ // Safari needs the timeout
+ // iOS and Firefox need it called immediately
+ // to be on the save side we do both
+ // except for IE
+ var _self = this;
+ if (!useragent.isIE) {
+ setTimeout(function() {
+ _self.textInput.focus();
+ });
+ }
+ this.textInput.focus();
+ };
+
+ this.blur = function() {
+ this.textInput.blur();
+ };
+
+ this.onFocus = function() {
+ this.renderer.showCursor();
+ this.renderer.visualizeFocus();
+ this._dispatchEvent("focus");
+ };
+
+ this.onBlur = function() {
+ this.renderer.hideCursor();
+ this.renderer.visualizeBlur();
+ this._dispatchEvent("blur");
+ };
+
+ this.onDocumentChange = function(e) {
+ var delta = e.data;
+ var range = delta.range;
+
+ if (range.start.row == range.end.row && delta.action != "insertLines" && delta.action != "removeLines")
+ var lastRow = range.end.row;
+ else
+ lastRow = Infinity;
+ this.renderer.updateLines(range.start.row, lastRow);
+
+ // update cursor because tab characters can influence the cursor position
+ this.renderer.updateCursor();
+ };
+
+ this.onTokenizerUpdate = function(e) {
+ var rows = e.data;
+ this.renderer.updateLines(rows.first, rows.last);
+ };
+
+ this.onCursorChange = function(e) {
+ this.renderer.updateCursor();
+
+ if (!this.$blockScrolling) {
+ this.renderer.scrollCursorIntoView();
+ }
+
+ // move text input over the cursor
+ // this is required for iOS and IME
+ this.renderer.moveTextAreaToCursor(this.textInput.getElement());
+
+ this.$highlightBrackets();
+ this.$updateHighlightActiveLine();
+ };
+
+ this.$updateHighlightActiveLine = function() {
+ var session = this.getSession();
+
+ if (session.$highlightLineMarker) {
+ session.removeMarker(session.$highlightLineMarker);
+ }
+ session.$highlightLineMarker = null;
+
+ if (this.getHighlightActiveLine() && (this.getSelectionStyle() != "line" || !this.selection.isMultiLine())) {
+ var cursor = this.getCursorPosition(),
+ foldLine = this.session.getFoldLine(cursor.row);
+ var range;
+ if (foldLine) {
+ range = new Range(foldLine.start.row, 0, foldLine.end.row + 1, 0);
+ } else {
+ range = new Range(cursor.row, 0, cursor.row+1, 0);
+ }
+ session.$highlightLineMarker = session.addMarker(range, "ace_active_line", "line");
+ }
+ };
+
+ this.onSelectionChange = function(e) {
+ var session = this.getSession();
+
+ if (session.$selectionMarker) {
+ session.removeMarker(session.$selectionMarker);
+ }
+ session.$selectionMarker = null;
+
+ if (!this.selection.isEmpty()) {
+ var range = this.selection.getRange();
+ var style = this.getSelectionStyle();
+ session.$selectionMarker = session.addMarker(range, "ace_selection", style);
+ } else {
+ this.$updateHighlightActiveLine();
+ }
+
+ if (this.$highlightSelectedWord)
+ this.session.getMode().highlightSelection(this);
+ };
+
+ this.onChangeFrontMarker = function() {
+ this.renderer.updateFrontMarkers();
+ };
+
+ this.onChangeBackMarker = function() {
+ this.renderer.updateBackMarkers();
+ };
+
+ this.onChangeBreakpoint = function() {
+ this.renderer.setBreakpoints(this.session.getBreakpoints());
+ };
+
+ this.onChangeAnnotation = function() {
+ this.renderer.setAnnotations(this.session.getAnnotations());
+ };
+
+ this.onChangeMode = function() {
+ this.renderer.updateText()
+ };
+
+ this.onChangeWrapLimit = function() {
+ this.renderer.updateFull();
+ };
+
+ this.onChangeWrapMode = function() {
+ this.renderer.onResize(true);
+ };
+
+ this.onChangeFold = function() {
+ // Update the active line marker as due to folding changes the current
+ // line range on the screen might have changed.
+ this.$updateHighlightActiveLine();
+ // TODO: This might be too much updating. Okay for now.
+ this.renderer.updateFull();
+ };
+
+ this.getCopyText = function() {
+ if (!this.selection.isEmpty()) {
+ return this.session.getTextRange(this.getSelectionRange());
+ }
+ else {
+ return "";
+ }
+ };
+
+ this.onCut = function() {
+ if (this.$readOnly)
+ return;
+
+ if (!this.selection.isEmpty()) {
+ this.session.remove(this.getSelectionRange())
+ this.clearSelection();
+ }
+ };
+
+ this.insert = function(text) {
+ if (this.$readOnly)
+ return;
+
+ var session = this.session;
+ var mode = session.getMode();
+
+ var cursor = this.getCursorPosition();
+
+ if (this.getBehavioursEnabled()) {
+ // Get a transform if the current mode wants one.
+ var transform = mode.transformAction(session.getState(cursor.row), 'insertion', this, session, text);
+ if (transform)
+ text = transform.text;
+ }
+
+ text = text.replace("\t", this.session.getTabString());
+
+ // remove selected text
+ if (!this.selection.isEmpty()) {
+ var cursor = this.session.remove(this.getSelectionRange());
+ this.clearSelection();
+ }
+ else if (this.session.getOverwrite()) {
+ var range = new Range.fromPoints(cursor, cursor);
+ range.end.column += text.length;
+ this.session.remove(range);
+ }
+
+ this.clearSelection();
+
+ var start = cursor.column;
+ var lineState = session.getState(cursor.row);
+ var shouldOutdent = mode.checkOutdent(lineState, session.getLine(cursor.row), text);
+ var line = session.getLine(cursor.row);
+ var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString());
+ var end = session.insert(cursor, text);
+
+ if (transform && transform.selection) {
+ if (transform.selection.length == 2) { // Transform relative to the current column
+ this.selection.setSelectionRange(
+ new Range(cursor.row, start + transform.selection[0],
+ cursor.row, start + transform.selection[1]));
+ } else { // Transform relative to the current row.
+ this.selection.setSelectionRange(
+ new Range(cursor.row + transform.selection[0],
+ transform.selection[1],
+ cursor.row + transform.selection[2],
+ transform.selection[3]));
+ }
+ }
+
+ var lineState = session.getState(cursor.row);
+
+ // TODO disabled multiline auto indent
+ // possibly doing the indent before inserting the text
+ // if (cursor.row !== end.row) {
+ if (session.getDocument().isNewLine(text)) {
+ this.moveCursorTo(cursor.row+1, 0);
+
+ var size = session.getTabSize();
+ var minIndent = Number.MAX_VALUE;
+
+ for (var row = cursor.row + 1; row <= end.row; ++row) {
+ var indent = 0;
+
+ line = session.getLine(row);
+ for (var i = 0; i < line.length; ++i)
+ if (line.charAt(i) == '\t')
+ indent += size;
+ else if (line.charAt(i) == ' ')
+ indent += 1;
+ else
+ break;
+ if (/[^\s]/.test(line))
+ minIndent = Math.min(indent, minIndent);
+ }
+
+ for (var row = cursor.row + 1; row <= end.row; ++row) {
+ var outdent = minIndent;
+
+ line = session.getLine(row);
+ for (var i = 0; i < line.length && outdent > 0; ++i)
+ if (line.charAt(i) == '\t')
+ outdent -= size;
+ else if (line.charAt(i) == ' ')
+ outdent -= 1;
+ session.remove(new Range(row, 0, row, i));
+ }
+ session.indentRows(cursor.row + 1, end.row, lineIndent);
+ } else {
+ if (shouldOutdent) {
+ mode.autoOutdent(lineState, session, cursor.row);
+ }
+ }
+ };
+
+ this.onTextInput = function(text) {
+ this.keyBinding.onTextInput(text);
+ };
+
+ this.onCommandKey = function(e, hashId, keyCode) {
+ this.keyBinding.onCommandKey(e, hashId, keyCode);
+ };
+
+ this.setOverwrite = function(overwrite) {
+ this.session.setOverwrite(overwrite);
+ };
+
+ this.getOverwrite = function() {
+ return this.session.getOverwrite();
+ };
+
+ this.toggleOverwrite = function() {
+ this.session.toggleOverwrite();
+ };
+
+ this.setScrollSpeed = function(speed) {
+ this.$mouseHandler.setScrollSpeed(speed);
+ };
+
+ this.getScrollSpeed = function() {
+ return this.$mouseHandler.getScrollSpeed()
+ };
+
+ this.$selectionStyle = "line";
+ this.setSelectionStyle = function(style) {
+ if (this.$selectionStyle == style) return;
+
+ this.$selectionStyle = style;
+ this.onSelectionChange();
+ this._dispatchEvent("changeSelectionStyle", {data: style});
+ };
+
+ this.getSelectionStyle = function() {
+ return this.$selectionStyle;
+ };
+
+ this.$highlightActiveLine = true;
+ this.setHighlightActiveLine = function(shouldHighlight) {
+ if (this.$highlightActiveLine == shouldHighlight) return;
+
+ this.$highlightActiveLine = shouldHighlight;
+ this.$updateHighlightActiveLine();
+ };
+
+ this.getHighlightActiveLine = function() {
+ return this.$highlightActiveLine;
+ };
+
+ this.$highlightSelectedWord = true;
+ this.setHighlightSelectedWord = function(shouldHighlight) {
+ if (this.$highlightSelectedWord == shouldHighlight)
+ return;
+
+ this.$highlightSelectedWord = shouldHighlight;
+ if (shouldHighlight)
+ this.session.getMode().highlightSelection(this);
+ else
+ this.session.getMode().clearSelectionHighlight(this);
+ };
+
+ this.getHighlightSelectedWord = function() {
+ return this.$highlightSelectedWord;
+ };
+
+ this.setShowInvisibles = function(showInvisibles) {
+ if (this.getShowInvisibles() == showInvisibles)
+ return;
+
+ this.renderer.setShowInvisibles(showInvisibles);
+ };
+
+ this.getShowInvisibles = function() {
+ return this.renderer.getShowInvisibles();
+ };
+
+ this.setShowPrintMargin = function(showPrintMargin) {
+ this.renderer.setShowPrintMargin(showPrintMargin);
+ };
+
+ this.getShowPrintMargin = function() {
+ return this.renderer.getShowPrintMargin();
+ };
+
+ this.setPrintMarginColumn = function(showPrintMargin) {
+ this.renderer.setPrintMarginColumn(showPrintMargin);
+ };
+
+ this.getPrintMarginColumn = function() {
+ return this.renderer.getPrintMarginColumn();
+ };
+
+ this.$readOnly = false;
+ this.setReadOnly = function(readOnly) {
+ this.$readOnly = readOnly;
+ };
+
+ this.getReadOnly = function() {
+ return this.$readOnly;
+ };
+
+ this.$modeBehaviours = false;
+ this.setBehavioursEnabled = function (enabled) {
+ this.$modeBehaviours = enabled;
+ }
+
+ this.getBehavioursEnabled = function () {
+ return this.$modeBehaviours;
+ }
+
+ this.removeRight = function() {
+ if (this.$readOnly)
+ return;
+
+ if (this.selection.isEmpty()) {
+ this.selection.selectRight();
+ }
+ this.session.remove(this.getSelectionRange())
+ this.clearSelection();
+ };
+
+ this.removeLeft = function() {
+ if (this.$readOnly)
+ return;
+
+ if (this.selection.isEmpty())
+ this.selection.selectLeft();
+
+ var range = this.getSelectionRange();
+ if (this.getBehavioursEnabled()) {
+ var session = this.session;
+ var state = session.getState(range.start.row);
+ var new_range = session.getMode().transformAction(state, 'deletion', this, session, range);
+ if (new_range !== false) {
+ range = new_range;
+ }
+ }
+
+ this.session.remove(range);
+ this.clearSelection();
+ };
+
+ this.removeWordRight = function() {
+ if (this.$readOnly)
+ return;
+
+ if (this.selection.isEmpty())
+ this.selection.selectWordRight();
+
+ this.session.remove(this.getSelectionRange());
+ this.clearSelection();
+ };
+
+ this.removeWordLeft = function() {
+ if (this.$readOnly)
+ return;
+
+ if (this.selection.isEmpty())
+ this.selection.selectWordLeft();
+
+ this.session.remove(this.getSelectionRange());
+ this.clearSelection();
+ };
+
+ this.removeToLineStart = function() {
+ if (this.$readOnly)
+ return;
+
+ if (this.selection.isEmpty())
+ this.selection.selectLineStart();
+
+ this.session.remove(this.getSelectionRange());
+ this.clearSelection();
+ };
+
+ this.removeToLineEnd = function() {
+ if (this.$readOnly)
+ return;
+
+ if (this.selection.isEmpty())
+ this.selection.selectLineEnd();
+
+ var range = this.getSelectionRange();
+ if (range.start.column == range.end.column && range.start.row == range.end.row) {
+ range.end.column = 0;
+ range.end.row++;
+ }
+
+ this.session.remove(range);
+ this.clearSelection();
+ };
+
+ this.splitLine = function() {
+ if (this.$readOnly)
+ return;
+
+ if (!this.selection.isEmpty()) {
+ this.session.remove(this.getSelectionRange());
+ this.clearSelection();
+ }
+
+ var cursor = this.getCursorPosition();
+ this.insert("\n");
+ this.moveCursorToPosition(cursor);
+ };
+
+ this.transposeLetters = function() {
+ if (this.$readOnly)
+ return;
+
+ if (!this.selection.isEmpty()) {
+ return;
+ }
+
+ var cursor = this.getCursorPosition();
+ var column = cursor.column;
+ if (column == 0)
+ return;
+
+ var line = this.session.getLine(cursor.row);
+ if (column < line.length) {
+ var swap = line.charAt(column) + line.charAt(column-1);
+ var range = new Range(cursor.row, column-1, cursor.row, column+1)
+ }
+ else {
+ var swap = line.charAt(column-1) + line.charAt(column-2);
+ var range = new Range(cursor.row, column-2, cursor.row, column)
+ }
+ this.session.replace(range, swap);
+ };
+
+ this.indent = function() {
+ if (this.$readOnly)
+ return;
+
+ var session = this.session;
+ var range = this.getSelectionRange();
+
+ if (range.start.row < range.end.row || range.start.column < range.end.column) {
+ var rows = this.$getSelectedRows();
+ session.indentRows(rows.first, rows.last, "\t");
+ } else {
+ var indentString;
+
+ if (this.session.getUseSoftTabs()) {
+ var size = session.getTabSize(),
+ position = this.getCursorPosition(),
+ column = session.documentToScreenColumn(position.row, position.column),
+ count = (size - column % size);
+
+ indentString = lang.stringRepeat(" ", count);
+ } else
+ indentString = "\t";
+ return this.onTextInput(indentString);
+ }
+ };
+
+ this.blockOutdent = function() {
+ if (this.$readOnly)
+ return;
+
+ var selection = this.session.getSelection();
+ this.session.outdentRows(selection.getRange());
+ };
+
+ this.toggleCommentLines = function() {
+ if (this.$readOnly)
+ return;
+
+ var state = this.session.getState(this.getCursorPosition().row);
+ var rows = this.$getSelectedRows()
+ this.session.getMode().toggleCommentLines(state, this.session, rows.first, rows.last);
+ };
+
+ this.removeLines = function() {
+ if (this.$readOnly)
+ return;
+
+ var rows = this.$getSelectedRows();
+ this.session.remove(new Range(rows.first, 0, rows.last+1, 0));
+ this.clearSelection();
+ };
+
+ this.moveLinesDown = function() {
+ if (this.$readOnly)
+ return;
+
+ this.$moveLines(function(firstRow, lastRow) {
+ return this.session.moveLinesDown(firstRow, lastRow);
+ });
+ };
+
+ this.moveLinesUp = function() {
+ if (this.$readOnly)
+ return;
+
+ this.$moveLines(function(firstRow, lastRow) {
+ return this.session.moveLinesUp(firstRow, lastRow);
+ });
+ };
+
+ this.moveText = function(range, toPosition) {
+ if (this.$readOnly)
+ return null;
+
+ return this.session.moveText(range, toPosition);
+ };
+
+ this.copyLinesUp = function() {
+ if (this.$readOnly)
+ return;
+
+ this.$moveLines(function(firstRow, lastRow) {
+ this.session.duplicateLines(firstRow, lastRow);
+ return 0;
+ });
+ };
+
+ this.copyLinesDown = function() {
+ if (this.$readOnly)
+ return;
+
+ this.$moveLines(function(firstRow, lastRow) {
+ return this.session.duplicateLines(firstRow, lastRow);
+ });
+ };
+
+
+ this.$moveLines = function(mover) {
+ var rows = this.$getSelectedRows();
+
+ var linesMoved = mover.call(this, rows.first, rows.last);
+
+ var selection = this.selection;
+ selection.setSelectionAnchor(rows.last+linesMoved+1, 0);
+ selection.$moveSelection(function() {
+ selection.moveCursorTo(rows.first+linesMoved, 0);
+ });
+ };
+
+ this.$getSelectedRows = function() {
+ var range = this.getSelectionRange().collapseRows();
+
+ return {
+ first: range.start.row,
+ last: range.end.row
+ };
+ };
+
+ this.onCompositionStart = function(text) {
+ this.renderer.showComposition(this.getCursorPosition());
+ };
+
+ this.onCompositionUpdate = function(text) {
+ this.renderer.setCompositionText(text);
+ };
+
+ this.onCompositionEnd = function() {
+ this.renderer.hideComposition();
+ };
+
+
+ this.getFirstVisibleRow = function() {
+ return this.renderer.getFirstVisibleRow();
+ };
+
+ this.getLastVisibleRow = function() {
+ return this.renderer.getLastVisibleRow();
+ };
+
+ this.isRowVisible = function(row) {
+ return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow());
+ };
+
+ this.$getVisibleRowCount = function() {
+ return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1;
+ };
+
+ this.$getPageDownRow = function() {
+ return this.renderer.getScrollBottomRow();
+ };
+
+ this.$getPageUpRow = function() {
+ var firstRow = this.renderer.getScrollTopRow();
+ var lastRow = this.renderer.getScrollBottomRow();
+
+ return firstRow - (lastRow - firstRow);
+ };
+
+ this.selectPageDown = function() {
+ var row = this.$getPageDownRow() + Math.floor(this.$getVisibleRowCount() / 2);
+
+ this.scrollPageDown();
+
+ var selection = this.getSelection();
+ var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());
+ var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);
+ selection.selectTo(dest.row, dest.column);
+ };
+
+ this.selectPageUp = function() {
+ var visibleRows = this.renderer.getScrollTopRow() - this.renderer.getScrollBottomRow();
+ var row = this.$getPageUpRow() + Math.round(visibleRows / 2);
+
+ this.scrollPageUp();
+
+ var selection = this.getSelection();
+ var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());
+ var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);
+ selection.selectTo(dest.row, dest.column);
+ };
+
+ this.gotoPageDown = function() {
+ var row = this.$getPageDownRow();
+ var column = this.getCursorPositionScreen().column;
+
+ this.scrollToRow(row);
+ this.getSelection().moveCursorToScreen(row, column);
+ };
+
+ this.gotoPageUp = function() {
+ var row = this.$getPageUpRow();
+ var column = this.getCursorPositionScreen().column;
+
+ this.scrollToRow(row);
+ this.getSelection().moveCursorToScreen(row, column);
+ };
+
+ this.scrollPageDown = function() {
+ this.scrollToRow(this.$getPageDownRow());
+ };
+
+ this.scrollPageUp = function() {
+ this.renderer.scrollToRow(this.$getPageUpRow());
+ };
+
+ this.scrollToRow = function(row) {
+ this.renderer.scrollToRow(row);
+ };
+
+ this.scrollToLine = function(line, center) {
+ this.renderer.scrollToLine(line, center);
+ };
+
+ this.centerSelection = function() {
+ var range = this.getSelectionRange();
+ var line = Math.floor(range.start.row + (range.end.row - range.start.row) / 2);
+ this.renderer.scrollToLine(line, true);
+ };
+
+ this.getCursorPosition = function() {
+ return this.selection.getCursor();
+ };
+
+ this.getCursorPositionScreen = function() {
+ return this.session.documentToScreenPosition(this.getCursorPosition());
+ };
+
+ this.getSelectionRange = function() {
+ return this.selection.getRange();
+ };
+
+
+ this.selectAll = function() {
+ this.$blockScrolling += 1;
+ this.selection.selectAll();
+ this.$blockScrolling -= 1;
+ };
+
+ this.clearSelection = function() {
+ this.selection.clearSelection();
+ };
+
+ this.moveCursorTo = function(row, column) {
+ this.selection.moveCursorTo(row, column);
+ };
+
+ this.moveCursorToPosition = function(pos) {
+ this.selection.moveCursorToPosition(pos);
+ };
+
+
+ this.gotoLine = function(lineNumber, row) {
+ this.selection.clearSelection();
+
+ this.$blockScrolling += 1;
+ this.moveCursorTo(lineNumber-1, row || 0);
+ this.$blockScrolling -= 1;
+
+ if (!this.isRowVisible(this.getCursorPosition().row)) {
+ this.scrollToLine(lineNumber, true);
+ }
+ },
+
+ this.navigateTo = function(row, column) {
+ this.clearSelection();
+ this.moveCursorTo(row, column);
+ };
+
+ this.navigateUp = function(times) {
+ this.selection.clearSelection();
+ times = times || 1;
+ this.selection.moveCursorBy(-times, 0);
+ };
+
+ this.navigateDown = function(times) {
+ this.selection.clearSelection();
+ times = times || 1;
+ this.selection.moveCursorBy(times, 0);
+ };
+
+ this.navigateLeft = function(times) {
+ if (!this.selection.isEmpty()) {
+ var selectionStart = this.getSelectionRange().start;
+ this.moveCursorToPosition(selectionStart);
+ }
+ else {
+ times = times || 1;
+ while (times--) {
+ this.selection.moveCursorLeft();
+ }
+ }
+ this.clearSelection();
+ };
+
+ this.navigateRight = function(times) {
+ if (!this.selection.isEmpty()) {
+ var selectionEnd = this.getSelectionRange().end;
+ this.moveCursorToPosition(selectionEnd);
+ }
+ else {
+ times = times || 1;
+ while (times--) {
+ this.selection.moveCursorRight();
+ }
+ }
+ this.clearSelection();
+ };
+
+ this.navigateLineStart = function() {
+ this.selection.moveCursorLineStart();
+ this.clearSelection();
+ };
+
+ this.navigateLineEnd = function() {
+ this.selection.moveCursorLineEnd();
+ this.clearSelection();
+ };
+
+ this.navigateFileEnd = function() {
+ this.selection.moveCursorFileEnd();
+ this.clearSelection();
+ };
+
+ this.navigateFileStart = function() {
+ this.selection.moveCursorFileStart();
+ this.clearSelection();
+ };
+
+ this.navigateWordRight = function() {
+ this.selection.moveCursorWordRight();
+ this.clearSelection();
+ };
+
+ this.navigateWordLeft = function() {
+ this.selection.moveCursorWordLeft();
+ this.clearSelection();
+ };
+
+ this.replace = function(replacement, options) {
+ if (options)
+ this.$search.set(options);
+
+ var range = this.$search.find(this.session);
+ this.$tryReplace(range, replacement);
+ if (range !== null)
+ this.selection.setSelectionRange(range);
+ },
+
+ this.replaceAll = function(replacement, options) {
+ if (options) {
+ this.$search.set(options);
+ }
+
+ var ranges = this.$search.findAll(this.session);
+ if (!ranges.length)
+ return;
+
+ var selection = this.getSelectionRange();
+ this.clearSelection();
+ this.selection.moveCursorTo(0, 0);
+
+ this.$blockScrolling += 1;
+ for (var i = ranges.length - 1; i >= 0; --i)
+ this.$tryReplace(ranges[i], replacement);
+
+ this.selection.setSelectionRange(selection);
+ this.$blockScrolling -= 1;
+ },
+
+ this.$tryReplace = function(range, replacement) {
+ var input = this.session.getTextRange(range);
+ var replacement = this.$search.replace(input, replacement);
+ if (replacement !== null) {
+ range.end = this.session.replace(range, replacement);
+ return range;
+ } else {
+ return null;
+ }
+ };
+
+ this.getLastSearchOptions = function() {
+ return this.$search.getOptions();
+ };
+
+ this.find = function(needle, options) {
+ this.clearSelection();
+ options = options || {};
+ options.needle = needle;
+ this.$search.set(options);
+ this.$find();
+ },
+
+ this.findNext = function(options) {
+ options = options || {};
+ if (typeof options.backwards == "undefined")
+ options.backwards = false;
+ this.$search.set(options);
+ this.$find();
+ };
+
+ this.findPrevious = function(options) {
+ options = options || {};
+ if (typeof options.backwards == "undefined")
+ options.backwards = true;
+ this.$search.set(options);
+ this.$find();
+ };
+
+ this.$find = function(backwards) {
+ if (!this.selection.isEmpty()) {
+ this.$search.set({needle: this.session.getTextRange(this.getSelectionRange())});
+ }
+
+ if (typeof backwards != "undefined")
+ this.$search.set({backwards: backwards});
+
+ var range = this.$search.find(this.session);
+ if (range) {
+ this.gotoLine(range.end.row+1, range.end.column);
+ this.selection.setSelectionRange(range);
+ }
+ };
+
+ this.undo = function() {
+ this.session.getUndoManager().undo();
+ };
+
+ this.redo = function() {
+ this.session.getUndoManager().redo();
+ };
+
+ this.destroy = function() {
+ this.renderer.destroy();
+ }
+
+}).call(Editor.prototype);
+
+
+exports.Editor = Editor;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Mihai Sucan <mihai DOT sucan AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/keyboard/textinput', ['require', 'exports', 'module' , 'pilot/event', 'pilot/useragent', 'pilot/dom'], function(require, exports, module) {
+
+var event = require("pilot/event");
+var useragent = require("pilot/useragent");
+var dom = require("pilot/dom");
+
+var TextInput = function(parentNode, host) {
+
+ var text = dom.createElement("textarea");
+ text.style.left = "-10000px";
+ parentNode.appendChild(text);
+
+ var PLACEHOLDER = String.fromCharCode(0);
+ sendText();
+
+ var inCompostion = false;
+ var copied = false;
+ var tempStyle = '';
+
+ function sendText(valueToSend) {
+ if (!copied) {
+ var value = valueToSend || text.value;
+ if (value) {
+ if (value.charCodeAt(value.length-1) == PLACEHOLDER.charCodeAt(0)) {
+ value = value.slice(0, -1);
+ if (value)
+ host.onTextInput(value);
+ } else
+ host.onTextInput(value);
+ }
+ }
+ copied = false;
+
+ // Safari doesn't fire copy events if no text is selected
+ text.value = PLACEHOLDER;
+ text.select();
+ }
+
+ var onTextInput = function(e) {
+ if (useragent.isIE && text.value.charCodeAt(0) > 128) return;
+ setTimeout(function() {
+ if (!inCompostion)
+ sendText();
+ }, 0);
+ };
+
+ var onCompositionStart = function(e) {
+ inCompostion = true;
+ if (!useragent.isIE) {
+ sendText();
+ text.value = "";
+ };
+ host.onCompositionStart();
+ if (!useragent.isGecko) setTimeout(onCompositionUpdate, 0);
+ };
+
+ var onCompositionUpdate = function() {
+ if (!inCompostion) return;
+ host.onCompositionUpdate(text.value);
+ };
+
+ var onCompositionEnd = function(e) {
+ inCompostion = false;
+ host.onCompositionEnd();
+ if (useragent.isGecko) {
+ sendText();
+ } else {
+ setTimeout(function () {
+ if (!inCompostion)
+ sendText();
+ }, 0);
+ }
+ };
+
+ var onCopy = function(e) {
+ copied = true;
+ var copyText = host.getCopyText();
+ if(copyText)
+ text.value = copyText;
+ else
+ e.preventDefault();
+ text.select();
+ setTimeout(function () {
+ sendText();
+ }, 0);
+ };
+
+ var onCut = function(e) {
+ copied = true;
+ var copyText = host.getCopyText();
+ if(copyText) {
+ text.value = copyText;
+ host.onCut();
+ } else
+ e.preventDefault();
+ text.select();
+ setTimeout(function () {
+ sendText();
+ }, 0);
+ };
+
+ event.addCommandKeyListener(text, host.onCommandKey.bind(host));
+ event.addListener(text, "keypress", onTextInput);
+ if (useragent.isIE) {
+ var keytable = { 13:1, 27:1 };
+ event.addListener(text, "keyup", function (e) {
+ if (inCompostion && (!text.value || keytable[e.keyCode]))
+ setTimeout(onCompositionEnd, 0);
+ if ((text.value.charCodeAt(0)|0) < 129) {
+ return;
+ };
+ inCompostion ? onCompositionUpdate() : onCompositionStart();
+ });
+ };
+ event.addListener(text, "textInput", onTextInput);
+ event.addListener(text, "paste", function(e) {
+ // Some browsers support the event.clipboardData API. Use this to get
+ // the pasted content which increases speed if pasting a lot of lines.
+ if (e.clipboardData && e.clipboardData.getData) {
+ sendText(e.clipboardData.getData("text/plain"));
+ e.preventDefault();
+ } else
+ // If a browser doesn't support any of the things above, use the regular
+ // method to detect the pasted input.
+ {
+ onTextInput();
+ }
+ });
+ if (!useragent.isIE) {
+ event.addListener(text, "propertychange", onTextInput);
+ };
+
+ if (useragent.isIE) {
+ event.addListener(text, "beforecopy", function(e) {
+ var copyText = host.getCopyText();
+ if(copyText)
+ clipboardData.setData("Text", copyText);
+ else
+ e.preventDefault();
+ });
+ event.addListener(parentNode, "keydown", function(e) {
+ if (e.ctrlKey && e.keyCode == 88) {
+ var copyText = host.getCopyText();
+ if (copyText) {
+ clipboardData.setData("Text", copyText);
+ host.onCut();
+ }
+ event.preventDefault(e)
+ }
+ });
+ }
+ else {
+ event.addListener(text, "copy", onCopy);
+ event.addListener(text, "cut", onCut);
+ }
+
+ event.addListener(text, "compositionstart", onCompositionStart);
+ if (useragent.isGecko) {
+ event.addListener(text, "text", onCompositionUpdate);
+ };
+ if (useragent.isWebKit) {
+ event.addListener(text, "keyup", onCompositionUpdate);
+ };
+ event.addListener(text, "compositionend", onCompositionEnd);
+
+ event.addListener(text, "blur", function() {
+ host.onBlur();
+ });
+
+ event.addListener(text, "focus", function() {
+ host.onFocus();
+ text.select();
+ });
+
+ this.focus = function() {
+ host.onFocus();
+ text.select();
+ text.focus();
+ };
+
+ this.blur = function() {
+ text.blur();
+ };
+
+ this.getElement = function() {
+ return text;
+ };
+
+ this.onContextMenu = function(mousePos, isEmpty){
+ if (mousePos) {
+ if(!tempStyle)
+ tempStyle = text.style.cssText;
+ text.style.cssText = 'position:fixed; z-index:1000;' +
+ 'left:' + (mousePos.x - 2) + 'px; top:' + (mousePos.y - 2) + 'px;'
+
+ }
+ if (isEmpty)
+ text.value='';
+ }
+
+ this.onContextMenuClose = function(){
+ setTimeout(function () {
+ if (tempStyle) {
+ text.style.cssText = tempStyle;
+ tempStyle = '';
+ }
+ sendText();
+ }, 0);
+ }
+};
+
+exports.TextInput = TextInput;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Mihai Sucan <mihai DOT sucan AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/mouse_handler', ['require', 'exports', 'module' , 'pilot/event', 'pilot/dom'], function(require, exports, module) {
+
+var event = require("pilot/event");
+var dom = require("pilot/dom");
+
+var STATE_UNKNOWN = 0;
+var STATE_SELECT = 1;
+var STATE_DRAG = 2;
+
+var DRAG_TIMER = 250; // milliseconds
+var DRAG_OFFSET = 5; // pixels
+
+var MouseHandler = function(editor) {
+ this.editor = editor;
+ event.addListener(editor.container, "mousedown", function(e) {
+ editor.focus();
+ return event.preventDefault(e);
+ });
+ event.addListener(editor.container, "selectstart", function(e) {
+ return event.preventDefault(e);
+ });
+
+ var mouseTarget = editor.renderer.getMouseEventTarget();
+ event.addListener(mouseTarget, "mousedown", this.onMouseDown.bind(this));
+ event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this));
+ event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this));
+ event.addMultiMouseDownListener(mouseTarget, 0, 4, 600, this.onMouseQuadClick.bind(this));
+ event.addMouseWheelListener(mouseTarget, this.onMouseWheel.bind(this));
+};
+
+(function() {
+
+ this.$scrollSpeed = 1;
+ this.setScrollSpeed = function(speed) {
+ this.$scrollSpeed = speed;
+ };
+
+ this.getScrollSpeed = function() {
+ return this.$scrollSpeed;
+ };
+
+ this.$getEventPosition = function(e) {
+ var pageX = event.getDocumentX(e);
+ var pageY = event.getDocumentY(e);
+ var pos = this.editor.renderer.screenToTextCoordinates(pageX, pageY);
+ pos.row = Math.max(0, Math.min(pos.row, this.editor.session.getLength()-1));
+ return pos;
+ };
+
+ this.$distance = function(ax, ay, bx, by) {
+ return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2));
+ };
+
+ this.onMouseDown = function(e) {
+ var pageX = event.getDocumentX(e);
+ var pageY = event.getDocumentY(e);
+ var pos = this.$getEventPosition(e);
+ var editor = this.editor;
+ var self = this;
+ var selectionRange = editor.getSelectionRange();
+ var selectionEmpty = selectionRange.isEmpty();
+ var state = STATE_UNKNOWN;
+ var inSelection = false;
+
+ var button = event.getButton(e);
+ if (button !== 0) {
+ if (selectionEmpty) {
+ editor.moveCursorToPosition(pos);
+ }
+ if(button == 2) {
+ editor.textInput.onContextMenu({x: pageX, y: pageY}, selectionEmpty);
+ event.capture(editor.container, function(){}, editor.textInput.onContextMenuClose);
+ }
+ return;
+ } else {
+ // Select the fold as the user clicks it.
+ var fold = editor.session.getFoldAt(pos.row, pos.column, 1);
+ if (fold) {
+ editor.selection.setSelectionRange(fold.range);
+ return;
+ }
+
+ inSelection = !editor.getReadOnly()
+ && !selectionEmpty
+ && selectionRange.contains(pos.row, pos.column);
+ }
+
+ if (!inSelection) {
+ // Directly pick STATE_SELECT, since the user is not clicking inside
+ // a selection.
+ onStartSelect(pos);
+ }
+
+ var mousePageX, mousePageY;
+ var overwrite = editor.getOverwrite();
+ var mousedownTime = (new Date()).getTime();
+ var dragCursor, dragRange;
+
+ var onMouseSelection = function(e) {
+ mousePageX = event.getDocumentX(e);
+ mousePageY = event.getDocumentY(e);
+ };
+
+ var onMouseSelectionEnd = function() {
+ clearInterval(timerId);
+ if (state == STATE_UNKNOWN)
+ onStartSelect(pos);
+ else if (state == STATE_DRAG)
+ onMouseDragSelectionEnd();
+
+ self.$clickSelection = null;
+ state = STATE_UNKNOWN;
+ };
+
+ var onMouseDragSelectionEnd = function() {
+ dom.removeCssClass(editor.container, "ace_dragging");
+ editor.session.removeMarker(dragSelectionMarker);
+
+ if (!self.$clickSelection) {
+ if (!dragCursor) {
+ editor.moveCursorToPosition(pos);
+ editor.selection.clearSelection(pos.row, pos.column);
+ }
+ }
+
+ if (!dragCursor)
+ return;
+
+ if (dragRange.contains(dragCursor.row, dragCursor.column)) {
+ dragCursor = null;
+ return;
+ }
+
+ editor.clearSelection();
+ var newRange = editor.moveText(dragRange, dragCursor);
+ if (!newRange) {
+ dragCursor = null;
+ return;
+ }
+
+ editor.selection.setSelectionRange(newRange);
+ };
+
+ var onSelectionInterval = function() {
+ if (mousePageX === undefined || mousePageY === undefined)
+ return;
+
+ if (state == STATE_UNKNOWN) {
+ var distance = self.$distance(pageX, pageY, mousePageX, mousePageY);
+ var time = (new Date()).getTime();
+
+
+ if (distance > DRAG_OFFSET) {
+ state = STATE_SELECT;
+ var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
+ cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));
+ onStartSelect(cursor);
+ } else if ((time - mousedownTime) > DRAG_TIMER) {
+ state = STATE_DRAG;
+ dragRange = editor.getSelectionRange();
+ var style = editor.getSelectionStyle();
+ dragSelectionMarker = editor.session.addMarker(dragRange, "ace_selection", style);
+ editor.clearSelection();
+ dom.addCssClass(editor.container, "ace_dragging");
+ }
+
+ }
+
+ if (state == STATE_DRAG)
+ onDragSelectionInterval();
+ else if (state == STATE_SELECT)
+ onUpdateSelectionInterval();
+ };
+
+ function onStartSelect(pos) {
+ if (e.shiftKey)
+ editor.selection.selectToPosition(pos)
+ else {
+ if (!self.$clickSelection) {
+ editor.moveCursorToPosition(pos);
+ editor.selection.clearSelection(pos.row, pos.column);
+ }
+ }
+ state = STATE_SELECT;
+ }
+
+ var onUpdateSelectionInterval = function() {
+ var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
+ cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));
+
+ if (self.$clickSelection) {
+ if (self.$clickSelection.contains(cursor.row, cursor.column)) {
+ editor.selection.setSelectionRange(self.$clickSelection);
+ } else {
+ if (self.$clickSelection.compare(cursor.row, cursor.column) == -1) {
+ var anchor = self.$clickSelection.end;
+ } else {
+ var anchor = self.$clickSelection.start;
+ }
+ editor.selection.setSelectionAnchor(anchor.row, anchor.column);
+ editor.selection.selectToPosition(cursor);
+ }
+ }
+ else {
+ editor.selection.selectToPosition(cursor);
+ }
+
+ editor.renderer.scrollCursorIntoView();
+ };
+
+ var onDragSelectionInterval = function() {
+ dragCursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);
+ dragCursor.row = Math.max(0, Math.min(dragCursor.row,
+ editor.session.getLength() - 1));
+
+ editor.moveCursorToPosition(dragCursor);
+ };
+
+ event.capture(editor.container, onMouseSelection, onMouseSelectionEnd);
+ var timerId = setInterval(onSelectionInterval, 20);
+
+ return event.preventDefault(e);
+ };
+
+ this.onMouseDoubleClick = function(e) {
+ var editor = this.editor;
+ var pos = this.$getEventPosition(e);
+
+ // If the user dclicked on a fold, then expand it.
+ var fold = editor.session.getFoldAt(pos.row, pos.column, 1);
+ if (fold) {
+ editor.session.expandFold(fold);
+ } else {
+ editor.moveCursorToPosition(pos);
+ editor.selection.selectWord();
+ this.$clickSelection = editor.getSelectionRange();
+ }
+ };
+
+ this.onMouseTripleClick = function(e) {
+ var pos = this.$getEventPosition(e);
+ this.editor.moveCursorToPosition(pos);
+ this.editor.selection.selectLine();
+ this.$clickSelection = this.editor.getSelectionRange();
+ };
+
+ this.onMouseQuadClick = function(e) {
+ this.editor.selectAll();
+ this.$clickSelection = this.editor.getSelectionRange();
+ };
+
+ this.onMouseWheel = function(e) {
+ var speed = this.$scrollSpeed * 2;
+
+ this.editor.renderer.scrollBy(e.wheelX * speed, e.wheelY * speed);
+ return event.preventDefault(e);
+ };
+
+
+}).call(MouseHandler.prototype);
+
+exports.MouseHandler = MouseHandler;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Julian Viereck <julian.viereck@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/keyboard/keybinding', ['require', 'exports', 'module' , 'pilot/useragent', 'pilot/keys', 'pilot/event', 'pilot/settings', 'pilot/canon', 'ace/commands/default_commands'], function(require, exports, module) {
+
+var useragent = require("pilot/useragent");
+var keyUtil = require("pilot/keys");
+var event = require("pilot/event");
+var settings = require("pilot/settings").settings;
+var canon = require("pilot/canon");
+require("ace/commands/default_commands");
+
+var KeyBinding = function(editor) {
+ this.$editor = editor;
+ this.$data = { };
+ this.$keyboardHandler = null;
+};
+
+(function() {
+ this.setKeyboardHandler = function(keyboardHandler) {
+ if (this.$keyboardHandler != keyboardHandler) {
+ this.$data = { };
+ this.$keyboardHandler = keyboardHandler;
+ }
+ };
+
+ this.getKeyboardHandler = function() {
+ return this.$keyboardHandler;
+ };
+
+ this.$callKeyboardHandler = function (e, hashId, keyOrText, keyCode) {
+ var env = {editor: this.$editor},
+ toExecute;
+
+ if (this.$keyboardHandler) {
+ toExecute =
+ this.$keyboardHandler.handleKeyboard(this.$data, hashId, keyOrText, keyCode, e);
+ }
+
+ // If there is nothing to execute yet, then use the default keymapping.
+ if (!toExecute || !toExecute.command) {
+ if (hashId != 0 || keyCode != 0) {
+ toExecute = {
+ command: canon.findKeyCommand(env, "editor", hashId, keyOrText)
+ }
+ } else {
+ toExecute = {
+ command: "inserttext",
+ args: {
+ text: keyOrText
+ }
+ }
+ }
+ }
+
+ if (toExecute) {
+ var success = canon.exec(toExecute.command,
+ env, "editor", toExecute.args);
+ if (success) {
+ return event.stopEvent(e);
+ }
+ }
+ };
+
+ this.onCommandKey = function(e, hashId, keyCode) {
+ var keyString = keyUtil.keyCodeToString(keyCode);
+ this.$callKeyboardHandler(e, hashId, keyString, keyCode);
+ };
+
+ this.onTextInput = function(text) {
+ this.$callKeyboardHandler({}, 0, text, 0);
+ }
+
+}).call(KeyBinding.prototype);
+
+exports.KeyBinding = KeyBinding;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Julian Viereck <julian.viereck@gmail.com>
+ * Mihai Sucan <mihai.sucan@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/commands/default_commands', ['require', 'exports', 'module' , 'pilot/lang', 'pilot/canon'], function(require, exports, module) {
+
+var lang = require("pilot/lang");
+var canon = require("pilot/canon");
+
+function bindKey(win, mac) {
+ return {
+ win: win,
+ mac: mac,
+ sender: "editor"
+ };
+}
+
+canon.addCommand({
+ name: "null",
+ exec: function(env, args, request) { }
+});
+
+canon.addCommand({
+ name: "selectall",
+ bindKey: bindKey("Ctrl-A", "Command-A"),
+ exec: function(env, args, request) { env.editor.selectAll(); }
+});
+canon.addCommand({
+ name: "removeline",
+ bindKey: bindKey("Ctrl-D", "Command-D"),
+ exec: function(env, args, request) { env.editor.removeLines(); }
+});
+canon.addCommand({
+ name: "gotoline",
+ bindKey: bindKey("Ctrl-L", "Command-L"),
+ exec: function(env, args, request) {
+ var line = parseInt(prompt("Enter line number:"));
+ if (!isNaN(line)) {
+ env.editor.gotoLine(line);
+ }
+ }
+});
+canon.addCommand({
+ name: "togglecomment",
+ bindKey: bindKey("Ctrl-7", "Command-7"),
+ exec: function(env, args, request) { env.editor.toggleCommentLines(); }
+});
+canon.addCommand({
+ name: "findnext",
+ bindKey: bindKey("Ctrl-K", "Command-G"),
+ exec: function(env, args, request) { env.editor.findNext(); }
+});
+canon.addCommand({
+ name: "findprevious",
+ bindKey: bindKey("Ctrl-Shift-K", "Command-Shift-G"),
+ exec: function(env, args, request) { env.editor.findPrevious(); }
+});
+canon.addCommand({
+ name: "find",
+ bindKey: bindKey("Ctrl-F", "Command-F"),
+ exec: function(env, args, request) {
+ var needle = prompt("Find:");
+ env.editor.find(needle);
+ }
+});
+canon.addCommand({
+ name: "replace",
+ bindKey: bindKey("Ctrl-R", "Command-Option-F"),
+ exec: function(env, args, request) {
+ var needle = prompt("Find:");
+ if (!needle)
+ return;
+ var replacement = prompt("Replacement:");
+ if (!replacement)
+ return;
+ env.editor.replace(replacement, {needle: needle});
+ }
+});
+canon.addCommand({
+ name: "replaceall",
+ bindKey: bindKey("Ctrl-Shift-R", "Command-Shift-Option-F"),
+ exec: function(env, args, request) {
+ var needle = prompt("Find:");
+ if (!needle)
+ return;
+ var replacement = prompt("Replacement:");
+ if (!replacement)
+ return;
+ env.editor.replaceAll(replacement, {needle: needle});
+ }
+});
+canon.addCommand({
+ name: "undo",
+ bindKey: bindKey("Ctrl-Z", "Command-Z"),
+ exec: function(env, args, request) { env.editor.undo(); }
+});
+canon.addCommand({
+ name: "redo",
+ bindKey: bindKey("Ctrl-Shift-Z|Ctrl-Y", "Command-Shift-Z|Command-Y"),
+ exec: function(env, args, request) { env.editor.redo(); }
+});
+canon.addCommand({
+ name: "overwrite",
+ bindKey: bindKey("Insert", "Insert"),
+ exec: function(env, args, request) { env.editor.toggleOverwrite(); }
+});
+canon.addCommand({
+ name: "copylinesup",
+ bindKey: bindKey("Ctrl-Alt-Up", "Command-Option-Up"),
+ exec: function(env, args, request) { env.editor.copyLinesUp(); }
+});
+canon.addCommand({
+ name: "movelinesup",
+ bindKey: bindKey("Alt-Up", "Option-Up"),
+ exec: function(env, args, request) { env.editor.moveLinesUp(); }
+});
+canon.addCommand({
+ name: "selecttostart",
+ bindKey: bindKey("Ctrl-Shift-Home|Alt-Shift-Up", "Command-Shift-Up"),
+ exec: function(env, args, request) { env.editor.getSelection().selectFileStart(); }
+});
+canon.addCommand({
+ name: "gotostart",
+ bindKey: bindKey("Ctrl-Home|Ctrl-Up", "Command-Home|Command-Up"),
+ exec: function(env, args, request) { env.editor.navigateFileStart(); }
+});
+canon.addCommand({
+ name: "selectup",
+ bindKey: bindKey("Shift-Up", "Shift-Up"),
+ exec: function(env, args, request) { env.editor.getSelection().selectUp(); }
+});
+canon.addCommand({
+ name: "golineup",
+ bindKey: bindKey("Up", "Up|Ctrl-P"),
+ exec: function(env, args, request) { env.editor.navigateUp(args.times); }
+});
+canon.addCommand({
+ name: "copylinesdown",
+ bindKey: bindKey("Ctrl-Alt-Down", "Command-Option-Down"),
+ exec: function(env, args, request) { env.editor.copyLinesDown(); }
+});
+canon.addCommand({
+ name: "movelinesdown",
+ bindKey: bindKey("Alt-Down", "Option-Down"),
+ exec: function(env, args, request) { env.editor.moveLinesDown(); }
+});
+canon.addCommand({
+ name: "selecttoend",
+ bindKey: bindKey("Ctrl-Shift-End|Alt-Shift-Down", "Command-Shift-Down"),
+ exec: function(env, args, request) { env.editor.getSelection().selectFileEnd(); }
+});
+canon.addCommand({
+ name: "gotoend",
+ bindKey: bindKey("Ctrl-End|Ctrl-Down", "Command-End|Command-Down"),
+ exec: function(env, args, request) { env.editor.navigateFileEnd(); }
+});
+canon.addCommand({
+ name: "selectdown",
+ bindKey: bindKey("Shift-Down", "Shift-Down"),
+ exec: function(env, args, request) { env.editor.getSelection().selectDown(); }
+});
+canon.addCommand({
+ name: "golinedown",
+ bindKey: bindKey("Down", "Down|Ctrl-N"),
+ exec: function(env, args, request) { env.editor.navigateDown(args.times); }
+});
+canon.addCommand({
+ name: "selectwordleft",
+ bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"),
+ exec: function(env, args, request) { env.editor.getSelection().selectWordLeft(); }
+});
+canon.addCommand({
+ name: "gotowordleft",
+ bindKey: bindKey("Ctrl-Left", "Option-Left"),
+ exec: function(env, args, request) { env.editor.navigateWordLeft(); }
+});
+canon.addCommand({
+ name: "selecttolinestart",
+ bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left"),
+ exec: function(env, args, request) { env.editor.getSelection().selectLineStart(); }
+});
+canon.addCommand({
+ name: "gotolinestart",
+ bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"),
+ exec: function(env, args, request) { env.editor.navigateLineStart(); }
+});
+canon.addCommand({
+ name: "selectleft",
+ bindKey: bindKey("Shift-Left", "Shift-Left"),
+ exec: function(env, args, request) { env.editor.getSelection().selectLeft(); }
+});
+canon.addCommand({
+ name: "gotoleft",
+ bindKey: bindKey("Left", "Left|Ctrl-B"),
+ exec: function(env, args, request) { env.editor.navigateLeft(args.times); }
+});
+canon.addCommand({
+ name: "selectwordright",
+ bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"),
+ exec: function(env, args, request) { env.editor.getSelection().selectWordRight(); }
+});
+canon.addCommand({
+ name: "gotowordright",
+ bindKey: bindKey("Ctrl-Right", "Option-Right"),
+ exec: function(env, args, request) { env.editor.navigateWordRight(); }
+});
+canon.addCommand({
+ name: "selecttolineend",
+ bindKey: bindKey("Alt-Shift-Right", "Command-Shift-Right"),
+ exec: function(env, args, request) { env.editor.getSelection().selectLineEnd(); }
+});
+canon.addCommand({
+ name: "gotolineend",
+ bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"),
+ exec: function(env, args, request) { env.editor.navigateLineEnd(); }
+});
+canon.addCommand({
+ name: "selectright",
+ bindKey: bindKey("Shift-Right", "Shift-Right"),
+ exec: function(env, args, request) { env.editor.getSelection().selectRight(); }
+});
+canon.addCommand({
+ name: "gotoright",
+ bindKey: bindKey("Right", "Right|Ctrl-F"),
+ exec: function(env, args, request) { env.editor.navigateRight(args.times); }
+});
+canon.addCommand({
+ name: "selectpagedown",
+ bindKey: bindKey("Shift-PageDown", "Shift-PageDown"),
+ exec: function(env, args, request) { env.editor.selectPageDown(); }
+});
+canon.addCommand({
+ name: "pagedown",
+ bindKey: bindKey(null, "PageDown"),
+ exec: function(env, args, request) { env.editor.scrollPageDown(); }
+});
+canon.addCommand({
+ name: "gotopagedown",
+ bindKey: bindKey("PageDown", "Option-PageDown|Ctrl-V"),
+ exec: function(env, args, request) { env.editor.gotoPageDown(); }
+});
+canon.addCommand({
+ name: "selectpageup",
+ bindKey: bindKey("Shift-PageUp", "Shift-PageUp"),
+ exec: function(env, args, request) { env.editor.selectPageUp(); }
+});
+canon.addCommand({
+ name: "pageup",
+ bindKey: bindKey(null, "PageUp"),
+ exec: function(env, args, request) { env.editor.scrollPageUp(); }
+});
+canon.addCommand({
+ name: "gotopageup",
+ bindKey: bindKey("PageUp", "Option-PageUp"),
+ exec: function(env, args, request) { env.editor.gotoPageUp(); }
+});
+canon.addCommand({
+ name: "selectlinestart",
+ bindKey: bindKey("Shift-Home", "Shift-Home"),
+ exec: function(env, args, request) { env.editor.getSelection().selectLineStart(); }
+});
+canon.addCommand({
+ name: "selectlineend",
+ bindKey: bindKey("Shift-End", "Shift-End"),
+ exec: function(env, args, request) { env.editor.getSelection().selectLineEnd(); }
+});
+canon.addCommand({
+ name: "del",
+ bindKey: bindKey("Delete", "Delete|Ctrl-D"),
+ exec: function(env, args, request) { env.editor.removeRight(); }
+});
+canon.addCommand({
+ name: "backspace",
+ bindKey: bindKey(
+ "Ctrl-Backspace|Command-Backspace|Option-Backspace|Shift-Backspace|Backspace",
+ "Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H"
+ ),
+ exec: function(env, args, request) { env.editor.removeLeft(); }
+});
+canon.addCommand({
+ name: "removetolinestart",
+ bindKey: bindKey(null, "Option-Backspace"),
+ exec: function(env, args, request) { env.editor.removeToLineStart(); }
+});
+canon.addCommand({
+ name: "removetolineend",
+ bindKey: bindKey(null, "Ctrl-K"),
+ exec: function(env, args, request) { env.editor.removeToLineEnd(); }
+});
+canon.addCommand({
+ name: "removewordleft",
+ bindKey: bindKey(null, "Alt-Backspace|Ctrl-Alt-Backspace"),
+ exec: function(env, args, request) { env.editor.removeWordLeft(); }
+});
+canon.addCommand({
+ name: "removewordright",
+ bindKey: bindKey(null, "Alt-Delete"),
+ exec: function(env, args, request) { env.editor.removeWordRight(); }
+});
+canon.addCommand({
+ name: "outdent",
+ bindKey: bindKey("Shift-Tab", "Shift-Tab"),
+ exec: function(env, args, request) { env.editor.blockOutdent(); }
+});
+canon.addCommand({
+ name: "indent",
+ bindKey: bindKey("Tab", "Tab"),
+ exec: function(env, args, request) { env.editor.indent(); }
+});
+canon.addCommand({
+ name: "inserttext",
+ exec: function(env, args, request) {
+ env.editor.insert(lang.stringRepeat(args.text || "", args.times || 1));
+ }
+});
+canon.addCommand({
+ name: "centerselection",
+ bindKey: bindKey(null, "Ctrl-L"),
+ exec: function(env, args, request) { env.editor.centerSelection(); }
+});
+canon.addCommand({
+ name: "splitline",
+ bindKey: bindKey(null, "Ctrl-O"),
+ exec: function(env, args, request) { env.editor.splitLine(); }
+});
+canon.addCommand({
+ name: "transposeletters",
+ bindKey: bindKey("Ctrl-T", "Ctrl-T"),
+ exec: function(env, args, request) { env.editor.transposeLetters(); }
+});
+
+});/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Mihai Sucan <mihai DOT sucan AT gmail DOT com>
+ * Julian Viereck <julian DOT viereck AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/edit_session', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/lang', 'pilot/event_emitter', 'ace/selection', 'ace/mode/text', 'ace/range', 'ace/document', 'ace/background_tokenizer', 'ace/edit_session/folding'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var lang = require("pilot/lang");
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+var Selection = require("ace/selection").Selection;
+var TextMode = require("ace/mode/text").Mode;
+var Range = require("ace/range").Range;
+var Document = require("ace/document").Document;
+var BackgroundTokenizer = require("ace/background_tokenizer").BackgroundTokenizer;
+
+var EditSession = function(text, mode) {
+ this.$modified = true;
+ this.$breakpoints = [];
+ this.$frontMarkers = {};
+ this.$backMarkers = {};
+ this.$markerId = 1;
+ this.$rowCache = [];
+ this.$rowCacheSize = 1000;
+ this.$wrapData = [];
+ this.$foldData = [];
+ this.$foldData.toString = function() {
+ var str = "";
+ this.forEach(function(foldLine) {
+ str += "\n" + foldLine.toString();
+ });
+ return str;
+ }
+ this.$docChangeCounter = 0;
+
+ if (text instanceof Document) {
+ this.setDocument(text);
+ } else {
+ this.setDocument(new Document(text));
+ }
+
+ this.selection = new Selection(this);
+ if (mode)
+ this.setMode(mode);
+ else
+ this.setMode(new TextMode());
+};
+
+
+(function() {
+
+ oop.implement(this, EventEmitter);
+
+ this.setDocument = function(doc) {
+ if (this.doc)
+ throw new Error("Document is already set");
+
+ this.doc = doc;
+ doc.on("change", this.onChange.bind(this));
+ doc.on("changeStart", this.onChangeStart.bind(this));
+ doc.on("changeEnd", this.onChangeEnd.bind(this));
+ this.on("changeFold", this.onChangeFold.bind(this));
+ };
+
+ this.getDocument = function() {
+ return this.doc;
+ };
+
+ this.onChangeStart = function() {
+ this.$docChangeCounter ++;
+ };
+
+ this.$resetRowCache = function(row) {
+ if (row == 0) {
+ this.$rowCache = [];
+ return;
+ }
+ var rowCache = this.$rowCache;
+ for (var i = 0; i < rowCache.length; i++) {
+ if (rowCache[i].docRow >= row) {
+ rowCache.splice(i, rowCache.length);
+ return;
+ }
+ }
+ }
+
+ this.onChangeEnd = function() {
+ this.$docChangeCounter --;
+ if (this.$docChangeCounter == 0
+ && !this.$fromUndo && this.$undoManager)
+ {
+ if (this.$deltasFold.length) {
+ this.$deltas.push({
+ group: "fold",
+ deltas: this.$deltasFold
+ });
+ this.$deltasFold = [];
+ }
+ if (this.$deltasDoc) {
+ this.$deltas.push({
+ group: "doc",
+ deltas: this.$deltasDoc
+ });
+ this.$deltasDoc = [];
+ }
+ this.$informUndoManager.schedule();
+ }
+ };
+
+ this.onChangeFold = function(e) {
+ var fold = e.data;
+ this.$resetRowCache(fold.start.row);
+ };
+
+ this.onChange = function(e) {
+ var delta = e.data;
+ this.$modified = true;
+
+ this.$resetRowCache(delta.range.start.row);
+
+ var removedFolds = this.$updateInternalDataOnChange(e);
+ if (!this.$fromUndo && this.$undoManager && !delta.ignore) {
+ this.$deltasDoc.push(delta);
+ if (removedFolds && removedFolds.length != 0) {
+ this.$deltasFold.push({
+ action: "removeFolds",
+ folds: removedFolds
+ });
+ }
+ }
+
+ this.bgTokenizer.start(delta.range.start.row);
+ this._dispatchEvent("change", e);
+ };
+
+ this.setValue = function(text) {
+ this.doc.setValue(text);
+ this.$resetRowCache(0);
+ this.$deltas = [];
+ this.$deltasDoc = [];
+ this.$deltasFold = [];
+ this.getUndoManager().reset();
+ };
+
+ this.getValue =
+ this.toString = function() {
+ return this.doc.getValue();
+ };
+
+ this.getSelection = function() {
+ return this.selection;
+ };
+
+ this.getState = function(row) {
+ return this.bgTokenizer.getState(row);
+ };
+
+ this.getTokens = function(firstRow, lastRow) {
+ return this.bgTokenizer.getTokens(firstRow, lastRow);
+ };
+
+ this.setUndoManager = function(undoManager) {
+ this.$undoManager = undoManager;
+ this.$resetRowCache(0);
+ this.$deltas = [];
+ this.$deltasDoc = [];
+ this.$deltasFold = [];
+
+ if (this.$informUndoManager) {
+ this.$informUndoManager.cancel();
+ }
+
+ if (undoManager) {
+ var self = this;
+ this.$syncInformUndoManager = function() {
+ self.$informUndoManager.cancel();
+ if (self.$deltas.length > 0)
+ undoManager.execute({
+ action : "aceupdate",
+ args : [self.$deltas, self]
+ });
+ self.$deltas = [];
+ }
+ this.$informUndoManager =
+ lang.deferredCall(this.$syncInformUndoManager);
+ }
+ };
+
+ this.$defaultUndoManager = {
+ undo: function() {},
+ redo: function() {},
+ reset: function() {}
+ };
+
+ this.getUndoManager = function() {
+ return this.$undoManager || this.$defaultUndoManager;
+ },
+
+ this.getTabString = function() {
+ if (this.getUseSoftTabs()) {
+ return lang.stringRepeat(" ", this.getTabSize());
+ } else {
+ return "\t";
+ }
+ };
+
+ this.$useSoftTabs = true;
+ this.setUseSoftTabs = function(useSoftTabs) {
+ if (this.$useSoftTabs === useSoftTabs) return;
+
+ this.$useSoftTabs = useSoftTabs;
+ };
+
+ this.getUseSoftTabs = function() {
+ return this.$useSoftTabs;
+ };
+
+ this.$tabSize = 4;
+ this.setTabSize = function(tabSize) {
+ if (isNaN(tabSize) || this.$tabSize === tabSize) return;
+
+ this.$modified = true;
+ this.$tabSize = tabSize;
+ this._dispatchEvent("changeTabSize");
+ };
+
+ this.getTabSize = function() {
+ return this.$tabSize;
+ };
+
+ this.isTabStop = function(position) {
+ return this.$useSoftTabs && (position.column % this.$tabSize == 0);
+ };
+
+ this.$overwrite = false;
+ this.setOverwrite = function(overwrite) {
+ if (this.$overwrite == overwrite) return;
+
+ this.$overwrite = overwrite;
+ this._dispatchEvent("changeOverwrite");
+ };
+
+ this.getOverwrite = function() {
+ return this.$overwrite;
+ };
+
+ this.toggleOverwrite = function() {
+ this.setOverwrite(!this.$overwrite);
+ };
+
+ this.getBreakpoints = function() {
+ return this.$breakpoints;
+ };
+
+ this.setBreakpoints = function(rows) {
+ this.$breakpoints = [];
+ for (var i=0; i<rows.length; i++) {
+ this.$breakpoints[rows[i]] = true;
+ }
+ this._dispatchEvent("changeBreakpoint", {});
+ };
+
+ this.clearBreakpoints = function() {
+ this.$breakpoints = [];
+ this._dispatchEvent("changeBreakpoint", {});
+ };
+
+ this.setBreakpoint = function(row) {
+ this.$breakpoints[row] = true;
+ this._dispatchEvent("changeBreakpoint", {});
+ };
+
+ this.clearBreakpoint = function(row) {
+ delete this.$breakpoints[row];
+ this._dispatchEvent("changeBreakpoint", {});
+ };
+
+ this.getBreakpoints = function() {
+ return this.$breakpoints;
+ };
+
+ this.addMarker = function(range, clazz, type, inFront) {
+ var id = this.$markerId++;
+
+ var marker = {
+ range : range,
+ type : type || "line",
+ renderer: typeof type == "function" ? type : null,
+ clazz : clazz,
+ inFront: !!inFront
+ }
+
+ if (inFront) {
+ this.$frontMarkers[id] = marker;
+ this._dispatchEvent("changeFrontMarker")
+ } else {
+ this.$backMarkers[id] = marker;
+ this._dispatchEvent("changeBackMarker")
+ }
+
+ return id;
+ };
+
+ this.removeMarker = function(markerId) {
+ var marker = this.$frontMarkers[markerId] || this.$backMarkers[markerId];
+ if (!marker)
+ return;
+
+ var markers = marker.inFront ? this.$frontMarkers : this.$backMarkers;
+ if (marker) {
+ delete (markers[markerId]);
+ this._dispatchEvent(marker.inFront ? "changeFrontMarker" : "changeBackMarker");
+ }
+ };
+
+ this.getMarkers = function(inFront) {
+ return inFront ? this.$frontMarkers : this.$backMarkers;
+ };
+
+ /**
+ * Error:
+ * {
+ * row: 12,
+ * column: 2, //can be undefined
+ * text: "Missing argument",
+ * type: "error" // or "warning" or "info"
+ * }
+ */
+ this.setAnnotations = function(annotations) {
+ this.$annotations = {};
+ for (var i=0; i<annotations.length; i++) {
+ var annotation = annotations[i];
+ var row = annotation.row;
+ if (this.$annotations[row])
+ this.$annotations[row].push(annotation);
+ else
+ this.$annotations[row] = [annotation];
+ }
+ this._dispatchEvent("changeAnnotation", {});
+ };
+
+ this.getAnnotations = function() {
+ return this.$annotations;
+ };
+
+ this.clearAnnotations = function() {
+ this.$annotations = {};
+ this._dispatchEvent("changeAnnotation", {});
+ };
+
+ this.$detectNewLine = function(text) {
+ var match = text.match(/^.*?(\r?\n)/m);
+ if (match) {
+ this.$autoNewLine = match[1];
+ } else {
+ this.$autoNewLine = "\n";
+ }
+ };
+
+ this.tokenRe = /^[\w\d]+/g;
+ this.nonTokenRe = /^(?:[^\w\d]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF])+/g;
+
+ this.getWordRange = function(row, column) {
+ var line = this.getLine(row);
+
+ var inToken = false;
+ if (column > 0) {
+ inToken = !!line.charAt(column - 1).match(this.tokenRe);
+ }
+
+ if (!inToken) {
+ inToken = !!line.charAt(column).match(this.tokenRe);
+ }
+
+ var re = inToken ? this.tokenRe : this.nonTokenRe;
+
+ var start = column;
+ if (start > 0) {
+ do {
+ start--;
+ }
+ while (start >= 0 && line.charAt(start).match(re));
+ start++;
+ }
+
+ var end = column;
+ while (end < line.length && line.charAt(end).match(re)) {
+ end++;
+ }
+
+ return new Range(row, start, row, end);
+ };
+
+ this.setNewLineMode = function(newLineMode) {
+ this.doc.setNewLineMode(newLineMode);
+ };
+
+ this.getNewLineMode = function() {
+ return this.doc.getNewLineMode();
+ };
+
+ this.$useWorker = true;
+ this.setUseWorker = function(useWorker) {
+ if (this.$useWorker == useWorker)
+ return;
+
+ this.$useWorker = useWorker;
+
+ this.$stopWorker();
+ if (useWorker)
+ this.$startWorker();
+ };
+
+ this.getUseWorker = function() {
+ return this.$useWorker;
+ };
+
+ this.onReloadTokenizer = function(e) {
+ var rows = e.data;
+ this.bgTokenizer.start(rows.first);
+ this._dispatchEvent("tokenizerUpdate", e);
+ };
+
+ this.$mode = null;
+ this.setMode = function(mode) {
+ if (this.$mode === mode) return;
+ this.$mode = mode;
+
+ this.$stopWorker();
+
+ if (this.$useWorker)
+ this.$startWorker();
+
+ var tokenizer = mode.getTokenizer();
+
+ if(tokenizer.addEventListener !== undefined) {
+ var onReloadTokenizer = this.onReloadTokenizer.bind(this);
+ tokenizer.addEventListener("update", onReloadTokenizer);
+ }
+
+ if (!this.bgTokenizer) {
+ this.bgTokenizer = new BackgroundTokenizer(tokenizer);
+ var _self = this;
+ this.bgTokenizer.addEventListener("update", function(e) {
+ _self._dispatchEvent("tokenizerUpdate", e);
+ });
+ } else {
+ this.bgTokenizer.setTokenizer(tokenizer);
+ }
+
+ this.bgTokenizer.setDocument(this.getDocument());
+ this.bgTokenizer.start(0);
+
+ this._dispatchEvent("changeMode");
+ };
+
+ this.$stopWorker = function() {
+ if (this.$worker)
+ this.$worker.terminate();
+
+ this.$worker = null;
+ };
+
+ this.$startWorker = function() {
+ if (typeof Worker !== "undefined" && !require.noWorker) {
+ try {
+ this.$worker = this.$mode.createWorker(this);
+ } catch (e) {
+ console.log("Could not load worker");
+ console.log(e);
+ this.$worker = null;
+ }
+ }
+ else
+ this.$worker = null;
+ };
+
+ this.getMode = function() {
+ return this.$mode;
+ };
+
+ this.$scrollTop = 0;
+ this.setScrollTopRow = function(scrollTopRow) {
+ if (this.$scrollTop === scrollTopRow) return;
+
+ this.$scrollTop = scrollTopRow;
+ this._dispatchEvent("changeScrollTop");
+ };
+
+ this.getScrollTopRow = function() {
+ return this.$scrollTop;
+ };
+
+ this.getWidth = function() {
+ this.$computeWidth();
+ return this.width;
+ };
+
+ this.getScreenWidth = function() {
+ this.$computeWidth();
+ return this.screenWidth;
+ };
+
+ this.$computeWidth = function(force) {
+ if (this.$modified || force) {
+ this.$modified = false;
+
+ var lines = this.doc.getAllLines();
+ var longestLine = 0;
+ var longestScreenLine = 0;
+
+ for ( var i = 0; i < lines.length; i++) {
+ var foldLine = this.getFoldLine(i),
+ line, len;
+
+ line = lines[i];
+ if (foldLine) {
+ var end = foldLine.range.end;
+ line = this.getFoldDisplayLine(foldLine);
+ // Continue after the foldLine.end.row. All the lines in
+ // between are folded.
+ i = end.row;
+ }
+ len = line.length;
+ longestLine = Math.max(longestLine, len);
+ if (!this.$useWrapMode) {
+ longestScreenLine = Math.max(
+ longestScreenLine,
+ this.$getStringScreenWidth(line)[0]
+ );
+ }
+ }
+ this.width = longestLine;
+
+ if (this.$useWrapMode) {
+ this.screenWidth = this.$wrapLimit;
+ } else {
+ this.screenWidth = longestScreenLine;
+ }
+ }
+ };
+
+ /**
+ * Get a verbatim copy of the given line as it is in the document
+ */
+ this.getLine = function(row) {
+ return this.doc.getLine(row);
+ };
+
+ this.getLines = function(firstRow, lastRow) {
+ return this.doc.getLines(firstRow, lastRow);
+ };
+
+ this.getLength = function() {
+ return this.doc.getLength();
+ };
+
+ this.getTextRange = function(range) {
+ return this.doc.getTextRange(range);
+ };
+
+ this.findMatchingBracket = function(position) {
+ if (position.column == 0) return null;
+
+ var charBeforeCursor = this.getLine(position.row).charAt(position.column-1);
+ if (charBeforeCursor == "") return null;
+
+ var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
+ if (!match) {
+ return null;
+ }
+
+ if (match[1]) {
+ return this.$findClosingBracket(match[1], position);
+ } else {
+ return this.$findOpeningBracket(match[2], position);
+ }
+ };
+
+ this.$brackets = {
+ ")": "(",
+ "(": ")",
+ "]": "[",
+ "[": "]",
+ "{": "}",
+ "}": "{"
+ };
+
+ this.$findOpeningBracket = function(bracket, position) {
+ var openBracket = this.$brackets[bracket];
+
+ var column = position.column - 2;
+ var row = position.row;
+ var depth = 1;
+
+ var line = this.getLine(row);
+
+ while (true) {
+ while(column >= 0) {
+ var ch = line.charAt(column);
+ if (ch == openBracket) {
+ depth -= 1;
+ if (depth == 0) {
+ return {row: row, column: column};
+ }
+ }
+ else if (ch == bracket) {
+ depth +=1;
+ }
+ column -= 1;
+ }
+ row -=1;
+ if (row < 0) break;
+
+ var line = this.getLine(row);
+ var column = line.length-1;
+ }
+ return null;
+ };
+
+ this.$findClosingBracket = function(bracket, position) {
+ var closingBracket = this.$brackets[bracket];
+
+ var column = position.column;
+ var row = position.row;
+ var depth = 1;
+
+ var line = this.getLine(row);
+ var lineCount = this.getLength();
+
+ while (true) {
+ while(column < line.length) {
+ var ch = line.charAt(column);
+ if (ch == closingBracket) {
+ depth -= 1;
+ if (depth == 0) {
+ return {row: row, column: column};
+ }
+ }
+ else if (ch == bracket) {
+ depth +=1;
+ }
+ column += 1;
+ }
+ row +=1;
+ if (row >= lineCount) break;
+
+ var line = this.getLine(row);
+ var column = 0;
+ }
+ return null;
+ };
+
+ this.insert = function(position, text) {
+ return this.doc.insert(position, text);
+ };
+
+ this.remove = function(range) {
+ return this.doc.remove(range);
+ };
+
+ this.undoChanges = function(deltas, dontSelect) {
+ if (!deltas.length)
+ return;
+
+ this.$fromUndo = true;
+ var lastUndoRange = null;
+ for (var i = deltas.length - 1; i != -1; i--) {
+ delta = deltas[i];
+ if (delta.group == "doc") {
+ this.doc.revertDeltas(delta.deltas);
+ lastUndoRange =
+ this.$getUndoSelection(delta.deltas, true, lastUndoRange);
+ } else {
+ delta.deltas.forEach(function(foldDelta) {
+ this.addFolds(foldDelta.folds);
+ }, this);
+ }
+ }
+ this.$fromUndo = false;
+ lastUndoRange &&
+ !dontSelect &&
+ this.selection.setSelectionRange(lastUndoRange);
+ return lastUndoRange;
+ },
+
+ this.redoChanges = function(deltas, dontSelect) {
+ if (!deltas.length)
+ return;
+
+ this.$fromUndo = true;
+ var lastUndoRange = null;
+ for (var i = 0; i < deltas.length; i++) {
+ delta = deltas[i];
+ if (delta.group == "doc") {
+ this.doc.applyDeltas(delta.deltas);
+ lastUndoRange =
+ this.$getUndoSelection(delta.deltas, false, lastUndoRange);
+ }
+ }
+ this.$fromUndo = false;
+ lastUndoRange &&
+ !dontSelect &&
+ this.selection.setSelectionRange(lastUndoRange);
+ return lastUndoRange;
+ },
+
+ this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) {
+ function isInsert(delta) {
+ var insert =
+ delta.action == "insertText" || delta.action == "insertLines";
+ return isUndo ? !insert : insert;
+ }
+
+ var delta = deltas[0];
+ var range, point;
+ var lastDeltaIsInsert = false;
+ if (isInsert(delta)) {
+ range = delta.range.clone();
+ lastDeltaIsInsert = true;
+ } else {
+ range = Range.fromPoints(delta.range.start, delta.range.start);
+ lastDeltaIsInsert = false;
+ }
+
+ for (var i = 1; i < deltas.length; i++) {
+ delta = deltas[i];
+ if (isInsert(delta)) {
+ point = delta.range.start;
+ if (range.compare(point.row, point.column) == -1) {
+ range.setStart(delta.range.start);
+ }
+ point = delta.range.end;
+ if (range.compare(point.row, point.column) == 1) {
+ range.setEnd(delta.range.end);
+ }
+ lastDeltaIsInsert = true;
+ } else {
+ point = delta.range.start;
+ if (range.compare(point.row, point.column) == -1) {
+ range =
+ Range.fromPoints(delta.range.start, delta.range.start);
+ }
+ lastDeltaIsInsert = false;
+ }
+ }
+
+ // Check if this range and the last undo range has something in common.
+ // If true, merge the ranges.
+ if (lastUndoRange != null) {
+ var cmp = lastUndoRange.compareRange(range);
+ if (cmp == 1) {
+ range.setStart(lastUndoRange.start);
+ } else if (cmp == -1) {
+ range.setEnd(lastUndoRange.end);
+ }
+ }
+
+ return range;
+ },
+
+ this.replace = function(range, text) {
+ return this.doc.replace(range, text);
+ };
+
+ /**
+ * Move a range of text from the given range to the given position.
+ *
+ * @param fromRange {Range} The range of text you want moved within the
+ * document.
+ * @param toPosition {Object} The location (row and column) where you want
+ * to move the text to.
+ * @return {Range} The new range where the text was moved to.
+ */
+ this.moveText = function(fromRange, toPosition) {
+ var text = this.getTextRange(fromRange);
+ this.remove(fromRange);
+
+ var toRow = toPosition.row;
+ var toColumn = toPosition.column;
+
+ // Make sure to update the insert location, when text is removed in
+ // front of the chosen point of insertion.
+ if (!fromRange.isMultiLine() && fromRange.start.row == toRow &&
+ fromRange.end.column < toColumn)
+ toColumn -= text.length;
+
+ if (fromRange.isMultiLine() && fromRange.end.row < toRow) {
+ var lines = this.doc.$split(text);
+ toRow -= lines.length - 1;
+ }
+
+ var endRow = toRow + fromRange.end.row - fromRange.start.row;
+ var endColumn = fromRange.isMultiLine() ?
+ fromRange.end.column :
+ toColumn + fromRange.end.column - fromRange.start.column;
+
+ var toRange = new Range(toRow, toColumn, endRow, endColumn);
+
+ this.insert(toRange.start, text);
+
+ return toRange;
+ };
+
+ this.indentRows = function(startRow, endRow, indentString) {
+ indentString = indentString.replace(/\t/g, this.getTabString());
+ for (var row=startRow; row<=endRow; row++) {
+ this.insert({row: row, column:0}, indentString);
+ }
+ };
+
+ this.outdentRows = function (range) {
+ var rowRange = range.collapseRows();
+ var deleteRange = new Range(0, 0, 0, 0);
+ var size = this.getTabSize();
+
+ for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) {
+ var line = this.getLine(i);
+
+ deleteRange.start.row = i;
+ deleteRange.end.row = i;
+ for (var j = 0; j < size; ++j)
+ if (line.charAt(j) != ' ')
+ break;
+ if (j < size && line.charAt(j) == '\t') {
+ deleteRange.start.column = j;
+ deleteRange.end.column = j + 1;
+ } else {
+ deleteRange.start.column = 0;
+ deleteRange.end.column = j;
+ }
+ this.remove(deleteRange);
+ }
+ };
+
+ this.moveLinesUp = function(firstRow, lastRow) {
+ if (firstRow <= 0) return 0;
+
+ var removed = this.doc.removeLines(firstRow, lastRow);
+ this.doc.insertLines(firstRow - 1, removed);
+ return -1;
+ };
+
+ this.moveLinesDown = function(firstRow, lastRow) {
+ if (lastRow >= this.doc.getLength()-1) return 0;
+
+ var removed = this.doc.removeLines(firstRow, lastRow);
+ this.doc.insertLines(firstRow+1, removed);
+ return 1;
+ };
+
+ this.duplicateLines = function(firstRow, lastRow) {
+ var firstRow = this.$clipRowToDocument(firstRow);
+ var lastRow = this.$clipRowToDocument(lastRow);
+
+ var lines = this.getLines(firstRow, lastRow);
+ this.doc.insertLines(firstRow, lines);
+
+ var addedRows = lastRow - firstRow + 1;
+ return addedRows;
+ };
+
+ this.$clipRowToDocument = function(row) {
+ return Math.max(0, Math.min(row, this.doc.getLength()-1));
+ };
+
+ // WRAPMODE
+ this.$wrapLimit = 80;
+ this.$useWrapMode = false;
+ this.$wrapLimitRange = {
+ min : null,
+ max : null
+ };
+
+ this.setUseWrapMode = function(useWrapMode) {
+ if (useWrapMode != this.$useWrapMode) {
+ this.$useWrapMode = useWrapMode;
+ this.$modified = true;
+ this.$resetRowCache(0);
+
+ // If wrapMode is activaed, the wrapData array has to be initialized.
+ if (useWrapMode) {
+ var len = this.getLength();
+ this.$wrapData = [];
+ for (i = 0; i < len; i++) {
+ this.$wrapData.push([]);
+ }
+ this.$updateWrapData(0, len - 1);
+ }
+
+ this._dispatchEvent("changeWrapMode");
+ }
+ };
+
+ this.getUseWrapMode = function() {
+ return this.$useWrapMode;
+ };
+
+ // Allow the wrap limit to move freely between min and max. Either
+ // parameter can be null to allow the wrap limit to be unconstrained
+ // in that direction. Or set both parameters to the same number to pin
+ // the limit to that value.
+ this.setWrapLimitRange = function(min, max) {
+ if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) {
+ this.$wrapLimitRange.min = min;
+ this.$wrapLimitRange.max = max;
+ this.$modified = true;
+ // This will force a recalculation of the wrap limit
+ this._dispatchEvent("changeWrapMode");
+ }
+ };
+
+ // This should generally only be called by the renderer when a resize
+ // is detected.
+ this.adjustWrapLimit = function(desiredLimit) {
+ var wrapLimit = this.$constrainWrapLimit(desiredLimit);
+ if (wrapLimit != this.$wrapLimit && wrapLimit > 0) {
+ this.$wrapLimit = wrapLimit;
+ this.$modified = true;
+ if (this.$useWrapMode) {
+ this.$updateWrapData(0, this.getLength() - 1);
+ this.$resetRowCache(0)
+ this._dispatchEvent("changeWrapLimit");
+ }
+ return true;
+ }
+ return false;
+ };
+
+ this.$constrainWrapLimit = function(wrapLimit) {
+ var min = this.$wrapLimitRange.min;
+ if (min)
+ wrapLimit = Math.max(min, wrapLimit);
+
+ var max = this.$wrapLimitRange.max;
+ if (max)
+ wrapLimit = Math.min(max, wrapLimit);
+
+ // What would a limit of 0 even mean?
+ return Math.max(1, wrapLimit);
+ };
+
+ this.getWrapLimit = function() {
+ return this.$wrapLimit;
+ };
+
+ this.getWrapLimitRange = function() {
+ // Avoid unexpected mutation by returning a copy
+ return {
+ min : this.$wrapLimitRange.min,
+ max : this.$wrapLimitRange.max
+ };
+ };
+
+ this.$updateInternalDataOnChange = function(e) {
+ var useWrapMode = this.$useWrapMode;
+ var len;
+ var action = e.data.action;
+ var firstRow = e.data.range.start.row,
+ lastRow = e.data.range.end.row,
+ start = e.data.range.start,
+ end = e.data.range.end;
+ var removedFolds = null;
+
+ if (action.indexOf("Lines") != -1) {
+ if (action == "insertLines") {
+ lastRow = firstRow + (e.data.lines.length);
+ } else {
+ lastRow = firstRow;
+ }
+ len = e.data.lines.length;
+ } else {
+ len = lastRow - firstRow;
+ }
+
+ if (len != 0) {
+ if (action.indexOf("remove") != -1) {
+ useWrapMode && this.$wrapData.splice(firstRow, len);
+
+ var foldLines = this.$foldData;
+ removedFolds = this.getFoldsInRange(e.data.range);
+ this.removeFolds(removedFolds);
+
+ var foldLine = this.getFoldLine(end.row);
+ var idx = 0;
+ if (foldLine) {
+ foldLine.addRemoveChars(end.row, end.column, start.column - end.column);
+ foldLine.shiftRow(-len);
+
+ var foldLineBefore = this.getFoldLine(firstRow);
+ if (foldLineBefore && foldLineBefore !== foldLine) {
+ foldLineBefore.merge(foldLine);
+ foldLine = foldLineBefore;
+ }
+ idx = foldLines.indexOf(foldLine) + 1;
+ }
+
+ for (idx; idx < foldLines.length; idx++) {
+ var foldLine = foldLines[idx];
+ if (foldLine.start.row >= end.row) {
+ foldLine.shiftRow(-len);
+ }
+ }
+
+ lastRow = firstRow;
+ } else {
+ var args;
+ if (useWrapMode) {
+ args = [firstRow, 0];
+ for (var i = 0; i < len; i++) args.push([]);
+ this.$wrapData.splice.apply(this.$wrapData, args);
+ }
+
+ // If some new line is added inside of a foldLine, then split
+ // the fold line up.
+ var foldLines = this.$foldData;
+ var foldLine = this.getFoldLine(firstRow);
+ var idx = 0;
+ if (foldLine) {
+ var cmp = foldLine.range.compareInside(start.row, start.column)
+ // Inside of the foldLine range. Need to split stuff up.
+ if (cmp == 0) {
+ foldLine = foldLine.split(start.row, start.column);
+ foldLine.shiftRow(len);
+ foldLine.addRemoveChars(
+ lastRow, 0, end.column - start.column);
+ } else
+ // Infront of the foldLine but same row. Need to shift column.
+ if (cmp == -1) {
+ foldLine.addRemoveChars(firstRow, 0, end.column - start.column);
+ foldLine.shiftRow(len);
+ }
+ // Nothing to do if the insert is after the foldLine.
+ idx = foldLines.indexOf(foldLine) + 1;
+ }
+
+ for (idx; idx < foldLines.length; idx++) {
+ var foldLine = foldLines[idx];
+ if (foldLine.start.row >= firstRow) {
+ foldLine.shiftRow(len);
+ }
+ }
+ }
+ } else {
+ // Realign folds. E.g. if you add some new chars before a fold, the
+ // fold should "move" to the right.
+ var column;
+ len = Math.abs(e.data.range.start.column - e.data.range.end.column);
+ if (action.indexOf("remove") != -1) {
+ // Get all the folds in the change range and remove them.
+ removedFolds = this.getFoldsInRange(e.data.range);
+ this.removeFolds(removedFolds);
+
+ len = -len;
+ }
+ var foldLine = this.getFoldLine(firstRow);
+ if (foldLine) {
+ foldLine.addRemoveChars(firstRow, start.column, len);
+ }
+ }
+
+ if (useWrapMode && this.$wrapData.length != this.doc.getLength()) {
+ console.error("doc.getLength() and $wrapData.length have to be the same!");
+ }
+
+ useWrapMode && this.$updateWrapData(firstRow, lastRow);
+
+ return removedFolds;
+ };
+
+ this.$updateWrapData = function(firstRow, lastRow) {
+ var lines = this.doc.getAllLines();
+ var tabSize = this.getTabSize();
+ var wrapData = this.$wrapData;
+ var wrapLimit = this.$wrapLimit;
+ var tokens;
+ var foldLine;
+
+ var row = firstRow;
+ lastRow = Math.min(lastRow, lines.length - 1);
+ while (row <= lastRow) {
+ foldLine = this.getFoldLine(row);
+ if (!foldLine) {
+ tokens = this.$getDisplayTokens(lang.stringTrimRight(lines[row]));
+ } else {
+ tokens = [];
+ foldLine.walk(
+ function(placeholder, row, column, lastColumn) {
+ var walkTokens;
+ if (placeholder) {
+ walkTokens = this.$getDisplayTokens(
+ placeholder, tokens.length);
+ walkTokens[0] = PLACEHOLDER_START;
+ for (var i = 1; i < walkTokens.length; i++) {
+ walkTokens[i] = PLACEHOLDER_BODY;
+ }
+ } else {
+ walkTokens = this.$getDisplayTokens(
+ lines[row].substring(lastColumn, column),
+ tokens.length);
+ }
+ tokens = tokens.concat(walkTokens);
+ }.bind(this),
+ foldLine.end.row,
+ lines[foldLine.end.row].length + 1
+ );
+ // Remove spaces/tabs from the back of the token array.
+ while (tokens.length != 0
+ && tokens[tokens.length - 1] >= SPACE)
+ {
+ tokens.pop();
+ }
+ }
+ wrapData[row] =
+ this.$computeWrapSplits(tokens, wrapLimit, tabSize);
+
+ row = this.getRowFoldEnd(row) + 1;
+ }
+ };
+
+ // "Tokens"
+ var CHAR = 1,
+ CHAR_EXT = 2,
+ PLACEHOLDER_START = 3,
+ PLACEHOLDER_BODY = 4,
+ SPACE = 10,
+ TAB = 11,
+ TAB_SPACE = 12;
+
+ this.$computeWrapSplits = function(tokens, wrapLimit, tabSize) {
+ if (tokens.length == 0) {
+ return [];
+ }
+
+ var tabSize = this.getTabSize();
+ var splits = [];
+ var displayLength = tokens.length;
+ var lastSplit = 0, lastDocSplit = 0;
+
+ function addSplit(screenPos) {
+ var displayed = tokens.slice(lastSplit, screenPos);
+
+ // The document size is the current size - the extra width for tabs
+ // and multipleWidth characters.
+ var len = displayed.length;
+ displayed.join("").
+ // Get all the TAB_SPACEs.
+ replace(/12/g, function(m) {
+ len -= 1;
+ }).
+ // Get all the CHAR_EXT/multipleWidth characters.
+ replace(/2/g, function(m) {
+ len -= 1;
+ });
+
+ lastDocSplit += len;
+ splits.push(lastDocSplit);
+ lastSplit = screenPos;
+ }
+
+ while (displayLength - lastSplit > wrapLimit) {
+ // This is, where the split should be.
+ var split = lastSplit + wrapLimit;
+
+ // If there is a space or tab at this split position, then making
+ // a split is simple.
+ if (tokens[split] >= SPACE) {
+ // Include all following spaces + tabs in this split as well.
+ while (tokens[split] >= SPACE) {
+ split ++;
+ }
+ addSplit(split);
+ continue;
+ }
+
+ // === ELSE ===
+ // Check if split is inside of a placeholder. Placeholder are
+ // not splitable. Therefore, seek the beginning of the placeholder
+ // and try to place the split beofre the placeholder's start.
+ if (tokens[split] == PLACEHOLDER_START
+ || tokens[split] == PLACEHOLDER_BODY)
+ {
+ // Seek the start of the placeholder and do the split
+ // before the placeholder. By definition there always
+ // a PLACEHOLDER_START between split and lastSplit.
+ for (split; split != lastSplit - 1; split--) {
+ if (tokens[split] == PLACEHOLDER_START) {
+ // split++; << No incremental here as we want to
+ // have the position before the Placeholder.
+ break;
+ }
+ }
+
+ // If the PLACEHOLDER_START is not the index of the
+ // last split, then we can do the split
+ if (split > lastSplit) {
+ addSplit(split);
+ continue;
+ }
+
+ // If the PLACEHOLDER_START IS the index of the last
+ // split, then we have to place the split after the
+ // placeholder. So, let's seek for the end of the placeholder.
+ split = lastSplit + wrapLimit;
+ for (split; split < tokens.length; split++) {
+ if (tokens[split] != PLACEHOLDER_BODY)
+ {
+ break;
+ }
+ }
+
+ // If spilt == tokens.length, then the placeholder is the last
+ // thing in the line and adding a new split doesn't make sense.
+ if (split == tokens.length) {
+ break; // Breaks the while-loop.
+ }
+
+ // Finally, add the split...
+ addSplit(split);
+ continue;
+ }
+
+ // === ELSE ===
+ // Search for the first non space/tab/placeholder token backwards.
+ for (split; split != lastSplit - 1; split--) {
+ if (tokens[split] >= PLACEHOLDER_START) {
+ split++;
+ break;
+ }
+ }
+ // If we found one, then add the split.
+ if (split > lastSplit) {
+ addSplit(split);
+ continue;
+ }
+
+ // === ELSE ===
+ split = lastSplit + wrapLimit;
+ // The split is inside of a CHAR or CHAR_EXT token and no space
+ // around -> force a split.
+ addSplit(lastSplit + wrapLimit);
+ }
+ return splits;
+ }
+
+ /**
+ * @param
+ * offset: The offset in screenColumn at which position str starts.
+ * Important for calculating the realTabSize.
+ */
+ this.$getDisplayTokens = function(str, offset) {
+ var arr = [];
+ var tabSize;
+ offset = offset || 0;
+
+ for (var i = 0; i < str.length; i++) {
+ var c = str.charCodeAt(i);
+ // Tab
+ if (c == 9) {
+ tabSize = this.getScreenTabSize(arr.length + offset);
+ arr.push(TAB);
+ for (var n = 1; n < tabSize; n++) {
+ arr.push(TAB_SPACE);
+ }
+ }
+ // Space
+ else if(c == 32) {
+ arr.push(SPACE);
+ }
+ // full width characters
+ else if (isFullWidth(c)) {
+ arr.push(CHAR, CHAR_EXT);
+ } else {
+ arr.push(CHAR);
+ }
+ }
+ return arr;
+ }
+
+ /**
+ * Calculates the width of the a string on the screen while assuming that
+ * the string starts at the first column on the screen.
+ *
+ * @param string str String to calculate the screen width of
+ * @return array
+ * [0]: number of columns for str on screen.
+ * [1]: docColumn position that was read until (useful with screenColumn)
+ */
+ this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) {
+ if (maxScreenColumn == 0) {
+ return [0, 0];
+ }
+ if (maxScreenColumn == null) {
+ maxScreenColumn = screenColumn +
+ str.length * Math.max(this.getTabSize(), 2);
+ }
+ screenColumn = screenColumn || 0;
+
+ var c, column;
+ for (column = 0; column < str.length; column++) {
+ c = str.charCodeAt(column);
+ // tab
+ if (c == 9) {
+ screenColumn += this.getScreenTabSize(screenColumn);
+ }
+ // full width characters
+ else if (isFullWidth(c)) {
+ screenColumn += 2;
+ } else {
+ screenColumn += 1;
+ }
+ if (screenColumn > maxScreenColumn) {
+ break
+ }
+ }
+
+ return [screenColumn, column];
+ }
+
+ this.getRowLength = function(row) {
+ if (!this.$useWrapMode || !this.$wrapData[row]) {
+ return 1;
+ } else {
+ return this.$wrapData[row].length + 1;
+ }
+ }
+
+ this.getRowHeight = function(config, row) {
+ return this.getRowLength(row) * config.lineHeight;
+ }
+
+ this.getScreenLastRowColumn = function(screenRow) {
+ // Note: This won't work if someone has more then
+ // 1.7976931348623158e+307 characters in one row. But I think we can
+ // live with this limitation ;)
+ return this.screenToDocumentColumn(screenRow, Number.MAX_VALUE / 10)
+ };
+
+ this.getDocumentLastRowColumn = function(docRow, docColumn) {
+ var screenRow = this.documentToScreenRow(docRow, docColumn);
+ return this.getScreenLastRowColumn(screenRow);
+ };
+
+ this.getDocumentLastRowColumnPosition = function(docRow, docColumn) {
+ var screenRow = this.documentToScreenRow(docRow, docColumn);
+ return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10);
+ };
+
+ this.getRowSplitData = function(row) {
+ if (!this.$useWrapMode) {
+ return undefined;
+ } else {
+ return this.$wrapData[row];
+ }
+ };
+
+ /**
+ * Returns the width of a tab character at screenColumn.
+ */
+ this.getScreenTabSize = function(screenColumn) {
+ return this.$tabSize - screenColumn % this.$tabSize;
+ };
+
+ this.screenToDocumentRow = function(screenRow, screenColumn) {
+ return this.screenToDocumentPosition(screenRow, screenColumn).row;
+ };
+
+ this.screenToDocumentColumn = function(screenRow, screenColumn) {
+ return this.screenToDocumentPosition(screenRow, screenColumn).column;
+ };
+
+ this.screenToDocumentPosition = function(screenRow, screenColumn) {
+ var line;
+ var docRow = 0;
+ var docColumn = 0;
+ var column;
+ var foldLineRowLength;
+ var row = 0;
+ var rowLength = 0;
+ var splits = null;
+
+ var rowCache = this.$rowCache;
+ var doCache = !rowCache.length;
+ for (var i = 0; i < rowCache.length; i++) {
+ if (rowCache[i].screenRow < screenRow) {
+ row = rowCache[i].screenRow;
+ docRow = rowCache[i].docRow;
+ doCache = i == rowCache.length - 1;
+ }
+ }
+ var docRowCacheLast = docRow;
+ // clamp row before clamping column, for selection on last line
+ var maxRow = this.getLength() - 1;
+
+ var foldLine = this.getNextFold(docRow);
+ var foldStart = foldLine ?foldLine.start.row :Infinity;
+
+ while (row <= screenRow) {
+ if (doCache
+ && docRow - docRowCacheLast > this.$rowCacheSize) {
+ rowCache.push({
+ docRow: docRow,
+ screenRow: row
+ });
+ docRowCacheLast = docRow;
+ }
+ rowLength = this.getRowLength(docRow);
+ if (row + rowLength - 1 >= screenRow || docRow >= maxRow) {
+ break;
+ } else {
+ row += rowLength;
+ docRow++;
+ if(docRow > foldStart) {
+ docRow = foldLine.end.row+1;
+ foldLine = this.getNextFold(docRow);
+ foldStart = foldLine ?foldLine.start.row :Infinity;
+ }
+ }
+ }
+
+ if (foldLine && foldLine.start.row <= docRow)
+ line = this.getFoldDisplayLine(foldLine);
+ else {
+ line = this.getLine(docRow);
+ foldLine = null;
+ }
+
+ if (this.$useWrapMode) {
+ splits = this.$wrapData[docRow];
+ if (splits) {
+ column = splits[screenRow - row]
+ if(screenRow > row && splits.length) {
+ docColumn = splits[screenRow - row - 1] || splits[splits.length - 1];
+ line = line.substring(docColumn);
+ }
+ }
+ }
+
+ docColumn += this.$getStringScreenWidth(line, screenColumn)[1];
+
+ // Need to do some clamping action here.
+ if (this.$useWrapMode) {
+ if (docColumn >= column) {
+ // We remove one character at the end such that the docColumn
+ // position returned is not associated to the next row on the
+ // screen.
+ docColumn = column - 1;
+ }
+ } else {
+ docColumn = Math.min(docColumn, line.length);
+ }
+
+ if (foldLine) {
+ return foldLine.idxToPosition(docColumn);
+ }
+
+ return {
+ row: docRow,
+ column: docColumn
+ }
+ };
+
+ this.documentToScreenPosition = function(docRow, docColumn) {
+ // Normalize the passed in arguments.
+ if (docColumn == null) {
+ docColumn = docRow.column;
+ docRow = docRow.row;
+ }
+
+ var wrapData;
+ // Special case in wrapMode if the doc is at the end of the document.
+ if (this.$useWrapMode) {
+ wrapData = this.$wrapData;
+ if (docRow > wrapData.length - 1) {
+ return {
+ row: this.getScreenLength(),
+ column: wrapData.length == 0
+ ? 0
+ : (wrapData[wrapData.length - 1].length - 1)
+ };
+ }
+ }
+
+ var screenRow = 0;
+ var screenColumn = 0;
+ var foldStartRow = null;
+ var fold = null;
+
+ // Clamp the docRow position in case it's inside of a folded block.
+ fold = this.getFoldAt(docRow, docColumn, 1);
+ if (fold) {
+ docRow = fold.start.row;
+ docColumn = fold.start.column;
+ }
+
+ var rowEnd, row = 0;
+ var rowCache = this.$rowCache;
+ //
+ var doCache = !rowCache.length;
+ for (var i = 0; i < rowCache.length; i++) {
+ if (rowCache[i].docRow < docRow) {
+ screenRow = rowCache[i].screenRow;
+ row = rowCache[i].docRow;
+ doCache = i == rowCache.length - 1;
+ }
+ }
+ var docRowCacheLast = row;
+
+ var foldLine = this.getNextFold(row);
+ var foldStart = foldLine ?foldLine.start.row :Infinity;
+
+ while (row < docRow) {
+ if (row >= foldStart) {
+ rowEnd = foldLine.end.row + 1;
+ if (rowEnd > docRow)
+ break;
+ foldLine = this.getNextFold(rowEnd);
+ foldStart = foldLine ?foldLine.start.row :Infinity;
+ } else {
+ rowEnd = row + 1;
+ }
+ if (doCache
+ && row - docRowCacheLast > this.$rowCacheSize) {
+ rowCache.push({
+ docRow: row,
+ screenRow: screenRow
+ });
+ docRowCacheLast = row;
+ }
+
+ screenRow += this.getRowLength(row);
+ row = rowEnd;
+ }
+
+ // Calculate the text line that is displayed in docRow on the screen.
+ var textLine = "";
+ // Check if the final row we want to reach is inside of a fold.
+ if (foldLine && row >= foldStart) {
+ textLine = this.getFoldDisplayLine(foldLine, docRow, docColumn);
+ foldStartRow = foldLine.start.row;
+ } else {
+ textLine = this.getLine(docRow).substring(0, docColumn);
+ foldStartRow = docRow;
+ }
+ // Clamp textLine if in wrapMode.
+ if (this.$useWrapMode) {
+ var wrapRow = wrapData[foldStartRow];
+ var screenRowOffset = 0;
+ while (textLine.length >= wrapRow[screenRowOffset]) {
+ screenRow ++;
+ screenRowOffset++;
+ }
+ textLine = textLine.substring(
+ wrapRow[screenRowOffset - 1] || 0, textLine.length);
+ }
+
+ return {
+ row: screenRow,
+ column: this.$getStringScreenWidth(textLine)[0]
+ };
+ };
+
+ this.documentToScreenColumn = function(row, docColumn) {
+ return this.documentToScreenPosition(row, docColumn).column;
+ };
+
+ this.documentToScreenRow = function(docRow, docColumn) {
+ return this.documentToScreenPosition(docRow, docColumn).row;
+ };
+
+ this.getScreenLength = function() {
+ var screenRows = 0;
+ var lastFoldLine = null;
+ var foldLine = null;
+ if (!this.$useWrapMode) {
+ screenRows = this.getLength();
+
+ // Remove the folded lines again.
+ var foldData = this.$foldData;
+ for (var i = 0; i < foldData.length; i++) {
+ foldLine = foldData[i];
+ screenRows -= foldLine.end.row - foldLine.start.row;
+ }
+ } else {
+ for (var row = 0; row < this.$wrapData.length; row++) {
+ if (foldLine = this.getFoldLine(row, lastFoldLine)) {
+ row = foldLine.end.row;
+ screenRows += 1;
+ } else {
+ screenRows += this.$wrapData[row].length + 1;
+ }
+ }
+ }
+
+ return screenRows;
+ }
+
+ // For every keystroke this gets called once per char in the whole doc!!
+ // Wouldn't hurt to make it a bit faster for c >= 0x1100
+ function isFullWidth(c) {
+ if (c < 0x1100)
+ return false;
+ return c >= 0x1100 && c <= 0x115F ||
+ c >= 0x11A3 && c <= 0x11A7 ||
+ c >= 0x11FA && c <= 0x11FF ||
+ c >= 0x2329 && c <= 0x232A ||
+ c >= 0x2E80 && c <= 0x2E99 ||
+ c >= 0x2E9B && c <= 0x2EF3 ||
+ c >= 0x2F00 && c <= 0x2FD5 ||
+ c >= 0x2FF0 && c <= 0x2FFB ||
+ c >= 0x3000 && c <= 0x303E ||
+ c >= 0x3041 && c <= 0x3096 ||
+ c >= 0x3099 && c <= 0x30FF ||
+ c >= 0x3105 && c <= 0x312D ||
+ c >= 0x3131 && c <= 0x318E ||
+ c >= 0x3190 && c <= 0x31BA ||
+ c >= 0x31C0 && c <= 0x31E3 ||
+ c >= 0x31F0 && c <= 0x321E ||
+ c >= 0x3220 && c <= 0x3247 ||
+ c >= 0x3250 && c <= 0x32FE ||
+ c >= 0x3300 && c <= 0x4DBF ||
+ c >= 0x4E00 && c <= 0xA48C ||
+ c >= 0xA490 && c <= 0xA4C6 ||
+ c >= 0xA960 && c <= 0xA97C ||
+ c >= 0xAC00 && c <= 0xD7A3 ||
+ c >= 0xD7B0 && c <= 0xD7C6 ||
+ c >= 0xD7CB && c <= 0xD7FB ||
+ c >= 0xF900 && c <= 0xFAFF ||
+ c >= 0xFE10 && c <= 0xFE19 ||
+ c >= 0xFE30 && c <= 0xFE52 ||
+ c >= 0xFE54 && c <= 0xFE66 ||
+ c >= 0xFE68 && c <= 0xFE6B ||
+ c >= 0xFF01 && c <= 0xFF60 ||
+ c >= 0xFFE0 && c <= 0xFFE6;
+ };
+
+}).call(EditSession.prototype);
+
+require("ace/edit_session/folding").Folding.call(EditSession.prototype);
+
+exports.EditSession = EditSession;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Julian Viereck <julian.viereck@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/selection', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/lang', 'pilot/event_emitter', 'ace/range'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var lang = require("pilot/lang");
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+var Range = require("ace/range").Range;
+
+var Selection = function(session) {
+ this.session = session;
+ this.doc = session.getDocument();
+
+ this.clearSelection();
+ this.selectionLead = this.doc.createAnchor(0, 0);
+ this.selectionAnchor = this.doc.createAnchor(0, 0);
+
+ var _self = this;
+ this.selectionLead.on("change", function(e) {
+ _self._dispatchEvent("changeCursor");
+ if (!_self.$isEmpty)
+ _self._dispatchEvent("changeSelection");
+ if (e.old.row == e.value.row)
+ _self.$updateDesiredColumn();
+ });
+
+ this.selectionAnchor.on("change", function() {
+ if (!_self.$isEmpty)
+ _self._dispatchEvent("changeSelection");
+ });
+};
+
+(function() {
+
+ oop.implement(this, EventEmitter);
+
+ this.isEmpty = function() {
+ return (this.$isEmpty || (
+ this.selectionAnchor.row == this.selectionLead.row &&
+ this.selectionAnchor.column == this.selectionLead.column
+ ));
+ };
+
+ this.isMultiLine = function() {
+ if (this.isEmpty()) {
+ return false;
+ }
+
+ return this.getRange().isMultiLine();
+ };
+
+ this.getCursor = function() {
+ return this.selectionLead.getPosition();
+ };
+
+ this.setSelectionAnchor = function(row, column) {
+ this.selectionAnchor.setPosition(row, column);
+
+ if (this.$isEmpty) {
+ this.$isEmpty = false;
+ this._dispatchEvent("changeSelection");
+ }
+ };
+
+ this.getSelectionAnchor = function() {
+ if (this.$isEmpty)
+ return this.getSelectionLead()
+ else
+ return this.selectionAnchor.getPosition();
+ };
+
+ this.getSelectionLead = function() {
+ return this.selectionLead.getPosition();
+ };
+
+ this.shiftSelection = function(columns) {
+ if (this.$isEmpty) {
+ this.moveCursorTo(this.selectionLead.row, this.selectionLead.column + columns);
+ return;
+ };
+
+ var anchor = this.getSelectionAnchor();
+ var lead = this.getSelectionLead();
+
+ var isBackwards = this.isBackwards();
+
+ if (!isBackwards || anchor.column !== 0)
+ this.setSelectionAnchor(anchor.row, anchor.column + columns);
+
+ if (isBackwards || lead.column !== 0) {
+ this.$moveSelection(function() {
+ this.moveCursorTo(lead.row, lead.column + columns);
+ });
+ }
+ };
+
+ this.isBackwards = function() {
+ var anchor = this.selectionAnchor;
+ var lead = this.selectionLead;
+ return (anchor.row > lead.row || (anchor.row == lead.row && anchor.column > lead.column));
+ };
+
+ this.getRange = function() {
+ var anchor = this.selectionAnchor;
+ var lead = this.selectionLead;
+
+ if (this.isEmpty())
+ return Range.fromPoints(lead, lead);
+
+ if (this.isBackwards()) {
+ return Range.fromPoints(lead, anchor);
+ }
+ else {
+ return Range.fromPoints(anchor, lead);
+ }
+ };
+
+ this.clearSelection = function() {
+ if (!this.$isEmpty) {
+ this.$isEmpty = true;
+ this._dispatchEvent("changeSelection");
+ }
+ };
+
+ this.selectAll = function() {
+ var lastRow = this.doc.getLength() - 1;
+ this.setSelectionAnchor(lastRow, this.doc.getLine(lastRow).length);
+ this.moveCursorTo(0, 0);
+ };
+
+ this.setSelectionRange = function(range, reverse) {
+ if (reverse) {
+ this.setSelectionAnchor(range.end.row, range.end.column);
+ this.selectTo(range.start.row, range.start.column);
+ } else {
+ this.setSelectionAnchor(range.start.row, range.start.column);
+ this.selectTo(range.end.row, range.end.column);
+ }
+ this.$updateDesiredColumn();
+ };
+
+ this.$updateDesiredColumn = function() {
+ var cursor = this.getCursor();
+ this.$desiredColumn = this.session.documentToScreenColumn(cursor.row, cursor.column);
+ };
+
+ this.$moveSelection = function(mover) {
+ var lead = this.selectionLead;
+ if (this.$isEmpty)
+ this.setSelectionAnchor(lead.row, lead.column);
+
+ mover.call(this);
+ };
+
+ this.selectTo = function(row, column) {
+ this.$moveSelection(function() {
+ this.moveCursorTo(row, column);
+ });
+ };
+
+ this.selectToPosition = function(pos) {
+ this.$moveSelection(function() {
+ this.moveCursorToPosition(pos);
+ });
+ };
+
+ this.selectUp = function() {
+ this.$moveSelection(this.moveCursorUp);
+ };
+
+ this.selectDown = function() {
+ this.$moveSelection(this.moveCursorDown);
+ };
+
+ this.selectRight = function() {
+ this.$moveSelection(this.moveCursorRight);
+ };
+
+ this.selectLeft = function() {
+ this.$moveSelection(this.moveCursorLeft);
+ };
+
+ this.selectLineStart = function() {
+ this.$moveSelection(this.moveCursorLineStart);
+ };
+
+ this.selectLineEnd = function() {
+ this.$moveSelection(this.moveCursorLineEnd);
+ };
+
+ this.selectFileEnd = function() {
+ this.$moveSelection(this.moveCursorFileEnd);
+ };
+
+ this.selectFileStart = function() {
+ this.$moveSelection(this.moveCursorFileStart);
+ };
+
+ this.selectWordRight = function() {
+ this.$moveSelection(this.moveCursorWordRight);
+ };
+
+ this.selectWordLeft = function() {
+ this.$moveSelection(this.moveCursorWordLeft);
+ };
+
+ this.selectWord = function() {
+ var cursor = this.getCursor();
+ var range = this.session.getWordRange(cursor.row, cursor.column);
+ this.setSelectionRange(range);
+ };
+
+ this.selectLine = function() {
+ var rowStart = this.selectionLead.row;
+ var rowEnd;
+
+ var foldLine = this.session.getFoldLine(rowStart);
+ if (foldLine) {
+ rowStart = foldLine.start.row;
+ rowEnd = foldLine.end.row;
+ } else {
+ rowEnd = rowStart;
+ }
+ this.setSelectionAnchor(rowStart, 0);
+ this.$moveSelection(function() {
+ this.moveCursorTo(rowEnd + 1, 0);
+ });
+ };
+
+ this.moveCursorUp = function() {
+ this.moveCursorBy(-1, 0);
+ };
+
+ this.moveCursorDown = function() {
+ this.moveCursorBy(1, 0);
+ };
+
+ this.moveCursorLeft = function() {
+ var cursor = this.selectionLead.getPosition(),
+ fold;
+
+ if (fold = this.session.getFoldAt(cursor.row, cursor.column, -1)) {
+ this.moveCursorTo(fold.start.row, fold.start.column);
+ } else if (cursor.column == 0) {
+ // cursor is a line (start
+ if (cursor.row > 0) {
+ this.moveCursorTo(cursor.row - 1, this.doc.getLine(cursor.row - 1).length);
+ }
+ }
+ else {
+ var tabSize = this.session.getTabSize();
+ if (this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(cursor.column-tabSize, cursor.column).split(" ").length-1 == tabSize)
+ this.moveCursorBy(0, -tabSize);
+ else
+ this.moveCursorBy(0, -1);
+ }
+ };
+
+ this.moveCursorRight = function() {
+ var cursor = this.selectionLead.getPosition(),
+ fold;
+ if (fold = this.session.getFoldAt(cursor.row, cursor.column, 1)) {
+ this.moveCursorTo(fold.end.row, fold.end.column);
+ } else if (this.selectionLead.column == this.doc.getLine(this.selectionLead.row).length) {
+ if (this.selectionLead.row < this.doc.getLength() - 1) {
+ this.moveCursorTo(this.selectionLead.row + 1, 0);
+ }
+ }
+ else {
+ var tabSize = this.session.getTabSize();
+ var cursor = this.selectionLead;
+ if (this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(cursor.column, cursor.column+tabSize).split(" ").length-1 == tabSize)
+ this.moveCursorBy(0, tabSize);
+ else
+ this.moveCursorBy(0, 1);
+ }
+ };
+
+ this.moveCursorLineStart = function() {
+ var row = this.selectionLead.row;
+ var column = this.selectionLead.column;
+ var screenRow = this.session.documentToScreenRow(row, column);
+
+ // Determ the doc-position of the first character at the screen line.
+ var firstColumnPosition =
+ this.session.screenToDocumentPosition(screenRow, 0);
+
+ // Determ the string "before" the cursor.
+ var beforeCursor = this.session.getDisplayLine(
+ row, column,
+ firstColumnPosition.row, firstColumnPosition.column);
+
+ //
+ var leadingSpace = beforeCursor.match(/^\s*/);
+ if (leadingSpace[0].length == 0
+ || leadingSpace[0].length >= column - firstColumnPosition.column)
+ {
+ this.moveCursorTo(
+ firstColumnPosition.row, firstColumnPosition.column);
+ } else {
+ this.moveCursorTo(
+ firstColumnPosition.row,
+ firstColumnPosition.column + leadingSpace[0].length);
+ }
+ };
+
+ this.moveCursorLineEnd = function() {
+ var lead = this.selectionLead;
+ var lastRowColumnPosition =
+ this.session.getDocumentLastRowColumnPosition(lead.row, lead.column);
+ this.moveCursorTo(
+ lastRowColumnPosition.row,
+ lastRowColumnPosition.column
+ );
+ };
+
+ this.moveCursorFileEnd = function() {
+ var row = this.doc.getLength() - 1;
+ var column = this.doc.getLine(row).length;
+ this.moveCursorTo(row, column);
+ };
+
+ this.moveCursorFileStart = function() {
+ this.moveCursorTo(0, 0);
+ };
+
+ this.moveCursorWordRight = function() {
+ var row = this.selectionLead.row;
+ var column = this.selectionLead.column;
+ var line = this.doc.getLine(row);
+ var rightOfCursor = line.substring(column);
+
+ var match;
+ this.session.nonTokenRe.lastIndex = 0;
+ this.session.tokenRe.lastIndex = 0;
+
+ var fold;
+ if (fold = this.session.getFoldAt(row, column, 1)) {
+ this.moveCursorTo(fold.end.row, fold.end.column);
+ return;
+ } else if (column == line.length) {
+ this.moveCursorRight();
+ return;
+ }
+ else if (match = this.session.nonTokenRe.exec(rightOfCursor)) {
+ column += this.session.nonTokenRe.lastIndex;
+ this.session.nonTokenRe.lastIndex = 0;
+ }
+ else if (match = this.session.tokenRe.exec(rightOfCursor)) {
+ column += this.session.tokenRe.lastIndex;
+ this.session.tokenRe.lastIndex = 0;
+ }
+
+ this.moveCursorTo(row, column);
+ };
+
+ this.moveCursorWordLeft = function() {
+ var row = this.selectionLead.row;
+ var column = this.selectionLead.column;
+
+ var fold;
+ if (fold = this.session.getFoldAt(row, column, -1)) {
+ this.moveCursorTo(fold.start.row, fold.start.column);
+ return;
+ }
+
+ if (column == 0) {
+ this.moveCursorLeft();
+ return;
+ }
+
+ var str = this.session.getFoldStringAt(row, column, -1);
+ if (str == null) {
+ str = this.doc.getLine(row).substring(0, column)
+ }
+ var leftOfCursor = lang.stringReverse(str);
+
+ var match;
+ this.session.nonTokenRe.lastIndex = 0;
+ this.session.tokenRe.lastIndex = 0;
+
+ if (match = this.session.nonTokenRe.exec(leftOfCursor)) {
+ column -= this.session.nonTokenRe.lastIndex;
+ this.session.nonTokenRe.lastIndex = 0;
+ }
+ else if (match = this.session.tokenRe.exec(leftOfCursor)) {
+ column -= this.session.tokenRe.lastIndex;
+ this.session.tokenRe.lastIndex = 0;
+ }
+
+ this.moveCursorTo(row, column);
+ };
+
+ this.moveCursorBy = function(rows, chars) {
+ var screenPos = this.session.documentToScreenPosition(
+ this.selectionLead.row,
+ this.selectionLead.column
+ );
+ var screenCol = (chars == 0 && this.$desiredColumn) || screenPos.column;
+ var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol);
+
+ this.moveCursorTo(docPos.row, docPos.column + chars, chars == 0);
+ };
+
+ this.moveCursorToPosition = function(position) {
+ this.moveCursorTo(position.row, position.column);
+ };
+
+ this.moveCursorTo = function(row, column, preventUpdateDesiredColumn) {
+ // Ensure the row/column is not inside of a fold.
+ var fold = this.session.getFoldAt(row, column, 1);
+ if (fold) {
+ row = fold.start.row;
+ column = fold.start.column;
+ }
+ this.selectionLead.setPosition(row, column);
+ if (!preventUpdateDesiredColumn)
+ this.$updateDesiredColumn(this.selectionLead.column);
+ };
+
+ this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) {
+ var pos = this.session.screenToDocumentPosition(row, column);
+ row = pos.row;
+ column = pos.column;
+ this.moveCursorTo(row, column, preventUpdateDesiredColumn);
+ };
+
+}).call(Selection.prototype);
+
+exports.Selection = Selection;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var Range = function(startRow, startColumn, endRow, endColumn) {
+ this.start = {
+ row: startRow,
+ column: startColumn
+ };
+
+ this.end = {
+ row: endRow,
+ column: endColumn
+ };
+};
+
+(function() {
+
+ this.toString = function() {
+ return ("Range: [" + this.start.row + "/" + this.start.column +
+ "] -> [" + this.end.row + "/" + this.end.column + "]");
+ };
+
+ this.contains = function(row, column) {
+ return this.compare(row, column) == 0;
+ };
+
+ /**
+ * Compares this range (A) with another range (B), where B is the passed in
+ * range.
+ *
+ * Return values:
+ * -2: (B) is infront of (A) and doesn't intersect with (A)
+ * -1: (B) begins before (A) but ends inside of (A)
+ * 0: (B) is completly inside of (A) OR (A) is complety inside of (B)
+ * +1: (B) begins inside of (A) but ends outside of (A)
+ * +2: (B) is after (A) and doesn't intersect with (A)
+ *
+ * 42: FTW state: (B) ends in (A) but starts outside of (A)
+ */
+ this.compareRange = function(range) {
+ var cmp,
+ end = range.end,
+ start = range.start;
+
+ cmp = this.compare(end.row, end.column);
+ if (cmp == 1) {
+ cmp = this.compare(start.row, start.column);
+ if (cmp == 1) {
+ return 2;
+ } else if (cmp == 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (cmp == -1) {
+ return -2;
+ } else {
+ cmp = this.compare(start.row, start.column);
+ if (cmp == -1) {
+ return -1;
+ } else if (cmp == 1) {
+ return 42;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ this.containsRange = function(range) {
+ var cmp = this.compareRange(range);
+ return (cmp == -1 || cmp == 0 || cmp == 1);
+ }
+
+ this.isEnd = function(row, column) {
+ return this.end.row == row && this.end.column == column;
+ }
+
+ this.isStart = function(row, column) {
+ return this.start.row == row && this.start.column == column;
+ }
+
+ this.setStart = function(row, column) {
+ if (typeof row == "object") {
+ this.start.column = row.column;
+ this.start.row = row.row;
+ } else {
+ this.start.row = row;
+ this.start.column = column;
+ }
+ }
+
+ this.setEnd = function(row, column) {
+ if (typeof row == "object") {
+ this.end.column = row.column;
+ this.end.row = row.row;
+ } else {
+ this.end.row = row;
+ this.end.column = column;
+ }
+ }
+
+ this.inside = function(row, column) {
+ if (this.compare(row, column) == 0) {
+ if (this.isEnd(row, column) || this.isStart(row, column)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ this.insideStart = function(row, column) {
+ if (this.compare(row, column) == 0) {
+ if (this.isEnd(row, column)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ this.insideEnd = function(row, column) {
+ if (this.compare(row, column) == 0) {
+ if (this.isStart(row, column)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ this.compare = function(row, column) {
+ if (!this.isMultiLine()) {
+ if (row === this.start.row) {
+ return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0);
+ };
+ }
+
+ if (row < this.start.row)
+ return -1;
+
+ if (row > this.end.row)
+ return 1;
+
+ if (this.start.row === row)
+ return column >= this.start.column ? 0 : -1;
+
+ if (this.end.row === row)
+ return column <= this.end.column ? 0 : 1;
+
+ return 0;
+ };
+
+ /**
+ * Like .compare(), but if isStart is true, return -1;
+ */
+ this.compareStart = function(row, column) {
+ if (this.start.row == row && this.start.column == column) {
+ return -1;
+ } else {
+ return this.compare(row, column);
+ }
+ }
+
+ /**
+ * Like .compare(), but if isEnd is true, return 1;
+ */
+ this.compareEnd = function(row, column) {
+ if (this.end.row == row && this.end.column == column) {
+ return 1;
+ } else {
+ return this.compare(row, column);
+ }
+ }
+
+ this.compareInside = function(row, column) {
+ if (this.end.row == row && this.end.column == column) {
+ return 1;
+ } else if (this.start.row == row && this.start.column == column) {
+ return -1;
+ } else {
+ return this.compare(row, column);
+ }
+ }
+
+ this.clipRows = function(firstRow, lastRow) {
+ if (this.end.row > lastRow) {
+ var end = {
+ row: lastRow+1,
+ column: 0
+ };
+ }
+
+ if (this.start.row > lastRow) {
+ var start = {
+ row: lastRow+1,
+ column: 0
+ };
+ }
+
+ if (this.start.row < firstRow) {
+ var start = {
+ row: firstRow,
+ column: 0
+ };
+ }
+
+ if (this.end.row < firstRow) {
+ var end = {
+ row: firstRow,
+ column: 0
+ };
+ }
+ return Range.fromPoints(start || this.start, end || this.end);
+ };
+
+ this.extend = function(row, column) {
+ var cmp = this.compare(row, column);
+
+ if (cmp == 0)
+ return this;
+ else if (cmp == -1)
+ var start = {row: row, column: column};
+ else
+ var end = {row: row, column: column};
+
+ return Range.fromPoints(start || this.start, end || this.end);
+ };
+
+ this.isEmpty = function() {
+ return (this.start.row == this.end.row && this.start.column == this.end.column);
+ };
+
+ this.isMultiLine = function() {
+ return (this.start.row !== this.end.row);
+ };
+
+ this.clone = function() {
+ return Range.fromPoints(this.start, this.end);
+ };
+
+ this.collapseRows = function() {
+ if (this.end.column == 0)
+ return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0)
+ else
+ return new Range(this.start.row, 0, this.end.row, 0)
+ };
+
+ this.toScreenRange = function(session) {
+ var screenPosStart =
+ session.documentToScreenPosition(this.start);
+ var screenPosEnd =
+ session.documentToScreenPosition(this.end);
+ return new Range(
+ screenPosStart.row, screenPosStart.column,
+ screenPosEnd.row, screenPosEnd.column
+ );
+ };
+
+}).call(Range.prototype);
+
+
+Range.fromPoints = function(start, end) {
+ return new Range(start.row, start.column, end.row, end.column);
+};
+
+exports.Range = Range;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Mihai Sucan <mihai DOT sucan AT gmail DOT com>
+ * Chris Spencer <chris.ag.spencer AT googlemail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/mode/text', ['require', 'exports', 'module' , 'ace/tokenizer', 'ace/mode/text_highlight_rules', 'ace/mode/behaviour'], function(require, exports, module) {
+
+var Tokenizer = require("ace/tokenizer").Tokenizer;
+var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules;
+var Behaviour = require("ace/mode/behaviour").Behaviour;
+
+var Mode = function() {
+ this.$tokenizer = new Tokenizer(new TextHighlightRules().getRules());
+ this.$behaviour = new Behaviour();
+};
+
+(function() {
+
+ this.getTokenizer = function() {
+ return this.$tokenizer;
+ };
+
+ this.toggleCommentLines = function(state, doc, startRow, endRow) {
+ };
+
+ this.getNextLineIndent = function(state, line, tab) {
+ return "";
+ };
+
+ this.checkOutdent = function(state, line, input) {
+ return false;
+ };
+
+ this.autoOutdent = function(state, doc, row) {
+ };
+
+ this.$getIndent = function(line) {
+ var match = line.match(/^(\s+)/);
+ if (match) {
+ return match[1];
+ }
+
+ return "";
+ };
+
+ this.createWorker = function(session) {
+ return null;
+ };
+
+ this.highlightSelection = function(editor) {
+ var session = editor.session;
+ if (!session.$selectionOccurrences)
+ session.$selectionOccurrences = [];
+
+ if (session.$selectionOccurrences.length)
+ this.clearSelectionHighlight(editor);
+
+ var selection = editor.getSelectionRange();
+ if (selection.isEmpty() || selection.isMultiLine())
+ return;
+
+ var startOuter = selection.start.column - 1;
+ var endOuter = selection.end.column + 1;
+ var line = session.getLine(selection.start.row);
+ var lineCols = line.length;
+ var needle = line.substring(Math.max(startOuter, 0),
+ Math.min(endOuter, lineCols));
+
+ // Make sure the outer characters are not part of the word.
+ if ((startOuter >= 0 && /^[\w\d]/.test(needle)) ||
+ (endOuter <= lineCols && /[\w\d]$/.test(needle)))
+ return;
+
+ needle = line.substring(selection.start.column, selection.end.column);
+ if (!/^[\w\d]+$/.test(needle))
+ return;
+
+ var cursor = editor.getCursorPosition();
+
+ var newOptions = {
+ wrap: true,
+ wholeWord: true,
+ caseSensitive: true,
+ needle: needle
+ };
+
+ var currentOptions = editor.$search.getOptions();
+ editor.$search.set(newOptions);
+
+ var ranges = editor.$search.findAll(session);
+ ranges.forEach(function(range) {
+ if (!range.contains(cursor.row, cursor.column)) {
+ var marker = session.addMarker(range, "ace_selected_word");
+ session.$selectionOccurrences.push(marker);
+ }
+ });
+
+ editor.$search.set(currentOptions);
+ };
+
+ this.clearSelectionHighlight = function(editor) {
+ if (!editor.session.$selectionOccurrences)
+ return;
+
+ editor.session.$selectionOccurrences.forEach(function(marker) {
+ editor.session.removeMarker(marker);
+ });
+
+ editor.session.$selectionOccurrences = [];
+ };
+
+ this.createModeDelegates = function (mapping) {
+ if (!this.$embeds) {
+ return;
+ }
+ this.$modes = {};
+ for (var i = 0; i < this.$embeds.length; i++) {
+ if (mapping[this.$embeds[i]]) {
+ this.$modes[this.$embeds[i]] = new mapping[this.$embeds[i]]();
+ }
+ }
+
+ var delegations = ['toggleCommentLines', 'getNextLineIndent', 'checkOutdent', 'autoOutdent', 'transformAction'];
+
+ for (var i = 0; i < delegations.length; i++) {
+ (function(scope) {
+ var functionName = delegations[i];
+ var defaultHandler = scope[functionName];
+ scope[delegations[i]] = function() {
+ return this.$delegator(functionName, arguments, defaultHandler);
+ }
+ } (this));
+ }
+ }
+
+ this.$delegator = function(method, args, defaultHandler) {
+ var state = args[0];
+
+ for (var i = 0; i < this.$embeds.length; i++) {
+ if (!this.$modes[this.$embeds[i]]) continue;
+
+ var split = state.split(this.$embeds[i]);
+ if (!split[0] && split[1]) {
+ args[0] = split[1];
+ var mode = this.$modes[this.$embeds[i]];
+ return mode[method].apply(mode, args);
+ }
+ }
+ var ret = defaultHandler.apply(this, args);
+ return defaultHandler ? ret : undefined;
+ };
+
+ this.transformAction = function(state, action, editor, session, param) {
+ if (this.$behaviour) {
+ var behaviours = this.$behaviour.getBehaviours();
+ for (var key in behaviours) {
+ if (behaviours[key][action]) {
+ var ret = behaviours[key][action].apply(this, arguments);
+ if (ret !== false) {
+ return ret;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+}).call(Mode.prototype);
+
+exports.Mode = Mode;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/tokenizer', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var Tokenizer = function(rules) {
+ this.rules = rules;
+
+ this.regExps = {};
+ this.matchMappings = {};
+ for ( var key in this.rules) {
+ var rule = this.rules[key];
+ var state = rule;
+ var ruleRegExps = [];
+ var matchTotal = 0;
+ var mapping = this.matchMappings[key] = {};
+
+ for ( var i = 0; i < state.length; i++) {
+ // Count number of matching groups. 2 extra groups from the full match
+ // And the catch-all on the end (used to force a match);
+ var matchcount = new RegExp("(?:(" + state[i].regex + ")|(.))").exec("a").length - 2;
+
+ // Replace any backreferences and offset appropriately.
+ var adjustedregex = state[i].regex.replace(/\\([0-9]+)/g, function (match, digit) {
+ return "\\" + (parseInt(digit, 10) + matchTotal + 1);
+ });
+
+ mapping[matchTotal] = {
+ rule: i,
+ len: matchcount
+ };
+ matchTotal += matchcount;
+
+ ruleRegExps.push(adjustedregex);
+ }
+
+ this.regExps[key] = new RegExp("(?:(" + ruleRegExps.join(")|(") + ")|(.))", "g");
+
+ }
+};
+
+(function() {
+
+ this.getLineTokens = function(line, startState) {
+ var currentState = startState;
+ var state = this.rules[currentState];
+ var mapping = this.matchMappings[currentState];
+ var re = this.regExps[currentState];
+ re.lastIndex = 0;
+
+ var match, tokens = [];
+
+ var lastIndex = 0;
+
+ var token = {
+ type: null,
+ value: ""
+ };
+
+ while (match = re.exec(line)) {
+ var type = "text";
+ var value = [match[0]];
+
+ for ( var i = 0; i < match.length-2; i++) {
+ if (match[i + 1] !== undefined) {
+ var rule = state[mapping[i].rule];
+
+ if (mapping[i].len > 1) {
+ value = match.slice(i+2, i+1+mapping[i].len);
+ }
+
+ if (typeof rule.token == "function")
+ type = rule.token.apply(this, value);
+ else
+ type = rule.token;
+
+ if (rule.next && rule.next !== currentState) {
+ currentState = rule.next;
+ state = this.rules[currentState];
+ mapping = this.matchMappings[currentState];
+ lastIndex = re.lastIndex;
+
+ re = this.regExps[currentState];
+ re.lastIndex = lastIndex;
+ }
+ break;
+ }
+ };
+
+ if (typeof type == "string") {
+ if (typeof value != "string") {
+ value = [value.join("")];
+ }
+ type = [type];
+ }
+
+ for ( var i = 0; i < value.length; i++) {
+ if (token.type !== type[i]) {
+ if (token.type) {
+ tokens.push(token);
+ }
+
+ token = {
+ type: type[i],
+ value: value[i]
+ }
+ } else {
+ token.value += value[i];
+ }
+ }
+
+ if (lastIndex == line.length)
+ break;
+
+ lastIndex = re.lastIndex;
+ };
+
+ if (token.type)
+ tokens.push(token);
+
+ return {
+ tokens : tokens,
+ state : currentState
+ };
+ };
+
+}).call(Tokenizer.prototype);
+
+exports.Tokenizer = Tokenizer;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/mode/text_highlight_rules', ['require', 'exports', 'module' , 'pilot/lang'], function(require, exports, module) {
+
+var lang = require("pilot/lang");
+
+var TextHighlightRules = function() {
+
+ // regexp must not have capturing parentheses
+ // regexps are ordered -> the first match is used
+
+ this.$rules = {
+ "start" : [ {
+ token : "empty_line",
+ regex : '^$'
+ }, {
+ token : "text",
+ regex : ".+"
+ } ]
+ };
+};
+
+(function() {
+
+ this.addRules = function(rules, prefix) {
+ for (var key in rules) {
+ var state = rules[key];
+ for (var i=0; i<state.length; i++) {
+ var rule = state[i];
+ if (rule.next) {
+ rule.next = prefix + rule.next;
+ } else {
+ rule.next = prefix + key;
+ }
+ }
+ this.$rules[prefix + key] = state;
+ }
+ };
+
+ this.getRules = function() {
+ return this.$rules;
+ };
+
+ this.embedRules = function (HighlightRules, prefix, escapeRules, states) {
+ var embedRules = new HighlightRules().getRules();
+ if (states) {
+ for (var i = 0; i < states.length; i++) {
+ states[i] = prefix + states[i];
+ }
+ } else {
+ states = [];
+ for (var key in embedRules) {
+ states.push(prefix + key);
+ }
+ }
+ this.addRules(embedRules, prefix);
+
+ for (var i = 0; i < states.length; i++) {
+ Array.prototype.unshift.apply(this.$rules[states[i]], lang.deepCopy(escapeRules));
+ }
+
+ if (!this.$embeds) {
+ this.$embeds = [];
+ }
+ this.$embeds.push(prefix);
+ }
+
+ this.getEmbeds = function() {
+ return this.$embeds;
+ }
+
+}).call(TextHighlightRules.prototype);
+
+exports.TextHighlightRules = TextHighlightRules;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Spencer <chris.ag.spencer AT googlemail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/mode/behaviour', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var Behaviour = function() {
+ this.$behaviours = {};
+};
+
+(function () {
+
+ this.add = function (name, action, callback) {
+ switch (undefined) {
+ case this.$behaviours:
+ this.$behaviours = {};
+ case this.$behaviours[name]:
+ this.$behaviours[name] = {};
+ }
+ this.$behaviours[name][action] = callback;
+ }
+
+ this.addBehaviours = function (behaviours) {
+ for (var key in behaviours) {
+ for (var action in behaviours[key]) {
+ this.add(key, action, behaviours[key][action]);
+ }
+ }
+ }
+
+ this.remove = function (name) {
+ if (this.$behaviours && this.$behaviours[name]) {
+ delete this.$behaviours[name];
+ }
+ }
+
+ this.inherit = function (mode, filter) {
+ if (typeof mode === "function") {
+ var behaviours = new mode().getBehaviours(filter);
+ } else {
+ var behaviours = mode.getBehaviours(filter);
+ }
+ this.addBehaviours(behaviours);
+ }
+
+ this.getBehaviours = function (filter) {
+ if (!filter) {
+ return this.$behaviours;
+ } else {
+ var ret = {}
+ for (var i = 0; i < filter.length; i++) {
+ if (this.$behaviours[filter[i]]) {
+ ret[filter[i]] = this.$behaviours[filter[i]];
+ }
+ }
+ return ret;
+ }
+ }
+
+}).call(Behaviour.prototype);
+
+exports.Behaviour = Behaviour;
+});/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/document', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+var Range = require("ace/range").Range;
+var Anchor = require("ace/anchor").Anchor;
+
+var Document = function(text) {
+ this.$lines = [];
+
+ if (Array.isArray(text)) {
+ this.insertLines(0, text);
+ }
+ // There has to be one line at least in the document. If you pass an empty
+ // string to the insert function, nothing will happen. Workaround.
+ else if (text.length == 0) {
+ this.$lines = [""];
+ } else {
+ this.insert({row: 0, column:0}, text);
+ }
+};
+
+(function() {
+
+ oop.implement(this, EventEmitter);
+
+ this.setValue = function(text) {
+ var len = this.getLength();
+ this.remove(new Range(0, 0, len, this.getLine(len-1).length));
+ this.insert({row: 0, column:0}, text);
+ };
+
+ this.getValue = function() {
+ return this.getAllLines().join(this.getNewLineCharacter());
+ };
+
+ this.createAnchor = function(row, column) {
+ return new Anchor(this, row, column);
+ };
+
+ // check for IE split bug
+ if ("aaa".split(/a/).length == 0)
+ this.$split = function(text) {
+ return text.replace(/\r\n|\r/g, "\n").split("\n");
+ }
+ else
+ this.$split = function(text) {
+ return text.split(/\r\n|\r|\n/);
+ };
+
+
+ this.$detectNewLine = function(text) {
+ var match = text.match(/^.*?(\r?\n)/m);
+ if (match) {
+ this.$autoNewLine = match[1];
+ } else {
+ this.$autoNewLine = "\n";
+ }
+ };
+
+ this.getNewLineCharacter = function() {
+ switch (this.$newLineMode) {
+ case "windows":
+ return "\r\n";
+
+ case "unix":
+ return "\n";
+
+ case "auto":
+ return this.$autoNewLine;
+ }
+ },
+
+ this.$autoNewLine = "\n";
+ this.$newLineMode = "auto";
+ this.setNewLineMode = function(newLineMode) {
+ if (this.$newLineMode === newLineMode) return;
+
+ this.$newLineMode = newLineMode;
+ };
+
+ this.getNewLineMode = function() {
+ return this.$newLineMode;
+ };
+
+ this.isNewLine = function(text) {
+ return (text == "\r\n" || text == "\r" || text == "\n");
+ };
+
+ /**
+ * Get a verbatim copy of the given line as it is in the document
+ */
+ this.getLine = function(row) {
+ return this.$lines[row] || "";
+ };
+
+ this.getLines = function(firstRow, lastRow) {
+ return this.$lines.slice(firstRow, lastRow + 1);
+ };
+
+ /**
+ * Returns all lines in the document as string array. Warning: The caller
+ * should not modify this array!
+ */
+ this.getAllLines = function() {
+ return this.getLines(0, this.getLength());
+ };
+
+ this.getLength = function() {
+ return this.$lines.length;
+ };
+
+ this.getTextRange = function(range) {
+ if (range.start.row == range.end.row) {
+ return this.$lines[range.start.row].substring(range.start.column,
+ range.end.column);
+ }
+ else {
+ var lines = [];
+ lines.push(this.$lines[range.start.row].substring(range.start.column));
+ lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1));
+ lines.push(this.$lines[range.end.row].substring(0, range.end.column));
+ return lines.join(this.getNewLineCharacter());
+ }
+ };
+
+ this.$clipPosition = function(position) {
+ var length = this.getLength();
+ if (position.row >= length) {
+ position.row = Math.max(0, length - 1);
+ position.column = this.getLine(length-1).length;
+ }
+ return position;
+ }
+
+ this.insert = function(position, text) {
+ if (text.length == 0)
+ return position;
+
+ position = this.$clipPosition(position);
+
+ if (this.getLength() <= 1)
+ this.$detectNewLine(text);
+
+ var lines = this.$split(text);
+ var firstLine = lines.splice(0, 1)[0];
+ var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];
+
+ this._dispatchEvent("changeStart");
+ position = this.insertInLine(position, firstLine);
+ if (lastLine !== null) {
+ position = this.insertNewLine(position); // terminate first line
+ position = this.insertLines(position.row, lines);
+ position = this.insertInLine(position, lastLine || "");
+ }
+ this._dispatchEvent("changeEnd");
+ return position;
+ };
+
+ this.insertLines = function(row, lines) {
+ if (lines.length == 0)
+ return {row: row, column: 0};
+
+ var args = [row, 0];
+ args.push.apply(args, lines);
+ this.$lines.splice.apply(this.$lines, args);
+
+ this._dispatchEvent("changeStart");
+ var range = new Range(row, 0, row + lines.length, 0);
+ var delta = {
+ action: "insertLines",
+ range: range,
+ lines: lines
+ };
+ this._dispatchEvent("change", { data: delta });
+ this._dispatchEvent("changeEnd");
+ return range.end;
+ },
+
+ this.insertNewLine = function(position) {
+ position = this.$clipPosition(position);
+ var line = this.$lines[position.row] || "";
+
+ this._dispatchEvent("changeStart");
+ this.$lines[position.row] = line.substring(0, position.column);
+ this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length));
+
+ var end = {
+ row : position.row + 1,
+ column : 0
+ };
+
+ var delta = {
+ action: "insertText",
+ range: Range.fromPoints(position, end),
+ text: this.getNewLineCharacter()
+ };
+ this._dispatchEvent("change", { data: delta });
+ this._dispatchEvent("changeEnd");
+
+ return end;
+ };
+
+ this.insertInLine = function(position, text) {
+ if (text.length == 0)
+ return position;
+
+ var line = this.$lines[position.row] || "";
+
+ this._dispatchEvent("changeStart");
+ this.$lines[position.row] = line.substring(0, position.column) + text
+ + line.substring(position.column);
+
+ var end = {
+ row : position.row,
+ column : position.column + text.length
+ };
+
+ var delta = {
+ action: "insertText",
+ range: Range.fromPoints(position, end),
+ text: text
+ };
+ this._dispatchEvent("change", { data: delta });
+ this._dispatchEvent("changeEnd");
+
+ return end;
+ };
+
+ this.remove = function(range) {
+ // clip to document
+ range.start = this.$clipPosition(range.start);
+ range.end = this.$clipPosition(range.end);
+
+ if (range.isEmpty())
+ return range.start;
+
+ var firstRow = range.start.row;
+ var lastRow = range.end.row;
+
+ this._dispatchEvent("changeStart");
+ if (range.isMultiLine()) {
+ var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;
+ var lastFullRow = lastRow - 1;
+
+ if (range.end.column > 0)
+ this.removeInLine(lastRow, 0, range.end.column);
+
+ if (lastFullRow >= firstFullRow)
+ this.removeLines(firstFullRow, lastFullRow);
+
+ if (firstFullRow != firstRow) {
+ this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length);
+ this.removeNewLine(range.start.row);
+ }
+ }
+ else {
+ this.removeInLine(firstRow, range.start.column, range.end.column);
+ }
+ this._dispatchEvent("changeEnd");
+ return range.start;
+ };
+
+ this.removeInLine = function(row, startColumn, endColumn) {
+ if (startColumn == endColumn)
+ return;
+
+ var range = new Range(row, startColumn, row, endColumn);
+ var line = this.getLine(row);
+ var removed = line.substring(startColumn, endColumn);
+ var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);
+ this._dispatchEvent("changeStart");
+ this.$lines.splice(row, 1, newLine);
+
+ var delta = {
+ action: "removeText",
+ range: range,
+ text: removed
+ };
+ this._dispatchEvent("change", { data: delta });
+ this._dispatchEvent("changeEnd");
+ return range.start;
+ };
+
+ /**
+ * Removes a range of full lines
+ *
+ * @param firstRow {Integer} The first row to be removed
+ * @param lastRow {Integer} The last row to be removed
+ * @return {String[]} The removed lines
+ */
+ this.removeLines = function(firstRow, lastRow) {
+ this._dispatchEvent("changeStart");
+ var range = new Range(firstRow, 0, lastRow + 1, 0);
+ var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
+
+ var delta = {
+ action: "removeLines",
+ range: range,
+ nl: this.getNewLineCharacter(),
+ lines: removed
+ };
+ this._dispatchEvent("change", { data: delta });
+ this._dispatchEvent("changeEnd");
+ return removed;
+ };
+
+ this.removeNewLine = function(row) {
+ var firstLine = this.getLine(row);
+ var secondLine = this.getLine(row+1);
+
+ var range = new Range(row, firstLine.length, row+1, 0);
+ var line = firstLine + secondLine;
+
+ this._dispatchEvent("changeStart");
+ this.$lines.splice(row, 2, line);
+
+ var delta = {
+ action: "removeText",
+ range: range,
+ text: this.getNewLineCharacter()
+ };
+ this._dispatchEvent("change", { data: delta });
+ this._dispatchEvent("changeEnd");
+ };
+
+ this.replace = function(range, text) {
+ if (text.length == 0 && range.isEmpty())
+ return range.start;
+
+ // Shortcut: If the text we want to insert is the same as it is already
+ // in the document, we don't have to replace anything.
+ if (text == this.getTextRange(range))
+ return range.end;
+
+ this._dispatchEvent("changeStart");
+ this.remove(range);
+ if (text) {
+ var end = this.insert(range.start, text);
+ }
+ else {
+ end = range.start;
+ }
+ this._dispatchEvent("changeEnd");
+
+ return end;
+ };
+
+ this.applyDeltas = function(deltas) {
+ for (var i=0; i<deltas.length; i++) {
+ var delta = deltas[i];
+ var range = Range.fromPoints(delta.range.start, delta.range.end);
+
+ if (delta.action == "insertLines")
+ this.insertLines(range.start.row, delta.lines)
+ else if (delta.action == "insertText")
+ this.insert(range.start, delta.text)
+ else if (delta.action == "removeLines")
+ this.removeLines(range.start.row, range.end.row - 1)
+ else if (delta.action == "removeText")
+ this.remove(range)
+ }
+ };
+
+ this.revertDeltas = function(deltas) {
+ for (var i=deltas.length-1; i>=0; i--) {
+ var delta = deltas[i];
+
+ var range = Range.fromPoints(delta.range.start, delta.range.end);
+
+ if (delta.action == "insertLines")
+ this.removeLines(range.start.row, range.end.row - 1)
+ else if (delta.action == "insertText")
+ this.remove(range)
+ else if (delta.action == "removeLines")
+ this.insertLines(range.start.row, delta.lines)
+ else if (delta.action == "removeText")
+ this.insert(range.start, delta.text)
+ }
+ };
+
+}).call(Document.prototype);
+
+exports.Document = Document;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/anchor', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/event_emitter'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+
+/**
+ * An Anchor is a floating pointer in the document. Whenever text is inserted or
+ * deleted before the cursor, the position of the cursor is updated
+ */
+var Anchor = exports.Anchor = function(doc, row, column) {
+ this.document = doc;
+
+ if (typeof column == "undefined")
+ this.setPosition(row.row, row.column);
+ else
+ this.setPosition(row, column);
+
+ this.$onChange = this.onChange.bind(this);
+ doc.on("change", this.$onChange);
+};
+
+(function() {
+
+ oop.implement(this, EventEmitter);
+
+ this.getPosition = function() {
+ return this.$clipPositionToDocument(this.row, this.column);
+ };
+
+ this.getDocument = function() {
+ return this.document;
+ };
+
+ this.onChange = function(e) {
+ var delta = e.data;
+ var range = delta.range;
+
+ if (range.start.row == range.end.row && range.start.row != this.row)
+ return;
+
+ if (range.start.row > this.row)
+ return;
+
+ if (range.start.row == this.row && range.start.column > this.column)
+ return;
+
+ var row = this.row;
+ var column = this.column;
+
+ if (delta.action === "insertText") {
+ if (range.start.row === row && range.start.column <= column) {
+ if (range.start.row === range.end.row) {
+ column += range.end.column - range.start.column;
+ }
+ else {
+ column -= range.start.column;
+ row += range.end.row - range.start.row;
+ }
+ }
+ else if (range.start.row !== range.end.row && range.start.row < row) {
+ row += range.end.row - range.start.row;
+ }
+ } else if (delta.action === "insertLines") {
+ if (range.start.row <= row) {
+ row += range.end.row - range.start.row;
+ }
+ }
+ else if (delta.action == "removeText") {
+ if (range.start.row == row && range.start.column < column) {
+ if (range.end.column >= column)
+ column = range.start.column;
+ else
+ column = Math.max(0, column - (range.end.column - range.start.column));
+
+ } else if (range.start.row !== range.end.row && range.start.row < row) {
+ if (range.end.row == row) {
+ column = Math.max(0, column - range.end.column) + range.start.column;
+ }
+ row -= (range.end.row - range.start.row);
+ }
+ else if (range.end.row == row) {
+ row -= range.end.row - range.start.row;
+ column = Math.max(0, column - range.end.column) + range.start.column;
+ }
+ } else if (delta.action == "removeLines") {
+ if (range.start.row <= row) {
+ if (range.end.row <= row)
+ row -= range.end.row - range.start.row;
+ else {
+ row = range.start.row;
+ column = 0;
+ }
+ }
+ }
+
+ this.setPosition(row, column, true);
+ };
+
+ this.setPosition = function(row, column, noClip) {
+ if (noClip) {
+ pos = {
+ row: row,
+ column: column
+ };
+ }
+ else {
+ pos = this.$clipPositionToDocument(row, column);
+ }
+
+ if (this.row == pos.row && this.column == pos.column)
+ return;
+
+ var old = {
+ row: this.row,
+ column: this.column
+ };
+
+ this.row = pos.row;
+ this.column = pos.column;
+ this._dispatchEvent("change", {
+ old: old,
+ value: pos
+ });
+ };
+
+ this.detach = function() {
+ this.document.removeEventListener("change", this.$onChange);
+ };
+
+ this.$clipPositionToDocument = function(row, column) {
+ var pos = {};
+
+ if (row >= this.document.getLength()) {
+ pos.row = Math.max(0, this.document.getLength() - 1);
+ pos.column = this.document.getLine(pos.row).length;
+ }
+ else if (row < 0) {
+ pos.row = 0;
+ pos.column = 0;
+ }
+ else {
+ pos.row = row;
+ pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
+ }
+
+ if (column < 0)
+ pos.column = 0;
+
+ return pos;
+ };
+
+}).call(Anchor.prototype);
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/background_tokenizer', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/event_emitter'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+
+var BackgroundTokenizer = function(tokenizer, editor) {
+ this.running = false;
+ this.lines = [];
+ this.currentLine = 0;
+ this.tokenizer = tokenizer;
+
+ var self = this;
+
+ this.$worker = function() {
+ if (!self.running) { return; }
+
+ var workerStart = new Date();
+ var startLine = self.currentLine;
+ var doc = self.doc;
+
+ var processedLines = 0;
+
+ var len = doc.getLength();
+ while (self.currentLine < len) {
+ self.lines[self.currentLine] = self.$tokenizeRows(self.currentLine, self.currentLine)[0];
+ self.currentLine++;
+
+ // only check every 5 lines
+ processedLines += 1;
+ if ((processedLines % 5 == 0) && (new Date() - workerStart) > 20) {
+ self.fireUpdateEvent(startLine, self.currentLine-1);
+ self.running = setTimeout(self.$worker, 20);
+ return;
+ }
+ }
+
+ self.running = false;
+
+ self.fireUpdateEvent(startLine, len - 1);
+ };
+};
+
+(function(){
+
+ oop.implement(this, EventEmitter);
+
+ this.setTokenizer = function(tokenizer) {
+ this.tokenizer = tokenizer;
+ this.lines = [];
+
+ this.start(0);
+ };
+
+ this.setDocument = function(doc) {
+ this.doc = doc;
+ this.lines = [];
+
+ this.stop();
+ };
+
+ this.fireUpdateEvent = function(firstRow, lastRow) {
+ var data = {
+ first: firstRow,
+ last: lastRow
+ };
+ this._dispatchEvent("update", {data: data});
+ };
+
+ this.start = function(startRow) {
+ this.currentLine = Math.min(startRow || 0, this.currentLine,
+ this.doc.getLength());
+
+ // remove all cached items below this line
+ this.lines.splice(this.currentLine, this.lines.length);
+
+ this.stop();
+ // pretty long delay to prevent the tokenizer from interfering with the user
+ this.running = setTimeout(this.$worker, 700);
+ };
+
+ this.stop = function() {
+ if (this.running)
+ clearTimeout(this.running);
+ this.running = false;
+ };
+
+ this.getTokens = function(firstRow, lastRow) {
+ return this.$tokenizeRows(firstRow, lastRow);
+ };
+
+ this.getState = function(row) {
+ return this.$tokenizeRows(row, row)[0].state;
+ };
+
+ this.$tokenizeRows = function(firstRow, lastRow) {
+ if (!this.doc)
+ return [];
+
+ var rows = [];
+
+ // determine start state
+ var state = "start";
+ var doCache = false;
+ if (firstRow > 0 && this.lines[firstRow - 1]) {
+ state = this.lines[firstRow - 1].state;
+ doCache = true;
+ } else if (firstRow == 0) {
+ state = "start";
+ doCache = true;
+ } else if (this.lines.length > 0) {
+ // Guess that we haven't changed state.
+ state = this.lines[this.lines.length-1].state;
+ }
+
+ var lines = this.doc.getLines(firstRow, lastRow);
+ for (var row=firstRow; row<=lastRow; row++) {
+ if (!this.lines[row]) {
+ var tokens = this.tokenizer.getLineTokens(lines[row-firstRow] || "", state);
+ var state = tokens.state;
+ rows.push(tokens);
+
+ if (doCache) {
+ this.lines[row] = tokens;
+ }
+ }
+ else {
+ var tokens = this.lines[row];
+ state = tokens.state;
+ rows.push(tokens);
+ }
+ }
+ return rows;
+ };
+
+}).call(BackgroundTokenizer.prototype);
+
+exports.BackgroundTokenizer = BackgroundTokenizer;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Julian Viereck <julian DOT viereck AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/edit_session/folding', ['require', 'exports', 'module' , 'ace/range', 'ace/edit_session/fold_line'], function(require, exports, module) {
+
+var Range = require("ace/range").Range;
+var FoldLine = require("ace/edit_session/fold_line").FoldLine;
+
+/**
+ * Simple fold-data struct.
+ **/
+function Fold(range, placeholder) {
+ this.foldLine = null;
+ this.placeholder = placeholder;
+ this.range = range;
+ this.start = range.start;
+ this.end = range.end;
+
+ this.sameRow = range.start.row == range.end.row;
+ this.subFolds = [];
+}
+
+Fold.prototype.toString = function() {
+ return '"' + this.placeholder + '" ' + this.range.toString();
+}
+
+Fold.prototype.setFoldLine = function(foldLine) {
+ this.foldLine = foldLine;
+ this.subFolds.forEach(function(fold) {
+ fold.setFoldLine(foldLine);
+ });
+}
+
+Fold.prototype.clone = function() {
+ var range = this.range.clone();
+ var fold = new Fold(range, this.placeholder);
+ this.subFolds.forEach(function(subFold) {
+ fold.subFolds.push(subFold.clone());
+ });
+ return fold;
+}
+
+function Folding() {
+ /**
+ * Looks up a fold at a given row/column. Possible values for side:
+ * -1: ignore a fold if fold.start = row/column
+ * +1: ignore a fold if fold.end = row/column
+ */
+ this.getFoldAt = function(row, column, side) {
+ var foldLine = this.getFoldLine(row);
+ if (foldLine) {
+ var folds = foldLine.folds,
+ fold;
+
+ for (var i = 0; i < folds.length; i++) {
+ fold = folds[i];
+ if (fold.range.contains(row, column)) {
+ if (side == 1 && fold.range.isEnd(row, column)) {
+ continue;
+ } else if (side == -1 && fold.range.isStart(row, column)) {
+ continue;
+ }
+ return fold;
+ }
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns all folds in the given range. Note, that this will return folds
+ *
+ */
+ this.getFoldsInRange = function(range) {
+ range = range.clone();
+ var start = range.start,
+ end = range.end;
+ var foldLines = this.$foldData,
+ folds,
+ fold;
+ var cmp,
+ foundFolds = [];
+
+ start.column += 1;
+ end.column -= 1;
+
+ for (var i = 0; i < foldLines.length; i++) {
+ cmp = foldLines[i].range.compareRange(range);
+ // Range is before foldLine. No intersection. This means,
+ // there might be other foldLines that intersect.
+ if (cmp == 2) {
+ continue;
+ } else
+ // Range is after foldLine. There can't be any other foldLines then,
+ // so let's give up.
+ if (cmp == -2) {
+ break;
+ }
+
+ folds = foldLines[i].folds;
+ for (var j = 0; j < folds.length; j++) {
+ fold = folds[j];
+ cmp = fold.range.compareRange(range);
+ if (cmp == -2) {
+ break;
+ } else if (cmp == 2) {
+ continue;
+ } else
+ // WTF-state: Can happen due to -1/+1 to start/end column.
+ if (cmp == 42) {
+ break;
+ }
+ foundFolds.push(fold);
+ }
+ }
+ return foundFolds;
+ }
+
+ /**
+ * Returns the string between folds at the given position.
+ * E.g.
+ * foo<fold>b|ar<fold>wolrd -> "bar"
+ * foo<fold>bar<fold>wol|rd -> "world"
+ * foo<fold>bar<fo|ld>wolrd -> <null>
+ *
+ * where | means the position of row/column
+ *
+ * The trim option determs if the return string should be trimed according
+ * to the "side" passed with the trim value:
+ *
+ * E.g.
+ * foo<fold>b|ar<fold>wolrd -trim=-1> "b"
+ * foo<fold>bar<fold>wol|rd -trim=+1> "rld"
+ * fo|o<fold>bar<fold>wolrd -trim=00> "foo"
+ */
+ this.getFoldStringAt = function(row, column, trim, foldLine) {
+ var foldLine = foldLine || this.getFoldLine(row);
+ if (!foldLine) {
+ return null;
+ } else {
+ var fold, lastFold, cmp, str;
+ lastFold = {
+ end: { column: 0 }
+ };
+ // TODO: Refactor to use getNextFoldTo function.
+ for (var i = 0; i < foldLine.folds.length; i++) {
+ fold = foldLine.folds[i];
+ cmp = fold.range.compareEnd(row, column);
+ if (cmp == -1) {
+ str = this.getLine(fold.start.row).
+ substring(lastFold.end.column, fold.start.column);
+ break;
+ } else if (cmp == 0) {
+ return null;
+ }
+ lastFold = fold;
+ }
+ if (!str) {
+ str = this.getLine(fold.start.row).
+ substring(lastFold.end.column);
+ }
+ if (trim == -1) {
+ return str.substring(0, column - lastFold.end.column);
+ } else if (trim == 1) {
+ return str.substring(column - lastFold.end.column)
+ } else {
+ return str;
+ }
+ }
+ }
+
+ this.getFoldLine = function(docRow, startFoldLine) {
+ var foldData = this.$foldData;
+ var i = 0;
+ if(startFoldLine)
+ i = foldData.indexOf(startFoldLine);
+ if(i == -1)
+ i = 0;
+ for (i; i < foldData.length; i++) {
+ var foldLine = foldData[i];
+ if (foldLine.start.row <= docRow && foldLine.end.row >= docRow) {
+ return foldLine;
+ } else if (foldLine.end.row > docRow) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ // returns the fold which starts after or contains docRow
+ this.getNextFold = function(docRow, startFoldLine) {
+ var foldData = this.$foldData, ans;
+ var i = 0;
+ if(startFoldLine)
+ i = foldData.indexOf(startFoldLine);
+ if(i == -1)
+ i = 0;
+ for (i; i < foldData.length; i++) {
+ var foldLine = foldData[i];
+ if (foldLine.end.row >= docRow) {
+ return foldLine;
+ }
+ }
+ return null;
+ }
+
+ this.getFoldedRowCount = function(first, last) {
+ var foldData = this.$foldData, rowCount = last-first+1;
+ for (var i = 0; i < foldData.length; i++) {
+ var foldLine = foldData[i],
+ end = foldLine.end.row,
+ start = foldLine.start.row;
+ if(end >= last) {
+ if(start < last) {
+ if(start >= first)
+ rowCount -= last-start;
+ else
+ rowCount = 0;//in one fold
+ }
+ break;
+ } else if(end >= first){
+ if (start >= first) //fold inside range
+ rowCount -= end-start;
+ else
+ rowCount -= end-first+1;
+ }
+ }
+ return rowCount;
+ }
+
+ this.$addFoldLine = function(foldLine) {
+ this.$foldData.push(foldLine);
+ this.$foldData.sort(function(a, b) {
+ return a.start.row - b.start.row;
+ });
+ return foldLine;
+ }
+
+ /**
+ * Adds a new fold.
+ *
+ * @returns
+ * The new created Fold object or an existing fold object in case the
+ * passed in range fits an existing fold exactly.
+ */
+ this.addFold = function(placeholder, startRow, startColumn, endRow, endColumn) {
+ var range;
+ var foldData = this.$foldData;
+ var foldRow = null;
+ var foldLine;
+ var fold;
+ var argsFold;
+ var folds;
+ var added = false;
+
+ if (placeholder instanceof Fold) {
+ argsFold = placeholder;
+ startRow = argsFold.range;
+ placeholder = argsFold.placeholder;
+ }
+
+ // Normalize parameters.
+ if (!(startRow instanceof Range)) {
+ range = new Range(startRow, startColumn, endRow, endColumn);
+ } else {
+ range = startRow;
+ startRow = range.start.row;
+ startColumn = range.start.column;
+ endRow = range.end.row;
+ endColumn = range.end.column;
+ }
+
+ // --- Some checking ---
+ if (placeholder.length < 2) {
+ throw "Placeholder has to be at least 2 characters";
+ }
+
+ if (startRow == endRow && endColumn - startColumn < 2) {
+ throw "The range has to be at least 2 characters width";
+ }
+
+ fold = this.getFoldAt(startRow, startColumn, 1);
+ if (fold
+ && fold.range.isEnd(endRow, endColumn)
+ && fold.range.isStart(startRow, startColumn))
+ {
+ return fold;
+ }
+
+ fold = this.getFoldAt(startRow, startColumn, 1);
+ if (fold && !fold.range.isStart(startRow, startColumn)) {
+ throw "A fold can't start inside of an already existing fold";
+ }
+
+ fold = this.getFoldAt(endRow, endColumn, -1);
+ if (fold && !fold.range.isEnd(endRow, endColumn)) {
+ throw "A fold can't end inside of an already existing fold";
+ }
+
+ if (endRow >= this.doc.getLength()) {
+ throw "End of fold is outside of the document.";
+ }
+
+ if (endColumn > this.getLine(endRow).length
+ || startColumn > this.getLine(startRow).length)
+ {
+ throw "End of fold is outside of the document.";
+ }
+
+ // --- Start adding the fold ---
+ // Use the passed in fold or create a new one.
+ fold = argsFold || new Fold(range, placeholder);
+
+ // Check if there are folds in the range we create the new fold for.
+ folds = this.getFoldsInRange(range);
+ if (folds.length > 0) {
+ // Remove the folds from fold data.
+ this.removeFolds(folds);
+ // Add the removed folds as subfolds on the new fold.
+ fold.subFolds = folds;
+ }
+
+ for (var i = 0; i < foldData.length; i++) {
+ foldLine = foldData[i];
+ if (endRow == foldLine.start.row) {
+ foldLine.addFold(fold);
+ added = true;
+ break;
+ } else if (startRow == foldLine.end.row) {
+ foldLine.addFold(fold);
+ added = true;
+ if (!fold.sameRow) {
+ // Check if we might have to merge two FoldLines.
+ foldLineNext = foldData[i + 1];
+ if (foldLineNext && foldLineNext.start.row == endRow) {
+ // We need to merge!
+ foldLine.merge(foldLineNext);
+ break;
+ }
+ }
+ break;
+ } else if (endRow <= foldLine.start.row) {
+ break;
+ }
+ }
+
+ if (!added) {
+ foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold));
+ }
+
+ if (this.$useWrapMode) {
+ this.$updateWrapData(foldLine.start.row, foldLine.start.row);
+ }
+
+ // Notify that fold data has changed.
+ this.$modified = true;
+ this._dispatchEvent("changeFold", { data: fold });
+
+ return fold;
+ };
+
+ this.addFolds = function(folds) {
+ folds.forEach(function(fold) {
+ this.addFold(fold);
+ }, this);
+ };
+
+ this.removeFold = function(fold) {
+ var foldLine = fold.foldLine;
+ var startRow = foldLine.start.row;
+ var endRow = foldLine.end.row;
+
+ var foldLines = this.$foldData,
+ folds = foldLine.folds;
+ // Simple case where there is only one fold in the FoldLine such that
+ // the entire fold line can get removed directly.
+ if (folds.length == 1) {
+ foldLines.splice(foldLines.indexOf(foldLine), 1);
+ } else
+ // If the fold is the last fold of the foldLine, just remove it.
+ if (foldLine.range.isEnd(fold.end.row, fold.end.column)) {
+ folds.pop();
+ foldLine.end.row = folds[folds.length - 1].end.row;
+ foldLine.end.column = folds[folds.length - 1].end.column;
+ } else
+ // If the fold is the first fold of the foldLine, just remove it.
+ if (foldLine.range.isStart(fold.start.row, fold.start.column)) {
+ folds.shift();
+ foldLine.start.row = folds[0].start.row;
+ foldLine.start.column = folds[0].start.column;
+ } else
+ // We know there are more then 2 folds and the fold is not at the edge.
+ // This means, the fold is somewhere in between.
+ //
+ // If the fold is in one row, we just can remove it.
+ if (fold.sameRow) {
+ folds.splice(folds.indexOf(fold), 1);
+ } else
+ // The fold goes over more then one row. This means remvoing this fold
+ // will cause the fold line to get splitted up.
+ {
+ var newFoldLine = foldLine.split(fold.start.row, fold.start.column);
+ newFoldLine.folds.shift();
+ foldLine.start.row = folds[0].start.row;
+ foldLine.start.column = folds[0].start.column;
+ this.$addFoldLine(newFoldLine);
+ }
+
+ if (this.$useWrapMode) {
+ this.$updateWrapData(startRow, endRow);
+ }
+
+ // Notify that fold data has changed.
+ this.$modified = true;
+ this._dispatchEvent("changeFold", { data: fold });
+ }
+
+ this.removeFolds = function(folds) {
+ // We need to clone the folds array passed in as it might be the folds
+ // array of a fold line and as we call this.removeFold(fold), folds
+ // are removed from folds and changes the current index.
+ var cloneFolds = [];
+ for (var i = 0; i < folds.length; i++) {
+ cloneFolds.push(folds[i]);
+ }
+
+ cloneFolds.forEach(function(fold) {
+ this.removeFold(fold);
+ }, this);
+ this.$modified = true;
+ }
+
+ this.expandFold = function(fold) {
+ this.removeFold(fold);
+ fold.subFolds.forEach(function(fold) {
+ this.addFold(fold);
+ }, this);
+ fold.subFolds = [];
+ }
+
+ this.expandFolds = function(folds) {
+ folds.forEach(function(fold) {
+ this.expandFold(fold);
+ }, this);
+ }
+
+ /**
+ * Checks if a given documentRow is folded. This is true if there are some
+ * folded parts such that some parts of the line is still visible.
+ **/
+ this.isRowFolded = function(docRow, startFoldRow) {
+ return !!this.getFoldLine(docRow, startFoldRow);
+ };
+
+ this.getRowFoldEnd = function(docRow, startFoldRow) {
+ var foldLine = this.getFoldLine(docRow, startFoldRow);
+ return (foldLine
+ ? foldLine.end.row
+ : docRow)
+ };
+
+ this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) {
+ if (startRow == null) {
+ startRow = foldLine.start.row;
+ startColumn = 0;
+ }
+
+ if (endRow == null) {
+ endRow = foldLine.end.row;
+ endColumn = this.getLine(endRow).length;
+ }
+
+ // Build the textline using the FoldLine walker.
+ var line = "";
+ var doc = this.doc;
+ var textLine = "";
+
+ foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) {
+ if (row < startRow) {
+ return;
+ } else if (row == startRow) {
+ if (column < startColumn) {
+ return;
+ }
+ lastColumn = Math.max(startColumn, lastColumn);
+ }
+ if (placeholder) {
+ textLine += placeholder;
+ } else {
+ textLine += doc.getLine(row).substring(lastColumn, column);
+ }
+ }.bind(this), endRow, endColumn);
+ return textLine;
+ };
+
+ this.getDisplayLine = function(row, endColumn, startRow, startColumn) {
+ var foldLine = this.getFoldLine(row);
+
+ if (!foldLine) {
+ var line;
+ line = this.doc.getLine(row);
+ return line.substring(startColumn || 0, endColumn || line.length);
+ } else {
+ return this.getFoldDisplayLine(
+ foldLine, row, endColumn, startRow, startColumn);
+ }
+ };
+
+ this.$cloneFoldData = function() {
+ var foldData = this.$foldData;
+ var fd = [];
+ fd = this.$foldData.map(function(foldLine) {
+ var folds = foldLine.folds.map(function(fold) {
+ return fold.clone();
+ });
+ return new FoldLine(fd, folds);
+ });
+
+ return fd;
+ };
+}
+
+exports.Folding = Folding;
+
+});/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Julian Viereck <julian DOT viereck AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/edit_session/fold_line', ['require', 'exports', 'module' , 'ace/range'], function(require, exports, module) {
+
+var Range = require("ace/range").Range;
+
+/**
+ * If the an array is passed in, the folds are expected to be sorted already.
+ */
+function FoldLine(foldData, folds) {
+ this.foldData = foldData;
+ if (Array.isArray(folds)) {
+ this.folds = folds;
+ } else {
+ folds = this.folds = [ folds ];
+ }
+
+ var last = folds[folds.length - 1]
+ this.range = new Range(folds[0].start.row, folds[0].start.column,
+ last.end.row, last.end.column);
+ this.start = this.range.start;
+ this.end = this.range.end;
+
+ this.folds.forEach(function(fold) {
+ fold.setFoldLine(this);
+ }, this);
+}
+
+(function() {
+ /**
+ * Note: This doesn't update wrapData!
+ */
+ this.shiftRow = function(shift) {
+ this.start.row += shift;
+ this.end.row += shift;
+ this.folds.forEach(function(fold) {
+ fold.start.row += shift;
+ fold.end.row += shift;
+ });
+ }
+
+ this.addFold = function(fold) {
+ if (fold.sameRow) {
+ if (fold.start.row < this.startRow || fold.endRow > this.endRow) {
+ throw "Can't add a fold to this FoldLine as it has no connection";
+ }
+ this.folds.push(fold);
+ this.folds.sort(function(a, b) {
+ return -a.range.compareEnd(b.start.row, b.start.column);
+ });
+ if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) {
+ this.end.row = fold.end.row;
+ this.end.column = fold.end.column;
+ } else if (this.range.compareStart(fold.end.row, fold.end.column) < 0) {
+ this.start.row = fold.start.row;
+ this.start.column = fold.start.column;
+ }
+ } else if (fold.start.row == this.end.row) {
+ this.folds.push(fold);
+ this.end.row = fold.end.row;
+ this.end.column = fold.end.column;
+ } else if (fold.end.row == this.start.row) {
+ this.folds.unshift(fold);
+ this.start.row = fold.start.row;
+ this.start.column = fold.start.column;
+ } else {
+ throw "Trying to add fold to FoldRow that doesn't have a matching row";
+ }
+ fold.foldLine = this;
+ }
+
+ this.containsRow = function(row) {
+ return row >= this.start.row && row <= this.end.row;
+ }
+
+ this.walk = function(callback, endRow, endColumn) {
+ var lastEnd = 0,
+ folds = this.folds,
+ fold,
+ comp, stop, isNewRow = true;
+
+ if (endRow == null) {
+ endRow = this.end.row;
+ endColumn = this.end.column;
+ }
+
+ for (var i = 0; i < folds.length; i++) {
+ fold = folds[i];
+
+ comp = fold.range.compareStart(endRow, endColumn);
+ // This fold is after the endRow/Column.
+ if (comp == -1) {
+ callback(null, endRow, endColumn, lastEnd, isNewRow);
+ return;
+ }
+
+ stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow);
+ stop = !stop && callback(fold.placeholder, fold.start.row, fold.start.column, lastEnd);
+
+ // If the user requested to stop the walk or endRow/endColumn is
+ // inside of this fold (comp == 0), then end here.
+ if (stop || comp == 0) {
+ return;
+ }
+
+ // Note the new lastEnd might not be on the same line. However,
+ // it's the callback's job to recognize this.
+ isNewRow = !fold.sameRow;
+ lastEnd = fold.end.column;
+ }
+ callback(null, endRow, endColumn, lastEnd, isNewRow);
+ }
+
+ this.getNextFoldTo = function(row, column) {
+ var fold, cmp;
+ for (var i = 0; i < this.folds.length; i++) {
+ fold = this.folds[i];
+ cmp = fold.range.compareEnd(row, column);
+ if (cmp == -1) {
+ return {
+ fold: fold,
+ kind: "after"
+ };
+ } else if (cmp == 0) {
+ return {
+ fold: fold,
+ kind: "inside"
+ }
+ }
+ }
+ return null;
+ }
+
+ this.addRemoveChars = function(row, column, len) {
+ var ret = this.getNextFoldTo(row, column),
+ fold, folds;
+ if (ret) {
+ fold = ret.fold;
+ if (ret.kind == "inside"
+ && fold.start.column != column
+ && fold.start.row != row)
+ {
+ throw "Moving characters inside of a fold should never be reached";
+ } else if (fold.start.row == row) {
+ folds = this.folds;
+ var i = folds.indexOf(fold);
+ if (i == 0) {
+ this.start.column += len;
+ }
+ for (i; i < folds.length; i++) {
+ fold = folds[i];
+ fold.start.column += len;
+ if (!fold.sameRow) {
+ return;
+ }
+ fold.end.column += len;
+ }
+ this.end.column += len;
+ }
+ }
+ }
+
+ this.split = function(row, column) {
+ var fold = this.getNextFoldTo(row, column).fold,
+ folds = this.folds;
+ var foldData = this.foldData;
+
+ if (!fold) {
+ return null;
+ }
+ var i = folds.indexOf(fold);
+ var foldBefore = folds[i - 1];
+ this.end.row = foldBefore.end.row;
+ this.end.column = foldBefore.end.column;
+
+ // Remove the folds after row/column and create a new FoldLine
+ // containing these removed folds.
+ folds = folds.splice(i, folds.length - i);
+
+ var newFoldLine = new FoldLine(foldData, folds);
+ foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine);
+ return newFoldLine;
+ }
+
+ this.merge = function(foldLineNext) {
+ var folds = foldLineNext.folds;
+ for (var i = 0; i < folds.length; i++) {
+ this.addFold(folds[i]);
+ }
+ // Remove the foldLineNext - no longer needed, as
+ // it's merged now with foldLineNext.
+ var foldData = this.foldData;
+ foldData.splice(foldData.indexOf(foldLineNext), 1);
+ }
+
+ this.toString = function() {
+ var ret = [this.range.toString() + ": [" ];
+
+ this.folds.forEach(function(fold) {
+ ret.push(" " + fold.toString());
+ });
+ ret.push("]")
+ return ret.join("\n");
+ }
+
+ this.idxToPosition = function(idx) {
+ var lastFoldEndColumn = 0;
+ var fold;
+
+ for (var i = 0; i < this.folds.length; i++) {
+ var fold = this.folds[i];
+
+ idx -= fold.start.column - lastFoldEndColumn;
+ if (idx < 0) {
+ return {
+ row: fold.start.row,
+ column: fold.start.column + idx
+ };
+ }
+
+ idx -= fold.placeholder.length;
+ if (idx < 0) {
+ return fold.start;
+ }
+
+ lastFoldEndColumn = fold.end.column;
+ }
+
+ return {
+ row: this.end.row,
+ column: this.end.column + idx
+ };
+ }
+}).call(FoldLine.prototype);
+
+exports.FoldLine = FoldLine;
+});/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Mihai Sucan <mihai DOT sucan AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/search', ['require', 'exports', 'module' , 'pilot/lang', 'pilot/oop', 'ace/range'], function(require, exports, module) {
+
+var lang = require("pilot/lang");
+var oop = require("pilot/oop");
+var Range = require("ace/range").Range;
+
+var Search = function() {
+ this.$options = {
+ needle: "",
+ backwards: false,
+ wrap: false,
+ caseSensitive: false,
+ wholeWord: false,
+ scope: Search.ALL,
+ regExp: false
+ };
+};
+
+Search.ALL = 1;
+Search.SELECTION = 2;
+
+(function() {
+
+ this.set = function(options) {
+ oop.mixin(this.$options, options);
+ return this;
+ };
+
+ this.getOptions = function() {
+ return lang.copyObject(this.$options);
+ };
+
+ this.find = function(session) {
+ if (!this.$options.needle)
+ return null;
+
+ if (this.$options.backwards) {
+ var iterator = this.$backwardMatchIterator(session);
+ } else {
+ iterator = this.$forwardMatchIterator(session);
+ }
+
+ var firstRange = null;
+ iterator.forEach(function(range) {
+ firstRange = range;
+ return true;
+ });
+
+ return firstRange;
+ };
+
+ this.findAll = function(session) {
+ if (!this.$options.needle)
+ return [];
+
+ if (this.$options.backwards) {
+ var iterator = this.$backwardMatchIterator(session);
+ } else {
+ iterator = this.$forwardMatchIterator(session);
+ }
+
+ var ranges = [];
+ iterator.forEach(function(range) {
+ ranges.push(range);
+ });
+
+ return ranges;
+ };
+
+ this.replace = function(input, replacement) {
+ var re = this.$assembleRegExp();
+ var match = re.exec(input);
+ if (match && match[0].length == input.length) {
+ if (this.$options.regExp) {
+ return input.replace(re, replacement);
+ } else {
+ return replacement;
+ }
+ } else {
+ return null;
+ }
+ };
+
+ this.$forwardMatchIterator = function(session) {
+ var re = this.$assembleRegExp();
+ var self = this;
+
+ return {
+ forEach: function(callback) {
+ self.$forwardLineIterator(session).forEach(function(line, startIndex, row) {
+ if (startIndex) {
+ line = line.substring(startIndex);
+ }
+
+ var matches = [];
+
+ line.replace(re, function(str) {
+ var offset = arguments[arguments.length-2];
+ matches.push({
+ str: str,
+ offset: startIndex + offset
+ });
+ return str;
+ });
+
+ for (var i=0; i<matches.length; i++) {
+ var match = matches[i];
+ var range = self.$rangeFromMatch(row, match.offset, match.str.length);
+ if (callback(range))
+ return true;
+ }
+
+ });
+ }
+ };
+ };
+
+ this.$backwardMatchIterator = function(session) {
+ var re = this.$assembleRegExp();
+ var self = this;
+
+ return {
+ forEach: function(callback) {
+ self.$backwardLineIterator(session).forEach(function(line, startIndex, row) {
+ if (startIndex) {
+ line = line.substring(startIndex);
+ }
+
+ var matches = [];
+
+ line.replace(re, function(str, offset) {
+ matches.push({
+ str: str,
+ offset: startIndex + offset
+ });
+ return str;
+ });
+
+ for (var i=matches.length-1; i>= 0; i--) {
+ var match = matches[i];
+ var range = self.$rangeFromMatch(row, match.offset, match.str.length);
+ if (callback(range))
+ return true;
+ }
+ });
+ }
+ };
+ };
+
+ this.$rangeFromMatch = function(row, column, length) {
+ return new Range(row, column, row, column+length);
+ };
+
+ this.$assembleRegExp = function() {
+ if (this.$options.regExp) {
+ var needle = this.$options.needle;
+ } else {
+ needle = lang.escapeRegExp(this.$options.needle);
+ }
+
+ if (this.$options.wholeWord) {
+ needle = "\\b" + needle + "\\b";
+ }
+
+ var modifier = "g";
+ if (!this.$options.caseSensitive) {
+ modifier += "i";
+ }
+
+ var re = new RegExp(needle, modifier);
+ return re;
+ };
+
+ this.$forwardLineIterator = function(session) {
+ var searchSelection = this.$options.scope == Search.SELECTION;
+
+ var range = session.getSelection().getRange();
+ var start = session.getSelection().getCursor();
+
+ var firstRow = searchSelection ? range.start.row : 0;
+ var firstColumn = searchSelection ? range.start.column : 0;
+ var lastRow = searchSelection ? range.end.row : session.getLength() - 1;
+
+ var wrap = this.$options.wrap;
+ var inWrap = false;
+
+ function getLine(row) {
+ var line = session.getLine(row);
+ if (searchSelection && row == range.end.row) {
+ line = line.substring(0, range.end.column);
+ }
+ if (inWrap && row == start.row) {
+ line = line.substring(0, start.column);
+ }
+ return line;
+ }
+
+ return {
+ forEach: function(callback) {
+ var row = start.row;
+
+ var line = getLine(row);
+ var startIndex = start.column;
+
+ var stop = false;
+ inWrap = false;
+
+ while (!callback(line, startIndex, row)) {
+
+ if (stop) {
+ return;
+ }
+
+ row++;
+ startIndex = 0;
+
+ if (row > lastRow) {
+ if (wrap) {
+ row = firstRow;
+ startIndex = firstColumn;
+ inWrap = true;
+ } else {
+ return;
+ }
+ }
+
+ if (row == start.row)
+ stop = true;
+
+ line = getLine(row);
+ }
+ }
+ };
+ };
+
+ this.$backwardLineIterator = function(session) {
+ var searchSelection = this.$options.scope == Search.SELECTION;
+
+ var range = session.getSelection().getRange();
+ var start = searchSelection ? range.end : range.start;
+
+ var firstRow = searchSelection ? range.start.row : 0;
+ var firstColumn = searchSelection ? range.start.column : 0;
+ var lastRow = searchSelection ? range.end.row : session.getLength() - 1;
+
+ var wrap = this.$options.wrap;
+
+ return {
+ forEach : function(callback) {
+ var row = start.row;
+
+ var line = session.getLine(row).substring(0, start.column);
+ var startIndex = 0;
+ var stop = false;
+ var inWrap = false;
+
+ while (!callback(line, startIndex, row)) {
+
+ if (stop)
+ return;
+
+ row--;
+ startIndex = 0;
+
+ if (row < firstRow) {
+ if (wrap) {
+ row = lastRow;
+ inWrap = true;
+ } else {
+ return;
+ }
+ }
+
+ if (row == start.row)
+ stop = true;
+
+ line = session.getLine(row);
+ if (searchSelection) {
+ if (row == firstRow)
+ startIndex = firstColumn;
+ else if (row == lastRow)
+ line = line.substring(0, range.end.column);
+ }
+
+ if (inWrap && row == start.row)
+ startIndex = start.column;
+ }
+ }
+ };
+ };
+
+}).call(Search.prototype);
+
+exports.Search = Search;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Mihai Sucan <mihai DOT sucan AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/undomanager', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+var UndoManager = function() {
+ this.reset();
+};
+
+(function() {
+
+ this.execute = function(options) {
+ var deltas = options.args[0];
+ this.$doc = options.args[1];
+ this.$undoStack.push(deltas);
+ this.$redoStack = [];
+ };
+
+ this.undo = function(dontSelect) {
+ var deltas = this.$undoStack.pop();
+ var undoSelectionRange = null;
+ if (deltas) {
+ undoSelectionRange =
+ this.$doc.undoChanges(deltas, dontSelect);
+ this.$redoStack.push(deltas);
+ }
+ return undoSelectionRange;
+ };
+
+ this.redo = function(dontSelect) {
+ var deltas = this.$redoStack.pop();
+ var redoSelectionRange = null;
+ if (deltas) {
+ redoSelectionRange =
+ this.$doc.redoChanges(deltas, dontSelect);
+ this.$undoStack.push(deltas);
+ }
+ return redoSelectionRange;
+ };
+
+ this.reset = function() {
+ this.$undoStack = [];
+ this.$redoStack = [];
+ };
+
+ this.hasUndo = function() {
+ return this.$undoStack.length > 0;
+ };
+
+ this.hasRedo = function() {
+ return this.$redoStack.length > 0;
+ };
+
+}).call(UndoManager.prototype);
+
+exports.UndoManager = UndoManager;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian@ajax.org>
+ * Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
+ * Julian Viereck <julian.viereck@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/virtual_renderer', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/dom', 'pilot/event', 'pilot/useragent', 'ace/layer/gutter', 'ace/layer/marker', 'ace/layer/text', 'ace/layer/cursor', 'ace/scrollbar', 'ace/renderloop', 'pilot/event_emitter', 'text/ace/css/editor.css'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var dom = require("pilot/dom");
+var event = require("pilot/event");
+var useragent = require("pilot/useragent");
+var GutterLayer = require("ace/layer/gutter").Gutter;
+var MarkerLayer = require("ace/layer/marker").Marker;
+var TextLayer = require("ace/layer/text").Text;
+var CursorLayer = require("ace/layer/cursor").Cursor;
+var ScrollBar = require("ace/scrollbar").ScrollBar;
+var RenderLoop = require("ace/renderloop").RenderLoop;
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+var editorCss = require("text/ace/css/editor.css");
+
+// import CSS once
+dom.importCssString(editorCss);
+
+var VirtualRenderer = function(container, theme) {
+ this.container = container;
+ dom.addCssClass(this.container, "ace_editor");
+
+ this.setTheme(theme);
+
+ this.$gutter = dom.createElement("div");
+ this.$gutter.className = "ace_gutter";
+ this.container.appendChild(this.$gutter);
+
+ this.scroller = dom.createElement("div");
+ this.scroller.className = "ace_scroller";
+ this.container.appendChild(this.scroller);
+
+ this.content = dom.createElement("div");
+ this.content.className = "ace_content";
+ this.scroller.appendChild(this.content);
+
+ this.$gutterLayer = new GutterLayer(this.$gutter);
+ this.$markerBack = new MarkerLayer(this.content);
+
+ var textLayer = this.$textLayer = new TextLayer(this.content);
+ this.canvas = textLayer.element;
+
+ this.$markerFront = new MarkerLayer(this.content);
+
+ this.characterWidth = textLayer.getCharacterWidth();
+ this.lineHeight = textLayer.getLineHeight();
+
+ this.$cursorLayer = new CursorLayer(this.content);
+ this.$cursorPadding = 8;
+
+ // Indicates whether the horizontal scrollbar is visible
+ this.$horizScroll = true;
+ this.$horizScrollAlwaysVisible = true;
+
+ this.scrollBar = new ScrollBar(container);
+ this.scrollBar.addEventListener("scroll", this.onScroll.bind(this));
+
+ this.scrollTop = 0;
+
+ this.cursorPos = {
+ row : 0,
+ column : 0
+ };
+
+ var _self = this;
+ this.$textLayer.addEventListener("changeCharaterSize", function() {
+ _self.characterWidth = textLayer.getCharacterWidth();
+ _self.lineHeight = textLayer.getLineHeight();
+ _self.$updatePrintMargin();
+ _self.onResize(true);
+
+ _self.$loop.schedule(_self.CHANGE_FULL);
+ });
+ event.addListener(this.$gutter, "click", this.$onGutterClick.bind(this));
+ event.addListener(this.$gutter, "dblclick", this.$onGutterClick.bind(this));
+
+ this.$size = {
+ width: 0,
+ height: 0,
+ scrollerHeight: 0,
+ scrollerWidth: 0
+ };
+
+ this.$loop = new RenderLoop(this.$renderChanges.bind(this));
+ this.$loop.schedule(this.CHANGE_FULL);
+
+ this.setPadding(4);
+ this.$updatePrintMargin();
+};
+
+(function() {
+ this.showGutter = true;
+
+ this.CHANGE_CURSOR = 1;
+ this.CHANGE_MARKER = 2;
+ this.CHANGE_GUTTER = 4;
+ this.CHANGE_SCROLL = 8;
+ this.CHANGE_LINES = 16;
+ this.CHANGE_TEXT = 32;
+ this.CHANGE_SIZE = 64;
+ this.CHANGE_MARKER_BACK = 128;
+ this.CHANGE_MARKER_FRONT = 256;
+ this.CHANGE_FULL = 512;
+
+ oop.implement(this, EventEmitter);
+
+ this.setSession = function(session) {
+ this.session = session;
+ this.$cursorLayer.setSession(session);
+ this.$markerBack.setSession(session);
+ this.$markerFront.setSession(session);
+ this.$gutterLayer.setSession(session);
+ this.$textLayer.setSession(session);
+ this.$loop.schedule(this.CHANGE_FULL);
+ };
+
+ /**
+ * Triggers partial update of the text layer
+ */
+ this.updateLines = function(firstRow, lastRow) {
+ if (lastRow === undefined)
+ lastRow = Infinity;
+
+ if (!this.$changedLines) {
+ this.$changedLines = {
+ firstRow: firstRow,
+ lastRow: lastRow
+ };
+ }
+ else {
+ if (this.$changedLines.firstRow > firstRow)
+ this.$changedLines.firstRow = firstRow;
+
+ if (this.$changedLines.lastRow < lastRow)
+ this.$changedLines.lastRow = lastRow;
+ }
+
+ this.$loop.schedule(this.CHANGE_LINES);
+ };
+
+ /**
+ * Triggers full update of the text layer
+ */
+ this.updateText = function() {
+ this.$loop.schedule(this.CHANGE_TEXT);
+ };
+
+ /**
+ * Triggers a full update of all layers
+ */
+ this.updateFull = function() {
+ this.$loop.schedule(this.CHANGE_FULL);
+ };
+
+ this.updateFontSize = function() {
+ this.$textLayer.checkForSizeChanges();
+ };
+
+ /**
+ * Triggers resize of the editor
+ */
+ this.onResize = function(force) {
+ var changes = this.CHANGE_SIZE;
+
+ var height = dom.getInnerHeight(this.container);
+ if (force || this.$size.height != height) {
+ this.$size.height = height;
+
+ this.scroller.style.height = height + "px";
+ this.scrollBar.setHeight(this.scroller.clientHeight);
+
+ if (this.session) {
+ this.scrollToY(this.getScrollTop());
+ changes = changes | this.CHANGE_FULL;
+ }
+ }
+
+ var width = dom.getInnerWidth(this.container);
+ if (force || this.$size.width != width) {
+ this.$size.width = width;
+
+ var gutterWidth = this.showGutter ? this.$gutter.offsetWidth : 0;
+ this.scroller.style.left = gutterWidth + "px";
+ this.scroller.style.width = Math.max(0, width - gutterWidth - this.scrollBar.getWidth()) + "px";
+
+ if (this.session.getUseWrapMode()) {
+ var availableWidth = this.scroller.clientWidth - this.$padding * 2;
+ var limit = Math.floor(availableWidth / this.characterWidth) - 1;
+ if (this.session.adjustWrapLimit(limit) || force) {
+ changes = changes | this.CHANGE_FULL;
+ }
+ }
+ }
+
+ this.$size.scrollerWidth = this.scroller.clientWidth;
+ this.$size.scrollerHeight = this.scroller.clientHeight;
+ this.$loop.schedule(changes);
+ };
+
+ this.$onGutterClick = function(e) {
+ var pageX = event.getDocumentX(e);
+ var pageY = event.getDocumentY(e);
+
+ this._dispatchEvent("gutter" + e.type, {
+ row: this.screenToTextCoordinates(pageX, pageY).row,
+ htmlEvent: e
+ });
+ };
+
+ this.setShowInvisibles = function(showInvisibles) {
+ if (this.$textLayer.setShowInvisibles(showInvisibles))
+ this.$loop.schedule(this.CHANGE_TEXT);
+ };
+
+ this.getShowInvisibles = function() {
+ return this.$textLayer.showInvisibles;
+ };
+
+ this.$showPrintMargin = true;
+ this.setShowPrintMargin = function(showPrintMargin) {
+ this.$showPrintMargin = showPrintMargin;
+ this.$updatePrintMargin();
+ };
+
+ this.getShowPrintMargin = function() {
+ return this.$showPrintMargin;
+ };
+
+ this.$printMarginColumn = 80;
+ this.setPrintMarginColumn = function(showPrintMargin) {
+ this.$printMarginColumn = showPrintMargin;
+ this.$updatePrintMargin();
+ };
+
+ this.getPrintMarginColumn = function() {
+ return this.$printMarginColumn;
+ };
+
+ this.getShowGutter = function(){
+ return this.showGutter;
+ };
+
+ this.setShowGutter = function(show){
+ if(this.showGutter === show)
+ return;
+ this.$gutter.style.display = show ? "block" : "none";
+ this.showGutter = show;
+ this.onResize(true);
+ };
+
+ this.$updatePrintMargin = function() {
+ var containerEl;
+
+ if (!this.$showPrintMargin && !this.$printMarginEl)
+ return;
+
+ if (!this.$printMarginEl) {
+ containerEl = dom.createElement("div");
+ containerEl.className = "ace_print_margin_layer";
+ this.$printMarginEl = dom.createElement("div");
+ this.$printMarginEl.className = "ace_print_margin";
+ containerEl.appendChild(this.$printMarginEl);
+ this.content.insertBefore(containerEl, this.$textLayer.element);
+ }
+
+ var style = this.$printMarginEl.style;
+ style.left = ((this.characterWidth * this.$printMarginColumn) + this.$padding * 2) + "px";
+ style.visibility = this.$showPrintMargin ? "visible" : "hidden";
+ };
+
+ this.getContainerElement = function() {
+ return this.container;
+ };
+
+ this.getMouseEventTarget = function() {
+ return this.content;
+ };
+
+ this.getTextAreaContainer = function() {
+ return this.container;
+ };
+
+ this.moveTextAreaToCursor = function(textarea) {
+ // in IE the native cursor always shines through
+ if (useragent.isIE)
+ return;
+
+ var pos = this.$cursorLayer.getPixelPosition();
+ if (!pos)
+ return;
+
+ var bounds = this.content.getBoundingClientRect();
+ var offset = (this.layerConfig && this.layerConfig.offset) || 0;
+
+ textarea.style.left = (bounds.left + pos.left + this.$padding) + "px";
+ textarea.style.top = (bounds.top + pos.top - this.scrollTop + offset) + "px";
+ };
+
+ this.getFirstVisibleRow = function() {
+ return (this.layerConfig || {}).firstRow || 0;
+ };
+
+ this.getFirstFullyVisibleRow = function(){
+ if (!this.layerConfig)
+ return 0;
+
+ return this.layerConfig.firstRow + (this.layerConfig.offset === 0 ? 0 : 1);
+ };
+
+ this.getLastFullyVisibleRow = function() {
+ if (!this.layerConfig)
+ return 0;
+
+ var flint = Math.floor((this.layerConfig.height + this.layerConfig.offset) / this.layerConfig.lineHeight);
+ return this.layerConfig.firstRow - 1 + flint;
+ };
+
+ this.getLastVisibleRow = function() {
+ return (this.layerConfig || {}).lastRow || 0;
+ };
+
+ this.$padding = null;
+ this.setPadding = function(padding) {
+ this.$padding = padding;
+ this.content.style.padding = "0 " + padding + "px";
+ this.$loop.schedule(this.CHANGE_FULL);
+ this.$updatePrintMargin();
+ };
+
+ this.getHScrollBarAlwaysVisible = function() {
+ return this.$horizScrollAlwaysVisible;
+ };
+
+ this.setHScrollBarAlwaysVisible = function(alwaysVisible) {
+ if (this.$horizScrollAlwaysVisible != alwaysVisible) {
+ this.$horizScrollAlwaysVisible = alwaysVisible;
+ if (!this.$horizScrollAlwaysVisible || !this.$horizScroll)
+ this.$loop.schedule(this.CHANGE_SCROLL);
+ }
+ };
+
+ this.onScroll = function(e) {
+ this.scrollToY(e.data);
+ };
+
+ this.$updateScrollBar = function() {
+ this.scrollBar.setInnerHeight(this.layerConfig.maxHeight);
+ this.scrollBar.setScrollTop(this.scrollTop);
+ };
+
+ this.$renderChanges = function(changes) {
+ if (!changes || !this.session)
+ return;
+
+ // text, scrolling and resize changes can cause the view port size to change
+ if (!this.layerConfig ||
+ changes & this.CHANGE_FULL ||
+ changes & this.CHANGE_SIZE ||
+ changes & this.CHANGE_TEXT ||
+ changes & this.CHANGE_LINES ||
+ changes & this.CHANGE_SCROLL
+ )
+ this.$computeLayerConfig();
+
+ // full
+ if (changes & this.CHANGE_FULL) {
+ this.$textLayer.update(this.layerConfig);
+ if (this.showGutter)
+ this.$gutterLayer.update(this.layerConfig);
+ this.$markerBack.update(this.layerConfig);
+ this.$markerFront.update(this.layerConfig);
+ this.$cursorLayer.update(this.layerConfig);
+ this.$updateScrollBar();
+ return;
+ }
+
+ // scrolling
+ if (changes & this.CHANGE_SCROLL) {
+ if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES)
+ this.$textLayer.update(this.layerConfig);
+ else
+ this.$textLayer.scrollLines(this.layerConfig);
+
+ if (this.showGutter)
+ this.$gutterLayer.update(this.layerConfig);
+ this.$markerBack.update(this.layerConfig);
+ this.$markerFront.update(this.layerConfig);
+ this.$cursorLayer.update(this.layerConfig);
+ this.$updateScrollBar();
+ return;
+ }
+
+ if (changes & this.CHANGE_TEXT) {
+ this.$textLayer.update(this.layerConfig);
+ if (this.showGutter)
+ this.$gutterLayer.update(this.layerConfig);
+ }
+ else if (changes & this.CHANGE_LINES) {
+ this.$updateLines();
+ this.$updateScrollBar();
+ if (this.showGutter)
+ this.$gutterLayer.update(this.layerConfig);
+ } else if (changes & this.CHANGE_GUTTER) {
+ if (this.showGutter)
+ this.$gutterLayer.update(this.layerConfig);
+ }
+
+ if (changes & this.CHANGE_CURSOR)
+ this.$cursorLayer.update(this.layerConfig);
+
+ if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) {
+ this.$markerFront.update(this.layerConfig);
+ }
+
+ if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) {
+ this.$markerBack.update(this.layerConfig);
+ }
+
+ if (changes & this.CHANGE_SIZE)
+ this.$updateScrollBar();
+ };
+
+ this.$computeLayerConfig = function() {
+ var session = this.session;
+
+ var offset = this.scrollTop % this.lineHeight;
+ var minHeight = this.$size.scrollerHeight + this.lineHeight;
+
+ var longestLine = this.$getLongestLine();
+ var widthChanged = !this.layerConfig ? true : (this.layerConfig.width != longestLine);
+
+ var horizScroll = this.$horizScrollAlwaysVisible || this.$size.scrollerWidth - longestLine < 0;
+ var horizScrollChanged = this.$horizScroll !== horizScroll;
+ this.$horizScroll = horizScroll;
+ if (horizScrollChanged)
+ this.scroller.style.overflowX = horizScroll ? "scroll" : "hidden";
+
+ var maxHeight = this.session.getScreenLength() * this.lineHeight;
+ this.scrollTop = Math.max(0, Math.min(this.scrollTop, maxHeight - this.$size.scrollerHeight));
+
+ var lineCount = Math.ceil(minHeight / this.lineHeight) - 1;
+ var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight));
+ var lastRow = firstRow + lineCount;
+
+ // Map lines on the screen to lines in the document.
+ var firstRowScreen, firstRowHeight;
+ var lineHeight = { lineHeight: this.lineHeight };
+ firstRow = session.screenToDocumentRow(firstRow, 0);
+
+ // Check if firstRow is inside of a foldLine. If true, then use the first
+ // row of the foldLine.
+ var foldLine = session.getFoldLine(firstRow);
+ if (foldLine) {
+ firstRow = foldLine.start.row;
+ }
+
+ firstRowScreen = session.documentToScreenRow(firstRow, 0);
+ firstRowHeight = session.getRowHeight(lineHeight, firstRow);
+
+ lastRow = Math.min(session.screenToDocumentRow(lastRow, 0), session.getLength() - 1);
+ minHeight = this.$size.scrollerHeight + session.getRowHeight(lineHeight, lastRow)+
+ firstRowHeight;
+
+ offset = this.scrollTop - firstRowScreen * this.lineHeight;
+
+ this.layerConfig = {
+ width : longestLine,
+ padding : this.$padding,
+ firstRow : firstRow,
+ firstRowScreen: firstRowScreen,
+ lastRow : lastRow,
+ lineHeight : this.lineHeight,
+ characterWidth : this.characterWidth,
+ minHeight : minHeight,
+ maxHeight : maxHeight,
+ offset : offset,
+ height : this.$size.scrollerHeight
+ };
+
+ // For debugging.
+ // console.log(JSON.stringify(this.layerConfig));
+
+ this.$gutterLayer.element.style.marginTop = (-offset) + "px";
+ this.content.style.marginTop = (-offset) + "px";
+ this.content.style.width = longestLine + "px";
+ this.content.style.height = minHeight + "px";
+
+ // scroller.scrollWidth was smaller than scrollLeft we needed
+ if (this.$desiredScrollLeft) {
+ this.scrollToX(this.$desiredScrollLeft);
+ this.$desiredScrollLeft = 0;
+ }
+
+ // Horizontal scrollbar visibility may have changed, which changes
+ // the client height of the scroller
+ if (horizScrollChanged)
+ this.onResize(true);
+ };
+
+ this.$updateLines = function() {
+ var firstRow = this.$changedLines.firstRow;
+ var lastRow = this.$changedLines.lastRow;
+ this.$changedLines = null;
+
+ var layerConfig = this.layerConfig;
+
+ // if the update changes the width of the document do a full redraw
+ if (layerConfig.width != this.$getLongestLine())
+ return this.$textLayer.update(layerConfig);
+
+ if (firstRow > layerConfig.lastRow + 1) { return; }
+ if (lastRow < layerConfig.firstRow) { return; }
+
+ // if the last row is unknown -> redraw everything
+ if (lastRow === Infinity) {
+ if (this.showGutter)
+ this.$gutterLayer.update(layerConfig);
+ this.$textLayer.update(layerConfig);
+ return;
+ }
+
+ // else update only the changed rows
+ this.$textLayer.updateLines(layerConfig, firstRow, lastRow);
+ };
+
+ this.$getLongestLine = function() {
+ var charCount = this.session.getScreenWidth() + 1;
+ if (this.$textLayer.showInvisibles)
+ charCount += 1;
+
+ return Math.max(this.$size.scrollerWidth - this.$padding * 2, Math.round(charCount * this.characterWidth));
+ };
+
+ this.updateFrontMarkers = function() {
+ this.$markerFront.setMarkers(this.session.getMarkers(true));
+ this.$loop.schedule(this.CHANGE_MARKER_FRONT);
+ };
+
+ this.updateBackMarkers = function() {
+ this.$markerBack.setMarkers(this.session.getMarkers());
+ this.$loop.schedule(this.CHANGE_MARKER_BACK);
+ };
+
+ this.addGutterDecoration = function(row, className){
+ this.$gutterLayer.addGutterDecoration(row, className);
+ this.$loop.schedule(this.CHANGE_GUTTER);
+ };
+
+ this.removeGutterDecoration = function(row, className){
+ this.$gutterLayer.removeGutterDecoration(row, className);
+ this.$loop.schedule(this.CHANGE_GUTTER);
+ };
+
+ this.setBreakpoints = function(rows) {
+ this.$gutterLayer.setBreakpoints(rows);
+ this.$loop.schedule(this.CHANGE_GUTTER);
+ };
+
+ this.setAnnotations = function(annotations) {
+ this.$gutterLayer.setAnnotations(annotations);
+ this.$loop.schedule(this.CHANGE_GUTTER);
+ };
+
+ this.updateCursor = function() {
+ this.$loop.schedule(this.CHANGE_CURSOR);
+ };
+
+ this.hideCursor = function() {
+ this.$cursorLayer.hideCursor();
+ };
+
+ this.showCursor = function() {
+ this.$cursorLayer.showCursor();
+ };
+
+ this.scrollCursorIntoView = function() {
+ // the editor is not visible
+ if (this.$size.scrollerHeight === 0)
+ return;
+
+ var pos = this.$cursorLayer.getPixelPosition();
+
+ var left = pos.left + this.$padding;
+ var top = pos.top;
+
+ if (this.scrollTop > top) {
+ this.scrollToY(top);
+ }
+
+ if (this.scrollTop + this.$size.scrollerHeight < top + this.lineHeight) {
+ this.scrollToY(top + this.lineHeight - this.$size.scrollerHeight);
+ }
+
+ var scrollLeft = this.scroller.scrollLeft;
+
+ if (scrollLeft > left) {
+ this.scrollToX(left);
+ }
+
+ if (scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) {
+ if (left > this.layerConfig.width)
+ this.$desiredScrollLeft = left + 2 * this.characterWidth;
+ else
+ this.scrollToX(Math.round(left + this.characterWidth - this.$size.scrollerWidth));
+ }
+ };
+
+ this.getScrollTop = function() {
+ return this.scrollTop;
+ };
+
+ this.getScrollLeft = function() {
+ return this.scroller.scrollLeft;
+ };
+
+ this.getScrollTopRow = function() {
+ return this.scrollTop / this.lineHeight;
+ };
+
+ this.getScrollBottomRow = function() {
+ return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1);
+ };
+
+ this.scrollToRow = function(row) {
+ this.scrollToY(row * this.lineHeight);
+ };
+
+ this.scrollToLine = function(line, center) {
+ var lineHeight = { lineHeight: this.lineHeight };
+ var offset = 0;
+ for (var l = 1; l < line; l++) {
+ offset += this.session.getRowHeight(lineHeight, l-1);
+ }
+
+ if (center) {
+ offset -= this.$size.scrollerHeight / 2;
+ }
+ this.scrollToY(offset);
+ };
+
+ this.scrollToY = function(scrollTop) {
+ // after calling scrollBar.setScrollTop
+ // scrollbar sends us event with same scrollTop. ignore it
+ scrollTop = Math.max(0, scrollTop);
+ if (this.scrollTop !== scrollTop) {
+ this.$loop.schedule(this.CHANGE_SCROLL);
+ this.scrollTop = scrollTop;
+ }
+ };
+
+ this.scrollToX = function(scrollLeft) {
+ if (scrollLeft <= this.$padding)
+ scrollLeft = 0;
+
+ this.scroller.scrollLeft = scrollLeft;
+ };
+
+ this.scrollBy = function(deltaX, deltaY) {
+ deltaY && this.scrollToY(this.scrollTop + deltaY);
+ deltaX && this.scrollToX(this.scroller.scrollLeft + deltaX);
+ };
+
+ this.screenToTextCoordinates = function(pageX, pageY) {
+ var canvasPos = this.scroller.getBoundingClientRect();
+
+ var col = Math.round((pageX + this.scroller.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft())
+ / this.characterWidth);
+ var row = Math.floor((pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop())
+ / this.lineHeight);
+
+ return this.session.screenToDocumentPosition(row, Math.max(col, 0));
+ };
+
+ this.textToScreenCoordinates = function(row, column) {
+ var canvasPos = this.scroller.getBoundingClientRect();
+ var pos = this.session.documentToScreenPosition(row, column);
+
+ var x = this.$padding + Math.round(pos.column * this.characterWidth);
+ var y = pos.row * this.lineHeight;
+
+ return {
+ pageX: canvasPos.left + x - this.getScrollLeft(),
+ pageY: canvasPos.top + y - this.getScrollTop()
+ };
+ };
+
+ this.visualizeFocus = function() {
+ dom.addCssClass(this.container, "ace_focus");
+ };
+
+ this.visualizeBlur = function() {
+ dom.removeCssClass(this.container, "ace_focus");
+ };
+
+ this.showComposition = function(position) {
+ if (!this.$composition) {
+ this.$composition = dom.createElement("div");
+ this.$composition.className = "ace_composition";
+ this.content.appendChild(this.$composition);
+ }
+
+ this.$composition.innerHTML = "&#160;";
+
+ var pos = this.$cursorLayer.getPixelPosition();
+ var style = this.$composition.style;
+ style.top = pos.top + "px";
+ style.left = (pos.left + this.$padding) + "px";
+ style.height = this.lineHeight + "px";
+
+ this.hideCursor();
+ };
+
+ this.setCompositionText = function(text) {
+ dom.setInnerText(this.$composition, text);
+ };
+
+ this.hideComposition = function() {
+ this.showCursor();
+
+ if (!this.$composition)
+ return;
+
+ var style = this.$composition.style;
+ style.top = "-10000px";
+ style.left = "-10000px";
+ };
+
+ this.setTheme = function(theme) {
+ var _self = this;
+
+ this.$themeValue = theme;
+ if (!theme || typeof theme == "string") {
+ theme = theme || "ace/theme/textmate";
+ require([theme], function(theme) {
+ afterLoad(theme);
+ });
+ } else {
+ afterLoad(theme);
+ }
+
+ function afterLoad(theme) {
+ if (_self.$theme)
+ dom.removeCssClass(_self.container, _self.$theme);
+
+ _self.$theme = theme ? theme.cssClass : null;
+
+ if (_self.$theme)
+ dom.addCssClass(_self.container, _self.$theme);
+
+ // force re-measure of the gutter width
+ if (_self.$size) {
+ _self.$size.width = 0;
+ _self.onResize();
+ }
+ }
+ };
+
+ this.getTheme = function() {
+ return this.$themeValue;
+ };
+
+ // Methods allows to add / remove CSS classnames to the editor element.
+ // This feature can be used by plug-ins to provide a visual indication of
+ // a certain mode that editor is in.
+
+ this.setStyle = function setStyle(style) {
+ dom.addCssClass(this.container, style);
+ };
+
+ this.unsetStyle = function unsetStyle(style) {
+ dom.removeCssClass(this.container, style);
+ };
+
+ this.destroy = function() {
+ this.$textLayer.destroy();
+ this.$cursorLayer.destroy();
+ };
+
+}).call(VirtualRenderer.prototype);
+
+exports.VirtualRenderer = VirtualRenderer;
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Julian Viereck <julian DOT viereck AT gmail DOT com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/layer/gutter', ['require', 'exports', 'module' , 'pilot/dom'], function(require, exports, module) {
+
+var dom = require("pilot/dom");
+
+var Gutter = function(parentEl) {
+ this.element = dom.createElement("div");
+ this.element.className = "ace_layer ace_gutter-layer";
+ parentEl.appendChild(this.element);
+
+ this.$breakpoints = [];
+ this.$annotations = [];
+ this.$decorations = [];
+};
+
+(function() {
+
+ this.setSession = function(session) {
+ this.session = session;
+ };
+
+ this.addGutterDecoration = function(row, className){
+ if (!this.$decorations[row])
+ this.$decorations[row] = "";
+ this.$decorations[row] += " ace_" + className;
+ }
+
+ this.removeGutterDecoration = function(row, className){
+ this.$decorations[row] = this.$decorations[row].replace(" ace_" + className, "");
+ };
+
+ this.setBreakpoints = function(rows) {
+ this.$breakpoints = rows.concat();
+ };
+
+ this.setAnnotations = function(annotations) {
+ // iterate over sparse array
+ this.$annotations = [];
+ for (var row in annotations) if (annotations.hasOwnProperty(row)) {
+ var rowAnnotations = annotations[row];
+ if (!rowAnnotations)
+ continue;
+
+ var rowInfo = this.$annotations[row] = {
+ text: []
+ };
+ for (var i=0; i<rowAnnotations.length; i++) {
+ var annotation = rowAnnotations[i];
+ rowInfo.text.push(annotation.text.replace(/"/g, "&quot;").replace(/'/g, "&#8217;").replace(/</, "&lt;"));
+ var type = annotation.type;
+ if (type == "error")
+ rowInfo.className = "ace_error";
+ else if (type == "warning" && rowInfo.className != "ace_error")
+ rowInfo.className = "ace_warning";
+ else if (type == "info" && (!rowInfo.className))
+ rowInfo.className = "ace_info";
+ }
+ }
+ };
+
+ this.update = function(config) {
+ this.$config = config;
+
+ var emptyAnno = {className: "", text: []};
+ var html = [];
+ var i = config.firstRow;
+ var lastRow = config.lastRow;
+ var fold = this.session.getNextFold(i);
+ var foldStart = fold ? fold.start.row : Infinity;
+
+ while (true) {
+ if(i > foldStart) {
+ i = fold.end.row + 1;
+ fold = this.session.getNextFold(i);
+ foldStart = fold ?fold.start.row :Infinity;
+ }
+ if(i > lastRow)
+ break;
+
+ var annotation = this.$annotations[i] || emptyAnno;
+ html.push("<div class='ace_gutter-cell",
+ this.$decorations[i] || "",
+ this.$breakpoints[i] ? " ace_breakpoint " : " ",
+ annotation.className,
+ "' title='", annotation.text.join("\n"),
+ "' style='height:", config.lineHeight, "px;'>", (i+1));
+
+ var wrappedRowLength = this.session.getRowLength(i) - 1;
+ while (wrappedRowLength--) {
+ html.push("</div><div class='ace_gutter-cell' style='height:", config.lineHeight, "px'>&brvbar;</div>");
+ }
+
+ html.push("</div>");
+
+ i++;
+ }
+ this.element = dom.setInnerHtml(this.element, html.join(""));
+ this.element.style.height = config.minHeight + "px";
+ };
+
+}).call(Gutter.prototype);
+
+exports.Gutter = Gutter;
+
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Julian Viereck <julian.viereck@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/layer/marker', ['require', 'exports', 'module' , 'ace/range', 'pilot/dom'], function(require, exports, module) {
+
+var Range = require("ace/range").Range;
+var dom = require("pilot/dom");
+
+var Marker = function(parentEl) {
+ this.element = dom.createElement("div");
+ this.element.className = "ace_layer ace_marker-layer";
+ parentEl.appendChild(this.element);
+};
+
+(function() {
+
+ this.setSession = function(session) {
+ this.session = session;
+ };
+
+ this.setMarkers = function(markers) {
+ this.markers = markers;
+ };
+
+ this.update = function(config) {
+ var config = config || this.config;
+ if (!config)
+ return;
+
+ this.config = config;
+
+ var html = [];
+ for ( var key in this.markers) {
+ var marker = this.markers[key];
+
+ var range = marker.range.clipRows(config.firstRow, config.lastRow);
+ if (range.isEmpty()) continue;
+
+ range = range.toScreenRange(this.session);
+
+ if (marker.renderer) {
+ var top = this.$getTop(range.start.row, config);
+ var left = Math.round(range.start.column * config.characterWidth);
+ marker.renderer(html, range, left, top, config);
+ }
+ else if (range.isMultiLine()) {
+ if (marker.type == "text") {
+ this.drawTextMarker(html, range, marker.clazz, config);
+ } else {
+ this.drawMultiLineMarker(html, range, marker.clazz, config);
+ }
+ }
+ else {
+ this.drawSingleLineMarker(html, range, marker.clazz, config);
+ }
+ }
+ this.element = dom.setInnerHtml(this.element, html.join(""));
+ };
+
+ this.$getTop = function(row, layerConfig) {
+ return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight;
+ };
+
+ this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
+ // selection start
+ var row = range.start.row;
+
+ var lineRange = new Range(row, range.start.column,
+ row, this.session.getScreenLastRowColumn(row));
+ this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1);
+
+ // selection end
+ var row = range.end.row;
+ var lineRange = new Range(row, 0, row, range.end.column);
+ this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig);
+
+ for (var row = range.start.row + 1; row < range.end.row; row++) {
+ lineRange.start.row = row;
+ lineRange.end.row = row;
+ lineRange.end.column = this.session.getScreenLastRowColumn(row);
+ this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1);
+ }
+ };
+
+ this.drawMultiLineMarker = function(stringBuilder, range, clazz, layerConfig) {
+ // from selection start to the end of the line
+ var height = layerConfig.lineHeight;
+ var width = Math.round(layerConfig.width - (range.start.column * layerConfig.characterWidth));
+ var top = this.$getTop(range.start.row, layerConfig);
+ var left = Math.round(range.start.column * layerConfig.characterWidth);
+
+ stringBuilder.push(
+ "<div class='", clazz, "' style='",
+ "height:", height, "px;",
+ "width:", width, "px;",
+ "top:", top, "px;",
+ "left:", left, "px;'></div>"
+ );
+
+ // from start of the last line to the selection end
+ var top = this.$getTop(range.end.row, layerConfig);
+ var width = Math.round(range.end.column * layerConfig.characterWidth);
+
+ stringBuilder.push(
+ "<div class='", clazz, "' style='",
+ "height:", height, "px;",
+ "top:", top, "px;",
+ "width:", width, "px;'></div>"
+ );
+
+ // all the complete lines
+ var height = (range.end.row - range.start.row - 1) * layerConfig.lineHeight;
+ if (height < 0)
+ return;
+ var top = this.$getTop(range.start.row + 1, layerConfig);
+
+ stringBuilder.push(
+ "<div class='", clazz, "' style='",
+ "height:", height, "px;",
+ "width:", layerConfig.width, "px;",
+ "top:", top, "px;'></div>"
+ );
+ };
+
+ this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig, extraLength) {
+ var height = layerConfig.lineHeight;
+ var width = Math.round((range.end.column + (extraLength || 0) - range.start.column) * layerConfig.characterWidth);
+ var top = this.$getTop(range.start.row, layerConfig);
+ var left = Math.round(range.start.column * layerConfig.characterWidth);
+
+ stringBuilder.push(
+ "<div class='", clazz, "' style='",
+ "height:", height, "px;",
+ "width:", width, "px;",
+ "top:", top, "px;",
+ "left:", left,"px;'></div>"
+ );
+ };
+
+}).call(Marker.prototype);
+
+exports.Marker = Marker;
+
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Julian Viereck <julian DOT viereck AT gmail DOT com>
+ * Mihai Sucan <mihai.sucan@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/layer/text', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/dom', 'pilot/lang', 'pilot/useragent', 'pilot/event_emitter'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var dom = require("pilot/dom");
+var lang = require("pilot/lang");
+var useragent = require("pilot/useragent");
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+
+var Text = function(parentEl) {
+ this.element = dom.createElement("div");
+ this.element.className = "ace_layer ace_text-layer";
+ parentEl.appendChild(this.element);
+
+ this.$characterSize = this.$measureSizes() || {width: 0, height: 0};
+ this.$pollSizeChanges();
+};
+
+(function() {
+
+ oop.implement(this, EventEmitter);
+
+ this.EOF_CHAR = "&para;";
+ this.EOL_CHAR = "&not;";
+ this.TAB_CHAR = "&rarr;";
+ this.SPACE_CHAR = "&middot;";
+
+ this.getLineHeight = function() {
+ return this.$characterSize.height || 1;
+ };
+
+ this.getCharacterWidth = function() {
+ return this.$characterSize.width || 1;
+ };
+
+ this.checkForSizeChanges = function() {
+ var size = this.$measureSizes();
+ if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) {
+ this.$characterSize = size;
+ this._dispatchEvent("changeCharaterSize", {data: size});
+ }
+ };
+
+ this.$pollSizeChanges = function() {
+ var self = this;
+ this.$pollSizeChangesTimer = setInterval(function() {
+ self.checkForSizeChanges();
+ }, 500);
+ };
+
+ this.$fontStyles = {
+ fontFamily : 1,
+ fontSize : 1,
+ fontWeight : 1,
+ fontStyle : 1,
+ lineHeight : 1
+ };
+
+ this.$measureSizes = function() {
+ var n = 1000;
+ if (!this.$measureNode) {
+ var measureNode = this.$measureNode = dom.createElement("div");
+ var style = measureNode.style;
+
+ style.width = style.height = "auto";
+ style.left = style.top = (-n * 40) + "px";
+
+ style.visibility = "hidden";
+ style.position = "absolute";
+ style.overflow = "visible";
+ style.whiteSpace = "nowrap";
+
+ // in FF 3.6 monospace fonts can have a fixed sub pixel width.
+ // that's why we have to measure many characters
+ // Note: characterWidth can be a float!
+ measureNode.innerHTML = lang.stringRepeat("Xy", n);
+
+ if (document.body) {
+ document.body.appendChild(measureNode);
+ } else {
+ var container = this.element.parentNode;
+ while (!dom.hasCssClass(container, "ace_editor"))
+ container = container.parentNode;
+ container.appendChild(measureNode);
+ }
+
+ }
+
+ var style = this.$measureNode.style;
+ var computedStyle = dom.computedStyle(this.element);
+ for (var prop in this.$fontStyles)
+ style[prop] = computedStyle[prop];
+
+ var size = {
+ height: this.$measureNode.offsetHeight,
+ width: this.$measureNode.offsetWidth / (n * 2)
+ };
+
+ // Size and width can be null if the editor is not visible or
+ // detached from the document
+ if (size.width == 0 && size.height == 0)
+ return null;
+
+ return size;
+ };
+
+ this.setSession = function(session) {
+ this.session = session;
+ };
+
+ this.showInvisibles = false;
+ this.setShowInvisibles = function(showInvisibles) {
+ if (this.showInvisibles == showInvisibles)
+ return false;
+
+ this.showInvisibles = showInvisibles;
+ return true;
+ };
+
+ this.$tabStrings = [];
+ this.$computeTabString = function() {
+ var tabSize = this.session.getTabSize();
+ var tabStr = this.$tabStrings = [0];
+ for (var i = 1; i < tabSize + 1; i++) {
+ if (this.showInvisibles) {
+ tabStr.push("<span class='ace_invisible'>"
+ + this.TAB_CHAR
+ + new Array(i).join("&#160;")
+ + "</span>");
+ } else {
+ tabStr.push(new Array(i+1).join("&#160;"));
+ }
+ }
+
+ };
+
+ this.updateLines = function(config, firstRow, lastRow) {
+ this.$computeTabString();
+ // Due to wrap line changes there can be new lines if e.g.
+ // the line to updated wrapped in the meantime.
+ if (this.config.lastRow != config.lastRow ||
+ this.config.firstRow != config.firstRow) {
+ this.scrollLines(config);
+ }
+ this.config = config;
+
+ var first = Math.max(firstRow, config.firstRow);
+ var last = Math.min(lastRow, config.lastRow);
+
+ var lineElements = this.element.childNodes,
+ lineElementsIdx = 0;
+
+ for (var row = config.firstRow; row < first; row++) {
+ var foldLine = this.session.getFoldLine(row);
+ if (foldLine) {
+ if (foldLine.containsRow(first)) {
+ break;
+ } else {
+ row = foldLine.end.row;
+ }
+ }
+ lineElementsIdx ++;
+ }
+
+ for (var i=first; i<=last; i++) {
+ var lineElement = lineElements[lineElementsIdx++];
+ if (!lineElement)
+ continue;
+
+ var html = [];
+ var tokens = this.session.getTokens(i, i);
+ this.$renderLine(html, i, tokens[0].tokens);
+ lineElement = dom.setInnerHtml(lineElement, html.join(""));
+
+ i = this.session.getRowFoldEnd(i);
+ }
+ };
+
+ this.scrollLines = function(config) {
+ this.$computeTabString();
+ var oldConfig = this.config;
+ this.config = config;
+
+ if (!oldConfig || oldConfig.lastRow < config.firstRow)
+ return this.update(config);
+
+ if (config.lastRow < oldConfig.firstRow)
+ return this.update(config);
+
+ var el = this.element;
+ if (oldConfig.firstRow < config.firstRow)
+ for (var row=this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row>0; row--)
+ el.removeChild(el.firstChild);
+
+ if (oldConfig.lastRow > config.lastRow)
+ for (var row=this.session.getFoldedRowCount(config.lastRow + 1, oldConfig.lastRow); row>0; row--)
+ el.removeChild(el.lastChild);
+
+ if (config.firstRow < oldConfig.firstRow) {
+ var fragment = this.$renderLinesFragment(config, config.firstRow, oldConfig.firstRow - 1);
+ if (el.firstChild)
+ el.insertBefore(fragment, el.firstChild);
+ else
+ el.appendChild(fragment);
+ }
+
+ if (config.lastRow > oldConfig.lastRow) {
+ var fragment = this.$renderLinesFragment(config, oldConfig.lastRow + 1, config.lastRow);
+ el.appendChild(fragment);
+ }
+ };
+
+ this.$renderLinesFragment = function(config, firstRow, lastRow) {
+ var fragment = document.createDocumentFragment(),
+ row = firstRow,
+ fold = this.session.getNextFold(row),
+ foldStart = fold ?fold.start.row :Infinity;
+
+ while (true) {
+ if(row > foldStart) {
+ row = fold.end.row+1;
+ fold = this.session.getNextFold(row);
+ foldStart = fold ?fold.start.row :Infinity;
+ }
+ if(row > lastRow)
+ break;
+
+ var lineEl = dom.createElement("div");
+
+ lineEl.className = "ace_line";
+
+ var html = [];
+ // Get the tokens per line as there might be some lines in between
+ // beeing folded.
+ // OPTIMIZE: If there is a long block of unfolded lines, just make
+ // this call once for that big block of unfolded lines.
+ var tokens = this.session.getTokens(row, row);
+ if (tokens.length == 1)
+ this.$renderLine(html, row, tokens[0].tokens);
+
+ // don't use setInnerHtml since we are working with an empty DIV
+ lineEl.innerHTML = html.join("");
+ fragment.appendChild(lineEl);
+
+ row++;
+ }
+ return fragment;
+ };
+
+ this.update = function(config) {
+ this.$computeTabString();
+ this.config = config;
+
+ var html = [];
+ var firstRow = config.firstRow, lastRow = config.lastRow;
+
+ var row = firstRow,
+ fold = this.session.getNextFold(row),
+ foldStart = fold ?fold.start.row :Infinity;
+
+ while (true) {
+ if(row > foldStart) {
+ row = fold.end.row+1;
+ fold = this.session.getNextFold(row);
+ foldStart = fold ?fold.start.row :Infinity;
+ }
+ if(row > lastRow)
+ break;
+
+ html.push("<div class='ace_line'>");
+ // Get the tokens per line as there might be some lines in between
+ // beeing folded.
+ // OPTIMIZE: If there is a long block of unfolded lines, just make
+ // this call once for that big block of unfolded lines.
+ var tokens = this.session.getTokens(row, row);
+ if (tokens.length == 1)
+ this.$renderLine(html, row, tokens[0].tokens);
+ html.push("</div>");
+
+ row++;
+ }
+ this.element = dom.setInnerHtml(this.element, html.join(""));
+ };
+
+ this.$textToken = {
+ "text": true,
+ "rparen": true,
+ "lparen": true
+ };
+
+ this.$renderToken = function(stringBuilder, screenColumn, token, value) {
+ var self = this;
+ var replaceReg = /\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u1100-\u115F]|[\u11A3-\u11A7]|[\u11FA-\u11FF]|[\u2329-\u232A]|[\u2E80-\u2E99]|[\u2E9B-\u2EF3]|[\u2F00-\u2FD5]|[\u2FF0-\u2FFB]|[\u3000-\u303E]|[\u3041-\u3096]|[\u3099-\u30FF]|[\u3105-\u312D]|[\u3131-\u318E]|[\u3190-\u31BA]|[\u31C0-\u31E3]|[\u31F0-\u321E]|[\u3220-\u3247]|[\u3250-\u32FE]|[\u3300-\u4DBF]|[\u4E00-\uA48C]|[\uA490-\uA4C6]|[\uA960-\uA97C]|[\uAC00-\uD7A3]|[\uD7B0-\uD7C6]|[\uD7CB-\uD7FB]|[\uF900-\uFAFF]|[\uFE10-\uFE19]|[\uFE30-\uFE52]|[\uFE54-\uFE66]|[\uFE68-\uFE6B]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g;
+ var replaceFunc = function(c, a, b, tabIdx, idx4) {
+ if (c.charCodeAt(0) == 32) {
+ return new Array(c.length+1).join("&#160;");
+ } else if (c == "\t") {
+ var tabSize = self.session.getScreenTabSize(screenColumn + tabIdx);
+ screenColumn += tabSize - 1;
+ return self.$tabStrings[tabSize];
+ } else if (c == "&") {
+ if (useragent.isOldGecko)
+ return "&";
+ else
+ return "&amp;";
+ } else if (c == "<") {
+ return "&lt;";
+ } else if (c.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)) {
+ if (self.showInvisibles) {
+ var space = new Array(c.length+1).join(self.SPACE_CHAR);
+ return "<span class='ace_invisible'>" + space + "</span>";
+ } else {
+ return "&#160;";
+ }
+ } else {
+ screenColumn += 1;
+ return "<span class='ace_cjk' style='width:" +
+ (self.config.characterWidth * 2) +
+ "px'>" + c + "</span>";
+ }
+ };
+
+ var output = value.replace(replaceReg, replaceFunc);
+
+ if (!this.$textToken[token.type]) {
+ var classes = "ace_" + token.type.replace(/\./g, " ace_");
+ stringBuilder.push("<span class='", classes, "'>", output, "</span>");
+ }
+ else {
+ stringBuilder.push(output);
+ }
+ return value.length;
+ };
+
+ this.$renderLineCore = function(stringBuilder, lastRow, tokens, splits) {
+ var chars = 0,
+ split = 0,
+ splitChars,
+ characterWidth = this.config.characterWidth,
+ screenColumn = 0,
+ self = this;
+
+ function addToken(token, value) {
+ screenColumn += self.$renderToken(
+ stringBuilder, screenColumn, token, value);
+ }
+
+ if (!splits || splits.length == 0) {
+ splitChars = Number.MAX_VALUE;
+ } else {
+ splitChars = splits[0];
+ }
+
+ stringBuilder.push("<div style='height:",
+ this.config.lineHeight, "px",
+ "'>");
+ for (var i = 0; i < tokens.length; i++) {
+ var token = tokens[i];
+ var value = token.value;
+
+ if (chars + value.length < splitChars) {
+ addToken(token, value);
+ chars += value.length;
+ } else {
+ while (chars + value.length >= splitChars) {
+ addToken(token, value.substring(0, splitChars - chars));
+ value = value.substring(splitChars - chars);
+ chars = splitChars;
+ stringBuilder.push("</div>",
+ "<div style='height:",
+ this.config.lineHeight, "px",
+ "'>");
+
+ split ++;
+ screenColumn = 0;
+ splitChars = splits[split] || Number.MAX_VALUE;
+ }
+ if (value.length != 0) {
+ chars += value.length;
+ addToken(token, value);
+ }
+ }
+ }
+
+ if (this.showInvisibles) {
+ if (lastRow !== this.session.getLength() - 1) {
+ stringBuilder.push("<span class='ace_invisible'>" + this.EOL_CHAR + "</span>");
+ } else {
+ stringBuilder.push("<span class='ace_invisible'>" + this.EOF_CHAR + "</span>");
+ }
+ }
+ stringBuilder.push("</div>");
+ };
+
+ this.$renderLine = function(stringBuilder, row, tokens) {
+ // Check if the line to render is folded or not. If not, things are
+ // simple, otherwise, we need to fake some things...
+ if (!this.session.isRowFolded(row)) {
+ var splits = this.session.getRowSplitData(row);
+ this.$renderLineCore(stringBuilder, row, tokens, splits);
+ } else {
+ this.$renderFoldLine(stringBuilder, row, tokens);
+ }
+ };
+
+ this.$renderFoldLine = function(stringBuilder, row, tokens) {
+ var session = this.session,
+ foldLine = session.getFoldLine(row),
+ renderTokens = [];
+
+ function addTokens(tokens, from, to) {
+ var idx = 0, col = 0;
+ while ((col + tokens[idx].value.length) < from) {
+ col += tokens[idx].value.length;
+ idx++;
+
+ if (idx == tokens.length) {
+ return;
+ }
+ }
+ if (col != from) {
+ var value = tokens[idx].value.substring(from - col);
+ // Check if the token value is longer then the from...to spacing.
+ if (value.length > (to - from)) {
+ value = value.substring(0, to - from);
+ }
+
+ renderTokens.push({
+ type: tokens[idx].type,
+ value: value
+ });
+
+ col = from + value.length;
+ idx += 1;
+ }
+
+ while (col < to) {
+ var value = tokens[idx].value;
+ if (value.length + col > to) {
+ value = value.substring(0, to - col);
+ }
+ renderTokens.push({
+ type: tokens[idx].type,
+ value: value
+ });
+ col += value.length;
+ idx += 1;
+ }
+ }
+
+ foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) {
+ if (placeholder) {
+ renderTokens.push({
+ type: "fold",
+ value: placeholder
+ });
+ } else {
+ if (isNewRow) {
+ tokens = this.session.getTokens(row, row)[0].tokens;
+ }
+ if (tokens.length != 0) {
+ addTokens(tokens, lastColumn, column);
+ }
+ }
+ }.bind(this), foldLine.end.row, this.session.getLine(foldLine.end.row).length);
+
+ // TODO: Build a fake splits array!
+ var splits = this.session.$useWrapMode?this.session.$wrapData[row]:null;
+ this.$renderLineCore(stringBuilder, row, renderTokens, splits);
+ };
+
+ this.destroy = function() {
+ clearInterval(this.$pollSizeChangesTimer);
+ };
+
+}).call(Text.prototype);
+
+exports.Text = Text;
+
+});
+/* vim:ts=4:sts=4:sw=4:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ * Julian Viereck <julian.viereck@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/layer/cursor', ['require', 'exports', 'module' , 'pilot/dom'], function(require, exports, module) {
+
+var dom = require("pilot/dom");
+
+var Cursor = function(parentEl) {
+ this.element = dom.createElement("div");
+ this.element.className = "ace_layer ace_cursor-layer";
+ parentEl.appendChild(this.element);
+
+ this.cursor = dom.createElement("div");
+ this.cursor.className = "ace_cursor ace_hidden";
+ this.element.appendChild(this.cursor);
+
+ this.isVisible = false;
+};
+
+(function() {
+
+ this.setSession = function(session) {
+ this.session = session;
+ };
+
+ this.hideCursor = function() {
+ this.isVisible = false;
+ dom.addCssClass(this.cursor, "ace_hidden");
+ clearInterval(this.blinkId);
+ };
+
+ this.showCursor = function() {
+ this.isVisible = true;
+ dom.removeCssClass(this.cursor, "ace_hidden");
+ this.cursor.style.visibility = "visible";
+ this.restartTimer();
+ };
+
+ this.restartTimer = function() {
+ clearInterval(this.blinkId);
+ if (!this.isVisible) {
+ return;
+ }
+
+ var cursor = this.cursor;
+ this.blinkId = setInterval(function() {
+ cursor.style.visibility = "hidden";
+ setTimeout(function() {
+ cursor.style.visibility = "visible";
+ }, 400);
+ }, 1000);
+ };
+
+ this.getPixelPosition = function(onScreen) {
+ if (!this.config || !this.session) {
+ return {
+ left : 0,
+ top : 0
+ };
+ }
+
+ var position = this.session.selection.getCursor();
+ var pos = this.session.documentToScreenPosition(position);
+ var cursorLeft = Math.round(pos.column * this.config.characterWidth);
+ var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) *
+ this.config.lineHeight;
+
+ return {
+ left : cursorLeft,
+ top : cursorTop
+ };
+ };
+
+ this.update = function(config) {
+ this.config = config;
+
+ this.pixelPos = this.getPixelPosition(true);
+
+ this.cursor.style.left = this.pixelPos.left + "px";
+ this.cursor.style.top = this.pixelPos.top + "px";
+ this.cursor.style.width = config.characterWidth + "px";
+ this.cursor.style.height = config.lineHeight + "px";
+
+ var overwrite = this.session.getOverwrite()
+ if (overwrite != this.overwrite) {
+ this.overwrite = overwrite;
+ if (overwrite)
+ dom.addCssClass(this.cursor, "ace_overwrite");
+ else
+ dom.removeCssClass(this.cursor, "ace_overwrite");
+ }
+
+ this.restartTimer();
+ };
+
+ this.destroy = function() {
+ clearInterval(this.blinkId);
+ }
+
+}).call(Cursor.prototype);
+
+exports.Cursor = Cursor;
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/scrollbar', ['require', 'exports', 'module' , 'pilot/oop', 'pilot/dom', 'pilot/event', 'pilot/event_emitter'], function(require, exports, module) {
+
+var oop = require("pilot/oop");
+var dom = require("pilot/dom");
+var event = require("pilot/event");
+var EventEmitter = require("pilot/event_emitter").EventEmitter;
+
+var ScrollBar = function(parent) {
+ this.element = dom.createElement("div");
+ this.element.className = "ace_sb";
+
+ this.inner = dom.createElement("div");
+ this.element.appendChild(this.inner);
+
+ parent.appendChild(this.element);
+
+ this.width = dom.scrollbarWidth();
+ this.element.style.width = this.width + "px";
+
+ event.addListener(this.element, "scroll", this.onScroll.bind(this));
+};
+
+(function() {
+ oop.implement(this, EventEmitter);
+
+ this.onScroll = function() {
+ this._dispatchEvent("scroll", {data: this.element.scrollTop});
+ };
+
+ this.getWidth = function() {
+ return this.width;
+ };
+
+ this.setHeight = function(height) {
+ this.element.style.height = height + "px";
+ };
+
+ this.setInnerHeight = function(height) {
+ this.inner.style.height = height + "px";
+ };
+
+ this.setScrollTop = function(scrollTop) {
+ this.element.scrollTop = scrollTop;
+ };
+
+}).call(ScrollBar.prototype);
+
+exports.ScrollBar = ScrollBar;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/renderloop', ['require', 'exports', 'module' , 'pilot/event'], function(require, exports, module) {
+
+var event = require("pilot/event");
+
+var RenderLoop = function(onRender) {
+ this.onRender = onRender;
+ this.pending = false;
+ this.changes = 0;
+};
+
+(function() {
+
+ this.schedule = function(change) {
+ //this.onRender(change);
+ //return;
+ this.changes = this.changes | change;
+ if (!this.pending) {
+ this.pending = true;
+ var _self = this;
+ this.setTimeoutZero(function() {
+ _self.pending = false;
+ var changes = _self.changes;
+ _self.changes = 0;
+ _self.onRender(changes);
+ })
+ }
+ };
+
+ this.setTimeoutZero = window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame;
+
+ if (this.setTimeoutZero) {
+
+ this.setTimeoutZero = this.setTimeoutZero.bind(window)
+ } else if (window.postMessage) {
+
+ this.messageName = "zero-timeout-message";
+
+ this.setTimeoutZero = function(callback) {
+ if (!this.attached) {
+ var _self = this;
+ event.addListener(window, "message", function(e) {
+ if (_self.callback && e.data == _self.messageName) {
+ event.stopPropagation(e);
+ _self.callback();
+ }
+ });
+ this.attached = true;
+ }
+ this.callback = callback;
+ window.postMessage(this.messageName, "*");
+ }
+
+ } else {
+
+ this.setTimeoutZero = function(callback) {
+ setTimeout(callback, 0);
+ }
+ }
+
+}).call(RenderLoop.prototype);
+
+exports.RenderLoop = RenderLoop;
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Ajax.org Code Editor (ACE).
+ *
+ * The Initial Developer of the Original Code is
+ * Ajax.org B.V.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Fabian Jakobs <fabian AT ajax DOT org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('ace/theme/textmate', ['require', 'exports', 'module' , 'pilot/dom'], function(require, exports, module) {
+
+ var dom = require("pilot/dom");
+
+ var cssText = ".ace-tm .ace_editor {\
+ border: 2px solid rgb(159, 159, 159);\
+}\
+\
+.ace-tm .ace_editor.ace_focus {\
+ border: 2px solid #327fbd;\
+}\
+\
+.ace-tm .ace_gutter {\
+ width: 50px;\
+ background: #e8e8e8;\
+ color: #333;\
+ overflow : hidden;\
+}\
+\
+.ace-tm .ace_gutter-layer {\
+ width: 100%;\
+ text-align: right;\
+}\
+\
+.ace-tm .ace_gutter-layer .ace_gutter-cell {\
+ padding-right: 6px;\
+}\
+\
+.ace-tm .ace_print_margin {\
+ width: 1px;\
+ background: #e8e8e8;\
+}\
+\
+.ace-tm .ace_text-layer {\
+ cursor: text;\
+}\
+\
+.ace-tm .ace_cursor {\
+ border-left: 2px solid black;\
+}\
+\
+.ace-tm .ace_cursor.ace_overwrite {\
+ border-left: 0px;\
+ border-bottom: 1px solid black;\
+}\
+ \
+.ace-tm .ace_line .ace_invisible {\
+ color: rgb(191, 191, 191);\
+}\
+\
+.ace-tm .ace_line .ace_keyword {\
+ color: blue;\
+}\
+\
+.ace-tm .ace_line .ace_constant.ace_buildin {\
+ color: rgb(88, 72, 246);\
+}\
+\
+.ace-tm .ace_line .ace_constant.ace_language {\
+ color: rgb(88, 92, 246);\
+}\
+\
+.ace-tm .ace_line .ace_constant.ace_library {\
+ color: rgb(6, 150, 14);\
+}\
+\
+.ace-tm .ace_line .ace_invalid {\
+ background-color: rgb(153, 0, 0);\
+ color: white;\
+}\
+\
+.ace-tm .ace_line .ace_fold {\
+ background-color: #E4E4E4;\
+ border-radius: 3px;\
+}\
+\
+.ace-tm .ace_line .ace_support.ace_function {\
+ color: rgb(60, 76, 114);\
+}\
+\
+.ace-tm .ace_line .ace_support.ace_constant {\
+ color: rgb(6, 150, 14);\
+}\
+\
+.ace-tm .ace_line .ace_support.ace_type,\
+.ace-tm .ace_line .ace_support.ace_class {\
+ color: rgb(109, 121, 222);\
+}\
+\
+.ace-tm .ace_line .ace_keyword.ace_operator {\
+ color: rgb(104, 118, 135);\
+}\
+\
+.ace-tm .ace_line .ace_string {\
+ color: rgb(3, 106, 7);\
+}\
+\
+.ace-tm .ace_line .ace_comment {\
+ color: rgb(76, 136, 107);\
+}\
+\
+.ace-tm .ace_line .ace_comment.ace_doc {\
+ color: rgb(0, 102, 255);\
+}\
+\
+.ace-tm .ace_line .ace_comment.ace_doc.ace_tag {\
+ color: rgb(128, 159, 191);\
+}\
+\
+.ace-tm .ace_line .ace_constant.ace_numeric {\
+ color: rgb(0, 0, 205);\
+}\
+\
+.ace-tm .ace_line .ace_variable {\
+ color: rgb(49, 132, 149);\
+}\
+\
+.ace-tm .ace_line .ace_xml_pe {\
+ color: rgb(104, 104, 91);\
+}\
+\
+.ace-tm .ace_marker-layer .ace_selection {\
+ background: rgb(181, 213, 255);\
+}\
+\
+.ace-tm .ace_marker-layer .ace_step {\
+ background: rgb(252, 255, 0);\
+}\
+\
+.ace-tm .ace_marker-layer .ace_stack {\
+ background: rgb(164, 229, 101);\
+}\
+\
+.ace-tm .ace_marker-layer .ace_bracket {\
+ margin: -1px 0 0 -1px;\
+ border: 1px solid rgb(192, 192, 192);\
+}\
+\
+.ace-tm .ace_marker-layer .ace_active_line {\
+ background: rgb(232, 242, 254);\
+}\
+\
+.ace-tm .ace_marker-layer .ace_selected_word {\
+ background: rgb(250, 250, 255);\
+ border: 1px solid rgb(200, 200, 250);\
+}\
+\
+.ace-tm .ace_string.ace_regex {\
+ color: rgb(255, 0, 0)\
+}";
+
+ // import CSS once
+ dom.importCssString(cssText);
+
+ exports.cssClass = "ace-tm";
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is DomTemplate.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com) (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/environment', ['require', 'exports', 'module' , 'pilot/settings'], function(require, exports, module) {
+
+
+var settings = require("pilot/settings").settings;
+
+/**
+ * Create an environment object
+ */
+function create() {
+ return {
+ settings: settings
+ };
+};
+
+exports.create = create;
+
+
+});
+define("text/ace/css/editor.css", [], ".ace_editor {" +
+ " position: absolute;" +
+ " overflow: hidden;" +
+ "" +
+ " font-family: \"Menlo\", \"Monaco\", \"Courier New\", monospace;" +
+ " font-size: 12px;" +
+ "}" +
+ "" +
+ ".ace_scroller {" +
+ " position: absolute;" +
+ " overflow-x: scroll;" +
+ " overflow-y: hidden;" +
+ "}" +
+ "" +
+ ".ace_content {" +
+ " position: absolute;" +
+ " box-sizing: border-box;" +
+ " -moz-box-sizing: border-box;" +
+ " -webkit-box-sizing: border-box;" +
+ "}" +
+ "" +
+ ".ace_composition {" +
+ " position: absolute;" +
+ " background: #555;" +
+ " color: #DDD;" +
+ " z-index: 4;" +
+ "}" +
+ "" +
+ ".ace_gutter {" +
+ " position: absolute;" +
+ " overflow-x: hidden;" +
+ " overflow-y: hidden;" +
+ " height: 100%;" +
+ "}" +
+ "" +
+ ".ace_gutter-cell.ace_error {" +
+ " background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B\");" +
+ " background-repeat: no-repeat;" +
+ " background-position: 4px center;" +
+ "}" +
+ "" +
+ ".ace_gutter-cell.ace_warning {" +
+ " background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B\");" +
+ " background-repeat: no-repeat;" +
+ " background-position: 4px center;" +
+ "}" +
+ "" +
+ ".ace_editor .ace_sb {" +
+ " position: absolute;" +
+ " overflow-x: hidden;" +
+ " overflow-y: scroll;" +
+ " right: 0;" +
+ "}" +
+ "" +
+ ".ace_editor .ace_sb div {" +
+ " position: absolute;" +
+ " width: 1px;" +
+ " left: 0;" +
+ "}" +
+ "" +
+ ".ace_editor .ace_print_margin_layer {" +
+ " z-index: 0;" +
+ " position: absolute;" +
+ " overflow: hidden;" +
+ " margin: 0;" +
+ " left: 0;" +
+ " height: 100%;" +
+ " width: 100%;" +
+ "}" +
+ "" +
+ ".ace_editor .ace_print_margin {" +
+ " position: absolute;" +
+ " height: 100%;" +
+ "}" +
+ "" +
+ ".ace_editor textarea {" +
+ " position: fixed;" +
+ " z-index: -1;" +
+ " width: 10px;" +
+ " height: 30px;" +
+ " opacity: 0;" +
+ " background: transparent;" +
+ " appearance: none;" +
+ " border: none;" +
+ " resize: none;" +
+ " outline: none;" +
+ " overflow: hidden;" +
+ "}" +
+ "" +
+ ".ace_layer {" +
+ " z-index: 1;" +
+ " position: absolute;" +
+ " overflow: hidden;" +
+ " white-space: nowrap;" +
+ " height: 100%;" +
+ " width: 100%;" +
+ "}" +
+ "" +
+ ".ace_text-layer {" +
+ " font-family: Monaco, \"Courier New\", monospace;" +
+ " color: black;" +
+ "}" +
+ "" +
+ ".ace_cjk {" +
+ " display: inline-block;" +
+ " text-align: center;" +
+ "}" +
+ "" +
+ ".ace_cursor-layer {" +
+ " z-index: 4;" +
+ " cursor: text;" +
+ " pointer-events: none;" +
+ "}" +
+ "" +
+ ".ace_cursor {" +
+ " z-index: 4;" +
+ " position: absolute;" +
+ "}" +
+ "" +
+ ".ace_cursor.ace_hidden {" +
+ " opacity: 0.2;" +
+ "}" +
+ "" +
+ ".ace_line {" +
+ " white-space: nowrap;" +
+ "}" +
+ "" +
+ ".ace_marker-layer {" +
+ " cursor: text;" +
+ " pointer-events: none;" +
+ "}" +
+ "" +
+ ".ace_marker-layer .ace_step {" +
+ " position: absolute;" +
+ " z-index: 3;" +
+ "}" +
+ "" +
+ ".ace_marker-layer .ace_selection {" +
+ " position: absolute;" +
+ " z-index: 4;" +
+ "}" +
+ "" +
+ ".ace_marker-layer .ace_bracket {" +
+ " position: absolute;" +
+ " z-index: 5;" +
+ "}" +
+ "" +
+ ".ace_marker-layer .ace_active_line {" +
+ " position: absolute;" +
+ " z-index: 2;" +
+ "}" +
+ "" +
+ ".ace_marker-layer .ace_selected_word {" +
+ " position: absolute;" +
+ " z-index: 6;" +
+ " box-sizing: border-box;" +
+ " -moz-box-sizing: border-box;" +
+ " -webkit-box-sizing: border-box;" +
+ "}" +
+ "" +
+ ".ace_line .ace_fold {" +
+ " cursor: pointer;" +
+ "}" +
+ "" +
+ ".ace_dragging .ace_marker-layer, .ace_dragging .ace_text-layer {" +
+ " cursor: move;" +
+ "}" +
+ "");
+
+define("text/styles.css", [], "html {" +
+ " height: 100%;" +
+ " width: 100%;" +
+ " overflow: hidden;" +
+ "}" +
+ "" +
+ "body {" +
+ " overflow: hidden;" +
+ " margin: 0;" +
+ " padding: 0;" +
+ " height: 100%;" +
+ " width: 100%;" +
+ " font-family: Arial, Helvetica, sans-serif, Tahoma, Verdana, sans-serif;" +
+ " font-size: 12px;" +
+ " background: rgb(14, 98, 165);" +
+ " color: white;" +
+ "}" +
+ "" +
+ "#logo {" +
+ " padding: 15px;" +
+ " margin-left: 65px;" +
+ "}" +
+ "" +
+ "#editor {" +
+ " position: absolute;" +
+ " top: 0px;" +
+ " left: 280px;" +
+ " bottom: 0px;" +
+ " right: 0px;" +
+ " background: white;" +
+ "}" +
+ "" +
+ "#controls {" +
+ " padding: 5px;" +
+ "}" +
+ "" +
+ "#controls td {" +
+ " text-align: right;" +
+ "}" +
+ "" +
+ "#controls td + td {" +
+ " text-align: left;" +
+ "}" +
+ "" +
+ "#cockpitInput {" +
+ " position: absolute;" +
+ " left: 280px;" +
+ " right: 0px;" +
+ " bottom: 0;" +
+ "" +
+ " border: none; outline: none;" +
+ " font-family: consolas, courier, monospace;" +
+ " font-size: 120%;" +
+ "}" +
+ "" +
+ "#cockpitOutput {" +
+ " padding: 10px;" +
+ " margin: 0 15px;" +
+ " border: 1px solid #AAA;" +
+ " -moz-border-radius-topleft: 10px;" +
+ " -moz-border-radius-topright: 10px;" +
+ " border-top-left-radius: 4px; border-top-right-radius: 4px;" +
+ " background: #DDD; color: #000;" +
+ "}");
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+require(["ace/ace"], function(ace) {
+ window.ace = ace;
+}); \ No newline at end of file
diff --git a/studio/static/js/ace/ace.js b/studio/static/js/ace/ace.js
new file mode 100644
index 0000000..7ac56c0
--- /dev/null
+++ b/studio/static/js/ace/ace.js
@@ -0,0 +1 @@
+(function(){var a=function(){return this}();if(a.require&&a.define)require.packaged=!0;else{var b=function(a,c,d){typeof a!="string"?b.original?b.original.apply(window,arguments):(console.error("dropping module because define wasn't a string."),console.trace()):(arguments.length==2&&(d=c),define.modules||(define.modules={}),define.modules[a]=d)};a.define&&(b.original=a.define),a.define=b;var c=function(a,b){if(Object.prototype.toString.call(a)==="[object Array]"){var e=[];for(var f=0,g=a.length;f<g;++f){var h=d(a[f]);if(!h&&c.original)return c.original.apply(window,arguments);e.push(h)}b&&b.apply(null,e)}else{if(typeof a=="string"){var i=d(a);if(!i&&c.original)return c.original.apply(window,arguments);b&&b();return i}if(c.original)return c.original.apply(window,arguments)}};a.require&&(c.original=a.require),a.require=c,require.packaged=!0;var d=function(a){var b=define.modules[a];if(b==null){console.error("Missing module: "+a);return null}if(typeof b=="function"){var c={};b(require,c,{id:a,uri:""}),define.modules[a]=c;return c}return b}}})(),define("pilot/fixoldbrowsers",["require","exports","module"],function(a,b,c){if(!Function.prototype.bind){var d=Array.prototype.slice;Function.prototype.bind=function(b){var c=this;if(typeof c.apply!="function"||typeof c.call!="function")return new TypeError;var e=d.call(arguments),f=function a(){if(this instanceof a){var b=Object.create(c.prototype);c.apply(b,e.concat(d.call(arguments)));return b}return c.call.apply(c,e.concat(d.call(arguments)))};f.length=typeof c=="function"?Math.max(c.length-e.length,0):0;return f}}var e=Function.prototype.call,f=Array.prototype,g=Object.prototype,h=e.bind(g.hasOwnProperty),i,j,k,l,m;if(m=h(g,"__defineGetter__"))i=e.bind(g.__defineGetter__),j=e.bind(g.__defineSetter__),k=e.bind(g.__lookupGetter__),l=e.bind(g.__lookupSetter__);Array.isArray||(Array.isArray=function(b){return Object.prototype.toString.call(b)==="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(b,c){var d=+this.length;for(var e=0;e<d;e++)e in this&&b.call(c,this[e],e,this)}),Array.prototype.map||(Array.prototype.map=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;var d=Array(c),e=arguments[1];for(var f=0;f<c;f++)f in this&&(d[f]=b.call(e,this[f],f,this));return d}),Array.prototype.filter||(Array.prototype.filter=function(b){var c=[],d=arguments[1];for(var e=0;e<this.length;e++)b.call(d,this[e])&&c.push(this[e]);return c}),Array.prototype.every||(Array.prototype.every=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(!b.call(c,this[d]))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(b.call(c,this[d]))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=0;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d++];break}if(++d>=c)throw new TypeError}while(!0);for(;d<c;d++)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=c-1;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d--];break}if(--d<0)throw new TypeError}while(!0);for(;d>=0;d--)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.indexOf||(Array.prototype.indexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||0;if(d>=c)return-1;d<0&&(d+=c);for(;d<c;d++){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||c;d<0&&(d+=c),d=Math.min(d,c-1);for(;d>=0;d--){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(b){return b.__proto__||b.constructor.prototype});if(!Object.getOwnPropertyDescriptor){var n="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(b,c){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(n+b);if(!h(b,c))return undefined;var d,e,f;d={enumerable:!0,configurable:!0};if(m){var i=b.__proto__;b.__proto__=g;var e=k(b,c),f=l(b,c);b.__proto__=i;if(e||f){e&&(descriptor.get=e),f&&(descriptor.set=f);return descriptor}}descriptor.value=b[c];return descriptor}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(b){return Object.keys(b)}),Object.create||(Object.create=function(b,c){var d;if(b===null)d={"__proto__":null};else{if(typeof b!="object")throw new TypeError("typeof prototype["+typeof b+"] != 'object'");var e=function(){};e.prototype=b,d=new e,d.__proto__=b}typeof c!="undefined"&&Object.defineProperties(d,c);return d});if(!Object.defineProperty){var o="Property description must be an object: ",p="Object.defineProperty called on non-object: ",q="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(b,c,d){if(typeof b!="object"&&typeof b!="function")throw new TypeError(p+b);if(typeof b!="object"||b===null)throw new TypeError(o+d);if(h(d,"value"))if(m&&(k(b,c)||l(b,c))){var e=b.__proto__;b.__proto__=g,delete b[c],b[c]=d.value,b.prototype}else b[c]=d.value;else{if(!m)throw new TypeError(q);h(d,"get")&&i(b,c,d.get),h(d,"set")&&j(b,c,d.set)}return b}}Object.defineProperties||(Object.defineProperties=function(b,c){for(var d in c)h(c,d)&&Object.defineProperty(b,d,c[d]);return b}),Object.seal||(Object.seal=function(b){return b}),Object.freeze||(Object.freeze=function(b){return b});try{Object.freeze(function(){})}catch(r){Object.freeze=function(b){return function(c){return typeof c=="function"?c:b(c)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(b){return b}),Object.isSealed||(Object.isSealed=function(b){return!1}),Object.isFrozen||(Object.isFrozen=function(b){return!1}),Object.isExtensible||(Object.isExtensible=function(b){return!0});if(!Object.keys){var s=!0,t=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],u=t.length;for(var v in{toString:null})s=!1;Object.keys=function a(b){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError("Object.keys called on a non-object");var a=[];for(var c in b)h(b,c)&&a.push(c);if(s)for(var d=0,e=u;d<e;d++){var f=t[d];h(b,f)&&a.push(f)}return a}}Date.prototype.toISOString||(Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+(this.getUTCMonth()+1)+"-"+this.getUTCDate()+"T"+this.getUTCHours()+":"+this.getUTCMinutes()+":"+this.getUTCSeconds()+"Z"}),Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(b){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),isNaN(Date.parse("T00:00"))&&(Date=function(a){var b=function(c,d,e,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j===1&&String(c)===c?new a(b.parse(c)):j>=7?new a(c,d,e,f,g,h,i):j>=6?new a(c,d,e,f,g,h):j>=5?new a(c,d,e,f,g):j>=4?new a(c,d,e,f):j>=3?new a(c,d,e):j>=2?new a(c,d):j>=1?new a(c):new a;k.constructor=b;return k}return a.apply(this,arguments)},c=new RegExp("^(?:((?:[+-]\\d\\d)?\\d\\d\\d\\d)(?:-(\\d\\d)(?:-(\\d\\d))?)?)?(?:T(\\d\\d):(\\d\\d)(?::(\\d\\d)(?:\\.(\\d\\d\\d))?)?)?(?:Z|([+-])(\\d\\d):(\\d\\d))?$");for(var d in a)b[d]=a[d];b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(d){var e=c.exec(d);if(e){e.shift();var f=e[0]===undefined;for(var g=0;g<10;g++){if(g===7)continue;e[g]=+(e[g]||(g<3?1:0)),g===1&&e[g]--}if(f)return((e[3]*60+e[4])*60+e[5])*1e3+e[6];var h=(e[8]*60+e[9])*60*1e3;e[6]==="-"&&(h=-h);return a.UTC.apply(this,e.slice(0,7))+h}return a.parse.apply(this,arguments)};return b}(Date));if(!String.prototype.trim){var w=/^\s\s*/,x=/\s\s*$/;String.prototype.trim=function(){return String(this).replace(w,"").replace(x,"")}}}),define("ace/ace",["require","exports","module","pilot/index","pilot/fixoldbrowsers","pilot/plugin_manager","pilot/dom","pilot/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/theme/textmate","pilot/environment"],function(a,b,c){a("pilot/index"),a("pilot/fixoldbrowsers");var d=a("pilot/plugin_manager").catalog;d.registerPlugins(["pilot/index"]);var e=a("pilot/dom"),f=a("pilot/event"),g=a("ace/editor").Editor,h=a("ace/edit_session").EditSession,i=a("ace/undomanager").UndoManager,j=a("ace/virtual_renderer").VirtualRenderer;b.edit=function(b){typeof b=="string"&&(b=document.getElementById(b));var c=new h(e.getInnerText(b));c.setUndoManager(new i),b.innerHTML="";var k=new g(new j(b,a("ace/theme/textmate")));k.setSession(c);var l=a("pilot/environment").create();d.startupPlugins({env:l}).then(function(){l.document=c,l.editor=k,k.resize(),f.addListener(window,"resize",function(){k.resize()}),b.env=l}),k.env=l;return k}}),define("pilot/index",["require","exports","module","pilot/fixoldbrowsers","pilot/types/basic","pilot/types/command","pilot/types/settings","pilot/commands/settings","pilot/commands/basic","pilot/settings/canon","pilot/canon"],function(a,b,c){b.startup=function(b,c){a("pilot/fixoldbrowsers"),a("pilot/types/basic").startup(b,c),a("pilot/types/command").startup(b,c),a("pilot/types/settings").startup(b,c),a("pilot/commands/settings").startup(b,c),a("pilot/commands/basic").startup(b,c),a("pilot/settings/canon").startup(b,c),a("pilot/canon").startup(b,c)},b.shutdown=function(b,c){a("pilot/types/basic").shutdown(b,c),a("pilot/types/command").shutdown(b,c),a("pilot/types/settings").shutdown(b,c),a("pilot/commands/settings").shutdown(b,c),a("pilot/commands/basic").shutdown(b,c),a("pilot/settings/canon").shutdown(b,c),a("pilot/canon").shutdown(b,c)}}),define("pilot/types/basic",["require","exports","module","pilot/types"],function(a,b,c){function m(a){if(a instanceof e)this.subtype=a;else{if(typeof a!="string")throw new Error("Can' handle array subtype");this.subtype=d.getType(a);if(this.subtype==null)throw new Error("Unknown array subtype: "+a)}}function l(a){if(typeof a.defer!="function")throw new Error("Instances of DeferredType need typeSpec.defer to be a function that returns a type");Object.keys(a).forEach(function(b){this[b]=a[b]},this)}function j(a){if(!Array.isArray(a.data)&&typeof a.data!="function")throw new Error("instances of SelectionType need typeSpec.data to be an array or function that returns an array:"+JSON.stringify(a));Object.keys(a).forEach(function(b){this[b]=a[b]},this)}var d=a("pilot/types"),e=d.Type,f=d.Conversion,g=d.Status,h=new e;h.stringify=function(a){return a},h.parse=function(a){if(typeof a!="string")throw new Error("non-string passed to text.parse()");return new f(a)},h.name="text";var i=new e;i.stringify=function(a){if(!a)return null;return""+a},i.parse=function(a){if(typeof a!="string")throw new Error("non-string passed to number.parse()");if(a.replace(/\s/g,"").length===0)return new f(null,g.INCOMPLETE,"");var b=new f(parseInt(a,10));isNaN(b.value)&&(b.status=g.INVALID,b.message="Can't convert \""+a+'" to a number.');return b},i.decrement=function(a){return a-1},i.increment=function(a){return a+1},i.name="number",j.prototype=new e,j.prototype.stringify=function(a){return a},j.prototype.parse=function(a){if(typeof a!="string")throw new Error("non-string passed to parse()");if(!this.data)throw new Error("Missing data on selection type extension.");var b=typeof this.data=="function"?this.data():this.data,c=!1,d,e=[];b.forEach(function(b){a==b?(d=this.fromString(b),c=!0):b.indexOf(a)===0&&e.push(this.fromString(b))},this);if(c)return new f(d);this.noMatch&&this.noMatch();if(e.length>0){var h="Possibilities"+(a.length===0?"":" for '"+a+"'");return new f(null,g.INCOMPLETE,h,e)}var h="Can't use '"+a+"'.";return new f(null,g.INVALID,h,e)},j.prototype.fromString=function(a){return a},j.prototype.decrement=function(a){var b=typeof this.data=="function"?this.data():this.data,c;if(a==null)c=b.length-1;else{var d=this.stringify(a),c=b.indexOf(d);c=c===0?b.length-1:c-1}return this.fromString(b[c])},j.prototype.increment=function(a){var b=typeof this.data=="function"?this.data():this.data,c;if(a==null)c=0;else{var d=this.stringify(a),c=b.indexOf(d);c=c===b.length-1?0:c+1}return this.fromString(b[c])},j.prototype.name="selection",b.SelectionType=j;var k=new j({name:"bool",data:["true","false"],stringify:function(a){return""+a},fromString:function(a){return a==="true"?!0:!1}});l.prototype=new e,l.prototype.stringify=function(a){return this.defer().stringify(a)},l.prototype.parse=function(a){return this.defer().parse(a)},l.prototype.decrement=function(a){var b=this.defer();return b.decrement?b.decrement(a):undefined},l.prototype.increment=function(a){var b=this.defer();return b.increment?b.increment(a):undefined},l.prototype.name="deferred",b.DeferredType=l,m.prototype=new e,m.prototype.stringify=function(a){return a.join(" ")},m.prototype.parse=function(a){return this.defer().parse(a)},m.prototype.name="array";var n=!1;b.startup=function(){n||(n=!0,d.registerType(h),d.registerType(i),d.registerType(k),d.registerType(j),d.registerType(l),d.registerType(m))},b.shutdown=function(){n=!1,d.unregisterType(h),d.unregisterType(i),d.unregisterType(k),d.unregisterType(j),d.unregisterType(l),d.unregisterType(m)}}),define("pilot/types",["require","exports","module"],function(a,b,c){function h(a,b){if(a.substr(-2)==="[]"){var c=a.slice(0,-2);return new g.array(c)}var d=g[a];typeof d=="function"&&(d=new d(b));return d}function f(){}function e(a,b,c,e){this.value=a,this.status=b||d.VALID,this.message=c,this.predictions=e||[]}var d={VALID:{toString:function(){return"VALID"},valueOf:function(){return 0}},INCOMPLETE:{toString:function(){return"INCOMPLETE"},valueOf:function(){return 1}},INVALID:{toString:function(){return"INVALID"},valueOf:function(){return 2}},combine:function(a){var b=d.VALID;for(var c=0;c<a.length;c++)a[c].valueOf()>b.valueOf()&&(b=a[c]);return b}};b.Status=d,b.Conversion=e,f.prototype={stringify:function(a){throw new Error("not implemented")},parse:function(a){throw new Error("not implemented")},name:undefined,increment:function(a){return undefined},decrement:function(a){return undefined},getDefault:function(){return this.parse("")}},b.Type=f;var g={};b.registerType=function(a){if(typeof a=="object"){if(!(a instanceof f))throw new Error("Can't registerType using: "+a);if(!a.name)throw new Error("All registered types must have a name");g[a.name]=a}else{if(typeof a!="function")throw new Error("Unknown type: "+a);if(!a.prototype.name)throw new Error("All registered types must have a name");g[a.prototype.name]=a}},b.registerTypes=function(c){Object.keys(c).forEach(function(a){var d=c[a];d.name=a,b.registerType(d)})},b.deregisterType=function(a){delete g[a.name]},b.getType=function(a){if(typeof a=="string")return h(a);if(typeof a=="object"){if(!a.name)throw new Error("Missing 'name' member to typeSpec");return h(a.name,a)}throw new Error("Can't extract type from "+a)}}),define("pilot/types/command",["require","exports","module","pilot/canon","pilot/types/basic","pilot/types"],function(a,b,c){var d=a("pilot/canon"),e=a("pilot/types/basic").SelectionType,f=a("pilot/types"),g=new e({name:"command",data:function(){return d.getCommandNames()},stringify:function(a){return a.name},fromString:function(a){return d.getCommand(a)}});b.startup=function(){f.registerType(g)},b.shutdown=function(){f.unregisterType(g)}}),define("pilot/canon",["require","exports","module","pilot/console","pilot/stacktrace","pilot/oop","pilot/useragent","pilot/keys","pilot/event_emitter","pilot/typecheck","pilot/catalog","pilot/types","pilot/lang"],function(a,b,c){function J(a){a=a||{},this.command=a.command,this.args=a.args,this.typed=a.typed,this._begunOutput=!1,this.start=new Date,this.end=null,this.completed=!1,this.error=!1}function G(a,b,c,e,f){function h(){a.exec(b,g.args,g),!g.isAsync&&!g.isDone&&g.done()}typeof a=="string"&&(a=q[a]);if(!a)return!1;var g=new J({sender:c,command:a,args:e||{},typed:f});if(g.getStatus()==l.INVALID){d.error("Canon.exec: Invalid parameter(s) passed to "+a.name);return!1}if(g.getStatus()==l.INCOMPLETE){var i,j=b[c];if(!j||!j.getArgsProvider||!(i=j.getArgsProvider()))i=F;i(g,function(){g.getStatus()==l.VALID&&h()});return!0}h();return!0}function F(a,b){var c=a.args,d=a.command.params;for(var e=0;e<d.length;e++){var f=d[e];if(a.getParamStatus(f)!=l.VALID||f.defaultValue===null){var g=f.description;f.defaultValue===null&&(g+=" (optional)");var h=prompt(g,f.defaultValue||"");if(!h){b();return}c[f.name]=h}}b()}function E(){return z}function D(a){return q[a]}function C(a){var b=typeof a=="string"?a:a.name;delete q[b],n.arrayRemove(z,b)}function B(a,b){var c=b.type;b.type=m.getType(c);if(b.type==null)throw new Error("In "+a+"/"+b.name+": can't find type for: "+JSON.stringify(c))}function A(a){if(!a.name)throw new Error("All registered commands must have a name");a.params==null&&(a.params=[]);if(!Array.isArray(a.params))throw new Error("command.params must be an array in "+a.name);a.params.forEach(function(b){if(!b.name)throw new Error("In "+a.name+": all params must have a name");B(a.name,b)},this),q[a.name]=a,a.bindKey&&w(a),z.push(a.name),z.sort()}function y(a,b,c,d){var e=x(a,b,c,d);return e?G(e,a,b,{}):!1}function x(a,b,c,d){j.isNumber(d)&&(d=h.keyCodeToString(d));var e=(s[c]||{})[d]||[];for(var f=0;f<e.length;f++)if(e[f].sender(a,b,c,d))return e[f].command;var g=r[b];return g&&g[c]&&g[c][d]}function w(a){var b=a.bindKey,c=b[v],d=r,e=s;if(!b.sender)throw new Error("All key bindings must have a sender");if(!b.mac&&b.mac!==null)throw new Error("All key bindings must have a mac key binding");if(!b.win&&b.win!==null)throw new Error("All key bindings must have a windows key binding");if(!!b[v])if(typeof b.sender=="string"){var f=t(b.sender,"\\|",null,!0);f.forEach(function(b){d[b]||(d[b]={}),c.split("|").forEach(function(c){u(c,a,d[b])})})}else{if(!j.isFunction(b.sender))throw new Error("Key binding must have a sender that is a string or function");var g={command:a,sender:b.sender};keyData=u(c),e[keyData.hashId]||(e[keyData.hashId]={}),e[keyData.hashId][keyData.key]?e[keyData.hashId][keyData.key].push(g):e[keyData.hashId][keyData.key]=[g]}}function u(a,b,c){var d,e=0,f=t(a,"\\-",null,!0),g=0,i=f.length;for(;g<i;++g)h.KEY_MODS[f[g]]?e=e|h.KEY_MODS[f[g]]:d=f[g]||"-";if(c==null)return{key:d,hashId:e};(c[e]||(c[e]={}))[d]=b}function t(a,b,c,d){return(d&&a.toLowerCase()||a).replace(/(?:^\s+|\n|\s+$)/g,"").split(new RegExp("[\\s ]*"+b+"[\\s ]*","g"),c||999)}var d=a("pilot/console"),e=a("pilot/stacktrace").Trace,f=a("pilot/oop"),g=a("pilot/useragent"),h=a("pilot/keys"),i=a("pilot/event_emitter").EventEmitter,j=a("pilot/typecheck"),k=a("pilot/catalog"),l=a("pilot/types").Status,m=a("pilot/types"),n=a("pilot/lang"),o={name:"command",description:"A command is a bit of functionality with optional typed arguments which can do something small like moving the cursor around the screen, or large like cloning a project from VCS.",indexOn:"name"};b.startup=function(a,b){k.addExtensionSpec(o)},b.shutdown=function(a,b){k.removeExtensionSpec(o)};var p={name:"thing",description:"thing is an example command",params:[{name:"param1",description:"an example parameter",type:"text",defaultValue:null}],exec:function(a,b,c){thing()}},q={},r={},s={},v=g.isMac?"mac":"win",z=[];b.removeCommand=C,b.addCommand=A,b.getCommand=D,b.getCommandNames=E,b.findKeyCommand=x,b.exec=G,b.execKeyCommand=y,b.upgradeType=B,f.implement(b,i);var H=[],I=100;f.implement(J.prototype,i),J.prototype.getParamStatus=function(a){var b=this.args||{};if(a.name in b){if(b[a.name]==null)return a.defaultValue===null?l.VALID:l.INCOMPLETE;var c,d=b[a.name].toString();try{c=a.type.parse(d)}catch(e){return l.INVALID}if(c.status!=l.VALID)return c.status}else if(a.defaultValue===undefined)return l.INCOMPLETE;return l.VALID},J.prototype.getParamNameStatus=function(a){var b=this.command.params||[];for(var c=0;c<b.length;c++)if(b[c].name==a)return this.getParamStatus(b[c]);throw"Parameter '"+a+"' not defined on command '"+this.command.name+"'"},J.prototype.getStatus=function(){var a=this.args||{},b=this.command.params;if(!b||b.length==0)return l.VALID;var c=[];for(var d=0;d<b.length;d++)c.push(this.getParamStatus(b[d]));return l.combine(c)},J.prototype._beginOutput=function(){this._begunOutput=!0,this.outputs=[],H.push(this);while(H.length>I)H.shiftObject();b._dispatchEvent("output",{requests:H,request:this})},J.prototype.doneWithError=function(a){this.error=!0,this.done(a)},J.prototype.async=function(){this.isAsync=!0,this._begunOutput||this._beginOutput()},J.prototype.output=function(a){this._begunOutput||this._beginOutput(),typeof a!="string"&&!(a instanceof Node)&&(a=a.toString()),this.outputs.push(a),this.isDone=!0,this._dispatchEvent("output",{});return this},J.prototype.done=function(a){this.completed=!0,this.end=new Date,this.duration=this.end.getTime()-this.start.getTime(),a&&this.output(a),this.isDone||(this.isDone=!0,this._dispatchEvent("output",{}))},b.Request=J}),define("pilot/console",["require","exports","module"],function(a,b,c){var d=function(){},e=["assert","count","debug","dir","dirxml","error","group","groupEnd","info","log","profile","profileEnd","time","timeEnd","trace","warn"];typeof window=="undefined"?e.forEach(function(a){b[a]=function(){var b=Array.prototype.slice.call(arguments),c={op:"log",method:a,args:b};postMessage(JSON.stringify(c))}}):e.forEach(function(a){window.console&&window.console[a]?b[a]=Function.prototype.bind.call(window.console[a],window.console):b[a]=d})}),define("pilot/stacktrace",["require","exports","module","pilot/useragent","pilot/console"],function(a,b,c){function i(){}function g(a){for(var b=0;b<a.length;++b){var c=a[b];typeof c=="object"?a[b]="#object":typeof c=="function"?a[b]="#function":typeof c=="string"&&(a[b]='"'+c+'"')}return a.join(",")}var d=a("pilot/useragent"),e=a("pilot/console"),f=function(){return d.isGecko?"firefox":d.isOpera?"opera":"other"}(),h={chrome:function(a){var b=a.stack;if(!b){e.log(a);return[]}return b.replace(/^.*?\n/,"").replace(/^.*?\n/,"").replace(/^.*?\n/,"").replace(/^[^\(]+?[\n$]/gm,"").replace(/^\s+at\s+/gm,"").replace(/^Object.<anonymous>\s*\(/gm,"{anonymous}()@").split("\n")},firefox:function(a){var b=a.stack;if(!b){e.log(a);return[]}b=b.replace(/(?:\n@:0)?\s+$/m,""),b=b.replace(/^\(/gm,"{anonymous}(");return b.split("\n")},opera:function(a){var b=a.message.split("\n"),c="{anonymous}",d=/Line\s+(\d+).*?script\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i,e,f,g;for(e=4,f=0,g=b.length;e<g;e+=2)d.test(b[e])&&(b[f++]=(RegExp.$3?RegExp.$3+"()@"+RegExp.$2+RegExp.$1:c+"()@"+RegExp.$2+":"+RegExp.$1)+" -- "+b[e+1].replace(/^\s+/,""));b.splice(f,b.length-f);return b},other:function(a){var b="{anonymous}",c=/function\s*([\w\-$]+)?\s*\(/i,d=[],e=0,f,h,i=10;while(a&&d.length<i){f=c.test(a.toString())?RegExp.$1||b:b,h=Array.prototype.slice.call(a.arguments),d[e++]=f+"("+g(h)+")";if(a===a.caller&&window.opera)break;a=a.caller}return d}};i.prototype={sourceCache:{},ajax:function(a){var b=this.createXMLHTTPObject();if(!!b){b.open("GET",a,!1),b.setRequestHeader("User-Agent","XMLHTTP/1.0"),b.send("");return b.responseText}},createXMLHTTPObject:function(){var a,b=[function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml3.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var c=0;c<b.length;c++)try{a=b[c](),this.createXMLHTTPObject=b[c];return a}catch(d){}},getSource:function(a){a in this.sourceCache||(this.sourceCache[a]=this.ajax(a).split("\n"));return this.sourceCache[a]},guessFunctions:function(a){for(var b=0;b<a.length;++b){var c=/{anonymous}\(.*\)@(\w+:\/\/([-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/,d=a[b],e=c.exec(d);if(e){var f=e[1],g=e[4];if(f&&g){var h=this.guessFunctionName(f,g);a[b]=d.replace("{anonymous}",h)}}}return a},guessFunctionName:function(a,b){try{return this.guessFunctionNameFromLines(b,this.getSource(a))}catch(c){return"getSource failed with url: "+a+", exception: "+c.toString()}},guessFunctionNameFromLines:function(a,b){var c=/function ([^(]*)\(([^)]*)\)/,d=/['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,e="",f=10;for(var g=0;g<f;++g){e=b[a-g]+e;if(e!==undefined){var h=d.exec(e);if(h)return h[1];h=c.exec(e);if(h&&h[1])return h[1]}}return"(?)"}};var j=new i,k=[/http:\/\/localhost:4020\/sproutcore.js:/];b.ignoreFramesMatching=function(a){k.push(a)},b.Trace=function(b,c){this._ex=b,this._stack=h[f](b),c&&(this._stack=j.guessFunctions(this._stack))},b.Trace.prototype.log=function(a){a<=0&&(a=999999999);var b=0;for(var c=0;c<this._stack.length&&b<a;c++){var d=this._stack[c],f=!0;k.forEach(function(a){a.test(d)&&(f=!1)}),f&&(e.debug(d),b++)}}}),define("pilot/useragent",["require","exports","module"],function(a,b,c){var d=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),e=navigator.userAgent,f=navigator.appVersion;b.isWin=d=="win",b.isMac=d=="mac",b.isLinux=d=="linux",b.isIE=!1,b.isGecko=b.isMozilla=window.controllers&&window.navigator.product==="Gecko",b.isOldGecko=b.isGecko&&/rv\:1/.test(navigator.userAgent),b.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",b.isWebKit=parseFloat(e.split("WebKit/")[1])||undefined,b.isAIR=e.indexOf("AdobeAIR")>=0,b.isIPad=e.indexOf("iPad")>=0,b.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},b.getOS=function(){return b.isMac?b.OS.MAC:b.isLinux?b.OS.LINUX:b.OS.WINDOWS}}),define("pilot/oop",["require","exports","module"],function(a,b,c){b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define("pilot/keys",["require","exports","module","pilot/oop"],function(a,b,c){var d=a("pilot/oop"),e=function(){var a={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,meta:8,command:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",188:",",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:'"'}};for(i in a.FUNCTION_KEYS){var b=a.FUNCTION_KEYS[i].toUpperCase();a[b]=parseInt(i,10)}d.mixin(a,a.MODIFIER_KEYS),d.mixin(a,a.PRINTABLE_KEYS),d.mixin(a,a.FUNCTION_KEYS);return a}();d.mixin(b,e),b.keyCodeToString=function(a){return(e[a]||String.fromCharCode(a)).toLowerCase()}}),define("pilot/event_emitter",["require","exports","module"],function(a,b,c){var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c&&!!c.length){var b=b||{};b.type=a;for(var d=0;d<c.length;d++)c[d](b)}},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c){var d=c.indexOf(b);d!==-1&&c.splice(d,1)}},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define("pilot/typecheck",["require","exports","module"],function(a,b,c){var d=Object.prototype.toString;b.isString=function(a){return a&&d.call(a)==="[object String]"},b.isBoolean=function(a){return a&&d.call(a)==="[object Boolean]"},b.isNumber=function(a){return a&&d.call(a)==="[object Number]"&&isFinite(a)},b.isObject=function(a){return a!==undefined&&(a===null||typeof a=="object"||Array.isArray(a)||b.isFunction(a))},b.isFunction=function(a){return a&&d.call(a)==="[object Function]"}}),define("pilot/catalog",["require","exports","module"],function(a,b,c){var d={};b.addExtensionSpec=function(a){d[a.name]=a},b.removeExtensionSpec=function(a){typeof a=="string"?delete d[a]:delete d[a.name]},b.getExtensionSpec=function(a){return d[a]},b.getExtensionSpecs=function(){return Object.keys(d)}}),define("pilot/lang",["require","exports","module"],function(a,b,c){b.stringReverse=function(a){return a.split("").reverse().join("")},b.stringRepeat=function(a,b){return Array(b+1).join(a)};var d=/^\s\s*/,e=/\s\s*$/;b.stringTrimLeft=function(a){return a.replace(d,"")},b.stringTrimRight=function(a){return a.replace(e,"")},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.copyArray=function(a){var b=[];for(i=0,l=a.length;i<l;i++)a[i]&&typeof a[i]=="object"?b[i]=this.copyObject(a[i]):b[i]=a[i];return b},b.deepCopy=function(a){if(typeof a!="object")return a;var b=a.constructor();for(var c in a)typeof a[c]=="object"?b[c]=this.deepCopy(a[c]):b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){b||(b=setTimeout(c,a||0));return d};d.schedule=d,d.call=function(){this.cancel(),a();return d},d.cancel=function(){clearTimeout(b),b=null;return d};return d}}),define("pilot/types/settings",["require","exports","module","pilot/types/basic","pilot/types","pilot/settings"],function(a,b,c){var d=a("pilot/types/basic").SelectionType,e=a("pilot/types/basic").DeferredType,f=a("pilot/types"),g=a("pilot/settings").settings,h,i=new d({name:"setting",data:function(){return k.settings.getSettingNames()},stringify:function(a){h=a;return a.name},fromString:function(a){h=g.getSetting(a);return h},noMatch:function(){h=null}}),j=new e({name:"settingValue",defer:function(){return h?h.type:f.getType("text")},getDefault:function(){var a=this.parse("");if(h){var b=h.get();if(a.predictions.length===0)a.predictions.push(b);else{var c=!1;for(;;){var d=a.predictions.indexOf(b);if(d===-1)break;a.predictions.splice(d,1),c=!0}c&&a.predictions.push(b)}}return a}}),k;b.startup=function(a,b){k=a.env,f.registerType(i),f.registerType(j)},b.shutdown=function(a,b){f.unregisterType(i),f.unregisterType(j)}}),define("pilot/settings",["require","exports","module","pilot/console","pilot/oop","pilot/types","pilot/event_emitter","pilot/catalog"],function(a,b,c){function l(){}function k(a){this._deactivated={},this._settings={},this._settingNames=[],a&&this.setPersister(a)}function j(a,b){this._settings=b,Object.keys(a).forEach(function(b){this[b]=a[b]},this),this.type=f.getType(this.type);if(this.type==null)throw new Error("In "+this.name+": can't find type for: "+JSON.stringify(a.type));if(!this.name)throw new Error("Setting.name == undefined. Ignoring.",this);if(!this.defaultValue===undefined)throw new Error("Setting.defaultValue == undefined",this);this.onChange&&this.on("change",this.onChange.bind(this)),this.set(this.defaultValue)}var d=a("pilot/console"),e=a("pilot/oop"),f=a("pilot/types"),g=a("pilot/event_emitter").EventEmitter,h=a("pilot/catalog"),i={name:"setting",description:"A setting is something that the application offers as a way to customize how it works",register:"env.settings.addSetting",indexOn:"name"};b.startup=function(a,b){h.addExtensionSpec(i)},b.shutdown=function(a,b){h.removeExtensionSpec(i)},j.prototype={get:function(){return this.value},set:function(a){this.value!==a&&(this.value=a,this._settings.persister&&this._settings.persister.persistValue(this._settings,this.name,a),this._dispatchEvent("change",{setting:this,value:a}))},resetValue:function(){this.set(this.defaultValue)}},e.implement(j.prototype,g),k.prototype={addSetting:function(a){var b=new j(a,this);this._settings[b.name]=b,this._settingNames.push(b.name),this._settingNames.sort()},addSettings:function(b){Object.keys(b).forEach(function(a){var c=b[a];"name"in c||(c.name=a),this.addSetting(c)},this)},removeSetting:function(a){var b=typeof a=="string"?a:a.name;a=this._settings[b],delete this._settings[b],util.arrayRemove(this._settingNames,b),settings.removeAllListeners("change")},removeSettings:function(b){Object.keys(b).forEach(function(a){var c=b[a];"name"in c||(c.name=a),this.removeSettings(c)},this)},getSettingNames:function(){return this._settingNames},getSetting:function(a){return this._settings[a]},setPersister:function(a){this._persister=a,a&&a.loadInitialValues(this)},resetAll:function(){this.getSettingNames().forEach(function(a){this.resetValue(a)},this)},_list:function(){var a=[];this.getSettingNames().forEach(function(b){a.push({key:b,value:this.getSetting(b).get()})},this);return a},_loadDefaultValues:function(){this._loadFromObject(this._getDefaultValues())},_loadFromObject:function(a){for(var b in a)if(a.hasOwnProperty(b)){var c=this._settings[b];if(c){var d=c.type.parse(a[b]);this.set(b,d)}else this.set(b,a[b])}},_saveToObject:function(){return this.getSettingNames().map(function(a){return this._settings[a].type.stringify(this.get(a))}.bind(this))},_getDefaultValues:function(){return this.getSettingNames().map(function(a){return this._settings[a].spec.defaultValue}.bind(this))}},b.settings=new k,l.prototype={loadInitialValues:function(a){a._loadDefaultValues();var b=cookie.get("settings");a._loadFromObject(JSON.parse(b))},persistValue:function(a,b,c){try{var e=JSON.stringify(a._saveToObject());cookie.set("settings",e)}catch(f){d.error("Unable to JSONify the settings! "+f);return}}},b.CookiePersister=l}),define("pilot/commands/settings",["require","exports","module","pilot/canon"],function(a,b,c){var d={name:"set",params:[{name:"setting",type:"setting",description:"The name of the setting to display or alter",defaultValue:null},{name:"value",type:"settingValue",description:"The new value for the chosen setting",defaultValue:null}],description:"define and show settings",exec:function(a,b,c){var d;if(!b.setting){var e=a.settings.getSettingNames();d="",e.sort(function(a,b){return a.localeCompare(b)}),e.forEach(function(b){var c=a.settings.getSetting(b),e="https://wiki.mozilla.org/Labs/Skywriter/Settings#"+c.name;d+='<a class="setting" href="'+e+'" title="View external documentation on setting: '+c.name+'" target="_blank">'+c.name+"</a> = "+c.value+"<br/>"})}else b.value===undefined?d="<strong>"+setting.name+"</strong> = "+setting.get():(b.setting.set(b.value),d="Setting: <strong>"+b.setting.name+"</strong> = "+b.setting.get());c.done(d)}},e={name:"unset",params:[{name:"setting",type:"setting",description:"The name of the setting to return to defaults"}],description:"unset a setting entirely",exec:function(a,b,c){var d=a.settings.get(b.setting);d?(d.reset(),c.done("Reset "+d.name+" to default: "+a.settings.get(b.setting))):c.doneWithError("No setting with the name <strong>"+b.setting+"</strong>.")}},f=a("pilot/canon");b.startup=function(a,b){f.addCommand(d),f.addCommand(e)},b.shutdown=function(a,b){f.removeCommand(d),f.removeCommand(e)}}),define("pilot/commands/basic",["require","exports","module","pilot/typecheck","pilot/canon"],function(require,exports,module){var checks=require("pilot/typecheck"),canon=require("pilot/canon"),helpMessages={plainPrefix:'<h2>Welcome to Skywriter - Code in the Cloud</h2><ul><li><a href="http://labs.mozilla.com/projects/skywriter" target="_blank">Home Page</a></li><li><a href="https://wiki.mozilla.org/Labs/Skywriter" target="_blank">Wiki</a></li><li><a href="https://wiki.mozilla.org/Labs/Skywriter/UserGuide" target="_blank">User Guide</a></li><li><a href="https://wiki.mozilla.org/Labs/Skywriter/Tips" target="_blank">Tips and Tricks</a></li><li><a href="https://wiki.mozilla.org/Labs/Skywriter/FAQ" target="_blank">FAQ</a></li><li><a href="https://wiki.mozilla.org/Labs/Skywriter/DeveloperGuide" target="_blank">Developers Guide</a></li></ul>',plainSuffix:'For more information, see the <a href="https://wiki.mozilla.org/Labs/Skywriter">Skywriter Wiki</a>.'},helpCommandSpec={name:"help",params:[{name:"search",type:"text",description:"Search string to narrow the output.",defaultValue:null}],description:"Get help on the available commands.",exec:function(a,b,c){var d=[],e=canon.getCommand(b.search);if(e&&e.exec)d.push(e.description?e.description:"No description for "+b.search);else{var f=!1;!b.search&&helpMessages.plainPrefix&&d.push(helpMessages.plainPrefix),e?(d.push("<h2>Sub-Commands of "+e.name+"</h2>"),d.push("<p>"+e.description+"</p>")):b.search?(b.search=="hidden"&&(b.search="",f=!0),d.push("<h2>Commands starting with '"+b.search+"':</h2>")):d.push("<h2>Available Commands:</h2>");var g=canon.getCommandNames();g.sort(),d.push("<table>");for(var h=0;h<g.length;h++){e=canon.getCommand(g[h]);if(!f&&e.hidden)continue;if(e.description===undefined)continue;if(b.search&&e.name.indexOf(b.search)!==0)continue;if(!b.search&&e.name.indexOf(" ")!=-1)continue;if(e&&e.name==b.search)continue;d.push("<tr>"),d.push('<th class="right">'+e.name+"</th>"),d.push("<td>"+e.description+"</td>"),d.push("</tr>")}d.push("</table>"),!b.search&&helpMessages.plainSuffix&&d.push(helpMessages.plainSuffix)}c.done(d.join(""))}},evalCommandSpec={name:"eval",params:[{name:"javascript",type:"text",description:"The JavaScript to evaluate"}],description:"evals given js code and show the result",hidden:!0,exec:function(env,args,request){var result,javascript=args.javascript;try{result=eval(javascript)}catch(e){result="<b>Error: "+e.message+"</b>"}var msg="",type="",x;if(checks.isFunction(result))msg=(result+"").replace(/\n/g,"<br>").replace(/ /g,"&#160"),type="function";else if(checks.isObject(result)){Array.isArray(result)?type="array":type="object";var items=[],value;for(x in result)result.hasOwnProperty(x)&&(checks.isFunction(result[x])?value="[function]":checks.isObject(result[x])?value="[object]":value=result[x],items.push({name:x,value:value}));items.sort(function(a,b){return a.name.toLowerCase()<b.name.toLowerCase()?-1:1});for(x=0;x<items.length;x++)msg+="<b>"+items[x].name+"</b>: "+items[x].value+"<br>"}else msg=result,type=typeof result;request.done("Result for eval <b>'"+javascript+"'</b>"+" (type: "+type+"): <br><br>"+msg)}},versionCommandSpec={name:"version",description:"show the Skywriter version",hidden:!0,exec:function(a,b,c){var d="Skywriter "+skywriter.versionNumber+" ("+skywriter.versionCodename+")";c.done(d)}},skywriterCommandSpec={name:"skywriter",hidden:!0,exec:function(a,b,c){var d=Math.floor(Math.random()*messages.length);c.done("Skywriter "+messages[d])}},messages=["really wants you to trick it out in some way.","is your Web editor.","would love to be like Emacs on the Web.","is written on the Web platform, so you can tweak it."],canon=require("pilot/canon");exports.startup=function(a,b){canon.addCommand(helpCommandSpec),canon.addCommand(evalCommandSpec),canon.addCommand(skywriterCommandSpec)},exports.shutdown=function(a,b){canon.removeCommand(helpCommandSpec),canon.removeCommand(evalCommandSpec),canon.removeCommand(skywriterCommandSpec)}}),define("pilot/settings/canon",["require","exports","module"],function(a,b,c){var d={name:"historyLength",description:"How many typed commands do we recall for reference?",type:"number",defaultValue:50};b.startup=function(a,b){a.env.settings.addSetting(d)},b.shutdown=function(a,b){a.env.settings.removeSetting(d)}}),define("pilot/plugin_manager",["require","exports","module","pilot/promise"],function(a,b,c){var d=a("pilot/promise").Promise;b.REASONS={APP_STARTUP:1,APP_SHUTDOWN:2,PLUGIN_ENABLE:3,PLUGIN_DISABLE:4,PLUGIN_INSTALL:5,PLUGIN_UNINSTALL:6,PLUGIN_UPGRADE:7,PLUGIN_DOWNGRADE:8},b.Plugin=function(a){this.name=a,this.status=this.INSTALLED},b.Plugin.prototype={NEW:0,INSTALLED:1,REGISTERED:2,STARTED:3,UNREGISTERED:4,SHUTDOWN:5,install:function(b,c){var e=new d;if(this.status>this.NEW){e.resolve(this);return e}a([this.name],function(a){a.install&&a.install(b,c),this.status=this.INSTALLED,e.resolve(this)}.bind(this));return e},register:function(b,c){var e=new d;if(this.status!=this.INSTALLED){e.resolve(this);return e}a([this.name],function(a){a.register&&a.register(b,c),this.status=this.REGISTERED,e.resolve(this)}.bind(this));return e},startup:function(c,e){e=e||b.REASONS.APP_STARTUP;var f=new d;if(this.status!=this.REGISTERED){f.resolve(this);return f}a([this.name],function(a){a.startup&&a.startup(c,e),this.status=this.STARTED,f.resolve(this)}.bind(this));return f},shutdown:function(b,c){this.status==this.STARTED&&(pluginModule=a(this.name),pluginModule.shutdown&&pluginModule.shutdown(b,c))}},b.PluginCatalog=function(){this.plugins={}},b.PluginCatalog.prototype={registerPlugins:function(a,c,e){var f=[];a.forEach(function(a){var d=this.plugins[a];d===undefined&&(d=new b.Plugin(a),this.plugins[a]=d,f.push(d.register(c,e)))}.bind(this));return d.group(f)},startupPlugins:function(a,b){var c=[];for(var e in this.plugins){var f=this.plugins[e];c.push(f.startup(a,b))}return d.group(c)}},b.catalog=new b.PluginCatalog}),define("pilot/promise",["require","exports","module","pilot/console","pilot/stacktrace"],function(a,b,c){var d=a("pilot/console"),e=a("pilot/stacktrace").Trace,f=-1,g=0,h=1,i=0,j=!1,k=[],l=[];Promise=function(){this._status=g,this._value=undefined,this._onSuccessHandlers=[],this._onErrorHandlers=[],this._id=i++,k[this._id]=this},Promise.prototype.isPromise=!0,Promise.prototype.isComplete=function(){return this._status!=g},Promise.prototype.isResolved=function(){return this._status==h},Promise.prototype.isRejected=function(){return this._status==f},Promise.prototype.then=function(a,b){typeof a=="function"&&(this._status===h?a.call(null,this._value):this._status===g&&this._onSuccessHandlers.push(a)),typeof b=="function"&&(this._status===f?b.call(null,this._value):this._status===g&&this._onErrorHandlers.push(b));return this},Promise.prototype.chainPromise=function(a){var b=new Promise;b._chainedFrom=this,this.then(function(c){try{b.resolve(a(c))}catch(d){b.reject(d)}},function(a){b.reject(a)});return b},Promise.prototype.resolve=function(a){return this._complete(this._onSuccessHandlers,h,a,"resolve")},Promise.prototype.reject=function(a){return this._complete(this._onErrorHandlers,f,a,"reject")},Promise.prototype._complete=function(a,b,c,f){if(this._status!=g){d.group("Promise already closed"),d.error("Attempted "+f+"() with ",c),d.error("Previous status = ",this._status,", previous value = ",this._value),d.trace(),this._completeTrace&&(d.error("Trace of previous completion:"),this._completeTrace.log(5)),d.groupEnd();return this}j&&(this._completeTrace=new e(new Error)),this._status=b,this._value=c,a.forEach(function(a){a.call(null,this._value)},this),this._onSuccessHandlers.length=0,this._onErrorHandlers.length=0,delete k[this._id],l.push(this);while(l.length>20)l.shift();return this},Promise.group=function(a){a instanceof Array||(a=Array.prototype.slice.call(arguments));if(a.length===0)return(new Promise).resolve([]);var b=new Promise,c=[],d=0,e=function(e){return function(g){c[e]=g,d++,b._status!==f&&d===a.length&&b.resolve(c)}};a.forEach(function(a,c){var d=e(c),f=b.reject.bind(b);a.then(d,f)});return b},b.Promise=Promise,b._outstanding=k,b._recent=l}),define("pilot/dom",["require","exports","module"],function(a,b,c){var d="http://www.w3.org/1999/xhtml";b.createElement=function(a,b){return document.createElementNS?document.createElementNS(b||d,a):document.createElement(a)},b.setText=function(a,b){a.innerText!==undefined&&(a.innerText=b),a.textContent!==undefined&&(a.textContent=b)},document.documentElement.classList?(b.hasCssClass=function(a,b){return a.classList.contains(b)},b.addCssClass=function(a,b){a.classList.add(b)},b.removeCssClass=function(a,b){a.classList.remove(b)},b.toggleCssClass=function(a,b){return a.classList.toggle(b)}):(b.hasCssClass=function(a,b){var c=a.className.split(/\s+/g);return c.indexOf(b)!==-1},b.addCssClass=function(a,c){b.hasCssClass(a,c)||(a.className+=" "+c)},b.removeCssClass=function(a,b){var c=a.className.split(/\s+/g);for(;;){var d=c.indexOf(b);if(d==-1)break;c.splice(d,1)}a.className=c.join(" ")},b.toggleCssClass=function(a,b){var c=a.className.split(/\s+/g),d=!0;for(;;){var e=c.indexOf(b);if(e==-1)break;d=!1,c.splice(e,1)}d&&c.push(b),a.className=c.join(" ");return d}),b.setCssClass=function(a,c,d){d?b.addCssClass(a,c):b.removeCssClass(a,c)},b.importCssString=function(a,b){b=b||document;if(b.createStyleSheet){var c=b.createStyleSheet();c.cssText=a}else{var e=b.createElementNS?b.createElementNS(d,"style"):b.createElement("style");e.appendChild(b.createTextNode(a));var f=b.getElementsByTagName("head")[0]||b.documentElement;f.appendChild(e)}},b.getInnerWidth=function(a){return parseInt(b.computedStyle(a,"paddingLeft"))+parseInt(b.computedStyle(a,"paddingRight"))+a.clientWidth},b.getInnerHeight=function(a){return parseInt(b.computedStyle(a,"paddingTop"))+parseInt(b.computedStyle(a,"paddingBottom"))+a.clientHeight},window.pageYOffset!==undefined?(b.getPageScrollTop=function(){return window.pageYOffset},b.getPageScrollLeft=function(){return window.pageXOffset}):(b.getPageScrollTop=function(){return document.body.scrollTop},b.getPageScrollLeft=function(){return document.body.scrollLeft}),window.getComputedStyle?b.computedStyle=function(a,b){if(b)return(window.getComputedStyle(a,"")||{})[b]||"";return window.getComputedStyle(a,"")||{}}:b.computedStyle=function(a,b){if(b)return a.currentStyle[b];return a.currentStyle},b.scrollbarWidth=function(){var a=b.createElement("p");a.style.width="100%",a.style.height="200px";var c=b.createElement("div"),d=c.style;d.position="absolute",d.left="-10000px",d.overflow="hidden",d.width="200px",d.height="150px",c.appendChild(a);var e=document.body||document.documentElement;e.appendChild(c);var f=a.offsetWidth;d.overflow="scroll";var g=a.offsetWidth;f==g&&(g=c.clientWidth),e.removeChild(c);return f-g},b.setInnerHtml=function(a,b){var c=a.cloneNode(!1);c.innerHTML=b,a.parentNode.replaceChild(c,a);return c},b.setInnerText=function(a,b){document.body&&"textContent"in document.body?a.textContent=b:a.innerText=b},b.getInnerText=function(a){return document.body&&"textContent"in document.body?a.textContent:a.innerText||a.textContent||""},b.getParentWindow=function(a){return a.defaultView||a.parentWindow},b.getSelectionStart=function(a){var b;try{b=a.selectionStart||0}catch(c){b=0}return b},b.setSelectionStart=function(a,b){return a.selectionStart=b},b.getSelectionEnd=function(a){var b;try{b=a.selectionEnd||0}catch(c){b=0}return b},b.setSelectionEnd=function(a,b){return a.selectionEnd=b}}),define("pilot/event",["require","exports","module","pilot/keys","pilot/useragent","pilot/dom"],function(a,b,c){function g(a,b,c){var f=0;e.isOpera&&e.isMac?f=0|(b.metaKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.ctrlKey?8:0):f=0|(b.ctrlKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.metaKey?8:0);if(c in d.MODIFIER_KEYS){switch(d.MODIFIER_KEYS[c]){case"Alt":f=2;break;case"Shift":f=4;break;case"Ctrl":f=1;break;default:f=8}c=0}f&8&&(c==91||c==93)&&(c=0);if(f==0&&!(c in d.FUNCTION_KEYS))return!1;return a(b,f,c)}var d=a("pilot/keys"),e=a("pilot/useragent"),f=a("pilot/dom");b.addListener=function(a,b,c){if(a.addEventListener)return a.addEventListener(b,c,!1);if(a.attachEvent){var d=function(){c(window.event)};c._wrapper=d,a.attachEvent("on"+b,d)}},b.removeListener=function(a,b,c){if(a.removeEventListener)return a.removeEventListener(b,c,!1);a.detachEvent&&a.detachEvent("on"+b,c._wrapper||c)},b.stopEvent=function(a){b.stopPropagation(a),b.preventDefault(a);return!1},b.stopPropagation=function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},b.preventDefault=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},b.getDocumentX=function(a){return a.clientX?a.clientX+f.getPageScrollLeft():a.pageX},b.getDocumentY=function(a){return a.clientY?a.clientY+f.getPageScrollTop():a.pageY},b.getButton=function(a){if(a.type=="dblclick")return 0;if(a.type=="contextmenu")return 2;return a.preventDefault?a.button:{1:0,2:2,4:1}[a.button]},document.documentElement.setCapture?b.capture=function(a,c,d){function f(e){c&&c(e),d&&d(),b.removeListener(a,"mousemove",c),b.removeListener(a,"mouseup",f),b.removeListener(a,"losecapture",f),a.releaseCapture()}function e(a){c(a);return b.stopPropagation(a)}b.addListener(a,"mousemove",c),b.addListener(a,"mouseup",f),b.addListener(a,"losecapture",f),a.setCapture()}:b.capture=function(a,b,c){function e(a){b&&b(a),c&&c(),document.removeEventListener("mousemove",d,!0),document.removeEventListener("mouseup",e,!0),a.stopPropagation()}function d(a){b(a),a.stopPropagation()}document.addEventListener("mousemove",d,!0),document.addEventListener("mouseup",e,!0)},b.addMouseWheelListener=function(a,c){var d=function(a){a.wheelDelta!==undefined?a.wheelDeltaX!==undefined?(a.wheelX=-a.wheelDeltaX/8,a.wheelY=-a.wheelDeltaY/8):(a.wheelX=0,a.wheelY=-a.wheelDelta/8):a.axis&&a.axis==a.HORIZONTAL_AXIS?(a.wheelX=(a.detail||0)*5,a.wheelY=0):(a.wheelX=0,a.wheelY=(a.detail||0)*5),c(a)};b.addListener(a,"DOMMouseScroll",d),b.addListener(a,"mousewheel",d)},b.addMultiMouseDownListener=function(a,c,d,f,g){var h=0,i,j,k=function(a){h+=1,h==1&&(i=a.clientX,j=a.clientY,setTimeout(function(){h=0},f||600));var e=b.getButton(a)==c;if(!e||Math.abs(a.clientX-i)>5||Math.abs(a.clientY-j)>5)h=0;h==d&&(h=0,g(a));if(e)return b.preventDefault(a)};b.addListener(a,"mousedown",k),e.isIE&&b.addListener(a,"dblclick",k)},b.addCommandKeyListener=function(a,c){var d=b.addListener;if(e.isOldGecko){var f=null;d(a,"keydown",function(a){f=a.keyCode}),d(a,"keypress",function(a){return g(c,a,f)})}else{var h=null;d(a,"keydown",function(a){h=a.keyIdentifier||a.keyCode;return g(c,a,a.keyCode)}),e.isMac&&e.isOpera&&d(a,"keypress",function(a){var b=a.keyIdentifier||a.keyCode;if(h!==b)return g(c,a,a.keyCode);h=null})}}}),define("ace/editor",["require","exports","module","pilot/fixoldbrowsers","pilot/oop","pilot/event","pilot/lang","pilot/useragent","ace/keyboard/textinput","ace/mouse_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","pilot/event_emitter"],function(a,b,c){a("pilot/fixoldbrowsers");var d=a("pilot/oop"),e=a("pilot/event"),f=a("pilot/lang"),g=a("pilot/useragent"),h=a("ace/keyboard/textinput").TextInput,i=a("ace/mouse_handler").MouseHandler,j=a("ace/keyboard/keybinding").KeyBinding,k=a("ace/edit_session").EditSession,l=a("ace/search").Search,m=a("ace/range").Range,n=a("pilot/event_emitter").EventEmitter,o=function(a,b){var c=a.getContainerElement();this.container=c,this.renderer=a,this.textInput=new h(a.getTextAreaContainer(),this),this.keyBinding=new j(this),g.isIPad||(this.$mouseHandler=new i(this)),this.$blockScrolling=0,this.$search=(new l).set({wrap:!0}),this.setSession(b||new k(""))};(function(){d.implement(this,n),this.$forwardEvents={gutterclick:1,gutterdblclick:1},this.$originalAddEventListener=this.addEventListener,this.$originalRemoveEventListener=this.removeEventListener,this.addEventListener=function(a,b){return this.$forwardEvents[a]?this.renderer.addEventListener(a,b):this.$originalAddEventListener(a,b)},this.removeEventListener=function(a,b){return this.$forwardEvents[a]?this.renderer.removeEventListener(a,b):this.$originalRemoveEventListener(a,b)},this.setKeyboardHandler=function(a){this.keyBinding.setKeyboardHandler(a)},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(a){if(this.session!=a){if(this.session){var b=this.session;this.session.removeEventListener("change",this.$onDocumentChange),this.session.removeEventListener("changeMode",this.$onChangeMode),this.session.removeEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.session.removeEventListener("changeTabSize",this.$onChangeTabSize),this.session.removeEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.session.removeEventListener("changeWrapMode",this.$onChangeWrapMode),this.session.removeEventListener("onChangeFold",this.$onChangeFold),this.session.removeEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.session.removeEventListener("changeBackMarker",this.$onChangeBackMarker),this.session.removeEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.session.removeEventListener("changeAnnotation",this.$onChangeAnnotation),this.session.removeEventListener("changeOverwrite",this.$onCursorChange);var c=this.session.getSelection();c.removeEventListener("changeCursor",this.$onCursorChange),c.removeEventListener("changeSelection",this.$onSelectionChange),this.session.setScrollTopRow(this.renderer.getScrollTopRow())}this.session=a,this.$onDocumentChange=this.onDocumentChange.bind(this),a.addEventListener("change",this.$onDocumentChange),this.renderer.setSession(a),this.$onChangeMode=this.onChangeMode.bind(this),a.addEventListener("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),a.addEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.updateText.bind(this.renderer),a.addEventListener("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),a.addEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),a.addEventListener("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),a.addEventListener("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener("changeOverwrite",this.$onCursorChange),this.selection=a.getSelection(),this.selection.addEventListener("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.renderer.scrollToRow(a.getScrollTopRow()),this.renderer.updateFull(),this._dispatchEvent("changeSession",{session:a,oldSession:b})}},this.getSession=function(){return this.session},this.getSelection=function(){return this.selection},this.resize=function(){this.renderer.onResize()},this.setTheme=function(a){this.renderer.setTheme(a)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(a){this.renderer.setStyle(a)},this.unsetStyle=function(a){this.renderer.unsetStyle(a)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(!this.$highlightPending){var a=this;this.$highlightPending=!0,setTimeout(function(){a.$highlightPending=!1;var b=a.session.findMatchingBracket(a.getCursorPosition());if(b){var c=new m(b.row,b.column,b.row,b.column+1);a.session.$bracketHighlight=a.session.addMarker(c,"ace_bracket")}},10)}},this.focus=function(){var a=this;g.isIE||setTimeout(function(){a.textInput.focus()}),this.textInput.focus()},this.blur=function(){this.textInput.blur()},this.onFocus=function(){this.renderer.showCursor(),this.renderer.visualizeFocus(),this._dispatchEvent("focus")},this.onBlur=function(){this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._dispatchEvent("blur")},this.onDocumentChange=function(a){var b=a.data,c=b.range;if(c.start.row==c.end.row&&b.action!="insertLines"&&b.action!="removeLines")var d=c.end.row;else d=Infinity;this.renderer.updateLines(c.start.row,d),this.renderer.updateCursor()},this.onTokenizerUpdate=function(a){var b=a.data;this.renderer.updateLines(b.first,b.last)},this.onCursorChange=function(a){this.renderer.updateCursor(),this.$blockScrolling||this.renderer.scrollCursorIntoView(),this.renderer.moveTextAreaToCursor(this.textInput.getElement()),this.$highlightBrackets(),this.$updateHighlightActiveLine()},this.$updateHighlightActiveLine=function(){var a=this.getSession();a.$highlightLineMarker&&a.removeMarker(a.$highlightLineMarker),a.$highlightLineMarker=null;if(this.getHighlightActiveLine()&&(this.getSelectionStyle()!="line"||!this.selection.isMultiLine())){var b=this.getCursorPosition(),c=this.session.getFoldLine(b.row),d;c?d=new m(c.start.row,0,c.end.row+1,0):d=new m(b.row,0,b.row+1,0),a.$highlightLineMarker=a.addMarker(d,"ace_active_line","line")}},this.onSelectionChange=function(a){var b=this.getSession();b.$selectionMarker&&b.removeMarker(b.$selectionMarker),b.$selectionMarker=null;if(!this.selection.isEmpty()){var c=this.selection.getRange(),d=this.getSelectionStyle();b.$selectionMarker=b.addMarker(c,"ace_selection",d)}else this.$updateHighlightActiveLine();this.$highlightSelectedWord&&this.session.getMode().highlightSelection(this)},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.setBreakpoints(this.session.getBreakpoints())},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(){this.renderer.updateText()},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getCopyText=function(){return this.selection.isEmpty()?"":this.session.getTextRange(this.getSelectionRange())},this.onCut=function(){this.$readOnly||this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection())},this.insert=function(a){if(!this.$readOnly){var b=this.session,c=b.getMode(),d=this.getCursorPosition();if(this.getBehavioursEnabled()){var e=c.transformAction(b.getState(d.row),"insertion",this,b,a);e&&(a=e.text)}a=a.replace("\t",this.session.getTabString());if(!this.selection.isEmpty()){var d=this.session.remove(this.getSelectionRange());this.clearSelection()}else if(this.session.getOverwrite()){var f=new m.fromPoints(d,d);f.end.column+=a.length,this.session.remove(f)}this.clearSelection();var g=d.column,h=b.getState(d.row),i=c.checkOutdent(h,b.getLine(d.row),a),j=b.getLine(d.row),k=c.getNextLineIndent(h,j.slice(0,d.column),b.getTabString()),l=b.insert(d,a);e&&e.selection&&(e.selection.length==2?this.selection.setSelectionRange(new m(d.row,g+e.selection[0],d.row,g+e.selection[1])):this.selection.setSelectionRange(new m(d.row+e.selection[0],e.selection[1],d.row+e.selection[2],e.selection[3])));var h=b.getState(d.row);if(b.getDocument().isNewLine(a)){this.moveCursorTo(d.row+1,0);var n=b.getTabSize(),o=Number.MAX_VALUE;for(var p=d.row+1;p<=l.row;++p){var q=0;j=b.getLine(p);for(var r=0;r<j.length;++r)if(j.charAt(r)=="\t")q+=n;else if(j.charAt(r)==" ")q+=1;else break;/[^\s]/.test(j)&&(o=Math.min(q,o))}for(var p=d.row+1;p<=l.row;++p){var s=o;j=b.getLine(p);for(var r=0;r<j.length&&s>0;++r)j.charAt(r)=="\t"?s-=n:j.charAt(r)==" "&&(s-=1);b.remove(new m(p,0,p,r))}b.indentRows(d.row+1,l.row,k)}else i&&c.autoOutdent(h,b,d.row)}},this.onTextInput=function(a){this.keyBinding.onTextInput(a)},this.onCommandKey=function(a,b,c){this.keyBinding.onCommandKey(a,b,c)},this.setOverwrite=function(a){this.session.setOverwrite(a)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(a){this.$mouseHandler.setScrollSpeed(a)},this.getScrollSpeed=function(){return this.$mouseHandler.getScrollSpeed()},this.$selectionStyle="line",this.setSelectionStyle=function(a){this.$selectionStyle!=a&&(this.$selectionStyle=a,this.onSelectionChange(),this._dispatchEvent("changeSelectionStyle",{data:a}))},this.getSelectionStyle=function(){return this.$selectionStyle},this.$highlightActiveLine=!0,this.setHighlightActiveLine=function(a){this.$highlightActiveLine!=a&&(this.$highlightActiveLine=a,this.$updateHighlightActiveLine())},this.getHighlightActiveLine=function(){return this.$highlightActiveLine},this.$highlightSelectedWord=!0,this.setHighlightSelectedWord=function(a){this.$highlightSelectedWord!=a&&(this.$highlightSelectedWord=a,a?this.session.getMode().highlightSelection(this):this.session.getMode().clearSelectionHighlight(this))},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setShowInvisibles=function(a){this.getShowInvisibles()!=a&&this.renderer.setShowInvisibles(a)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setShowPrintMargin=function(a){this.renderer.setShowPrintMargin(a)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(a){this.renderer.setPrintMarginColumn(a)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.$readOnly=!1,this.setReadOnly=function(a){this.$readOnly=a},this.getReadOnly=function(){return this.$readOnly},this.$modeBehaviours=!1,this.setBehavioursEnabled=function(a){this.$modeBehaviours=a},this.getBehavioursEnabled=function(){return this.$modeBehaviours},this.removeRight=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectRight(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeLeft=function(){if(!this.$readOnly){this.selection.isEmpty()&&this.selection.selectLeft();var a=this.getSelectionRange();if(this.getBehavioursEnabled()){var b=this.session,c=b.getState(a.start.row),d=b.getMode().transformAction(c,"deletion",this,b,a);d!==!1&&(a=d)}this.session.remove(a),this.clearSelection()}},this.removeWordRight=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeWordLeft=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeToLineStart=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeToLineEnd=function(){if(!this.$readOnly){this.selection.isEmpty()&&this.selection.selectLineEnd();var a=this.getSelectionRange();a.start.column==a.end.column&&a.start.row==a.end.row&&(a.end.column=0,a.end.row++),this.session.remove(a),this.clearSelection()}},this.splitLine=function(){if(!this.$readOnly){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var a=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(a)}},this.transposeLetters=function(){if(!this.$readOnly){if(!this.selection.isEmpty())return;var a=this.getCursorPosition(),b=a.column;if(b==0)return;var c=this.session.getLine(a.row);if(b<c.length)var d=c.charAt(b)+c.charAt(b-1),e=new m(a.row,b-1,a.row,b+1);else var d=c.charAt(b-1)+c.charAt(b-2),e=new m(a.row,b-2,a.row,b);this.session.replace(e,d)}},this.indent=function(){if(!this.$readOnly){var a=this.session,b=this.getSelectionRange();if(!(b.start.row<b.end.row||b.start.column<b.end.column)){var d;if(this.session.getUseSoftTabs()){var e=a.getTabSize(),g=this.getCursorPosition(),h=a.documentToScreenColumn(g.row,g.column),i=e-h%e;d=f.stringRepeat(" ",i)}else d="\t";return this.onTextInput(d)}var c=this.$getSelectedRows();a.indentRows(c.first,c.last,"\t")}},this.blockOutdent=function(){if(!this.$readOnly){var a=this.session.getSelection();this.session.outdentRows(a.getRange())}},this.toggleCommentLines=function(){if(!this.$readOnly){var a=this.session.getState(this.getCursorPosition().row),b=this.$getSelectedRows();this.session.getMode().toggleCommentLines(a,this.session,b.first,b.last)}},this.removeLines=function(){if(!this.$readOnly){var a=this.$getSelectedRows();this.session.remove(new m(a.first,0,a.last+1,0)),this.clearSelection()}},this.moveLinesDown=function(){this.$readOnly||this.$moveLines(function(a,b){return this.session.moveLinesDown(a,b)})},this.moveLinesUp=function(){this.$readOnly||this.$moveLines(function(a,b){return this.session.moveLinesUp(a,b)})},this.moveText=function(a,b){if(this.$readOnly)return null;return this.session.moveText(a,b)},this.copyLinesUp=function(){this.$readOnly||this.$moveLines(function(a,b){this.session.duplicateLines(a,b);return 0})},this.copyLinesDown=function(){this.$readOnly||this.$moveLines(function(a,b){return this.session.duplicateLines(a,b)})},this.$moveLines=function(a){var b=this.$getSelectedRows(),c=a.call(this,b.first,b.last),d=this.selection;d.setSelectionAnchor(b.last+c+1,0),d.$moveSelection(function(){d.moveCursorTo(b.first+c,0)})},this.$getSelectedRows=function(){var a=this.getSelectionRange().collapseRows();return{first:a.start.row,last:a.end.row}},this.onCompositionStart=function(a){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(a){this.renderer.setCompositionText(a)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(a){return a>=this.getFirstVisibleRow()&&a<=this.getLastVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$getPageDownRow=function(){return this.renderer.getScrollBottomRow()},this.$getPageUpRow=function(){var a=this.renderer.getScrollTopRow(),b=this.renderer.getScrollBottomRow();return a-(b-a)},this.selectPageDown=function(){var a=this.$getPageDownRow()+Math.floor(this.$getVisibleRowCount()/2);this.scrollPageDown();var b=this.getSelection(),c=this.session.documentToScreenPosition(b.getSelectionLead()),d=this.session.screenToDocumentPosition(a,c.column);b.selectTo(d.row,d.column)},this.selectPageUp=function(){var a=this.renderer.getScrollTopRow()-this.renderer.getScrollBottomRow(),b=this.$getPageUpRow()+Math.round(a/2);this.scrollPageUp();var c=this.getSelection(),d=this.session.documentToScreenPosition(c.getSelectionLead()),e=this.session.screenToDocumentPosition(b,d.column);c.selectTo(e.row,e.column)},this.gotoPageDown=function(){var a=this.$getPageDownRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.gotoPageUp=function(){var a=this.$getPageUpRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.scrollPageDown=function(){this.scrollToRow(this.$getPageDownRow())},this.scrollPageUp=function(){this.renderer.scrollToRow(this.$getPageUpRow())},this.scrollToRow=function(a){this.renderer.scrollToRow(a)},this.scrollToLine=function(a,b){this.renderer.scrollToLine(a,b)},this.centerSelection=function(){var a=this.getSelectionRange(),b=Math.floor(a.start.row+(a.end.row-a.start.row)/2);this.renderer.scrollToLine(b,!0)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(a,b){this.selection.moveCursorTo(a,b)},this.moveCursorToPosition=function(a){this.selection.moveCursorToPosition(a)},this.gotoLine=function(a,b){this.selection.clearSelection(),this.$blockScrolling+=1,this.moveCursorTo(a-1,b||0),this.$blockScrolling-=1,this.isRowVisible(this.getCursorPosition().row)||this.scrollToLine(a,!0)},this.navigateTo=function(a,b){this.clearSelection(),this.moveCursorTo(a,b)},this.navigateUp=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(-a,0)},this.navigateDown=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(a,0)},this.navigateLeft=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().start;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().end;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(a,b){b&&this.$search.set(b);var c=this.$search.find(this.session);this.$tryReplace(c,a),c!==null&&this.selection.setSelectionRange(c)},this.replaceAll=function(a,b){b&&this.$search.set(b);var c=this.$search.findAll(this.session);if(!!c.length){var d=this.getSelectionRange();this.clearSelection(),this.selection.moveCursorTo(0,0),this.$blockScrolling+=1;for(var e=c.length-1;e>=0;--e)this.$tryReplace(c[e],a);this.selection.setSelectionRange(d),this.$blockScrolling-=1}},this.$tryReplace=function(a,b){var c=this.session.getTextRange(a),b=this.$search.replace(c,b);if(b!==null){a.end=this.session.replace(a,b);return a}return null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(a,b){this.clearSelection(),b=b||{},b.needle=a,this.$search.set(b),this.$find()},this.findNext=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!1),this.$search.set(a),this.$find()},this.findPrevious=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!0),this.$search.set(a),this.$find()},this.$find=function(a){this.selection.isEmpty()||this.$search.set({needle:this.session.getTextRange(this.getSelectionRange())}),typeof a!="undefined"&&this.$search.set({backwards:a});var b=this.$search.find(this.session);b&&(this.gotoLine(b.end.row+1,b.end.column),this.selection.setSelectionRange(b))},this.undo=function(){this.session.getUndoManager().undo()},this.redo=function(){this.session.getUndoManager().redo()},this.destroy=function(){this.renderer.destroy()}}).call(o.prototype),b.Editor=o}),define("ace/keyboard/textinput",["require","exports","module","pilot/event","pilot/useragent","pilot/dom"],function(a,b,c){var d=a("pilot/event"),e=a("pilot/useragent"),f=a("pilot/dom"),g=function(a,b){function k(a){if(!i){var d=a||c.value;d&&(d.charCodeAt(d.length-1)==g.charCodeAt(0)?(d=d.slice(0,-1),d&&b.onTextInput(d)):b.onTextInput(d))}i=!1,c.value=g,c.select()}var c=f.createElement("textarea");c.style.left="-10000px",a.appendChild(c);var g=String.fromCharCode(0);k();var h=!1,i=!1,j="",l=function(a){e.isIE&&c.value.charCodeAt(0)>128||setTimeout(function(){h||k()},0)},m=function(a){h=!0,e.isIE||(k(),c.value=""),b.onCompositionStart(),e.isGecko||setTimeout(n,0)},n=function(){!h||b.onCompositionUpdate(c.value)},o=function(a){h=!1,b.onCompositionEnd(),e.isGecko?k():setTimeout(function(){h||k()},0)},p=function(a){i=!0;var d=b.getCopyText();d?c.value=d:a.preventDefault(),c.select(),setTimeout(function(){k()},0)},q=function(a){i=!0;var d=b.getCopyText();d?(c.value=d,b.onCut()):a.preventDefault(),c.select(),setTimeout(function(){k()},0)};d.addCommandKeyListener(c,b.onCommandKey.bind(b)),d.addListener(c,"keypress",l);if(e.isIE){var r={13:1,27:1};d.addListener(c,"keyup",function(a){h&&(!c.value||r[a.keyCode])&&setTimeout(o,0);(c.value.charCodeAt(0)|0)<129||(h?n():m())})}d.addListener(c,"textInput",l),d.addListener(c,"paste",function(a){a.clipboardData&&a.clipboardData.getData?(k(a.clipboardData.getData("text/plain")),a.preventDefault()):l()}),e.isIE||d.addListener(c,"propertychange",l),e.isIE?(d.addListener(c,"beforecopy",function(a){var c=b.getCopyText();c?clipboardData.setData("Text",c):a.preventDefault()}),d.addListener(a,"keydown",function(a){if(a.ctrlKey&&a.keyCode==88){var c=b.getCopyText();c&&(clipboardData.setData("Text",c),b.onCut()),d.preventDefault(a)}})):(d.addListener(c,"copy",p),d.addListener(c,"cut",q)),d.addListener(c,"compositionstart",m),e.isGecko&&d.addListener(c,"text",n),e.isWebKit&&d.addListener(c,"keyup",n),d.addListener(c,"compositionend",o),d.addListener(c,"blur",function(){b.onBlur()}),d.addListener(c,"focus",function(){b.onFocus(),c.select()}),this.focus=function(){b.onFocus(),c.select(),c.focus()},this.blur=function(){c.blur()},this.getElement=function(){return c},this.onContextMenu=function(a,b){a&&(j||(j=c.style.cssText),c.style.cssText="position:fixed; z-index:1000;left:"+(a.x-2)+"px; top:"+(a.y-2)+"px;"),b&&(c.value="")},this.onContextMenuClose=function(){setTimeout(function(){j&&(c.style.cssText=j,j=""),k()},0)}};b.TextInput=g}),define("ace/mouse_handler",["require","exports","module","pilot/event","pilot/dom"],function(a,b,c){var d=a("pilot/event"),e=a("pilot/dom"),f=0,g=1,h=2,i=250,j=5,k=function(a){this.editor=a,d.addListener(a.container,"mousedown",function(b){a.focus();return d.preventDefault(b)}),d.addListener(a.container,"selectstart",function(a){return d.preventDefault(a)});var b=a.renderer.getMouseEventTarget();d.addListener(b,"mousedown",this.onMouseDown.bind(this)),d.addMultiMouseDownListener(b,0,2,500,this.onMouseDoubleClick.bind(this)),d.addMultiMouseDownListener(b,0,3,600,this.onMouseTripleClick.bind(this)),d.addMultiMouseDownListener(b,0,4,600,this.onMouseQuadClick.bind(this)),d.addMouseWheelListener(b,this.onMouseWheel.bind(this))};(function(){this.$scrollSpeed=1,this.setScrollSpeed=function(a){this.$scrollSpeed=a},this.getScrollSpeed=function(){return this.$scrollSpeed},this.$getEventPosition=function(a){var b=d.getDocumentX(a),c=d.getDocumentY(a),e=this.editor.renderer.screenToTextCoordinates(b,c);e.row=Math.max(0,Math.min(e.row,this.editor.session.getLength()-1));return e},this.$distance=function(a,b,c,d){return Math.sqrt(Math.pow(c-a,2)+Math.pow(d-b,2))},this.onMouseDown=function(a){function D(b){a.shiftKey?l.selection.selectToPosition(b):m.$clickSelection||(l.moveCursorToPosition(b),l.selection.clearSelection(b.row,b.column)),p=g}var b=d.getDocumentX(a),c=d.getDocumentY(a),k=this.$getEventPosition(a),l=this.editor,m=this,n=l.getSelectionRange(),o=n.isEmpty(),p=f,q=!1,r=d.getButton(a);if(r!==0)o&&l.moveCursorToPosition(k),r==2&&(l.textInput.onContextMenu({x:b,y:c},o),d.capture(l.container,function(){},l.textInput.onContextMenuClose));else{var s=l.session.getFoldAt(k.row,k.column,1);if(s){l.selection.setSelectionRange(s.range);return}q=!l.getReadOnly()&&!o&&n.contains(k.row,k.column),q||D(k);var t,u,v=l.getOverwrite(),w=(new Date).getTime(),x,y,z=function(a){t=d.getDocumentX(a),u=d.getDocumentY(a)},A=function(){clearInterval(G),p==f?D(k):p==h&&B(),m.$clickSelection=null,p=f},B=function(){e.removeCssClass(l.container,"ace_dragging"),l.session.removeMarker(dragSelectionMarker),m.$clickSelection||x||(l.moveCursorToPosition(k),l.selection.clearSelection(k.row,k.column));if(!!x){if(y.contains(x.row,x.column)){x=null;return}l.clearSelection();var a=l.moveText(y,x);if(!a){x=null;return}l.selection.setSelectionRange(a)}},C=function(){if(t!==undefined&&u!==undefined){if(p==f){var a=m.$distance(b,c,t,u),d=(new Date).getTime();if(a>j){p=g;var k=l.renderer.screenToTextCoordinates(t,u);k.row=Math.max(0,Math.min(k.row,l.session.getLength()-1)),D(k)}else if(d-w>i){p=h,y=l.getSelectionRange();var n=l.getSelectionStyle();dragSelectionMarker=l.session.addMarker(y,"ace_selection",n),l.clearSelection(),e.addCssClass(l.container,"ace_dragging")}}p==h?F():p==g&&E()}},E=function(){var a=l.renderer.screenToTextCoordinates(t,u);a.row=Math.max(0,Math.min(a.row,l.session.getLength()-1));if(m.$clickSelection)if(m.$clickSelection.contains(a.row,a.column))l.selection.setSelectionRange(m.$clickSelection);else{if(m.$clickSelection.compare(a.row,a.column)==-1)var b=m.$clickSelection.end;else var b=m.$clickSelection.start;l.selection.setSelectionAnchor(b.row,b.column),l.selection.selectToPosition(a)}else l.selection.selectToPosition(a);l.renderer.scrollCursorIntoView()},F=function(){x=l.renderer.screenToTextCoordinates(t,u),x.row=Math.max(0,Math.min(x.row,l.session.getLength()-1)),l.moveCursorToPosition(x)};d.capture(l.container,z,A);var G=setInterval(C,20);return d.preventDefault(a)}},this.onMouseDoubleClick=function(a){var b=this.editor,c=this.$getEventPosition(a),d=b.session.getFoldAt(c.row,c.column,1);d?b.session.expandFold(d):(b.moveCursorToPosition(c),b.selection.selectWord(),this.$clickSelection=b.getSelectionRange())},this.onMouseTripleClick=function(a){var b=this.$getEventPosition(a);this.editor.moveCursorToPosition(b),this.editor.selection.selectLine(),this.$clickSelection=this.editor.getSelectionRange()},this.onMouseQuadClick=function(a){this.editor.selectAll(),this.$clickSelection=this.editor.getSelectionRange()},this.onMouseWheel=function(a){var b=this.$scrollSpeed*2;this.editor.renderer.scrollBy(a.wheelX*b,a.wheelY*b);return d.preventDefault(a)}}).call(k.prototype),b.MouseHandler=k}),define("ace/keyboard/keybinding",["require","exports","module","pilot/useragent","pilot/keys","pilot/event","pilot/settings","pilot/canon","ace/commands/default_commands"],function(a,b,c){var d=a("pilot/useragent"),e=a("pilot/keys"),f=a("pilot/event"),g=a("pilot/settings").settings,h=a("pilot/canon");a("ace/commands/default_commands");var i=function(a){this.$editor=a,this.$data={},this.$keyboardHandler=null};(function(){this.setKeyboardHandler=function(a){this.$keyboardHandler!=a&&(this.$data={},this.$keyboardHandler=a)},this.getKeyboardHandler=function(){return this.$keyboardHandler},this.$callKeyboardHandler=function(a,b,c,d){var e={editor:this.$editor},g;this.$keyboardHandler&&(g=this.$keyboardHandler.handleKeyboard(this.$data,b,c,d,a));if(!g||!g.command)b!=0||d!=0?g={command:h.findKeyCommand(e,"editor",b,c)}:g={command:"inserttext",args:{text:c}};if(g){var i=h.exec(g.command,e,"editor",g.args);if(i)return f.stopEvent(a)}},this.onCommandKey=function(a,b,c){var d=e.keyCodeToString(c);this.$callKeyboardHandler(a,b,d,c)},this.onTextInput=function(a){this.$callKeyboardHandler({},0,a,0)}}).call(i.prototype),b.KeyBinding=i}),define("ace/commands/default_commands",["require","exports","module","pilot/lang","pilot/canon"],function(a,b,c){function f(a,b){return{win:a,mac:b,sender:"editor"}}var d=a("pilot/lang"),e=a("pilot/canon");e.addCommand({name:"null",exec:function(a,b,c){}}),e.addCommand({name:"selectall",bindKey:f("Ctrl-A","Command-A"),exec:function(a,b,c){a.editor.selectAll()}}),e.addCommand({name:"removeline",bindKey:f("Ctrl-D","Command-D"),exec:function(a,b,c){a.editor.removeLines()}}),e.addCommand({name:"gotoline",bindKey:f("Ctrl-L","Command-L"),exec:function(a,b,c){var d=parseInt(prompt("Enter line number:"));isNaN(d)||a.editor.gotoLine(d)}}),e.addCommand({name:"togglecomment",bindKey:f("Ctrl-7","Command-7"),exec:function(a,b,c){a.editor.toggleCommentLines()}}),e.addCommand({name:"findnext",bindKey:f("Ctrl-K","Command-G"),exec:function(a,b,c){a.editor.findNext()}}),e.addCommand({name:"findprevious",bindKey:f("Ctrl-Shift-K","Command-Shift-G"),exec:function(a,b,c){a.editor.findPrevious()}}),e.addCommand({name:"find",bindKey:f("Ctrl-F","Command-F"),exec:function(a,b,c){var d=prompt("Find:");a.editor.find(d)}}),e.addCommand({name:"replace",bindKey:f("Ctrl-R","Command-Option-F"),exec:function(a,b,c){var d=prompt("Find:");if(!!d){var e=prompt("Replacement:");if(!e)return;a.editor.replace(e,{needle:d})}}}),e.addCommand({name:"replaceall",bindKey:f("Ctrl-Shift-R","Command-Shift-Option-F"),exec:function(a,b,c){var d=prompt("Find:");if(!!d){var e=prompt("Replacement:");if(!e)return;a.editor.replaceAll(e,{needle:d})}}}),e.addCommand({name:"undo",bindKey:f("Ctrl-Z","Command-Z"),exec:function(a,b,c){a.editor.undo()}}),e.addCommand({name:"redo",bindKey:f("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(a,b,c){a.editor.redo()}}),e.addCommand({name:"overwrite",bindKey:f("Insert","Insert"),exec:function(a,b,c){a.editor.toggleOverwrite()}}),e.addCommand({name:"copylinesup",bindKey:f("Ctrl-Alt-Up","Command-Option-Up"),exec:function(a,b,c){a.editor.copyLinesUp()}}),e.addCommand({name:"movelinesup",bindKey:f("Alt-Up","Option-Up"),exec:function(a,b,c){a.editor.moveLinesUp()}}),e.addCommand({name:"selecttostart",bindKey:f("Ctrl-Shift-Home|Alt-Shift-Up","Command-Shift-Up"),exec:function(a,b,c){a.editor.getSelection().selectFileStart()}}),e.addCommand({name:"gotostart",bindKey:f("Ctrl-Home|Ctrl-Up","Command-Home|Command-Up"),exec:function(a,b,c){a.editor.navigateFileStart()}}),e.addCommand({name:"selectup",bindKey:f("Shift-Up","Shift-Up"),exec:function(a,b,c){a.editor.getSelection().selectUp()}}),e.addCommand({name:"golineup",bindKey:f("Up","Up|Ctrl-P"),exec:function(a,b,c){a.editor.navigateUp(b.times)}}),e.addCommand({name:"copylinesdown",bindKey:f("Ctrl-Alt-Down","Command-Option-Down"),exec:function(a,b,c){a.editor.copyLinesDown()}}),e.addCommand({name:"movelinesdown",bindKey:f("Alt-Down","Option-Down"),exec:function(a,b,c){a.editor.moveLinesDown()}}),e.addCommand({name:"selecttoend",bindKey:f("Ctrl-Shift-End|Alt-Shift-Down","Command-Shift-Down"),exec:function(a,b,c){a.editor.getSelection().selectFileEnd()}}),e.addCommand({name:"gotoend",bindKey:f("Ctrl-End|Ctrl-Down","Command-End|Command-Down"),exec:function(a,b,c){a.editor.navigateFileEnd()}}),e.addCommand({name:"selectdown",bindKey:f("Shift-Down","Shift-Down"),exec:function(a,b,c){a.editor.getSelection().selectDown()}}),e.addCommand({name:"golinedown",bindKey:f("Down","Down|Ctrl-N"),exec:function(a,b,c){a.editor.navigateDown(b.times)}}),e.addCommand({name:"selectwordleft",bindKey:f("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(a,b,c){a.editor.getSelection().selectWordLeft()}}),e.addCommand({name:"gotowordleft",bindKey:f("Ctrl-Left","Option-Left"),exec:function(a,b,c){a.editor.navigateWordLeft()}}),e.addCommand({name:"selecttolinestart",bindKey:f("Alt-Shift-Left","Command-Shift-Left"),exec:function(a,b,c){a.editor.getSelection().selectLineStart()}}),e.addCommand({name:"gotolinestart",bindKey:f("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(a,b,c){a.editor.navigateLineStart()}}),e.addCommand({name:"selectleft",bindKey:f("Shift-Left","Shift-Left"),exec:function(a,b,c){a.editor.getSelection().selectLeft()}}),e.addCommand({name:"gotoleft",bindKey:f("Left","Left|Ctrl-B"),exec:function(a,b,c){a.editor.navigateLeft(b.times)}}),e.addCommand({name:"selectwordright",bindKey:f("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(a,b,c){a.editor.getSelection().selectWordRight()}}),e.addCommand({name:"gotowordright",bindKey:f("Ctrl-Right","Option-Right"),exec:function(a,b,c){a.editor.navigateWordRight()}}),e.addCommand({name:"selecttolineend",bindKey:f("Alt-Shift-Right","Command-Shift-Right"),exec:function(a,b,c){a.editor.getSelection().selectLineEnd()}}),e.addCommand({name:"gotolineend",bindKey:f("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(a,b,c){a.editor.navigateLineEnd()}}),e.addCommand({name:"selectright",bindKey:f("Shift-Right","Shift-Right"),exec:function(a,b,c){a.editor.getSelection().selectRight()}}),e.addCommand({name:"gotoright",bindKey:f("Right","Right|Ctrl-F"),exec:function(a,b,c){a.editor.navigateRight(b.times)}}),e.addCommand({name:"selectpagedown",bindKey:f("Shift-PageDown","Shift-PageDown"),exec:function(a,b,c){a.editor.selectPageDown()}}),e.addCommand({name:"pagedown",bindKey:f(null,"PageDown"),exec:function(a,b,c){a.editor.scrollPageDown()}}),e.addCommand({name:"gotopagedown",bindKey:f("PageDown","Option-PageDown|Ctrl-V"),exec:function(a,b,c){a.editor.gotoPageDown()}}),e.addCommand({name:"selectpageup",bindKey:f("Shift-PageUp","Shift-PageUp"),exec:function(a,b,c){a.editor.selectPageUp()}}),e.addCommand({name:"pageup",bindKey:f(null,"PageUp"),exec:function(a,b,c){a.editor.scrollPageUp()}}),e.addCommand({name:"gotopageup",bindKey:f("PageUp","Option-PageUp"),exec:function(a,b,c){a.editor.gotoPageUp()}}),e.addCommand({name:"selectlinestart",bindKey:f("Shift-Home","Shift-Home"),exec:function(a,b,c){a.editor.getSelection().selectLineStart()}}),e.addCommand({name:"selectlineend",bindKey:f("Shift-End","Shift-End"),exec:function(a,b,c){a.editor.getSelection().selectLineEnd()}}),e.addCommand({name:"del",bindKey:f("Delete","Delete|Ctrl-D"),exec:function(a,b,c){a.editor.removeRight()}}),e.addCommand({name:"backspace",bindKey:f("Ctrl-Backspace|Command-Backspace|Option-Backspace|Shift-Backspace|Backspace","Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(a,b,c){a.editor.removeLeft()}}),e.addCommand({name:"removetolinestart",bindKey:f(null,"Option-Backspace"),exec:function(a,b,c){a.editor.removeToLineStart()}}),e.addCommand({name:"removetolineend",bindKey:f(null,"Ctrl-K"),exec:function(a,b,c){a.editor.removeToLineEnd()}}),e.addCommand({name:"removewordleft",bindKey:f(null,"Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(a,b,c){a.editor.removeWordLeft()}}),e.addCommand({name:"removewordright",bindKey:f(null,"Alt-Delete"),exec:function(a,b,c){a.editor.removeWordRight()}}),e.addCommand({name:"outdent",bindKey:f("Shift-Tab","Shift-Tab"),exec:function(a,b,c){a.editor.blockOutdent()}}),e.addCommand({name:"indent",bindKey:f("Tab","Tab"),exec:function(a,b,c){a.editor.indent()}}),e.addCommand({name:"inserttext",exec:function(a,b,c){a.editor.insert(d.stringRepeat(b.text||"",b.times||1))}}),e.addCommand({name:"centerselection",bindKey:f(null,"Ctrl-L"),exec:function(a,b,c){a.editor.centerSelection()}}),e.addCommand({name:"splitline",bindKey:f(null,"Ctrl-O"),exec:function(a,b,c){a.editor.splitLine()}}),e.addCommand({name:"transposeletters",bindKey:f("Ctrl-T","Ctrl-T"),exec:function(a,b,c){a.editor.transposeLetters()}})}),define("ace/edit_session",["require","exports","module","pilot/oop","pilot/lang","pilot/event_emitter","ace/selection","ace/mode/text","ace/range","ace/document","ace/background_tokenizer","ace/edit_session/folding"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("pilot/event_emitter").EventEmitter,g=a("ace/selection").Selection,h=a("ace/mode/text").Mode,j=a("ace/range").Range,k=a("ace/document").Document,l=a("ace/background_tokenizer").BackgroundTokenizer,m=function(a,b){this.$modified=!0,this.$breakpoints=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$rowCache=[],this.$rowCacheSize=1e3,this.$wrapData=[],this.$foldData=[],this.$foldData.toString=function(){var a="";this.forEach(function(b){a+="\n"+b.toString()});return a},this.$docChangeCounter=0,a instanceof k?this.setDocument(a):this.setDocument(new k(a)),this.selection=new g(this),b?this.setMode(b):this.setMode(new h)};(function(){function o(a){if(a<4352)return!1;return a>=4352&&a<=4447||a>=4515&&a<=4519||a>=4602&&a<=4607||a>=9001&&a<=9002||a>=11904&&a<=11929||a>=11931&&a<=12019||a>=12032&&a<=12245||a>=12272&&a<=12283||a>=12288&&a<=12350||a>=12353&&a<=12438||a>=12441&&a<=12543||a>=12549&&a<=12589||a>=12593&&a<=12686||a>=12688&&a<=12730||a>=12736&&a<=12771||a>=12784&&a<=12830||a>=12832&&a<=12871||a>=12880&&a<=13054||a>=13056&&a<=19903||a>=19968&&a<=42124||a>=42128&&a<=42182||a>=43360&&a<=43388||a>=44032&&a<=55203||a>=55216&&a<=55238||a>=55243&&a<=55291||a>=63744&&a<=64255||a>=65040&&a<=65049||a>=65072&&a<=65106||a>=65108&&a<=65126||a>=65128&&a<=65131||a>=65281&&a<=65376||a>=65504&&a<=65510}d.implement(this,f),this.setDocument=function(a){if(this.doc)throw new Error("Document is already set");this.doc=a,a.on("change",this.onChange.bind(this)),a.on("changeStart",this.onChangeStart.bind(this)),a.on("changeEnd",this.onChangeEnd.bind(this)),this.on("changeFold",this.onChangeFold.bind(this))},this.getDocument=function(){return this.doc},this.onChangeStart=function(){this.$docChangeCounter++},this.$resetRowCache=function(a){if(a==0)this.$rowCache=[];else{var b=this.$rowCache;for(var c=0;c<b.length;c++)if(b[c].docRow>=a){b.splice(c,b.length);return}}},this.onChangeEnd=function(){this.$docChangeCounter--,this.$docChangeCounter==0&&!this.$fromUndo&&this.$undoManager&&(this.$deltasFold.length&&(this.$deltas.push({group:"fold",deltas:this.$deltasFold}),this.$deltasFold=[]),this.$deltasDoc&&(this.$deltas.push({group:"doc",deltas:this.$deltasDoc}),this.$deltasDoc=[]),this.$informUndoManager.schedule())},this.onChangeFold=function(a){var b=a.data;this.$resetRowCache(b.start.row)},this.onChange=function(a){var b=a.data;this.$modified=!0,this.$resetRowCache(b.range.start.row);var c=this.$updateInternalDataOnChange(a);!this.$fromUndo&&this.$undoManager&&!b.ignore&&(this.$deltasDoc.push(b),c&&c.length!=0&&this.$deltasFold.push({action:"removeFolds",folds:c})),this.bgTokenizer.start(b.range.start.row),this._dispatchEvent("change",a)},this.setValue=function(a){this.doc.setValue(a),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(a){return this.bgTokenizer.getState(a)},this.getTokens=function(a,b){return this.bgTokenizer.getTokens(a,b)},this.setUndoManager=function(a){this.$undoManager=a,this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(a){var b=this;this.$syncInformUndoManager=function(){b.$informUndoManager.cancel(),b.$deltas.length>0&&a.execute({action:"aceupdate",args:[b.$deltas,b]}),b.$deltas=[]},this.$informUndoManager=e.deferredCall(this.$syncInformUndoManager)}},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?e.stringRepeat(" ",this.getTabSize()):"\t"},this.$useSoftTabs=!0,this.setUseSoftTabs=function(a){this.$useSoftTabs!==a&&(this.$useSoftTabs=a)},this.getUseSoftTabs=function(){return this.$useSoftTabs},this.$tabSize=4,this.setTabSize=function(a){!isNaN(a)&&this.$tabSize!==a&&(this.$modified=!0,this.$tabSize=a,this._dispatchEvent("changeTabSize"))},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(a){return this.$useSoftTabs&&a.column%this.$tabSize==0},this.$overwrite=!1,this.setOverwrite=function(a){this.$overwrite!=a&&(this.$overwrite=a,this._dispatchEvent("changeOverwrite"))},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(a){this.$breakpoints=[];for(var b=0;b<a.length;b++)this.$breakpoints[a[b]]=!0;this._dispatchEvent("changeBreakpoint",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._dispatchEvent("changeBreakpoint",{})},this.setBreakpoint=function(a){this.$breakpoints[a]=!0,this._dispatchEvent("changeBreakpoint",{})},this.clearBreakpoint=function(a){delete this.$breakpoints[a],this._dispatchEvent("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.addMarker=function(a,b,c,d){var e=this.$markerId++,f={range:a,type:c||"line",renderer:typeof c=="function"?c:null,clazz:b,inFront:!!d};d?(this.$frontMarkers[e]=f,this._dispatchEvent("changeFrontMarker")):(this.$backMarkers[e]=f,this._dispatchEvent("changeBackMarker"));return e},this.removeMarker=function(a){var b=this.$frontMarkers[a]||this.$backMarkers[a];if(!!b){var c=b.inFront?this.$frontMarkers:this.$backMarkers;b&&(delete c[a],this._dispatchEvent(b.inFront?"changeFrontMarker":"changeBackMarker"))}},this.getMarkers=function(a){return a?this.$frontMarkers:this.$backMarkers},this.setAnnotations=function(a){this.$annotations={};for(var b=0;b<a.length;b++){var c=a[b],d=c.row;this.$annotations[d]?this.$annotations[d].push(c):this.$annotations[d]=[c]}this._dispatchEvent("changeAnnotation",{})},this.getAnnotations=function(){return this.$annotations},this.clearAnnotations=function(){this.$annotations={},this._dispatchEvent("changeAnnotation",{})},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r?\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.tokenRe=/^[\w\d]+/g,this.nonTokenRe=/^(?:[^\w\d]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF])+/g,this.getWordRange=function(a,b){var c=this.getLine(a),d=!1;b>0&&(d=!!c.charAt(b-1).match(this.tokenRe)),d||(d=!!c.charAt(b).match(this.tokenRe));var e=d?this.tokenRe:this.nonTokenRe,f=b;if(f>0){do f--;while(f>=0&&c.charAt(f).match(e));f++}var g=b;while(g<c.length&&c.charAt(g).match(e))g++;return new j(a,f,a,g)},this.setNewLineMode=function(a){this.doc.setNewLineMode(a)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.$useWorker=!0,this.setUseWorker=function(a){this.$useWorker!=a&&(this.$useWorker=a,this.$stopWorker(),a&&this.$startWorker())},this.getUseWorker=function(){return this.$useWorker},this.onReloadTokenizer=function(a){var b=a.data;this.bgTokenizer.start(b.first),this._dispatchEvent("tokenizerUpdate",a)},this.$mode=null,this.setMode=function(a){if(this.$mode!==a){this.$mode=a,this.$stopWorker(),this.$useWorker&&this.$startWorker();var b=a.getTokenizer();if(b.addEventListener!==undefined){var c=this.onReloadTokenizer.bind(this);b.addEventListener("update",c)}if(!this.bgTokenizer){this.bgTokenizer=new l(b);var d=this;this.bgTokenizer.addEventListener("update",function(a){d._dispatchEvent("tokenizerUpdate",a)})}else this.bgTokenizer.setTokenizer(b);this.bgTokenizer.setDocument(this.getDocument()),this.bgTokenizer.start(0),this._dispatchEvent("changeMode")}},this.$stopWorker=function(){this.$worker&&this.$worker.terminate(),this.$worker=null},this.$startWorker=function(){if(typeof Worker!="undefined"&&!a.noWorker)try{this.$worker=this.$mode.createWorker(this)}catch(b){console.log("Could not load worker"),console.log(b),this.$worker=null}else this.$worker=null},this.getMode=function(){return this.$mode},this.$scrollTop=0,this.setScrollTopRow=function(a){this.$scrollTop!==a&&(this.$scrollTop=a,this._dispatchEvent("changeScrollTop"))},this.getScrollTopRow=function(){return this.$scrollTop},this.getWidth=function(){this.$computeWidth();return this.width},this.getScreenWidth=function(){this.$computeWidth();return this.screenWidth},this.$computeWidth=function(a){if(this.$modified||a){this.$modified=!1;var b=this.doc.getAllLines(),c=0,d=0;for(var e=0;e<b.length;e++){var f=this.getFoldLine(e),g,h;g=b[e];if(f){var i=f.range.end;g=this.getFoldDisplayLine(f),e=i.row}h=g.length,c=Math.max(c,h),this.$useWrapMode||(d=Math.max(d,this.$getStringScreenWidth(g)[0]))}this.width=c,this.$useWrapMode?this.screenWidth=this.$wrapLimit:this.screenWidth=d}},this.getLine=function(a){return this.doc.getLine(a)},this.getLines=function(a,b){return this.doc.getLines(a,b)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(a){return this.doc.getTextRange(a)},this.findMatchingBracket=function(a){if(a.column==0)return null;var b=this.getLine(a.row).charAt(a.column-1);if(b=="")return null;var c=b.match(/([\(\[\{])|([\)\]\}])/);if(!c)return null;return c[1]?this.$findClosingBracket(c[1],a):this.$findOpeningBracket(c[2],a)},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(a,b){var c=this.$brackets[a],d=b.column-2,e=b.row,f=1,g=this.getLine(e);for(;;){while(d>=0){var h=g.charAt(d);if(h==c){f-=1;if(f==0)return{row:e,column:d}}else h==a&&(f+=1);d-=1}e-=1;if(e<0)break;var g=this.getLine(e),d=g.length-1}return null},this.$findClosingBracket=function(a,b){var c=this.$brackets[a],d=b.column,e=b.row,f=1,g=this.getLine(e),h=this.getLength();for(;;){while(d<g.length){var i=g.charAt(d);if(i==c){f-=1;if(f==0)return{row:e,column:d}}else i==a&&(f+=1);d+=1}e+=1;if(e>=h)break;var g=this.getLine(e),d=0}return null},this.insert=function(a,b){return this.doc.insert(a,b)},this.remove=function(a){return this.doc.remove(a)},this.undoChanges=function(a,b){if(!!a.length){this.$fromUndo=!0;var c=null;for(var d=a.length-1;d!=-1;d--)delta=a[d],delta.group=="doc"?(this.doc.revertDeltas(delta.deltas),c=this.$getUndoSelection(delta.deltas,!0,c)):delta.deltas.forEach(function(a){this.addFolds(a.folds)},this);this.$fromUndo=!1,c&&!b&&this.selection.setSelectionRange(c);return c}},this.redoChanges=function(a,b){if(!!a.length){this.$fromUndo=!0;var c=null;for(var d=0;d<a.length;d++)delta=a[d],delta.group=="doc"&&(this.doc.applyDeltas(delta.deltas),c=this.$getUndoSelection(delta.deltas,!1,c));this.$fromUndo=!1,c&&!b&&this.selection.setSelectionRange(c);return c}},this.$getUndoSelection=function(a,b,c){function d(a){var c=a.action=="insertText"||a.action=="insertLines";return b?!c:c}var e=a[0],f,g,h=!1;d(e)?(f=e.range.clone(),h=!0):(f=j.fromPoints(e.range.start,e.range.start),h=!1);for(var i=1;i<a.length;i++)e=a[i],d(e)?(g=e.range.start,f.compare(g.row,g.column)==-1&&f.setStart(e.range.start),g=e.range.end,f.compare(g.row,g.column)==1&&f.setEnd(e.range.end),h=!0):(g=e.range.start,f.compare(g.row,g.column)==-1&&(f=j.fromPoints(e.range.start,e.range.start)),h=!1);if(c!=null){var k=c.compareRange(f);k==1?f.setStart(c.start):k==-1&&f.setEnd(c.end)}return f},this.replace=function(a,b){return this.doc.replace(a,b)},this.moveText=function(a,b){var c=this.getTextRange(a);this.remove(a);var d=b.row,e=b.column;!a.isMultiLine()&&a.start.row==d&&a.end.column<e&&(e-=c.length);if(a.isMultiLine()&&a.end.row<d){var f=this.doc.$split(c);d-=f.length-1}var g=d+a.end.row-a.start.row,h=a.isMultiLine()?a.end.column:e+a.end.column-a.start.column,i=new j(d,e,g,h);this.insert(i.start,c);return i},this.indentRows=function(a,b,c){c=c.replace(/\t/g,this.getTabString());for(var d=a;d<=b;d++)this.insert({row:d,column:0},c)},this.outdentRows=function(a){var b=a.collapseRows(),c=new j(0,0,0,0),d=this.getTabSize();for(var e=b.start.row;e<=b.end.row;++e){var f=this.getLine(e);c.start.row=e,c.end.row=e;for(var g=0;g<d;++g)if(f.charAt(g)!=" ")break;g<d&&f.charAt(g)=="\t"?(c.start.column=g,c.end.column=g+1):(c.start.column=0,c.end.column=g),this.remove(c)}},this.moveLinesUp=function(a,b){if(a<=0)return 0;var c=this.doc.removeLines(a,b);this.doc.insertLines(a-1,c);return-1},this.moveLinesDown=function(a,b){if(b>=this.doc.getLength()-1)return 0;var c=this.doc.removeLines(a,b);this.doc.insertLines(a+1,c);return 1},this.duplicateLines=function(a,b){var a=this.$clipRowToDocument(a),b=this.$clipRowToDocument(b),c=this.getLines(a,b);this.doc.insertLines(a,c);var d=b-a+1;return d},this.$clipRowToDocument=function(a){return Math.max(0,Math.min(a,this.doc.getLength()-1))},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(a){if(a!=this.$useWrapMode){this.$useWrapMode=a,this.$modified=!0,this.$resetRowCache(0);if(a){var b=this.getLength();this.$wrapData=[];for(i=0;i<b;i++)this.$wrapData.push([]);this.$updateWrapData(0,b-1)}this._dispatchEvent("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(a,b){if(this.$wrapLimitRange.min!==a||this.$wrapLimitRange.max!==b)this.$wrapLimitRange.min=a,this.$wrapLimitRange.max=b,this.$modified=!0,this._dispatchEvent("changeWrapMode")},this.adjustWrapLimit=function(a){var b=this.$constrainWrapLimit(a);if(b!=this.$wrapLimit&&b>0){this.$wrapLimit=b,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._dispatchEvent("changeWrapLimit"));return!0}return!1},this.$constrainWrapLimit=function(a){var b=this.$wrapLimitRange.min;b&&(a=Math.max(b,a));var c=this.$wrapLimitRange.max;c&&(a=Math.min(c,a));return Math.max(1,a)},this.getWrapLimit=function(){return this.$wrapLimit},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(a){var b=this.$useWrapMode,c,d=a.data.action,e=a.data.range.start.row,f=a.data.range.end.row,g=a.data.range.start,h=a.data.range.end,i=null;d.indexOf("Lines")!=-1?(d=="insertLines"?f=e+a.data.lines.length:f=e,c=a.data.lines.length):c=f-e;if(c!=0)if(d.indexOf("remove")!=-1){b&&this.$wrapData.splice(e,c);var j=this.$foldData;i=this.getFoldsInRange(a.data.range),this.removeFolds(i);var k=this.getFoldLine(h.row),l=0;if(k){k.addRemoveChars(h.row,h.column,g.column-h.column),k.shiftRow(-c);var m=this.getFoldLine(e);m&&m!==k&&(m.merge(k),k=m),l=j.indexOf(k)+1}for(l;l<j.length;l++){var k=j[l];k.start.row>=h.row&&k.shiftRow(-c)}f=e}else{var n;if(b){n=[e,0];for(var o=0;o<c;o++)n.push([]);this.$wrapData.splice.apply(this.$wrapData,n)}var j=this.$foldData,k=this.getFoldLine(e),l=0;if(k){var p=k.range.compareInside(g.row,g.column);p==0?(k=k.split(g.row,g.column),k.shiftRow(c),k.addRemoveChars(f,0,h.column-g.column)):p==-1&&(k.addRemoveChars(e,0,h.column-g.column),k.shiftRow(c)),l=j.indexOf(k)+1}for(l;l<j.length;l++){var k=j[l];k.start.row>=e&&k.shiftRow(c)}}else{var q;c=Math.abs(a.data.range.start.column-a.data.range.end.column),d.indexOf("remove")!=-1&&(i=this.getFoldsInRange(a.data.range),this.removeFolds(i),c=-c);var k=this.getFoldLine(e);k&&k.addRemoveChars(e,g.column,c)}b&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),b&&this.$updateWrapData(e,f);return i},this.$updateWrapData=function(a,b){var c=this.doc.getAllLines(),d=this.getTabSize(),f=this.$wrapData,i=this.$wrapLimit,j,l,m=a;b=Math.min(b,c.length-1);while(m<=b){l=this.getFoldLine(m);if(!l)j=this.$getDisplayTokens(e.stringTrimRight(c[m]));else{j=[],l.walk(function(a,b,d,e){var f;if(a){f=this.$getDisplayTokens(a,j.length),f[0]=g;for(var i=1;i<f.length;i++)f[i]=h}else f=this.$getDisplayTokens(c[b].substring(e,d),j.length);j=j.concat(f)}.bind(this),l.end.row,c[l.end.row].length+1);while(j.length!=0&&j[j.length-1]>=k)j.pop()}f[m]=this.$computeWrapSplits(j,i,d),m=this.getRowFoldEnd(m)+1}};var b=1,c=2,g=3,h=4,k=10,m=11,n=12;this.$computeWrapSplits=function(a,b,c){function j(b){var c=a.slice(f,b),e=c.length;c.join("").replace(/12/g,function(a){e-=1}).replace(/2/g,function(a){e-=1}),i+=e,d.push(i),f=b}if(a.length==0)return[];var c=this.getTabSize(),d=[],e=a.length,f=0,i=0;while(e-f>b){var l=f+b;if(a[l]>=k){while(a[l]>=k)l++;j(l);continue}if(a[l]==g||a[l]==h){for(l;l!=f-1;l--)if(a[l]==g)break;if(l>f){j(l);continue}l=f+b;for(l;l<a.length;l++)if(a[l]!=h)break;if(l==a.length)break;j(l);continue}for(l;l!=f-1;l--)if(a[l]>=g){l++;break}if(l>f){j(l);continue}l=f+b,j(f+b)}return d},this.$getDisplayTokens=function(a,d){var e=[],f;d=d||0;for(var g=0;g<a.length;g++){var h=a.charCodeAt(g);if(h==9){f=this.getScreenTabSize(e.length+d),e.push(m);for(var i=1;i<f;i++)e.push(n)}else h==32?e.push(k):o(h)?e.push(b,c):e.push(b)}return e},this.$getStringScreenWidth=function(a,b,c){if(b==0)return[0,0];b==null&&(b=c+a.length*Math.max(this.getTabSize(),2)),c=c||0;var d,e;for(e=0;e<a.length;e++){d=a.charCodeAt(e),d==9?c+=this.getScreenTabSize(c):o(d)?c+=2:c+=1;if(c>b)break}return[c,e]},this.getRowLength=function(a){return!this.$useWrapMode||!this.$wrapData[a]?1:this.$wrapData[a].length+1},this.getRowHeight=function(a,b){return this.getRowLength(b)*a.lineHeight},this.getScreenLastRowColumn=function(a){return this.screenToDocumentColumn(a,Number.MAX_VALUE/10)},this.getDocumentLastRowColumn=function(a,b){var c=this.documentToScreenRow(a,b);return this.getScreenLastRowColumn(c)},this.getDocumentLastRowColumnPosition=function(a,b){var c=this.documentToScreenRow(a,b);return this.screenToDocumentPosition(c,Number.MAX_VALUE/10)},this.getRowSplitData=function(a){return this.$useWrapMode?this.$wrapData[a]:undefined},this.getScreenTabSize=function(a){return this.$tabSize-a%this.$tabSize},this.screenToDocumentRow=function(a,b){return this.screenToDocumentPosition(a,b).row},this.screenToDocumentColumn=function(a,b){return this.screenToDocumentPosition(a,b).column},this.screenToDocumentPosition=function(a,b){var c,d=0,e=0,f,g,h=0,i=0,j=null,k=this.$rowCache,l=!k.length;for(var m=0;m<k.length;m++)k[m].screenRow<a&&(h=k[m].screenRow,d=k[m].docRow,l=m==k.length-1);var n=d,o=this.getLength()-1,p=this.getNextFold(d),q=p?p.start.row:Infinity;while(h<=a){l&&d-n>this.$rowCacheSize&&(k.push({docRow:d,screenRow:h}),n=d),i=this.getRowLength(d);if(h+i-1>=a||d>=o)break;h+=i,d++,d>q&&(d=p.end.row+1,p=this.getNextFold(d),q=p?p.start.row:Infinity)}p&&p.start.row<=d?c=this.getFoldDisplayLine(p):(c=this.getLine(d),p=null),this.$useWrapMode&&(j=this.$wrapData[d],j&&(f=j[a-h],a>h&&j.length&&(e=j[a-h-1]||j[j.length-1],c=c.substring(e)))),e+=this.$getStringScreenWidth(c,b)[1],this.$useWrapMode?e>=f&&(e=f-1):e=Math.min(e,c.length);if(p)return p.idxToPosition(e);return{row:d,column:e}},this.documentToScreenPosition=function(a,b){b==null&&(b=a.column,a=a.row);var c;if(this.$useWrapMode){c=this.$wrapData;if(a>c.length-1)return{row:this.getScreenLength(),column:c.length==0?0:c[c.length-1].length-1}}var d=0,e=0,f=null,g=null;g=this.getFoldAt(a,b,1),g&&(a=g.start.row,b=g.start.column);var h,i=0,j=this.$rowCache,k=!j.length;for(var l=0;l<j.length;l++)j[l].docRow<a&&(d=j[l].screenRow,i=j[l].docRow,k=l==j.length-1);var m=i,n=this.getNextFold(i),o=n?n.start.row:Infinity;while(i<a){if(i>=o){h=n.end.row+1;if(h>a)break;n=this.getNextFold(h),o=n?n.start.row:Infinity}else h=i+1;k&&i-m>this.$rowCacheSize&&(j.push({docRow:i,screenRow:d}),m=i),d+=this.getRowLength(i),i=h}var p="";n&&i>=o?(p=this.getFoldDisplayLine(n,a,b),f=n.start.row):(p=this.getLine(a).substring(0,b),f=a);if(this.$useWrapMode){var q=c[f],r=0;while(p.length>=q[r])d++,r++;p=p.substring(q[r-1]||0,p.length)}return{row:d,column:this.$getStringScreenWidth(p)[0]}},this.documentToScreenColumn=function(a,b){return this.documentToScreenPosition(a,b).column},this.documentToScreenRow=function(a,b){return this.documentToScreenPosition(a,b).row},this.getScreenLength=function(){var a=0,b=null,c=null;if(!this.$useWrapMode){a=this.getLength();var d=this.$foldData;for(var e=0;e<d.length;e++)c=d[e],a-=c.end.row-c.start.row}else for(var f=0;f<this.$wrapData.length;f++)(c=this.getFoldLine(f,b))?(f=c.end.row,a+=1):a+=this.$wrapData[f].length+1;return a}}).call(m.prototype),a("ace/edit_session/folding").Folding.call(m.prototype),b.EditSession=m}),define("ace/selection",["require","exports","module","pilot/oop","pilot/lang","pilot/event_emitter","ace/range"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("pilot/event_emitter").EventEmitter,g=a("ace/range").Range,h=function(a){this.session=a,this.doc=a.getDocument(),this.clearSelection(),this.selectionLead=this.doc.createAnchor(0,0),this.selectionAnchor=this.doc.createAnchor(0,0);var b=this;this.selectionLead.on("change",function(a){b._dispatchEvent("changeCursor"),b.$isEmpty||b._dispatchEvent("changeSelection"),a.old.row==a.value.row&&b.$updateDesiredColumn()}),this.selectionAnchor.on("change",function(){b.$isEmpty||b._dispatchEvent("changeSelection")})};(function(){d.implement(this,f),this.isEmpty=function(){return this.$isEmpty||this.selectionAnchor.row==this.selectionLead.row&&this.selectionAnchor.column==this.selectionLead.column},this.isMultiLine=function(){if(this.isEmpty())return!1;return this.getRange().isMultiLine()},this.getCursor=function(){return this.selectionLead.getPosition()},this.setSelectionAnchor=function(a,b){this.selectionAnchor.setPosition(a,b),this.$isEmpty&&(this.$isEmpty=!1,this._dispatchEvent("changeSelection"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.selectionAnchor.getPosition()},this.getSelectionLead=function(){return this.selectionLead.getPosition()},this.shiftSelection=function(a){if(this.$isEmpty)this.moveCursorTo(this.selectionLead.row,this.selectionLead.column+a);else{var b=this.getSelectionAnchor(),c=this.getSelectionLead(),d=this.isBackwards();(!d||b.column!==0)&&this.setSelectionAnchor(b.row,b.column+a),(d||c.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(c.row,c.column+a)})}},this.isBackwards=function(){var a=this.selectionAnchor,b=this.selectionLead;return a.row>b.row||a.row==b.row&&a.column>b.column},this.getRange=function(){var a=this.selectionAnchor,b=this.selectionLead;if(this.isEmpty())return g.fromPoints(b,b);return this.isBackwards()?g.fromPoints(b,a):g.fromPoints(a,b)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._dispatchEvent("changeSelection"))},this.selectAll=function(){var a=this.doc.getLength()-1;this.setSelectionAnchor(a,this.doc.getLine(a).length),this.moveCursorTo(0,0)},this.setSelectionRange=function(a,b){b?(this.setSelectionAnchor(a.end.row,a.end.column),this.selectTo(a.start.row,a.start.column)):(this.setSelectionAnchor(a.start.row,a.start.column),this.selectTo(a.end.row,a.end.column)),this.$updateDesiredColumn()},this.$updateDesiredColumn=function(){var a=this.getCursor();this.$desiredColumn=this.session.documentToScreenColumn(a.row,a.column)},this.$moveSelection=function(a){var b=this.selectionLead;this.$isEmpty&&this.setSelectionAnchor(b.row,b.column),a.call(this)},this.selectTo=function(a,b){this.$moveSelection(function(){this.moveCursorTo(a,b)})},this.selectToPosition=function(a){this.$moveSelection(function(){this.moveCursorToPosition(a)})},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.selectWord=function(){var a=this.getCursor(),b=this.session.getWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectLine=function(){var a=this.selectionLead.row,b,c=this.session.getFoldLine(a);c?(a=c.start.row,b=c.end.row):b=a,this.setSelectionAnchor(a,0),this.$moveSelection(function(){this.moveCursorTo(b+1,0)})},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,-1))this.moveCursorTo(b.start.row,b.start.column);else if(a.column==0)a.row>0&&this.moveCursorTo(a.row-1,this.doc.getLine(a.row-1).length);else{var c=this.session.getTabSize();this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column-c,a.column).split(" ").length-1==c?this.moveCursorBy(0,-c):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,1))this.moveCursorTo(b.end.row,b.end.column);else if(this.selectionLead.column==this.doc.getLine(this.selectionLead.row).length)this.selectionLead.row<this.doc.getLength()-1&&this.moveCursorTo(this.selectionLead.row+1,0);else{var c=this.session.getTabSize(),a=this.selectionLead;this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column,a.column+c).split(" ").length-1==c?this.moveCursorBy(0,c):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.session.documentToScreenRow(a,b),d=this.session.screenToDocumentPosition(c,0),e=this.session.getDisplayLine(a,b,d.row,d.column),f=e.match(/^\s*/);f[0].length==0||f[0].length>=b-d.column?this.moveCursorTo(d.row,d.column):this.moveCursorTo(d.row,d.column+f[0].length)},this.moveCursorLineEnd=function(){var a=this.selectionLead,b=this.session.getDocumentLastRowColumnPosition(a.row,a.column);this.moveCursorTo(b.row,b.column)},this.moveCursorFileEnd=function(){var a=this.doc.getLength()-1,b=this.doc.getLine(a).length;this.moveCursorTo(a,b)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorWordRight=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.doc.getLine(a),d=c.substring(b),e;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var f;if(f=this.session.getFoldAt(a,b,1))this.moveCursorTo(f.end.row,f.end.column);else{if(b==c.length){this.moveCursorRight();return}if(e=this.session.nonTokenRe.exec(d))b+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0;else if(e=this.session.tokenRe.exec(d))b+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)}},this.moveCursorWordLeft=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c;if(c=this.session.getFoldAt(a,b,-1))this.moveCursorTo(c.start.row,c.start.column);else{if(b==0){this.moveCursorLeft();return}var d=this.session.getFoldStringAt(a,b,-1);d==null&&(d=this.doc.getLine(a).substring(0,b));var f=e.stringReverse(d),g;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(g=this.session.nonTokenRe.exec(f))b-=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0;else if(g=this.session.tokenRe.exec(f))b-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)}},this.moveCursorBy=function(a,b){var c=this.session.documentToScreenPosition(this.selectionLead.row,this.selectionLead.column),d=b==0&&this.$desiredColumn||c.column,e=this.session.screenToDocumentPosition(c.row+a,d);this.moveCursorTo(e.row,e.column+b,b==0)},this.moveCursorToPosition=function(a){this.moveCursorTo(a.row,a.column)},this.moveCursorTo=function(a,b,c){var d=this.session.getFoldAt(a,b,1);d&&(a=d.start.row,b=d.start.column),this.selectionLead.setPosition(a,b),c||this.$updateDesiredColumn(this.selectionLead.column)},this.moveCursorToScreen=function(a,b,c){var d=this.session.screenToDocumentPosition(a,b);a=d.row,b=d.column,this.moveCursorTo(a,b,c)}}).call(h.prototype),b.Selection=h}),define("ace/range",["require","exports","module"],function(a,b,c){var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(a,b){return this.compare(a,b)==0},this.compareRange=function(a){var b,c=a.end,d=a.start;b=this.compare(c.row,c.column);if(b==1){b=this.compare(d.row,d.column);return b==1?2:b==0?1:0}if(b==-1)return-2;b=this.compare(d.row,d.column);return b==-1?-1:b==1?42:0},this.containsRange=function(a){var b=this.compareRange(a);return b==-1||b==0||b==1},this.isEnd=function(a,b){return this.end.row==a&&this.end.column==b},this.isStart=function(a,b){return this.start.row==a&&this.start.column==b},this.setStart=function(a,b){typeof a=="object"?(this.start.column=a.column,this.start.row=a.row):(this.start.row=a,this.start.column=b)},this.setEnd=function(a,b){typeof a=="object"?(this.end.column=a.column,this.end.row=a.row):(this.end.row=a,this.end.column=b)},this.inside=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)||this.isStart(a,b)?!1:!0;return!1},this.insideStart=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)?!1:!0;return!1},this.insideEnd=function(a,b){if(this.compare(a,b)==0)return this.isStart(a,b)?!1:!0;return!1},this.compare=function(a,b){if(!this.isMultiLine()&&a===this.start.row)return b<this.start.column?-1:b>this.end.column?1:0;if(a<this.start.row)return-1;if(a>this.end.row)return 1;if(this.start.row===a)return b>=this.start.column?0:-1;if(this.end.row===a)return b<=this.end.column?0:1;return 0},this.compareStart=function(a,b){return this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.compareEnd=function(a,b){return this.end.row==a&&this.end.column==b?1:this.compare(a,b)},this.compareInside=function(a,b){return this.end.row==a&&this.end.column==b?1:this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour"],function(a,b,c){var d=a("ace/tokenizer").Tokenizer,e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=a("ace/mode/behaviour").Behaviour,g=function(){this.$tokenizer=new d((new e).getRules()),this.$behaviour=new f};(function(){this.getTokenizer=function(){return this.$tokenizer},this.toggleCommentLines=function(a,b,c,d){},this.getNextLineIndent=function(a,b,c){return""},this.checkOutdent=function(a,b,c){return!1},this.autoOutdent=function(a,b,c){},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""},this.createWorker=function(a){return null},this.highlightSelection=function(a){var b=a.session;b.$selectionOccurrences||(b.$selectionOccurrences=[]),b.$selectionOccurrences.length&&this.clearSelectionHighlight(a);var c=a.getSelectionRange();if(!c.isEmpty()&&!c.isMultiLine()){var d=c.start.column-1,e=c.end.column+1,f=b.getLine(c.start.row),g=f.length,h=f.substring(Math.max(d,0),Math.min(e,g));if(d>=0&&/^[\w\d]/.test(h)||e<=g&&/[\w\d]$/.test(h))return;h=f.substring(c.start.column,c.end.column);if(!/^[\w\d]+$/.test(h))return;var i=a.getCursorPosition(),j={wrap:!0,wholeWord:!0,caseSensitive:!0,needle:h},k=a.$search.getOptions();a.$search.set(j);var l=a.$search.findAll(b);l.forEach(function(a){if(!a.contains(i.row,i.column)){var c=b.addMarker(a,"ace_selected_word");b.$selectionOccurrences.push(c)}}),a.$search.set(k)}},this.clearSelectionHighlight=function(a){!a.session.$selectionOccurrences||(a.session.$selectionOccurrences.forEach(function(b){a.session.removeMarker(b)}),a.session.$selectionOccurrences=[])},this.createModeDelegates=function(a){if(!!this.$embeds){this.$modes={};for(var b=0;b<this.$embeds.length;b++)a[this.$embeds[b]]&&(this.$modes[this.$embeds[b]]=new a[this.$embeds[b]]);var c=["toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction"];for(var b=0;b<c.length;b++)(function(a){var d=c[b],e=a[d];a[c[b]]=function(){return this.$delegator(d,arguments,e)}})(this)}},this.$delegator=function(a,b,c){var d=b[0];for(var e=0;e<this.$embeds.length;e++){if(!this.$modes[this.$embeds[e]])continue;var f=d.split(this.$embeds[e]);if(!f[0]&&f[1]){b[0]=f[1];var g=this.$modes[this.$embeds[e]];return g[a].apply(g,b)}}var h=c.apply(this,b);return c?h:undefined},this.transformAction=function(a,b,c,d,e){if(this.$behaviour){var f=this.$behaviour.getBehaviours();for(var g in f)if(f[g][b]){var h=f[g][b].apply(this,arguments);if(h!==!1)return h}}return!1}}).call(g.prototype),b.Mode=g}),define("ace/tokenizer",["require","exports","module"],function(a,b,c){var d=function(a){this.rules=a,this.regExps={},this.matchMappings={};for(var b in this.rules){var c=this.rules[b],d=c,e=[],f=0,g=this.matchMappings[b]={};for(var h=0;h<d.length;h++){var i=(new RegExp("(?:("+d[h].regex+")|(.))")).exec("a").length-2,j=d[h].regex.replace(/\\([0-9]+)/g,function(a,b){return"\\"+(parseInt(b,10)+f+1)});g[f]={rule:h,len:i},f+=i,e.push(j)}this.regExps[b]=new RegExp("(?:("+e.join(")|(")+")|(.))","g")}};(function(){this.getLineTokens=function(a,b){var c=b,d=this.rules[c],e=this.matchMappings[c],f=this.regExps[c];f.lastIndex=0;var g,h=[],i=0,j={type:null,value:""};while(g=f.exec(a)){var k="text",l=[g[0]];for(var m=0;m<g.length-2;m++)if(g[m+1]!==undefined){var n=d[e[m].rule];e[m].len>1&&(l=g.slice(m+2,m+1+e[m].len)),typeof n.token=="function"?k=n.token.apply(this,l):k=n.token,n.next&&n.next!==c&&(c=n.next,d=this.rules[c],e=this.matchMappings[c],i=f.lastIndex,f=this.regExps[c],f.lastIndex=i);break}typeof k=="string"&&(typeof l!="string"&&(l=[l.join("")]),k=[k]);for(var m=0;m<l.length;m++)j.type!==k[m]?(j.type&&h.push(j),j={type:k[m],value:l[m]}):j.value+=l[m];if(i==a.length)break;i=f.lastIndex}j.type&&h.push(j);return{tokens:h,state:c}}}).call(d.prototype),b.Tokenizer=d}),define("ace/mode/text_highlight_rules",["require","exports","module","pilot/lang"],function(a,b,c){var d=a("pilot/lang"),e=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{token:"text",regex:".+"}]}};(function(){this.addRules=function(a,b){for(var c in a){var d=a[c];for(var e=0;e<d.length;e++){var f=d[e];f.next?f.next=b+f.next:f.next=b+c}this.$rules[b+c]=d}},this.getRules=function(){return this.$rules},this.embedRules=function(a,b,c,e){var f=(new a).getRules();if(e)for(var g=0;g<e.length;g++)e[g]=b+e[g];else{e=[];for(var h in f)e.push(b+h)}this.addRules(f,b);for(var g=0;g<e.length;g++)Array.prototype.unshift.apply(this.$rules[e[g]],d.deepCopy(c));this.$embeds||(this.$embeds=[]),this.$embeds.push(b)},this.getEmbeds=function(){return this.$embeds}}).call(e.prototype),b.TextHighlightRules=e}),define("ace/mode/behaviour",["require","exports","module"],function(a,b,c){var d=function(){this.$behaviours={}};(function(){this.add=function(a,b,c){switch(undefined){case this.$behaviours:this.$behaviours={};case this.$behaviours[a]:this.$behaviours[a]={}}this.$behaviours[a][b]=c},this.addBehaviours=function(a){for(var b in a)for(var c in a[b])this.add(b,c,a[b][c])},this.remove=function(a){this.$behaviours&&this.$behaviours[a]&&delete this.$behaviours[a]},this.inherit=function(a,b){if(typeof a=="function")var c=(new a).getBehaviours(b);else var c=a.getBehaviours(b);this.addBehaviours(c)},this.getBehaviours=function(a){if(!a)return this.$behaviours;var b={};for(var c=0;c<a.length;c++)this.$behaviours[a[c]]&&(b[a[c]]=this.$behaviours[a[c]]);return b}}).call(d.prototype),b.Behaviour=d}),define("ace/document",["require","exports","module","pilot/oop","pilot/event_emitter","ace/range","ace/anchor"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=a("ace/range").Range,g=a("ace/anchor").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[""]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},"aaa".split(/a/).length==0?this.$split=function(a){return a.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(a){return a.split(/\r\n|\r|\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r?\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(a){this.$newLineMode!==a&&(this.$newLineMode=a)},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a=="\r\n"||a=="\r"||a=="\n"},this.getLine=function(a){return this.$lines[a]||""},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column));return b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length);return a},this.insert=function(a,b){if(b.length==0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];this._dispatchEvent("changeStart"),a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||"")),this._dispatchEvent("changeEnd");return a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c),this._dispatchEvent("changeStart");var d=new f(a,0,a+b.length,0),e={action:"insertLines",range:d,lines:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:"insertText",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:d}),this._dispatchEvent("changeEnd");return c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:"insertText",range:f.fromPoints(a,d),text:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;this._dispatchEvent("changeStart");if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);this._dispatchEvent("changeEnd");return a.start},this.removeInLine=function(a,b,c){if(b!=c){var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this._dispatchEvent("changeStart"),this.$lines.splice(a,1,h);var i={action:"removeText",range:d,text:g};this._dispatchEvent("change",{data:i}),this._dispatchEvent("changeEnd");return d.start}},this.removeLines=function(a,b){this._dispatchEvent("changeStart");var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:"removeLines",range:c,nl:this.getNewLineCharacter(),lines:d};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this._dispatchEvent("changeStart"),this.$lines.splice(a,2,e);var g={action:"removeText",range:d,text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:g}),this._dispatchEvent("changeEnd")},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this._dispatchEvent("changeStart"),this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;this._dispatchEvent("changeEnd");return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.insertLines(d.start.row,c.lines):c.action=="insertText"?this.insert(d.start,c.text):c.action=="removeLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="removeText"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="insertText"?this.remove(d):c.action=="removeLines"?this.insertLines(d.start.row,c.lines):c.action=="removeText"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define("ace/anchor",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c=="undefined"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on("change",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row!=c.end.row||c.start.row==this.row){if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action==="insertText"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action==="insertLines"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action=="removeText"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action=="removeLines"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)}},this.setPosition=function(a,b,c){c?pos={row:a,column:b}:pos=this.$clipPositionToDocument(a,b);if(this.row!=pos.row||this.column!=pos.column){var d={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._dispatchEvent("change",{old:d,value:pos})}},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0);return c}}).call(f.prototype)}),define("ace/background_tokenizer",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=function(a,b){this.running=!1,this.lines=[],this.currentLine=0,this.tokenizer=a;var c=this;this.$worker=function(){if(!!c.running){var a=new Date,b=c.currentLine,d=c.doc,e=0,f=d.getLength();while(c.currentLine<f){c.lines[c.currentLine]=c.$tokenizeRows(c.currentLine,c.currentLine)[0],c.currentLine++,e+=1;if(e%5==0&&new Date-a>20){c.fireUpdateEvent(b,c.currentLine-1),c.running=setTimeout(c.$worker,20);return}}c.running=!1,c.fireUpdateEvent(b,f-1)}}};(function(){d.implement(this,e),this.setTokenizer=function(a){this.tokenizer=a,this.lines=[],this.start(0)},this.setDocument=function(a){this.doc=a,this.lines=[],this.stop()},this.fireUpdateEvent=function(a,b){var c={first:a,last:b};this._dispatchEvent("update",{data:c})},this.start=function(a){this.currentLine=Math.min(a||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(a,b){return this.$tokenizeRows(a,b)},this.getState=function(a){return this.$tokenizeRows(a,a)[0].state},this.$tokenizeRows=function(a,b){if(!this.doc)return[];var c=[],d="start",e=!1;a>0&&this.lines[a-1]?(d=this.lines[a-1].state,e=!0):a==0?(d="start",e=!0):this.lines.length>0&&(d=this.lines[this.lines.length-1].state);var f=this.doc.getLines(a,b);for(var g=a;g<=b;g++)if(!this.lines[g]){var h=this.tokenizer.getLineTokens(f[g-a]||"",d),d=h.state;c.push(h),e&&(this.lines[g]=h)}else{var h=this.lines[g];d=h.state,c.push(h)}return c}}).call(f.prototype),b.BackgroundTokenizer=f}),define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line"],function(a,b,c){function g(){this.getFoldAt=function(a,b,c){var d=this.getFoldLine(a);if(!d)return null;var e=d.folds,f;for(var g=0;g<e.length;g++){f=e[g];if(f.range.contains(a,b)){if(c==1&&f.range.isEnd(a,b))continue;if(c==-1&&f.range.isStart(a,b))continue;return f}}},this.getFoldsInRange=function(a){a=a.clone();var b=a.start,c=a.end,d=this.$foldData,e,f,g,h=[];b.column+=1,c.column-=1;for(var i=0;i<d.length;i++){g=d[i].range.compareRange(a);if(g==2)continue;if(g==-2)break;e=d[i].folds;for(var j=0;j<e.length;j++){f=e[j],g=f.range.compareRange(a);if(g==-2)break;if(g==2)continue;if(g==42)break;h.push(f)}}return h},this.getFoldStringAt=function(a,b,c,d){var d=d||this.getFoldLine(a);if(!d)return null;var e,f,g,h;f={end:{column:0}};for(var i=0;i<d.folds.length;i++){e=d.folds[i],g=e.range.compareEnd(a,b);if(g==-1){h=this.getLine(e.start.row).substring(f.end.column,e.start.column);break}if(g==0)return null;f=e}h||(h=this.getLine(e.start.row).substring(f.end.column));return c==-1?h.substring(0,b-f.end.column):c==1?h.substring(b-f.end.column):h},this.getFoldLine=function(a,b){var c=this.$foldData,d=0;b&&(d=c.indexOf(b)),d==-1&&(d=0);for(d;d<c.length;d++){var e=c[d];if(e.start.row<=a&&e.end.row>=a)return e;if(e.end.row>a)return null}return null},this.getNextFold=function(a,b){var c=this.$foldData,d,e=0;b&&(e=c.indexOf(b)),e==-1&&(e=0);for(e;e<c.length;e++){var f=c[e];if(f.end.row>=a)return f}return null},this.getFoldedRowCount=function(a,b){var c=this.$foldData,d=b-a+1;for(var e=0;e<c.length;e++){var f=c[e],g=f.end.row,h=f.start.row;if(g>=b){h<b&&(h>=a?d-=b-h:d=0);break}g>=a&&(h>=a?d-=g-h:d-=g-a+1)}return d},this.$addFoldLine=function(a){this.$foldData.push(a),this.$foldData.sort(function(a,b){return a.start.row-b.start.row});return a},this.addFold=function(a,b,c,g,h){var i,j=this.$foldData,k=null,l,m,n,o,p=!1;a instanceof f&&(n=a,b=n.range,a=n.placeholder),b instanceof d?(i=b,b=i.start.row,c=i.start.column,g=i.end.row,h=i.end.column):i=new d(b,c,g,h);if(a.length<2)throw"Placeholder has to be at least 2 characters";if(b==g&&h-c<2)throw"The range has to be at least 2 characters width";m=this.getFoldAt(b,c,1);if(m&&m.range.isEnd(g,h)&&m.range.isStart(b,c))return m;m=this.getFoldAt(b,c,1);if(m&&!m.range.isStart(b,c))throw"A fold can't start inside of an already existing fold";m=this.getFoldAt(g,h,-1);if(m&&!m.range.isEnd(g,h))throw"A fold can't end inside of an already existing fold";if(g>=this.doc.getLength())throw"End of fold is outside of the document.";if(h>this.getLine(g).length||c>this.getLine(b).length)throw"End of fold is outside of the document.";m=n||new f(i,a),o=this.getFoldsInRange(i),o.length>0&&(this.removeFolds(o),m.subFolds=o);for(var q=0;q<j.length;q++){l=j[q];if(g==l.start.row){l.addFold(m),p=!0;break}if(b==l.end.row){l.addFold(m),p=!0;if(!m.sameRow){foldLineNext=j[q+1];if(foldLineNext&&foldLineNext.start.row==g){l.merge(foldLineNext);break}}break}if(g<=l.start.row)break}p||(l=this.$addFoldLine(new e(this.$foldData,m))),this.$useWrapMode&&this.$updateWrapData(l.start.row,l.start.row),this.$modified=!0,this._dispatchEvent("changeFold",{data:m});return m},this.addFolds=function(a){a.forEach(function(a){this.addFold(a)},this)},this.removeFold=function(a){var b=a.foldLine,c=b.start.row,d=b.end.row,e=this.$foldData,f=b.folds;if(f.length==1)e.splice(e.indexOf(b),1);else if(b.range.isEnd(a.end.row,a.end.column))f.pop(),b.end.row=f[f.length-1].end.row,b.end.column=f[f.length-1].end.column;else if(b.range.isStart(a.start.row,a.start.column))f.shift(),b.start.row=f[0].start.row,b.start.column=f[0].start.column;else if(a.sameRow)f.splice(f.indexOf(a),1);else{var g=b.split(a.start.row,a.start.column);g.folds.shift(),b.start.row=f[0].start.row,b.start.column=f[0].start.column,this.$addFoldLine(g)}this.$useWrapMode&&this.$updateWrapData(c,d),this.$modified=!0,this._dispatchEvent("changeFold",{data:a})},this.removeFolds=function(a){var b=[];for(var c=0;c<a.length;c++)b.push(a[c]);b.forEach(function(a){this.removeFold(a)},this),this.$modified=!0},this.expandFold=function(a){this.removeFold(a),a.subFolds.forEach(function(a){this.addFold(a)},this),a.subFolds=[]},this.expandFolds=function(a){a.forEach(function(a){this.expandFold(a)},this)},this.isRowFolded=function(a,b){return!!this.getFoldLine(a,b)},this.getRowFoldEnd=function(a,b){var c=this.getFoldLine(a,b);return c?c.end.row:a},this.getFoldDisplayLine=function(a,b,c,d,e){d==null&&(d=a.start.row,e=0),b==null&&(b=a.end.row,c=this.getLine(b).length);var f="",g=this.doc,h="";a.walk(function(a,b,c,f,i){if(!(b<d)){if(b==d){if(c<e)return;f=Math.max(e,f)}a?h+=a:h+=g.getLine(b).substring(f,c)}}.bind(this),b,c);return h},this.getDisplayLine=function(a,b,c,d){var e=this.getFoldLine(a);if(!e){var f;f=this.doc.getLine(a);return f.substring(d||0,b||f.length)}return this.getFoldDisplayLine(e,a,b,c,d)},this.$cloneFoldData=function(){var a=this.$foldData,b=[];b=this.$foldData.map(function(a){var c=a.folds.map(function(a){return a.clone()});return new e(b,c)});return b}}function f(a,b){this.foldLine=null,this.placeholder=b,this.range=a,this.start=a.start,this.end=a.end,this.sameRow=a.start.row==a.end.row,this.subFolds=[]}var d=a("ace/range").Range,e=a("ace/edit_session/fold_line").FoldLine;f.prototype.toString=function(){return'"'+this.placeholder+'" '+this.range.toString()},f.prototype.setFoldLine=function(a){this.foldLine=a,this.subFolds.forEach(function(b){b.setFoldLine(a)})},f.prototype.clone=function(){var a=this.range.clone(),b=new f(a,this.placeholder);this.subFolds.forEach(function(a){b.subFolds.push(a.clone())});return b},b.Folding=g}),define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(a,b,c){function e(a,b){this.foldData=a,Array.isArray(b)?this.folds=b:b=this.folds=[b];var c=b[b.length-1];this.range=new d(b[0].start.row,b[0].start.column,c.end.row,c.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(a){a.setFoldLine(this)},this)}var d=a("ace/range").Range;(function(){this.shiftRow=function(a){this.start.row+=a,this.end.row+=a,this.folds.forEach(function(b){b.start.row+=a,b.end.row+=a})},this.addFold=function(a){if(a.sameRow){if(a.start.row<this.startRow||a.endRow>this.endRow)throw"Can't add a fold to this FoldLine as it has no connection";this.folds.push(a),this.folds.sort(function(a,b){return-a.range.compareEnd(b.start.row,b.start.column)}),this.range.compareEnd(a.start.row,a.start.column)>0?(this.end.row=a.end.row,this.end.column=a.end.column):this.range.compareStart(a.end.row,a.end.column)<0&&(this.start.row=a.start.row,this.start.column=a.start.column)}else if(a.start.row==this.end.row)this.folds.push(a),this.end.row=a.end.row,this.end.column=a.end.column;else if(a.end.row==this.start.row)this.folds.unshift(a),this.start.row=a.start.row,this.start.column=a.start.column;else throw"Trying to add fold to FoldRow that doesn't have a matching row";a.foldLine=this},this.containsRow=function(a){return a>=this.start.row&&a<=this.end.row},this.walk=function(a,b,c){var d=0,e=this.folds,f,g,h,i=!0;b==null&&(b=this.end.row,c=this.end.column);for(var j=0;j<e.length;j++){f=e[j],g=f.range.compareStart(b,c);if(g==-1){a(null,b,c,d,i);return}h=a(null,f.start.row,f.start.column,d,i),h=!h&&a(f.placeholder,f.start.row,f.start.column,d);if(h||g==0)return;i=!f.sameRow,d=f.end.column}a(null,b,c,d,i)},this.getNextFoldTo=function(a,b){var c,d;for(var e=0;e<this.folds.length;e++){c=this.folds[e],d=c.range.compareEnd(a,b);if(d==-1)return{fold:c,kind:"after"};if(d==0)return{fold:c,kind:"inside"}}return null},this.addRemoveChars=function(a,b,c){var d=this.getNextFoldTo(a,b),e,f;if(d){e=d.fold;if(d.kind=="inside"&&e.start.column!=b&&e.start.row!=a)throw"Moving characters inside of a fold should never be reached";if(e.start.row==a){f=this.folds;var g=f.indexOf(e);g==0&&(this.start.column+=c);for(g;g<f.length;g++){e=f[g],e.start.column+=c;if(!e.sameRow)return;e.end.column+=c}this.end.column+=c}}},this.split=function(a,b){var c=this.getNextFoldTo(a,b).fold,d=this.folds,f=this.foldData;if(!c)return null;var g=d.indexOf(c),h=d[g-1];this.end.row=h.end.row,this.end.column=h.end.column,d=d.splice(g,d.length-g);var i=new e(f,d);f.splice(f.indexOf(this)+1,0,i);return i},this.merge=function(a){var b=a.folds;for(var c=0;c<b.length;c++)this.addFold(b[c]);var d=this.foldData;d.splice(d.indexOf(a),1)},this.toString=function(){var a=[this.range.toString()+": ["];this.folds.forEach(function(b){a.push(" "+b.toString())}),a.push("]");return a.join("\n")},this.idxToPosition=function(a){var b=0,c;for(var d=0;d<this.folds.length;d++){var c=this.folds[d];a-=c.start.column-b;if(a<0)return{row:c.start.row,column:c.start.column+a};a-=c.placeholder.length;if(a<0)return c.start;b=c.end.column}return{row:this.end.row,column:this.end.column+a}}}).call(e.prototype),b.FoldLine=e}),define("ace/search",["require","exports","module","pilot/lang","pilot/oop","ace/range"],function(a,b,c){var d=a("pilot/lang"),e=a("pilot/oop"),f=a("ace/range").Range,g=function(){this.$options={needle:"",backwards:!1,wrap:!1,caseSensitive:!1,wholeWord:!1,scope:g.ALL,regExp:!1}};g.ALL=1,g.SELECTION=2,function(){this.set=function(a){e.mixin(this.$options,a);return this},this.getOptions=function(){return d.copyObject(this.$options)},this.find=function(a){if(!this.$options.needle)return null;if(this.$options.backwards)var b=this.$backwardMatchIterator(a);else b=this.$forwardMatchIterator(a);var c=null;b.forEach(function(a){c=a;return!0});return c},this.findAll=function(a){if(!this.$options.needle)return[];if(this.$options.backwards)var b=this.$backwardMatchIterator(a);else b=this.$forwardMatchIterator(a);var c=[];b.forEach(function(a){c.push(a)});return c},this.replace=function(a,b){var c=this.$assembleRegExp(),d=c.exec(a);return d&&d[0].length==a.length?this.$options.regExp?a.replace(c,b):b:null},this.$forwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$forwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a){var b=arguments[arguments.length-2];g.push({str:a,offset:e+b});return a});for(var h=0;h<g.length;h++){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$backwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$backwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a,b){g.push({str:a,offset:e+b});return a});for(var h=g.length-1;h>=0;h--){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$rangeFromMatch=function(a,b,c){return new f(a,b,a,b+c)},this.$assembleRegExp=function(){if(this.$options.regExp)var a=this.$options.needle;else a=d.escapeRegExp(this.$options.needle);this.$options.wholeWord&&(a="\\b"+a+"\\b");var b="g";this.$options.caseSensitive||(b+="i");var c=new RegExp(a,b);return c},this.$forwardLineIterator=function(a){function k(e){var f=a.getLine(e);b&&e==c.end.row&&(f=f.substring(0,c.end.column)),j&&e==d.row&&(f=f.substring(0,d.column));return f}var b=this.$options.scope==g.SELECTION,c=a.getSelection().getRange(),d=a.getSelection().getCursor(),e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap,j=!1;return{forEach:function(a){var b=d.row,c=k(b),g=d.column,l=!1;j=!1;while(!a(c,g,b)){if(l)return;b++,g=0;if(b>h)if(i)b=e,g=f,j=!0;else return;b==d.row&&(l=!0),c=k(b)}}}},this.$backwardLineIterator=function(a){var b=this.$options.scope==g.SELECTION,c=a.getSelection().getRange(),d=b?c.end:c.start,e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap;return{forEach:function(g){var j=d.row,k=a.getLine(j).substring(0,d.column),l=0,m=!1,n=!1;while(!g(k,l,j)){if(m)return;j--,l=0;if(j<e)if(i)j=h,n=!0;else return;j==d.row&&(m=!0),k=a.getLine(j),b&&(j==e?l=f:j==h&&(k=k.substring(0,c.end.column))),n&&j==d.row&&(l=d.column)}}}}}.call(g.prototype),b.Search=g}),define("ace/undomanager",["require","exports","module"],function(a,b,c){var d=function(){this.reset()};(function(){this.execute=function(a){var b=a.args[0];this.$doc=a.args[1],this.$undoStack.push(b),this.$redoStack=[]},this.undo=function(a){var b=this.$undoStack.pop(),c=null;b&&(c=this.$doc.undoChanges(b,a),this.$redoStack.push(b));return c},this.redo=function(a){var b=this.$redoStack.pop(),c=null;b&&(c=this.$doc.redoChanges(b,a),this.$undoStack.push(b));return c},this.reset=function(){this.$undoStack=[],this.$redoStack=[]},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0}}).call(d.prototype),b.UndoManager=d}),define("ace/virtual_renderer",["require","exports","module","pilot/oop","pilot/dom","pilot/event","pilot/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/renderloop","pilot/event_emitter","text/ace/css/editor.css"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/dom"),f=a("pilot/event"),g=a("pilot/useragent"),h=a("ace/layer/gutter").Gutter,i=a("ace/layer/marker").Marker,j=a("ace/layer/text").Text,k=a("ace/layer/cursor").Cursor,l=a("ace/scrollbar").ScrollBar,m=a("ace/renderloop").RenderLoop,n=a("pilot/event_emitter").EventEmitter,o=a("text/ace/css/editor.css");e.importCssString(o);var p=function(a,b){this.container=a,e.addCssClass(this.container,"ace_editor"),this.setTheme(b),this.$gutter=e.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=e.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=e.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new h(this.$gutter),this.$markerBack=new i(this.content);var c=this.$textLayer=new j(this.content);this.canvas=c.element,this.$markerFront=new i(this.content),this.characterWidth=c.getCharacterWidth(),this.lineHeight=c.getLineHeight(),this.$cursorLayer=new k(this.content),this.$cursorPadding=8,this.$horizScroll=!0,this.$horizScrollAlwaysVisible=!0,this.scrollBar=new l(a),this.scrollBar.addEventListener("scroll",this.onScroll.bind(this)),this.scrollTop=0,this.cursorPos={row:0,column:0};var d=this;this.$textLayer.addEventListener("changeCharaterSize",function(){d.characterWidth=c.getCharacterWidth(),d.lineHeight=c.getLineHeight(),d.$updatePrintMargin(),d.onResize(!0),d.$loop.schedule(d.CHANGE_FULL)}),f.addListener(this.$gutter,"click",this.$onGutterClick.bind(this)),f.addListener(this.$gutter,"dblclick",this.$onGutterClick.bind(this)),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0},this.$loop=new m(this.$renderChanges.bind(this)),this.$loop.schedule(this.CHANGE_FULL),this.setPadding(4),this.$updatePrintMargin()};(function(){this.showGutter=!0,this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,d.implement(this,n),this.setSession=function(a){this.session=a,this.$cursorLayer.setSession(a),this.$markerBack.setSession(a),this.$markerFront.setSession(a),this.$gutterLayer.setSession(a),this.$textLayer.setSession(a),this.$loop.schedule(this.CHANGE_FULL)},this.updateLines=function(a,b){b===undefined&&(b=Infinity),this.$changedLines?(this.$changedLines.firstRow>a&&(this.$changedLines.firstRow=a),this.$changedLines.lastRow<b&&(this.$changedLines.lastRow=b)):this.$changedLines={firstRow:a,lastRow:b},this.$loop.schedule(this.CHANGE_LINES)},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(){this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.onResize=function(a){var b=this.CHANGE_SIZE,c=e.getInnerHeight(this.container);if(a||this.$size.height!=c)this.$size.height=c,this.scroller.style.height=c+"px",this.scrollBar.setHeight(this.scroller.clientHeight),this.session&&(this.scrollToY(this.getScrollTop()),b=b|this.CHANGE_FULL);var d=e.getInnerWidth(this.container);if(a||this.$size.width!=d){this.$size.width=d;var f=this.showGutter?this.$gutter.offsetWidth:0;this.scroller.style.left=f+"px",this.scroller.style.width=Math.max(0,d-f-this.scrollBar.getWidth())+"px";if(this.session.getUseWrapMode()){var g=this.scroller.clientWidth-this.$padding*2,h=Math.floor(g/this.characterWidth)-1;if(this.session.adjustWrapLimit(h)||a)b=b|this.CHANGE_FULL}}this.$size.scrollerWidth=this.scroller.clientWidth,this.$size.scrollerHeight=this.scroller.clientHeight,this.$loop.schedule(b)},this.$onGutterClick=function(a){var b=f.getDocumentX(a),c=f.getDocumentY(a);this._dispatchEvent("gutter"+a.type,{row:this.screenToTextCoordinates(b,c).row,htmlEvent:a})},this.setShowInvisibles=function(a){this.$textLayer.setShowInvisibles(a)&&this.$loop.schedule(this.CHANGE_TEXT)},this.getShowInvisibles=function(){return this.$textLayer.showInvisibles},this.$showPrintMargin=!0,this.setShowPrintMargin=function(a){this.$showPrintMargin=a,this.$updatePrintMargin()},this.getShowPrintMargin=function(){return this.$showPrintMargin},this.$printMarginColumn=80,this.setPrintMarginColumn=function(a){this.$printMarginColumn=a,this.$updatePrintMargin()},this.getPrintMarginColumn=function(){return this.$printMarginColumn},this.getShowGutter=function(){return this.showGutter},this.setShowGutter=function(a){this.showGutter!==a&&(this.$gutter.style.display=a?"block":"none",this.showGutter=a,this.onResize(!0))},this.$updatePrintMargin=function(){var a;if(!!this.$showPrintMargin||!!this.$printMarginEl){this.$printMarginEl||(a=e.createElement("div"),a.className="ace_print_margin_layer",this.$printMarginEl=e.createElement("div"),this.$printMarginEl.className="ace_print_margin",a.appendChild(this.$printMarginEl),this.content.insertBefore(a,this.$textLayer.element));var b=this.$printMarginEl.style;b.left=this.characterWidth*this.$printMarginColumn+this.$padding*2+"px",b.visibility=this.$showPrintMargin?"visible":"hidden"}},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.content},this.getTextAreaContainer=function(){return this.container},this.moveTextAreaToCursor=function(a){if(!g.isIE){var b=this.$cursorLayer.getPixelPosition();if(!b)return;var c=this.content.getBoundingClientRect(),d=this.layerConfig&&this.layerConfig.offset||0;a.style.left=c.left+b.left+this.$padding+"px",a.style.top=c.top+b.top-this.scrollTop+d+"px"}},this.getFirstVisibleRow=function(){return(this.layerConfig||{}).firstRow||0},this.getFirstFullyVisibleRow=function(){if(!this.layerConfig)return 0;return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){if(!this.layerConfig)return 0;var a=Math.floor((this.layerConfig.height+this.layerConfig.offset)/this.layerConfig.lineHeight);return this.layerConfig.firstRow-1+a},this.getLastVisibleRow=function(){return(this.layerConfig||{}).lastRow||0},this.$padding=null,this.setPadding=function(a){this.$padding=a,this.content.style.padding="0 "+a+"px",this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.getHScrollBarAlwaysVisible=function(){return this.$horizScrollAlwaysVisible},this.setHScrollBarAlwaysVisible=function(a){this.$horizScrollAlwaysVisible!=a&&(this.$horizScrollAlwaysVisible=a,(!this.$horizScrollAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL))},this.onScroll=function(a){this.scrollToY(a.data)},this.$updateScrollBar=function(){this.scrollBar.setInnerHeight(this.layerConfig.maxHeight),this.scrollBar.setScrollTop(this.scrollTop)},this.$renderChanges=function(a){if(!!a&&!!this.session){(!this.layerConfig||a&this.CHANGE_FULL||a&this.CHANGE_SIZE||a&this.CHANGE_TEXT||a&this.CHANGE_LINES||a&this.CHANGE_SCROLL)&&this.$computeLayerConfig();if(a&this.CHANGE_FULL){this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$updateScrollBar();return}if(a&this.CHANGE_SCROLL){a&this.CHANGE_TEXT||a&this.CHANGE_LINES?this.$textLayer.update(this.layerConfig):this.$textLayer.scrollLines(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$updateScrollBar();return}a&this.CHANGE_TEXT?(this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_LINES?(this.$updateLines(),this.$updateScrollBar(),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_GUTTER&&this.showGutter&&this.$gutterLayer.update(this.layerConfig),a&this.CHANGE_CURSOR&&this.$cursorLayer.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(this.layerConfig),a&this.CHANGE_SIZE&&this.$updateScrollBar()}},this.$computeLayerConfig=function(){var a=this.session,b=this.scrollTop%this.lineHeight,c=this.$size.scrollerHeight+this.lineHeight,d=this.$getLongestLine(),e=this.layerConfig?this.layerConfig.width!=d:!0,f=this.$horizScrollAlwaysVisible||this.$size.scrollerWidth-d<0,g=this.$horizScroll!==f;this.$horizScroll=f,g&&(this.scroller.style.overflowX=f?"scroll":"hidden");var h=this.session.getScreenLength()*this.lineHeight;this.scrollTop=Math.max(0,Math.min(this.scrollTop,h-this.$size.scrollerHeight));var i=Math.ceil(c/this.lineHeight)-1,j=Math.max(0,Math.round((this.scrollTop-b)/this.lineHeight)),k=j+i,l,m,n={lineHeight:this.lineHeight};j=a.screenToDocumentRow(j,0);var o=a.getFoldLine(j);o&&(j=o.start.row),l=a.documentToScreenRow(j,0),m=a.getRowHeight(n,j),k=Math.min(a.screenToDocumentRow(k,0),a.getLength()-1),c=this.$size.scrollerHeight+a.getRowHeight(n,k)+m,b=this.scrollTop-l*this.lineHeight,this.layerConfig={width:d,padding:this.$padding,firstRow:j,firstRowScreen:l,lastRow:k,lineHeight:this.lineHeight,characterWidth:this.characterWidth,minHeight:c,maxHeight:h,offset:b,height:this.$size.scrollerHeight},this.$gutterLayer.element.style.marginTop=-b+"px",this.content.style.marginTop=-b+"px",this.content.style.width=d+"px",this.content.style.height=c+"px",this.$desiredScrollLeft&&(this.scrollToX(this.$desiredScrollLeft),this.$desiredScrollLeft=0),g&&this.onResize(!0)},this.$updateLines=function(){var a=this.$changedLines.firstRow,b=this.$changedLines.lastRow;this.$changedLines=null;var c=this.layerConfig;if(c.width!=this.$getLongestLine())return this.$textLayer.update(c);if(!(a>c.lastRow+1)){if(b<c.firstRow)return;if(b===Infinity){this.showGutter&&this.$gutterLayer.update(c),this.$textLayer.update(c);return}this.$textLayer.updateLines(c,a,b)}},this.$getLongestLine=function(){var a=this.session.getScreenWidth()+1;this.$textLayer.showInvisibles&&(a+=1);return Math.max(this.$size.scrollerWidth-this.$padding*2,Math.round(a*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(a,b){this.$gutterLayer.addGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.removeGutterDecoration=function(a,b){this.$gutterLayer.removeGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.setBreakpoints=function(a){this.$gutterLayer.setBreakpoints(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(a){this.$gutterLayer.setAnnotations(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollCursorIntoView=function(){if(this.$size.scrollerHeight!==0){var a=this.$cursorLayer.getPixelPosition(),b=a.left+this.$padding,c=a.top;this.scrollTop>c&&this.scrollToY(c),this.scrollTop+this.$size.scrollerHeight<c+this.lineHeight&&this.scrollToY(c+this.lineHeight-this.$size.scrollerHeight);var d=this.scroller.scrollLeft;d>b&&this.scrollToX(b),d+this.$size.scrollerWidth<b+this.characterWidth&&(b>this.layerConfig.width?this.$desiredScrollLeft=b+2*this.characterWidth:this.scrollToX(Math.round(b+this.characterWidth-this.$size.scrollerWidth)))}},this.getScrollTop=function(){return this.scrollTop},this.getScrollLeft=function(){return this.scroller.scrollLeft},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(a){this.scrollToY(a*this.lineHeight)},this.scrollToLine=function(a,b){var c={lineHeight:this.lineHeight},d=0;for(var e=1;e<a;e++)d+=this.session.getRowHeight(c,e-1);b&&(d-=this.$size.scrollerHeight/2),this.scrollToY(d)},this.scrollToY=function(a){a=Math.max(0,a),this.scrollTop!==a&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=a)},this.scrollToX=function(a){a<=this.$padding&&(a=0),this.scroller.scrollLeft=a},this.scrollBy=function(a,b){b&&this.scrollToY(this.scrollTop+b),a&&this.scrollToX(this.scroller.scrollLeft+a)},this.screenToTextCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=Math.round((a+this.scroller.scrollLeft-c.left-this.$padding-e.getPageScrollLeft())/this.characterWidth),f=Math.floor((b+this.scrollTop-c.top-e.getPageScrollTop())/this.lineHeight);return this.session.screenToDocumentPosition(f,Math.max(d,0))},this.textToScreenCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=this.session.documentToScreenPosition(a,b),e=this.$padding+Math.round(d.column*this.characterWidth),f=d.row*this.lineHeight;return{pageX:c.left+e-this.getScrollLeft(),pageY:c.top+f-this.getScrollTop()}},this.visualizeFocus=function(){e.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){e.removeCssClass(this.container,"ace_focus")},this.showComposition=function(a){this.$composition||(this.$composition=e.createElement("div"),this.$composition.className="ace_composition",this.content.appendChild(this.$composition)),this.$composition.innerHTML="&#160;";var b=this.$cursorLayer.getPixelPosition(),c=this.$composition.style;c.top=b.top+"px",c.left=b.left+this.$padding+"px",c.height=this.lineHeight+"px",this.hideCursor()},this.setCompositionText=function(a){e.setInnerText(this.$composition,a)},this.hideComposition=function(){this.showCursor();if(!!this.$composition){var a=this.$composition.style;a.top="-10000px",a.left="-10000px"}},this.setTheme=function(b){function d(a){c.$theme&&e.removeCssClass(c.container,c.$theme),c.$theme=a?a.cssClass:null,c.$theme&&e.addCssClass(c.container,c.$theme),c.$size&&(c.$size.width=0,c.onResize())}var c=this;this.$themeValue=b,!b||typeof b=="string"?(b=b||"ace/theme/textmate",a([b],function(a){d(a)})):d(b)},this.getTheme=function(){return this.$themeValue},this.setStyle=function(b){e.addCssClass(this.container,b)},this.unsetStyle=function(b){e.removeCssClass(this.container,b)},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(p.prototype),b.VirtualRenderer=p}),define("ace/layer/gutter",["require","exports","module","pilot/dom"],function(a,b,c){var d=a("pilot/dom"),e=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_gutter-layer",a.appendChild(this.element),this.$breakpoints=[],this.$annotations=[],this.$decorations=[]};(function(){this.setSession=function(a){this.session=a},this.addGutterDecoration=function(a,b){this.$decorations[a]||(this.$decorations[a]=""),this.$decorations[a]+=" ace_"+b},this.removeGutterDecoration=function(a,b){this.$decorations[a]=this.$decorations[a].replace(" ace_"+b,"")},this.setBreakpoints=function(a){this.$breakpoints=a.concat()},this.setAnnotations=function(a){this.$annotations=[];for(var b in a)if(a.hasOwnProperty(b)){var c=a[b];if(!c)continue;var d=this.$annotations[b]={text:[]};for(var e=0;e<c.length;e++){var f=c[e];d.text.push(f.text.replace(/"/g,"&quot;").replace(/'/g,"&#8217;").replace(/</,"&lt;"));var g=f.type;g=="error"?d.className="ace_error":g=="warning"&&d.className!="ace_error"?d.className="ace_warning":g=="info"&&!d.className&&(d.className="ace_info")}}},this.update=function(a){this.$config=a;var b={className:"",text:[]},c=[],e=a.firstRow,f=a.lastRow,g=this.session.getNextFold(e),h=g?g.start.row:Infinity;for(;;){e>h&&(e=g.end.row+1,g=this.session.getNextFold(e),h=g?g.start.row:Infinity);if(e>f)break;var i=this.$annotations[e]||b;c.push("<div class='ace_gutter-cell",this.$decorations[e]||"",this.$breakpoints[e]?" ace_breakpoint ":" ",i.className,"' title='",i.text.join("\n"),"' style='height:",a.lineHeight,"px;'>",e+1);var j=this.session.getRowLength(e)-1;while(j--)c.push("</div><div class='ace_gutter-cell' style='height:",a.lineHeight,"px'>&brvbar;</div>");c.push("</div>"),e++}this.element=d.setInnerHtml(this.element,c.join("")),this.element.style.height=a.minHeight+"px"}}).call(e.prototype),b.Gutter=e}),define("ace/layer/marker",["require","exports","module","ace/range","pilot/dom"],function(a,b,c){var d=a("ace/range").Range,e=a("pilot/dom"),f=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_marker-layer",a.appendChild(this.element)};(function(){this.setSession=function(a){this.session=a},this.setMarkers=function(a){this.markers=a},this.update=function(a){var a=a||this.config;if(!!a){this.config=a;var b=[];for(var c in this.markers){var d=this.markers[c],f=d.range.clipRows(a.firstRow,a.lastRow);if(f.isEmpty())continue;f=f.toScreenRange(this.session);if(d.renderer){var g=this.$getTop(f.start.row,a),h=Math.round(f.start.column*a.characterWidth);d.renderer(b,f,h,g,a)}else f.isMultiLine()?d.type=="text"?this.drawTextMarker(b,f,d.clazz,a):this.drawMultiLineMarker(b,f,d.clazz,a):this.drawSingleLineMarker(b,f,d.clazz,a)}this.element=e.setInnerHtml(this.element,b.join(""))}},this.$getTop=function(a,b){return(a-b.firstRowScreen)*b.lineHeight},this.drawTextMarker=function(a,b,c,e){var f=b.start.row,g=new d(f,b.start.column,f,this.session.getScreenLastRowColumn(f));this.drawSingleLineMarker(a,g,c,e,1);var f=b.end.row,g=new d(f,0,f,b.end.column);this.drawSingleLineMarker(a,g,c,e);for(var f=b.start.row+1;f<b.end.row;f++)g.start.row=f,g.end.row=f,g.end.column=this.session.getScreenLastRowColumn(f),this.drawSingleLineMarker(a,g,c,e,1)},this.drawMultiLineMarker=function(a,b,c,d){var e=d.lineHeight,f=Math.round(d.width-b.start.column*d.characterWidth),g=this.$getTop(b.start.row,d),h=Math.round(b.start.column*d.characterWidth);a.push("<div class='",c,"' style='","height:",e,"px;","width:",f,"px;","top:",g,"px;","left:",h,"px;'></div>");var g=this.$getTop(b.end.row,d),f=Math.round(b.end.column*d.characterWidth);a.push("<div class='",c,"' style='","height:",e,"px;","top:",g,"px;","width:",f,"px;'></div>");var e=(b.end.row-b.start.row-1)*d.lineHeight;if(!(e<0)){var g=this.$getTop(b.start.row+1,d);a.push("<div class='",c,"' style='","height:",e,"px;","width:",d.width,"px;","top:",g,"px;'></div>")}},this.drawSingleLineMarker=function(a,b,c,d,e){var f=d.lineHeight,g=Math.round((b.end.column+(e||0)-b.start.column)*d.characterWidth),h=this.$getTop(b.start.row,d),i=Math.round(b.start.column*d.characterWidth);a.push("<div class='",c,"' style='","height:",f,"px;","width:",g,"px;","top:",h,"px;","left:",i,"px;'></div>")}}).call(f.prototype),b.Marker=f}),define("ace/layer/text",["require","exports","module","pilot/oop","pilot/dom","pilot/lang","pilot/useragent","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/dom"),f=a("pilot/lang"),g=a("pilot/useragent"),h=a("pilot/event_emitter").EventEmitter,i=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_text-layer",a.appendChild(this.element),this.$characterSize=this.$measureSizes()||{width:0,height:0},this.$pollSizeChanges()};(function(){d.implement(this,h),this.EOF_CHAR="&para;",this.EOL_CHAR="&not;",this.TAB_CHAR="&rarr;",this.SPACE_CHAR="&middot;",this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var a=this.$measureSizes();a&&(this.$characterSize.width!==a.width||this.$characterSize.height!==a.height)&&(this.$characterSize=a,this._dispatchEvent("changeCharaterSize",{data:a}))},this.$pollSizeChanges=function(){var a=this;this.$pollSizeChangesTimer=setInterval(function(){a.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=function(){var a=1e3;if(!this.$measureNode){var b=this.$measureNode=e.createElement("div"),c=b.style;c.width=c.height="auto",c.left=c.top=-a*40+"px",c.visibility="hidden",c.position="absolute",c.overflow="visible",c.whiteSpace="nowrap",b.innerHTML=f.stringRepeat("Xy",a);if(document.body)document.body.appendChild(b);else{var d=this.element.parentNode;while(!e.hasCssClass(d,"ace_editor"))d=d.parentNode;d.appendChild(b)}}var c=this.$measureNode.style,g=e.computedStyle(this.element);for(var h in this.$fontStyles)c[h]=g[h];var i={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(a*2)};if(i.width==0&&i.height==0)return null;return i},this.setSession=function(a){this.session=a},this.showInvisibles=!1,this.setShowInvisibles=function(a){if(this.showInvisibles==a)return!1;this.showInvisibles=a;return!0},this.$tabStrings=[],this.$computeTabString=function(){var a=this.session.getTabSize(),b=this.$tabStrings=[0];for(var c=1;c<a+1;c++)this.showInvisibles?b.push("<span class='ace_invisible'>"+this.TAB_CHAR+Array(c).join("&#160;")+"</span>"):b.push(Array(c+1).join("&#160;"))},this.updateLines=function(a,b,c){this.$computeTabString(),(this.config.lastRow!=a.lastRow||this.config.firstRow!=a.firstRow)&&this.scrollLines(a),this.config=a;var d=Math.max(b,a.firstRow),f=Math.min(c,a.lastRow),g=this.element.childNodes,h=0;for(var i=a.firstRow;i<d;i++){var j=this.session.getFoldLine(i);if(j){if(j.containsRow(d))break;i=j.end.row}h++}for(var k=d;k<=f;k++){var l=g[h++];if(!l)continue;var m=[],n=this.session.getTokens(k,k);this.$renderLine(m,k,n[0].tokens),l=e.setInnerHtml(l,m.join("")),k=this.session.getRowFoldEnd(k)}},this.scrollLines=function(a){this.$computeTabString();var b=this.config;this.config=a;if(!b||b.lastRow<a.firstRow)return this.update(a);if(a.lastRow<b.firstRow)return this.update(a);var c=this.element;if(b.firstRow<a.firstRow)for(var d=this.session.getFoldedRowCount(b.firstRow,a.firstRow-1);d>0;d--)c.removeChild(c.firstChild);if(b.lastRow>a.lastRow)for(var d=this.session.getFoldedRowCount(a.lastRow+1,b.lastRow);d>0;d--)c.removeChild(c.lastChild);if(a.firstRow<b.firstRow){var e=this.$renderLinesFragment(a,a.firstRow,b.firstRow-1);c.firstChild?c.insertBefore(e,c.firstChild):c.appendChild(e)}if(a.lastRow>b.lastRow){var e=this.$renderLinesFragment(a,b.lastRow+1,a.lastRow);c.appendChild(e)}},this.$renderLinesFragment=function(a,b,c){var d=document.createDocumentFragment(),f=b,g=this.session.getNextFold(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFold(f),h=g?g.start.row:Infinity);if(f>c)break;var i=e.createElement("div");i.className="ace_line";var j=[],k=this.session.getTokens(f,f);k.length==1&&this.$renderLine(j,f,k[0].tokens),i.innerHTML=j.join(""),d.appendChild(i),f++}return d},this.update=function(a){this.$computeTabString(),this.config=a;var b=[],c=a.firstRow,d=a.lastRow,f=c,g=this.session.getNextFold(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFold(f),h=g?g.start.row:Infinity);if(f>d)break;b.push("<div class='ace_line'>");var i=this.session.getTokens(f,f);i.length==1&&this.$renderLine(b,f,i[0].tokens),b.push("</div>"),f++}this.element=e.setInnerHtml(this.element,b.join(""))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(a,b,c,d){var e=this,f=/\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u1100-\u115F]|[\u11A3-\u11A7]|[\u11FA-\u11FF]|[\u2329-\u232A]|[\u2E80-\u2E99]|[\u2E9B-\u2EF3]|[\u2F00-\u2FD5]|[\u2FF0-\u2FFB]|[\u3000-\u303E]|[\u3041-\u3096]|[\u3099-\u30FF]|[\u3105-\u312D]|[\u3131-\u318E]|[\u3190-\u31BA]|[\u31C0-\u31E3]|[\u31F0-\u321E]|[\u3220-\u3247]|[\u3250-\u32FE]|[\u3300-\u4DBF]|[\u4E00-\uA48C]|[\uA490-\uA4C6]|[\uA960-\uA97C]|[\uAC00-\uD7A3]|[\uD7B0-\uD7C6]|[\uD7CB-\uD7FB]|[\uF900-\uFAFF]|[\uFE10-\uFE19]|[\uFE30-\uFE52]|[\uFE54-\uFE66]|[\uFE68-\uFE6B]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g,h=function(a,c,d,f,h){if(a.charCodeAt(0)==32)return Array(a.length+1).join("&#160;");if(a=="\t"){var i=e.session.getScreenTabSize(b+f);b+=i-1;return e.$tabStrings[i]}if(a=="&")return g.isOldGecko?"&":"&amp;";if(a=="<")return"&lt;";if(a.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)){if(e.showInvisibles){var j=Array(a.length+1).join(e.SPACE_CHAR);return"<span class='ace_invisible'>"+j+"</span>"}return"&#160;"}b+=1;return"<span class='ace_cjk' style='width:"+e.config.characterWidth*2+"px'>"+a+"</span>"},i=d.replace(f,h);if(!this.$textToken[c.type]){var j="ace_"+c.type.replace(/\./g," ace_");a.push("<span class='",j,"'>",i,"</span>")}else a.push(i);return d.length},this.$renderLineCore=function(a,b,c,d){function k(b,c){i+=j.$renderToken(a,i,b,c)}var e=0,f=0,g,h=this.config.characterWidth,i=0,j=this;!d||d.length==0?g=Number.MAX_VALUE:g=d[0],a.push("<div style='height:",this.config.lineHeight,"px","'>");for(var l=0;l<c.length;l++){var m=c[l],n=m.value;if(e+n.length<g)k(m,n),e+=n.length;else{while(e+n.length>=g)k(m,n.substring(0,g-e)),n=n.substring(g-e),e=g,a.push("</div>","<div style='height:",this.config.lineHeight,"px","'>"),f++,i=0,g=d[f]||Number.MAX_VALUE;n.length!=0&&(e+=n.length,k(m,n))}}this.showInvisibles&&(b!==this.session.getLength()-1?a.push("<span class='ace_invisible'>"+this.EOL_CHAR+"</span>"):a.push("<span class='ace_invisible'>"+this.EOF_CHAR+"</span>")),a.push("</div>")},this.$renderLine=function(a,b,c){if(!this.session.isRowFolded(b)){var d=this.session.getRowSplitData(b);this.$renderLineCore(a,b,c,d)}else this.$renderFoldLine(a,b,c)},this.$renderFoldLine=function(a,b,c){function g(a,b,c){var d=0,e=0;while(e+a[d].value.length<b){e+=a[d].value.length,d++;if(d==a.length)return}if(e!=b){var g=a[d].value.substring(b-e);g.length>c-b&&(g=g.substring(0,c-b)),f.push({type:a[d].type,value:g}),e=b+g.length,d+=1}while(e<c){var g=a[d].value;g.length+e>c&&(g=g.substring(0,c-e)),f.push({type:a[d].type,value:g}),e+=g.length,d+=1}}var d=this.session,e=d.getFoldLine(b),f=[];e.walk(function(a,b,d,e,h){a?f.push({type:"fold",value:a}):(h&&(c=this.session.getTokens(b,b)[0].tokens),c.length!=0&&g(c,e,d))}.bind(this),e.end.row,this.session.getLine(e.end.row).length);var h=this.session.$useWrapMode?this.session.$wrapData[b]:null;this.$renderLineCore(a,b,f,h)},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer)}}).call(i.prototype),b.Text=i}),define("ace/layer/cursor",["require","exports","module","pilot/dom"],function(a,b,c){var d=a("pilot/dom"),e=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_cursor-layer",a.appendChild(this.element),this.cursor=d.createElement("div"),this.cursor.className="ace_cursor ace_hidden",this.element.appendChild(this.cursor),this.isVisible=!1};(function(){this.setSession=function(a){this.session=a},this.hideCursor=function(){this.isVisible=!1,d.addCssClass(this.cursor,"ace_hidden"),clearInterval(this.blinkId)},this.showCursor=function(){this.isVisible=!0,d.removeCssClass(this.cursor,"ace_hidden"),this.cursor.style.visibility="visible",this.restartTimer()},this.restartTimer=function(){clearInterval(this.blinkId);if(!!this.isVisible){var a=this.cursor;this.blinkId=setInterval(function(){a.style.visibility="hidden",setTimeout(function(){a.style.visibility="visible"},400)},1e3)}},this.getPixelPosition=function(a){if(!this.config||!this.session)return{left:0,top:0};var b=this.session.selection.getCursor(),c=this.session.documentToScreenPosition(b),d=Math.round(c.column*this.config.characterWidth),e=(c.row-(a?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:d,top:e}},this.update=function(a){this.config=a,this.pixelPos=this.getPixelPosition(!0),this.cursor.style.left=this.pixelPos.left+"px",this.cursor.style.top=this.pixelPos.top+"px",this.cursor.style.width=a.characterWidth+"px",this.cursor.style.height=a.lineHeight+"px";var b=this.session.getOverwrite();b!=this.overwrite&&(this.overwrite=b,b?d.addCssClass(this.cursor,"ace_overwrite"):d.removeCssClass(this.cursor,"ace_overwrite")),this.restartTimer()},this.destroy=function(){clearInterval(this.blinkId)}}).call(e.prototype),b.Cursor=e}),define("ace/scrollbar",["require","exports","module","pilot/oop","pilot/dom","pilot/event","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/dom"),f=a("pilot/event"),g=a("pilot/event_emitter").EventEmitter,h=function(a){this.element=e.createElement("div"),this.element.className="ace_sb",this.inner=e.createElement("div"),this.element.appendChild(this.inner),a.appendChild(this.element),this.width=e.scrollbarWidth(),this.element.style.width=this.width+"px",f.addListener(this.element,"scroll",this.onScroll.bind(this))};(function(){d.implement(this,g),this.onScroll=function(){this._dispatchEvent("scroll",{data:this.element.scrollTop})},this.getWidth=function(){return this.width},this.setHeight=function(a){this.element.style.height=a+"px"},this.setInnerHeight=function(a){this.inner.style.height=a+"px"},this.setScrollTop=function(a){this.element.scrollTop=a}}).call(h.prototype),b.ScrollBar=h}),define("ace/renderloop",["require","exports","module","pilot/event"],function(a,b,c){var d=a("pilot/event"),e=function(a){this.onRender=a,this.pending=!1,this.changes=0};(function(){this.schedule=function(a){this.changes=this.changes|a;if(!this.pending){this.pending=!0;var b=this;this.setTimeoutZero(function(){b.pending=!1;var a=b.changes;b.changes=0,b.onRender(a)})}},this.setTimeoutZero=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame,this.setTimeoutZero?this.setTimeoutZero=this.setTimeoutZero.bind(window):window.postMessage?(this.messageName="zero-timeout-message",this.setTimeoutZero=function(a){if(!this.attached){var b=this;d.addListener(window,"message",function(a){b.callback&&a.data==b.messageName&&(d.stopPropagation(a),b.callback())}),this.attached=!0}this.callback=a,window.postMessage(this.messageName,"*")}):this.setTimeoutZero=function(a){setTimeout(a,0)}}).call(e.prototype),b.RenderLoop=e}),define("ace/theme/textmate",["require","exports","module","pilot/dom"],function(a,b,c){var d=a("pilot/dom"),e=".ace-tm .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-tm .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-tm .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-tm .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-tm .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-tm .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-tm .ace_text-layer {\n cursor: text;\n}\n\n.ace-tm .ace_cursor {\n border-left: 2px solid black;\n}\n\n.ace-tm .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid black;\n}\n \n.ace-tm .ace_line .ace_invisible {\n color: rgb(191, 191, 191);\n}\n\n.ace-tm .ace_line .ace_keyword {\n color: blue;\n}\n\n.ace-tm .ace_line .ace_constant.ace_buildin {\n color: rgb(88, 72, 246);\n}\n\n.ace-tm .ace_line .ace_constant.ace_language {\n color: rgb(88, 92, 246);\n}\n\n.ace-tm .ace_line .ace_constant.ace_library {\n color: rgb(6, 150, 14);\n}\n\n.ace-tm .ace_line .ace_invalid {\n background-color: rgb(153, 0, 0);\n color: white;\n}\n\n.ace-tm .ace_line .ace_fold {\n background-color: #E4E4E4;\n border-radius: 3px;\n}\n\n.ace-tm .ace_line .ace_support.ace_function {\n color: rgb(60, 76, 114);\n}\n\n.ace-tm .ace_line .ace_support.ace_constant {\n color: rgb(6, 150, 14);\n}\n\n.ace-tm .ace_line .ace_support.ace_type,\n.ace-tm .ace_line .ace_support.ace_class {\n color: rgb(109, 121, 222);\n}\n\n.ace-tm .ace_line .ace_keyword.ace_operator {\n color: rgb(104, 118, 135);\n}\n\n.ace-tm .ace_line .ace_string {\n color: rgb(3, 106, 7);\n}\n\n.ace-tm .ace_line .ace_comment {\n color: rgb(76, 136, 107);\n}\n\n.ace-tm .ace_line .ace_comment.ace_doc {\n color: rgb(0, 102, 255);\n}\n\n.ace-tm .ace_line .ace_comment.ace_doc.ace_tag {\n color: rgb(128, 159, 191);\n}\n\n.ace-tm .ace_line .ace_constant.ace_numeric {\n color: rgb(0, 0, 205);\n}\n\n.ace-tm .ace_line .ace_variable {\n color: rgb(49, 132, 149);\n}\n\n.ace-tm .ace_line .ace_xml_pe {\n color: rgb(104, 104, 91);\n}\n\n.ace-tm .ace_marker-layer .ace_selection {\n background: rgb(181, 213, 255);\n}\n\n.ace-tm .ace_marker-layer .ace_step {\n background: rgb(252, 255, 0);\n}\n\n.ace-tm .ace_marker-layer .ace_stack {\n background: rgb(164, 229, 101);\n}\n\n.ace-tm .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgb(192, 192, 192);\n}\n\n.ace-tm .ace_marker-layer .ace_active_line {\n background: rgb(232, 242, 254);\n}\n\n.ace-tm .ace_marker-layer .ace_selected_word {\n background: rgb(250, 250, 255);\n border: 1px solid rgb(200, 200, 250);\n}\n\n.ace-tm .ace_string.ace_regex {\n color: rgb(255, 0, 0)\n}";d.importCssString(e),b.cssClass="ace-tm"}),define("pilot/environment",["require","exports","module","pilot/settings"],function(a,b,c){function e(){return{settings:d}}var d=a("pilot/settings").settings;b.create=e}),define("text/ace/css/editor.css",[],'.ace_editor { position: absolute; overflow: hidden; font-family: "Menlo", "Monaco", "Courier New", monospace; font-size: 12px;}.ace_scroller { position: absolute; overflow-x: scroll; overflow-y: hidden;}.ace_content { position: absolute; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;}.ace_composition { position: absolute; background: #555; color: #DDD; z-index: 4;}.ace_gutter { position: absolute; overflow-x: hidden; overflow-y: hidden; height: 100%;}.ace_gutter-cell.ace_error { background-image: url("data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B"); background-repeat: no-repeat; background-position: 4px center;}.ace_gutter-cell.ace_warning { background-image: url("data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03\'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B"); background-repeat: no-repeat; background-position: 4px center;}.ace_editor .ace_sb { position: absolute; overflow-x: hidden; overflow-y: scroll; right: 0;}.ace_editor .ace_sb div { position: absolute; width: 1px; left: 0;}.ace_editor .ace_print_margin_layer { z-index: 0; position: absolute; overflow: hidden; margin: 0; left: 0; height: 100%; width: 100%;}.ace_editor .ace_print_margin { position: absolute; height: 100%;}.ace_editor textarea { position: fixed; z-index: -1; width: 10px; height: 30px; opacity: 0; background: transparent; appearance: none; border: none; resize: none; outline: none; overflow: hidden;}.ace_layer { z-index: 1; position: absolute; overflow: hidden; white-space: nowrap; height: 100%; width: 100%;}.ace_text-layer { font-family: Monaco, "Courier New", monospace; color: black;}.ace_cjk { display: inline-block; text-align: center;}.ace_cursor-layer { z-index: 4; cursor: text; pointer-events: none;}.ace_cursor { z-index: 4; position: absolute;}.ace_cursor.ace_hidden { opacity: 0.2;}.ace_line { white-space: nowrap;}.ace_marker-layer { cursor: text; pointer-events: none;}.ace_marker-layer .ace_step { position: absolute; z-index: 3;}.ace_marker-layer .ace_selection { position: absolute; z-index: 4;}.ace_marker-layer .ace_bracket { position: absolute; z-index: 5;}.ace_marker-layer .ace_active_line { position: absolute; z-index: 2;}.ace_marker-layer .ace_selected_word { position: absolute; z-index: 6; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;}.ace_line .ace_fold { cursor: pointer;}.ace_dragging .ace_marker-layer, .ace_dragging .ace_text-layer { cursor: move;}'),define("text/styles.css",[],"html { height: 100%; width: 100%; overflow: hidden;}body { overflow: hidden; margin: 0; padding: 0; height: 100%; width: 100%; font-family: Arial, Helvetica, sans-serif, Tahoma, Verdana, sans-serif; font-size: 12px; background: rgb(14, 98, 165); color: white;}#logo { padding: 15px; margin-left: 65px;}#editor { position: absolute; top: 0px; left: 280px; bottom: 0px; right: 0px; background: white;}#controls { padding: 5px;}#controls td { text-align: right;}#controls td + td { text-align: left;}#cockpitInput { position: absolute; left: 280px; right: 0px; bottom: 0; border: none; outline: none; font-family: consolas, courier, monospace; font-size: 120%;}#cockpitOutput { padding: 10px; margin: 0 15px; border: 1px solid #AAA; -moz-border-radius-topleft: 10px; -moz-border-radius-topright: 10px; border-top-left-radius: 4px; border-top-right-radius: 4px; background: #DDD; color: #000;}"),require(["ace/ace"],function(a){window.ace=a}) \ No newline at end of file
diff --git a/studio/static/js/ace/cockpit-uncompressed.js b/studio/static/js/ace/cockpit-uncompressed.js
new file mode 100644
index 0000000..2b75f23
--- /dev/null
+++ b/studio/static/js/ace/cockpit-uncompressed.js
@@ -0,0 +1,2504 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Kevin Dangoor (kdangoor@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('cockpit/index', ['require', 'exports', 'module' , 'pilot/index', 'cockpit/cli', 'cockpit/ui/settings', 'cockpit/ui/cli_view', 'cockpit/commands/basic'], function(require, exports, module) {
+
+
+exports.startup = function(data, reason) {
+ require('pilot/index');
+ require('cockpit/cli').startup(data, reason);
+ // window.testCli = require('cockpit/test/testCli');
+
+ require('cockpit/ui/settings').startup(data, reason);
+ require('cockpit/ui/cli_view').startup(data, reason);
+ require('cockpit/commands/basic').startup(data, reason);
+};
+
+/*
+exports.shutdown(data, reason) {
+};
+*/
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('cockpit/cli', ['require', 'exports', 'module' , 'pilot/console', 'pilot/lang', 'pilot/oop', 'pilot/event_emitter', 'pilot/types', 'pilot/canon'], function(require, exports, module) {
+
+
+var console = require('pilot/console');
+var lang = require('pilot/lang');
+var oop = require('pilot/oop');
+var EventEmitter = require('pilot/event_emitter').EventEmitter;
+
+//var keyboard = require('keyboard/keyboard');
+var types = require('pilot/types');
+var Status = require('pilot/types').Status;
+var Conversion = require('pilot/types').Conversion;
+var canon = require('pilot/canon');
+
+/**
+ * Normally type upgrade is done when the owning command is registered, but
+ * out commandParam isn't part of a command, so it misses out.
+ */
+exports.startup = function(data, reason) {
+ canon.upgradeType('command', commandParam);
+};
+
+/**
+ * The information required to tell the user there is a problem with their
+ * input.
+ * TODO: There a several places where {start,end} crop up. Perhaps we should
+ * have a Cursor object.
+ */
+function Hint(status, message, start, end, predictions) {
+ this.status = status;
+ this.message = message;
+
+ if (typeof start === 'number') {
+ this.start = start;
+ this.end = end;
+ this.predictions = predictions;
+ }
+ else {
+ var arg = start;
+ this.start = arg.start;
+ this.end = arg.end;
+ this.predictions = arg.predictions;
+ }
+}
+Hint.prototype = {
+};
+/**
+ * Loop over the array of hints finding the one we should display.
+ * @param hints array of hints
+ */
+Hint.sort = function(hints, cursor) {
+ // Calculate 'distance from cursor'
+ if (cursor !== undefined) {
+ hints.forEach(function(hint) {
+ if (hint.start === Argument.AT_CURSOR) {
+ hint.distance = 0;
+ }
+ else if (cursor < hint.start) {
+ hint.distance = hint.start - cursor;
+ }
+ else if (cursor > hint.end) {
+ hint.distance = cursor - hint.end;
+ }
+ else {
+ hint.distance = 0;
+ }
+ }, this);
+ }
+ // Sort
+ hints.sort(function(hint1, hint2) {
+ // Compare first based on distance from cursor
+ if (cursor !== undefined) {
+ var diff = hint1.distance - hint2.distance;
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ // otherwise go with hint severity
+ return hint2.status - hint1.status;
+ });
+ // tidy-up
+ if (cursor !== undefined) {
+ hints.forEach(function(hint) {
+ delete hint.distance;
+ }, this);
+ }
+ return hints;
+};
+exports.Hint = Hint;
+
+/**
+ * A Hint that arose as a result of a Conversion
+ */
+function ConversionHint(conversion, arg) {
+ this.status = conversion.status;
+ this.message = conversion.message;
+ if (arg) {
+ this.start = arg.start;
+ this.end = arg.end;
+ }
+ else {
+ this.start = 0;
+ this.end = 0;
+ }
+ this.predictions = conversion.predictions;
+};
+oop.inherits(ConversionHint, Hint);
+
+
+/**
+ * We record where in the input string an argument comes so we can report errors
+ * against those string positions.
+ * We publish a 'change' event when-ever the text changes
+ * @param emitter Arguments use something else to pass on change events.
+ * Currently this will be the creating Requisition. This prevents dependency
+ * loops and prevents us from needing to merge listener lists.
+ * @param text The string (trimmed) that contains the argument
+ * @param start The position of the text in the original input string
+ * @param end See start
+ * @param prefix Knowledge of quotation marks and whitespace used prior to the
+ * text in the input string allows us to re-generate the original input from
+ * the arguments.
+ * @param suffix Any quotation marks and whitespace used after the text.
+ * Whitespace is normally placed in the prefix to the succeeding argument, but
+ * can be used here when this is the last argument.
+ * @constructor
+ */
+function Argument(emitter, text, start, end, prefix, suffix) {
+ this.emitter = emitter;
+ this.setText(text);
+ this.start = start;
+ this.end = end;
+ this.prefix = prefix;
+ this.suffix = suffix;
+}
+Argument.prototype = {
+ /**
+ * Return the result of merging these arguments.
+ * TODO: What happens when we're merging arguments for the single string
+ * case and some of the arguments are in quotation marks?
+ */
+ merge: function(following) {
+ if (following.emitter != this.emitter) {
+ throw new Error('Can\'t merge Arguments from different EventEmitters');
+ }
+ return new Argument(
+ this.emitter,
+ this.text + this.suffix + following.prefix + following.text,
+ this.start, following.end,
+ this.prefix,
+ following.suffix);
+ },
+
+ /**
+ * See notes on events in Assignment. We might need to hook changes here
+ * into a CliRequisition so they appear of the command line.
+ */
+ setText: function(text) {
+ if (text == null) {
+ throw new Error('Illegal text for Argument: ' + text);
+ }
+ var ev = { argument: this, oldText: this.text, text: text };
+ this.text = text;
+ this.emitter._dispatchEvent('argumentChange', ev);
+ },
+
+ /**
+ * Helper when we're putting arguments back together
+ */
+ toString: function() {
+ // TODO: There is a bug here - we should re-escape escaped characters
+ // But can we do that reliably?
+ return this.prefix + this.text + this.suffix;
+ }
+};
+
+/**
+ * Merge an array of arguments into a single argument.
+ * All Arguments in the array are expected to have the same emitter
+ */
+Argument.merge = function(argArray, start, end) {
+ start = (start === undefined) ? 0 : start;
+ end = (end === undefined) ? argArray.length : end;
+
+ var joined;
+ for (var i = start; i < end; i++) {
+ var arg = argArray[i];
+ if (!joined) {
+ joined = arg;
+ }
+ else {
+ joined = joined.merge(arg);
+ }
+ }
+ return joined;
+};
+
+/**
+ * We sometimes need a way to say 'this error occurs where ever the cursor is'
+ */
+Argument.AT_CURSOR = -1;
+
+
+/**
+ * A link between a parameter and the data for that parameter.
+ * The data for the parameter is available as in the preferred type and as
+ * an Argument for the CLI.
+ * <p>We also record validity information where applicable.
+ * <p>For values, null and undefined have distinct definitions. null means
+ * that a value has been provided, undefined means that it has not.
+ * Thus, null is a valid default value, and common because it identifies an
+ * parameter that is optional. undefined means there is no value from
+ * the command line.
+ * @constructor
+ */
+function Assignment(param, requisition) {
+ this.param = param;
+ this.requisition = requisition;
+ this.setValue(param.defaultValue);
+};
+Assignment.prototype = {
+ /**
+ * The parameter that we are assigning to
+ * @readonly
+ */
+ param: undefined,
+
+ /**
+ * Report on the status of the last parse() conversion.
+ * @see types.Conversion
+ */
+ conversion: undefined,
+
+ /**
+ * The current value in a type as specified by param.type
+ */
+ value: undefined,
+
+ /**
+ * The string version of the current value
+ */
+ arg: undefined,
+
+ /**
+ * The current value (i.e. not the string representation)
+ * Use setValue() to mutate
+ */
+ value: undefined,
+ setValue: function(value) {
+ if (this.value === value) {
+ return;
+ }
+
+ if (value === undefined) {
+ this.value = this.param.defaultValue;
+ this.conversion = this.param.getDefault ?
+ this.param.getDefault() :
+ this.param.type.getDefault();
+ this.arg = undefined;
+ } else {
+ this.value = value;
+ this.conversion = undefined;
+ var text = (value == null) ? '' : this.param.type.stringify(value);
+ if (this.arg) {
+ this.arg.setText(text);
+ }
+ }
+
+ this.requisition._assignmentChanged(this);
+ },
+
+ /**
+ * The textual representation of the current value
+ * Use setValue() to mutate
+ */
+ arg: undefined,
+ setArgument: function(arg) {
+ if (this.arg === arg) {
+ return;
+ }
+ this.arg = arg;
+ this.conversion = this.param.type.parse(arg.text);
+ this.conversion.arg = arg; // TODO: make this automatic?
+ this.value = this.conversion.value;
+ this.requisition._assignmentChanged(this);
+ },
+
+ /**
+ * Create a list of the hints associated with this parameter assignment.
+ * Generally there will be only one hint generated because we're currently
+ * only displaying one hint at a time, ordering by distance from cursor
+ * and severity. Since distance from cursor will be the same for all hints
+ * from this assignment all but the most severe will ever be used. It might
+ * make sense with more experience to alter this to function to be getHint()
+ */
+ getHint: function() {
+ // Allow the parameter to provide documentation
+ if (this.param.getCustomHint && this.value && this.arg) {
+ var hint = this.param.getCustomHint(this.value, this.arg);
+ if (hint) {
+ return hint;
+ }
+ }
+
+ // If there is no argument, use the cursor position
+ var message = '<strong>' + this.param.name + '</strong>: ';
+ if (this.param.description) {
+ // TODO: This should be a short description - do we need to trim?
+ message += this.param.description.trim();
+
+ // Ensure the help text ends with '. '
+ if (message.charAt(message.length - 1) !== '.') {
+ message += '.';
+ }
+ if (message.charAt(message.length - 1) !== ' ') {
+ message += ' ';
+ }
+ }
+ var status = Status.VALID;
+ var start = this.arg ? this.arg.start : Argument.AT_CURSOR;
+ var end = this.arg ? this.arg.end : Argument.AT_CURSOR;
+ var predictions;
+
+ // Non-valid conversions will have useful information to pass on
+ if (this.conversion) {
+ status = this.conversion.status;
+ if (this.conversion.message) {
+ message += this.conversion.message;
+ }
+ predictions = this.conversion.predictions;
+ }
+
+ // Hint if the param is required, but not provided
+ var argProvided = this.arg && this.arg.text !== '';
+ var dataProvided = this.value !== undefined || argProvided;
+ if (this.param.defaultValue === undefined && !dataProvided) {
+ status = Status.INVALID;
+ message += '<strong>Required<\strong>';
+ }
+
+ return new Hint(status, message, start, end, predictions);
+ },
+
+ /**
+ * Basically <tt>setValue(conversion.predictions[0])</tt> done in a safe
+ * way.
+ */
+ complete: function() {
+ if (this.conversion && this.conversion.predictions &&
+ this.conversion.predictions.length > 0) {
+ this.setValue(this.conversion.predictions[0]);
+ }
+ },
+
+ /**
+ * If the cursor is at 'position', do we have sufficient data to start
+ * displaying the next hint. This is both complex and important.
+ * For example, if the user has just typed:<ul>
+ * <li>'set tabstop ' then they clearly want to know about the valid
+ * values for the tabstop setting, so the hint is based on the next
+ * parameter.
+ * <li>'set tabstop' (without trailing space) - they will probably still
+ * want to know about the valid values for the tabstop setting because
+ * there is no confusion about the setting in question.
+ * <li>'set tabsto' they've not finished typing a setting name so the hint
+ * should be based on the current parameter.
+ * <li>'set tabstop' (when there is an additional tabstopstyle setting) we
+ * can't make assumptions about the setting - we're not finished.
+ * </ul>
+ * <p>Note that the input for 2 and 4 is identical, only the configuration
+ * has changed, so hint display is environmental.
+ *
+ * <p>This function works out if the cursor is before the end of this
+ * assignment (assuming that we've asked the same thing of the previous
+ * assignment) and then attempts to work out if we should use the hint from
+ * the next assignment even though technically the cursor is still inside
+ * this one due to the rules above.
+ */
+ isPositionCaptured: function(position) {
+ if (!this.arg) {
+ return false;
+ }
+
+ // Note we don't check if position >= this.arg.start because that's
+ // implied by the fact that we're asking the assignments in turn, and
+ // we want to avoid thing falling between the cracks, but we do need
+ // to check that the argument does have a position
+ if (this.arg.start === -1) {
+ return false;
+ }
+
+ // We're clearly done if the position is past the end of the text
+ if (position > this.arg.end) {
+ return false;
+ }
+
+ // If we're AT the end, the position is captured if either the status
+ // is not valid or if there are other valid options including current
+ if (position === this.arg.end) {
+ return this.conversion.status !== Status.VALID ||
+ this.conversion.predictions.length !== 0;
+ }
+
+ // Otherwise we're clearly inside
+ return true;
+ },
+
+ /**
+ * Replace the current value with the lower value if such a concept
+ * exists.
+ */
+ decrement: function() {
+ var replacement = this.param.type.decrement(this.value);
+ if (replacement != null) {
+ this.setValue(replacement);
+ }
+ },
+
+ /**
+ * Replace the current value with the higher value if such a concept
+ * exists.
+ */
+ increment: function() {
+ var replacement = this.param.type.increment(this.value);
+ if (replacement != null) {
+ this.setValue(replacement);
+ }
+ },
+
+ /**
+ * Helper when we're rebuilding command lines.
+ */
+ toString: function() {
+ return this.arg ? this.arg.toString() : '';
+ }
+};
+exports.Assignment = Assignment;
+
+
+/**
+ * This is a special parameter to reflect the command itself.
+ */
+var commandParam = {
+ name: '__command',
+ type: 'command',
+ description: 'The command to execute',
+
+ /**
+ * Provide some documentation for a command.
+ */
+ getCustomHint: function(command, arg) {
+ var docs = [];
+ docs.push('<strong><tt> &gt; ');
+ docs.push(command.name);
+ if (command.params && command.params.length > 0) {
+ command.params.forEach(function(param) {
+ if (param.defaultValue === undefined) {
+ docs.push(' [' + param.name + ']');
+ }
+ else {
+ docs.push(' <em>[' + param.name + ']</em>');
+ }
+ }, this);
+ }
+ docs.push('</tt></strong><br/>');
+
+ docs.push(command.description ? command.description : '(No description)');
+ docs.push('<br/>');
+
+ if (command.params && command.params.length > 0) {
+ docs.push('<ul>');
+ command.params.forEach(function(param) {
+ docs.push('<li>');
+ docs.push('<strong><tt>' + param.name + '</tt></strong>: ');
+ docs.push(param.description ? param.description : '(No description)');
+ if (param.defaultValue === undefined) {
+ docs.push(' <em>[Required]</em>');
+ }
+ else if (param.defaultValue === null) {
+ docs.push(' <em>[Optional]</em>');
+ }
+ else {
+ docs.push(' <em>[Default: ' + param.defaultValue + ']</em>');
+ }
+ docs.push('</li>');
+ }, this);
+ docs.push('</ul>');
+ }
+
+ return new Hint(Status.VALID, docs.join(''), arg);
+ }
+};
+
+/**
+ * A Requisition collects the information needed to execute a command.
+ * There is no point in a requisition for parameter-less commands because there
+ * is no information to collect. A Requisition is a collection of assignments
+ * of values to parameters, each handled by an instance of Assignment.
+ * CliRequisition adds functions for parsing input from a command line to this
+ * class.
+ * <h2>Events<h2>
+ * We publish the following events:<ul>
+ * <li>argumentChange: The text of some argument has changed. It is likely that
+ * any UI component displaying this argument will need to be updated. (Note that
+ * this event is actually published by the Argument itself - see the docs for
+ * Argument for more details)
+ * The event object looks like: { argument: A, oldText: B, text: B }
+ * <li>commandChange: The command has changed. It is likely that a UI
+ * structure will need updating to match the parameters of the new command.
+ * The event object looks like { command: A }
+ * @constructor
+ */
+function Requisition(env) {
+ this.env = env;
+ this.commandAssignment = new Assignment(commandParam, this);
+}
+
+Requisition.prototype = {
+ /**
+ * The command that we are about to execute.
+ * @see setCommandConversion()
+ * @readonly
+ */
+ commandAssignment: undefined,
+
+ /**
+ * The count of assignments. Excludes the commandAssignment
+ * @readonly
+ */
+ assignmentCount: undefined,
+
+ /**
+ * The object that stores of Assignment objects that we are filling out.
+ * The Assignment objects are stored under their param.name for named
+ * lookup. Note: We make use of the property of Javascript objects that
+ * they are not just hashmaps, but linked-list hashmaps which iterate in
+ * insertion order.
+ * Excludes the commandAssignment.
+ */
+ _assignments: undefined,
+
+ /**
+ * The store of hints generated by the assignments. We are trying to prevent
+ * the UI from needing to access this in broad form, but instead use
+ * methods that query part of this structure.
+ */
+ _hints: undefined,
+
+ /**
+ * When the command changes, we need to keep a bunch of stuff in sync
+ */
+ _assignmentChanged: function(assignment) {
+ // This is all about re-creating Assignments
+ if (assignment.param.name !== '__command') {
+ return;
+ }
+
+ this._assignments = {};
+
+ if (assignment.value) {
+ assignment.value.params.forEach(function(param) {
+ this._assignments[param.name] = new Assignment(param, this);
+ }, this);
+ }
+
+ this.assignmentCount = Object.keys(this._assignments).length;
+ this._dispatchEvent('commandChange', { command: assignment.value });
+ },
+
+ /**
+ * Assignments have an order, so we need to store them in an array.
+ * But we also need named access ...
+ */
+ getAssignment: function(nameOrNumber) {
+ var name = (typeof nameOrNumber === 'string') ?
+ nameOrNumber :
+ Object.keys(this._assignments)[nameOrNumber];
+ return this._assignments[name];
+ },
+
+ /**
+ * Where parameter name == assignment names - they are the same.
+ */
+ getParameterNames: function() {
+ return Object.keys(this._assignments);
+ },
+
+ /**
+ * A *shallow* clone of the assignments.
+ * This is useful for systems that wish to go over all the assignments
+ * finding values one way or another and wish to trim an array as they go.
+ */
+ cloneAssignments: function() {
+ return Object.keys(this._assignments).map(function(name) {
+ return this._assignments[name];
+ }, this);
+ },
+
+ /**
+ * Collect the statuses from the Assignments.
+ * The hints returned are sorted by severity
+ */
+ _updateHints: function() {
+ // TODO: work out when to clear this out for the plain Requisition case
+ // this._hints = [];
+ this.getAssignments(true).forEach(function(assignment) {
+ this._hints.push(assignment.getHint());
+ }, this);
+ Hint.sort(this._hints);
+
+ // We would like to put some initial help here, but for anyone but
+ // a complete novice a 'type help' message is very annoying, so we
+ // need to find a way to only display this message once, or for
+ // until the user click a 'close' button or similar
+ // TODO: Add special case for '' input
+ },
+
+ /**
+ * Returns the most severe status
+ */
+ getWorstHint: function() {
+ return this._hints[0];
+ },
+
+ /**
+ * Extract the names and values of all the assignments, and return as
+ * an object.
+ */
+ getArgsObject: function() {
+ var args = {};
+ this.getAssignments().forEach(function(assignment) {
+ args[assignment.param.name] = assignment.value;
+ }, this);
+ return args;
+ },
+
+ /**
+ * Access the arguments as an array.
+ * @param includeCommand By default only the parameter arguments are
+ * returned unless (includeCommand === true), in which case the list is
+ * prepended with commandAssignment.arg
+ */
+ getAssignments: function(includeCommand) {
+ var args = [];
+ if (includeCommand === true) {
+ args.push(this.commandAssignment);
+ }
+ Object.keys(this._assignments).forEach(function(name) {
+ args.push(this.getAssignment(name));
+ }, this);
+ return args;
+ },
+
+ /**
+ * Reset all the assignments to their default values
+ */
+ setDefaultValues: function() {
+ this.getAssignments().forEach(function(assignment) {
+ assignment.setValue(undefined);
+ }, this);
+ },
+
+ /**
+ * Helper to call canon.exec
+ */
+ exec: function() {
+ canon.exec(this.commandAssignment.value,
+ this.env,
+ "cli",
+ this.getArgsObject(),
+ this.toCanonicalString());
+ },
+
+ /**
+ * Extract a canonical version of the input
+ */
+ toCanonicalString: function() {
+ var line = [];
+ line.push(this.commandAssignment.value.name);
+ Object.keys(this._assignments).forEach(function(name) {
+ var assignment = this._assignments[name];
+ var type = assignment.param.type;
+ // TODO: This will cause problems if there is a non-default value
+ // after a default value. Also we need to decide when to use
+ // named parameters in place of positional params. Both can wait.
+ if (assignment.value !== assignment.param.defaultValue) {
+ line.push(' ');
+ line.push(type.stringify(assignment.value));
+ }
+ }, this);
+ return line.join('');
+ }
+};
+oop.implement(Requisition.prototype, EventEmitter);
+exports.Requisition = Requisition;
+
+
+/**
+ * An object used during command line parsing to hold the various intermediate
+ * data steps.
+ * <p>The 'output' of the update is held in 2 objects: input.hints which is an
+ * array of hints to display to the user. In the future this will become a
+ * single value.
+ * <p>The other output value is input.requisition which gives access to an
+ * args object for use in executing the final command.
+ *
+ * <p>The majority of the functions in this class are called in sequence by the
+ * constructor. Their task is to add to <tt>hints</tt> fill out the requisition.
+ * <p>The general sequence is:<ul>
+ * <li>_tokenize(): convert _typed into _parts
+ * <li>_split(): convert _parts into _command and _unparsedArgs
+ * <li>_assign(): convert _unparsedArgs into requisition
+ * </ul>
+ *
+ * @param typed {string} The instruction as typed by the user so far
+ * @param options {object} A list of optional named parameters. Can be any of:
+ * <b>flags</b>: Flags for us to check against the predicates specified with the
+ * commands. Defaulted to <tt>keyboard.buildFlags({ });</tt>
+ * if not specified.
+ * @constructor
+ */
+function CliRequisition(env, options) {
+ Requisition.call(this, env);
+
+ if (options && options.flags) {
+ /**
+ * TODO: We were using a default of keyboard.buildFlags({ });
+ * This allowed us to have commands that only existed in certain contexts
+ * - i.e. Javascript specific commands.
+ */
+ this.flags = options.flags;
+ }
+}
+oop.inherits(CliRequisition, Requisition);
+(function() {
+ /**
+ * Called by the UI when ever the user interacts with a command line input
+ * @param input A structure that details the state of the input field.
+ * It should look something like: { typed:a, cursor: { start:b, end:c } }
+ * Where a is the contents of the input field, and b and c are the start
+ * and end of the cursor/selection respectively.
+ */
+ CliRequisition.prototype.update = function(input) {
+ this.input = input;
+ this._hints = [];
+
+ var args = this._tokenize(input.typed);
+ this._split(args);
+
+ if (this.commandAssignment.value) {
+ this._assign(args);
+ }
+
+ this._updateHints();
+ };
+
+ /**
+ * Return an array of Status scores so we can create a marked up
+ * version of the command line input.
+ */
+ CliRequisition.prototype.getInputStatusMarkup = function() {
+ // 'scores' is an array which tells us what chars are errors
+ // Initialize with everything VALID
+ var scores = this.toString().split('').map(function(ch) {
+ return Status.VALID;
+ });
+ // For all chars in all hints, check and upgrade the score
+ this._hints.forEach(function(hint) {
+ for (var i = hint.start; i <= hint.end; i++) {
+ if (hint.status > scores[i]) {
+ scores[i] = hint.status;
+ }
+ }
+ }, this);
+ return scores;
+ };
+
+ /**
+ * Reconstitute the input from the args
+ */
+ CliRequisition.prototype.toString = function() {
+ return this.getAssignments(true).map(function(assignment) {
+ return assignment.toString();
+ }, this).join('');
+ };
+
+ var superUpdateHints = CliRequisition.prototype._updateHints;
+ /**
+ * Marks up hints in a number of ways:
+ * - Makes INCOMPLETE hints that are not near the cursor INVALID since
+ * they can't be completed by typing
+ * - Finds the most severe hint, and annotates the array with it
+ * - Finds the hint to display, and also annotates the array with it
+ * TODO: I'm wondering if array annotation is evil and we should replace
+ * this with an object. Need to find out more.
+ */
+ CliRequisition.prototype._updateHints = function() {
+ superUpdateHints.call(this);
+
+ // Not knowing about cursor positioning, the requisition and assignments
+ // can't know this, but anything they mark as INCOMPLETE is actually
+ // INVALID unless the cursor is actually inside that argument.
+ var c = this.input.cursor;
+ this._hints.forEach(function(hint) {
+ var startInHint = c.start >= hint.start && c.start <= hint.end;
+ var endInHint = c.end >= hint.start && c.end <= hint.end;
+ var inHint = startInHint || endInHint;
+ if (!inHint && hint.status === Status.INCOMPLETE) {
+ hint.status = Status.INVALID;
+ }
+ }, this);
+
+ Hint.sort(this._hints);
+ };
+
+ /**
+ * Accessor for the hints array.
+ * While we could just use the hints property, using getHints() is
+ * preferred for symmetry with Requisition where it needs a function due to
+ * lack of an atomic update system.
+ */
+ CliRequisition.prototype.getHints = function() {
+ return this._hints;
+ };
+
+ /**
+ * Look through the arguments attached to our assignments for the assignment
+ * at the given position.
+ */
+ CliRequisition.prototype.getAssignmentAt = function(position) {
+ var assignments = this.getAssignments(true);
+ for (var i = 0; i < assignments.length; i++) {
+ var assignment = assignments[i];
+ if (!assignment.arg) {
+ // There is no argument in this assignment, we've fallen off
+ // the end of the obvious answers - it must be this one.
+ return assignment;
+ }
+ if (assignment.isPositionCaptured(position)) {
+ return assignment;
+ }
+ }
+
+ return assignment;
+ };
+
+ /**
+ * Split up the input taking into account ' and "
+ */
+ CliRequisition.prototype._tokenize = function(typed) {
+ // For blank input, place a dummy empty argument into the list
+ if (typed == null || typed.length === 0) {
+ return [ new Argument(this, '', 0, 0, '', '') ];
+ }
+
+ var OUTSIDE = 1; // The last character was whitespace
+ var IN_SIMPLE = 2; // The last character was part of a parameter
+ var IN_SINGLE_Q = 3; // We're inside a single quote: '
+ var IN_DOUBLE_Q = 4; // We're inside double quotes: "
+
+ var mode = OUTSIDE;
+
+ // First we un-escape. This list was taken from:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Unicode
+ // We are generally converting to their real values except for \', \"
+ // and '\ ' which we are converting to unicode private characters so we
+ // can distinguish them from ', " and ' ', which have special meaning.
+ // They need swapping back post-split - see unescape2()
+ typed = typed
+ .replace(/\\\\/g, '\\')
+ .replace(/\\b/g, '\b')
+ .replace(/\\f/g, '\f')
+ .replace(/\\n/g, '\n')
+ .replace(/\\r/g, '\r')
+ .replace(/\\t/g, '\t')
+ .replace(/\\v/g, '\v')
+ .replace(/\\n/g, '\n')
+ .replace(/\\r/g, '\r')
+ .replace(/\\ /g, '\uF000')
+ .replace(/\\'/g, '\uF001')
+ .replace(/\\"/g, '\uF002');
+
+ function unescape2(str) {
+ return str
+ .replace(/\uF000/g, ' ')
+ .replace(/\uF001/g, '\'')
+ .replace(/\uF002/g, '"');
+ }
+
+ var i = 0;
+ var start = 0; // Where did this section start?
+ var prefix = '';
+ var args = [];
+
+ while (true) {
+ if (i >= typed.length) {
+ // There is nothing else to read - tidy up
+ if (mode !== OUTSIDE) {
+ var str = unescape2(typed.substring(start, i));
+ args.push(new Argument(this, str, start, i, prefix, ''));
+ }
+ else {
+ if (i !== start) {
+ // There's a bunch of whitespace at the end of the
+ // command add it to the last argument's suffix,
+ // creating an empty argument if needed.
+ var extra = typed.substring(start, i);
+ var lastArg = args[args.length - 1];
+ if (!lastArg) {
+ lastArg = new Argument(this, '', i, i, extra, '');
+ args.push(lastArg);
+ }
+ else {
+ lastArg.suffix += extra;
+ }
+ }
+ }
+ break;
+ }
+
+ var c = typed[i];
+ switch (mode) {
+ case OUTSIDE:
+ if (c === '\'') {
+ prefix = typed.substring(start, i + 1);
+ mode = IN_SINGLE_Q;
+ start = i + 1;
+ }
+ else if (c === '"') {
+ prefix = typed.substring(start, i + 1);
+ mode = IN_DOUBLE_Q;
+ start = i + 1;
+ }
+ else if (/ /.test(c)) {
+ // Still whitespace, do nothing
+ }
+ else {
+ prefix = typed.substring(start, i);
+ mode = IN_SIMPLE;
+ start = i;
+ }
+ break;
+
+ case IN_SIMPLE:
+ // There is an edge case of xx'xx which we are assuming to
+ // be a single parameter (and same with ")
+ if (c === ' ') {
+ var str = unescape2(typed.substring(start, i));
+ args.push(new Argument(this, str,
+ start, i, prefix, ''));
+ mode = OUTSIDE;
+ start = i;
+ prefix = '';
+ }
+ break;
+
+ case IN_SINGLE_Q:
+ if (c === '\'') {
+ var str = unescape2(typed.substring(start, i));
+ args.push(new Argument(this, str,
+ start - 1, i + 1, prefix, c));
+ mode = OUTSIDE;
+ start = i + 1;
+ prefix = '';
+ }
+ break;
+
+ case IN_DOUBLE_Q:
+ if (c === '"') {
+ var str = unescape2(typed.substring(start, i));
+ args.push(new Argument(this, str,
+ start - 1, i + 1, prefix, c));
+ mode = OUTSIDE;
+ start = i + 1;
+ prefix = '';
+ }
+ break;
+ }
+
+ i++;
+ }
+
+ return args;
+ };
+
+ /**
+ * Looks in the canon for a command extension that matches what has been
+ * typed at the command line.
+ */
+ CliRequisition.prototype._split = function(args) {
+ var argsUsed = 1;
+ var arg;
+
+ while (argsUsed <= args.length) {
+ var arg = Argument.merge(args, 0, argsUsed);
+ this.commandAssignment.setArgument(arg);
+
+ if (!this.commandAssignment.value) {
+ // Not found. break with value == null
+ break;
+ }
+
+ /*
+ // Previously we needed a way to hide commands depending context.
+ // We have not resurrected that feature yet.
+ if (!keyboard.flagsMatch(command.predicates, this.flags)) {
+ // If the predicates say 'no match' then go LA LA LA
+ command = null;
+ break;
+ }
+ */
+
+ if (this.commandAssignment.value.exec) {
+ // Valid command, break with command valid
+ for (var i = 0; i < argsUsed; i++) {
+ args.shift();
+ }
+ break;
+ }
+
+ argsUsed++;
+ }
+ };
+
+ /**
+ * Work out which arguments are applicable to which parameters.
+ * <p>This takes #_command.params and #_unparsedArgs and creates a map of
+ * param names to 'assignment' objects, which have the following properties:
+ * <ul>
+ * <li>param - The matching parameter.
+ * <li>index - Zero based index into where the match came from on the input
+ * <li>value - The matching input
+ * </ul>
+ */
+ CliRequisition.prototype._assign = function(args) {
+ if (args.length === 0) {
+ this.setDefaultValues();
+ return;
+ }
+
+ // Create an error if the command does not take parameters, but we have
+ // been given them ...
+ if (this.assignmentCount === 0) {
+ // TODO: previously we were doing some extra work to avoid this if
+ // we determined that we had args that were all whitespace, but
+ // probably given our tighter tokenize() this won't be an issue?
+ this._hints.push(new Hint(Status.INVALID,
+ this.commandAssignment.value.name +
+ ' does not take any parameters',
+ Argument.merge(args)));
+ return;
+ }
+
+ // Special case: if there is only 1 parameter, and that's of type
+ // text we put all the params into the first param
+ if (this.assignmentCount === 1) {
+ var assignment = this.getAssignment(0);
+ if (assignment.param.type.name === 'text') {
+ assignment.setArgument(Argument.merge(args));
+ return;
+ }
+ }
+
+ var assignments = this.cloneAssignments();
+ var names = this.getParameterNames();
+
+ // Extract all the named parameters
+ var used = [];
+ assignments.forEach(function(assignment) {
+ var namedArgText = '--' + assignment.name;
+
+ var i = 0;
+ while (true) {
+ var arg = args[i];
+ if (namedArgText !== arg.text) {
+ i++;
+ if (i >= args.length) {
+ break;
+ }
+ continue;
+ }
+
+ // boolean parameters don't have values, default to false
+ if (assignment.param.type.name === 'boolean') {
+ assignment.setValue(true);
+ }
+ else {
+ if (i + 1 < args.length) {
+ // Missing value portion of this named param
+ this._hints.push(new Hint(Status.INCOMPLETE,
+ 'Missing value for: ' + namedArgText,
+ args[i]));
+ }
+ else {
+ args.splice(i + 1, 1);
+ assignment.setArgument(args[i + 1]);
+ }
+ }
+
+ lang.arrayRemove(names, assignment.name);
+ args.splice(i, 1);
+ // We don't need to i++ if we splice
+ }
+ }, this);
+
+ // What's left are positional parameters assign in order
+ names.forEach(function(name) {
+ var assignment = this.getAssignment(name);
+ if (args.length === 0) {
+ // No more values
+ assignment.setValue(undefined); // i.e. default
+ }
+ else {
+ var arg = args[0];
+ args.splice(0, 1);
+ assignment.setArgument(arg);
+ }
+ }, this);
+
+ if (args.length > 0) {
+ var remaining = Argument.merge(args);
+ this._hints.push(new Hint(Status.INVALID,
+ 'Input \'' + remaining.text + '\' makes no sense.',
+ remaining));
+ }
+ };
+
+})();
+exports.CliRequisition = CliRequisition;
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('cockpit/ui/settings', ['require', 'exports', 'module' , 'pilot/types', 'pilot/types/basic'], function(require, exports, module) {
+
+
+var types = require("pilot/types");
+var SelectionType = require('pilot/types/basic').SelectionType;
+
+var direction = new SelectionType({
+ name: 'direction',
+ data: [ 'above', 'below' ]
+});
+
+var hintDirectionSetting = {
+ name: "hintDirection",
+ description: "Are hints shown above or below the command line?",
+ type: "direction",
+ defaultValue: "above"
+};
+
+var outputDirectionSetting = {
+ name: "outputDirection",
+ description: "Is the output window shown above or below the command line?",
+ type: "direction",
+ defaultValue: "above"
+};
+
+var outputHeightSetting = {
+ name: "outputHeight",
+ description: "What height should the output panel be?",
+ type: "number",
+ defaultValue: 300
+};
+
+exports.startup = function(data, reason) {
+ types.registerType(direction);
+ data.env.settings.addSetting(hintDirectionSetting);
+ data.env.settings.addSetting(outputDirectionSetting);
+ data.env.settings.addSetting(outputHeightSetting);
+};
+
+exports.shutdown = function(data, reason) {
+ types.unregisterType(direction);
+ data.env.settings.removeSetting(hintDirectionSetting);
+ data.env.settings.removeSetting(outputDirectionSetting);
+ data.env.settings.removeSetting(outputHeightSetting);
+};
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('cockpit/ui/cli_view', ['require', 'exports', 'module' , 'text!cockpit/ui/cli_view.css', 'pilot/event', 'pilot/dom', 'pilot/keys', 'pilot/canon', 'pilot/types', 'cockpit/cli', 'cockpit/ui/request_view'], function(require, exports, module) {
+
+
+var editorCss = require("text!cockpit/ui/cli_view.css");
+var event = require("pilot/event");
+var dom = require("pilot/dom");
+
+dom.importCssString(editorCss);
+
+var event = require("pilot/event");
+var keys = require("pilot/keys");
+var canon = require("pilot/canon");
+var Status = require('pilot/types').Status;
+
+var CliRequisition = require('cockpit/cli').CliRequisition;
+var Hint = require('cockpit/cli').Hint;
+var RequestView = require('cockpit/ui/request_view').RequestView;
+
+var NO_HINT = new Hint(Status.VALID, '', 0, 0);
+
+/**
+ * On startup we need to:
+ * 1. Add 3 sets of elements to the DOM for:
+ * - command line output
+ * - input hints
+ * - completion
+ * 2. Attach a set of events so the command line works
+ */
+exports.startup = function(data, reason) {
+ var cli = new CliRequisition(data.env);
+ var cliView = new CliView(cli, data.env);
+ data.env.cli = cli;
+};
+
+/**
+ * A class to handle the simplest UI implementation
+ */
+function CliView(cli, env) {
+ cli.cliView = this;
+ this.cli = cli;
+ this.doc = document;
+ this.win = dom.getParentWindow(this.doc);
+ this.env = env;
+
+ // TODO: we should have a better way to specify command lines???
+ this.element = this.doc.getElementById('cockpitInput');
+ if (!this.element) {
+ // console.log('No element with an id of cockpit. Bailing on cli');
+ return;
+ }
+
+ this.settings = env.settings;
+ this.hintDirection = this.settings.getSetting('hintDirection');
+ this.outputDirection = this.settings.getSetting('outputDirection');
+ this.outputHeight = this.settings.getSetting('outputHeight');
+
+ // If the requisition tells us something has changed, we use this to know
+ // if we should ignore it
+ this.isUpdating = false;
+
+ this.createElements();
+ this.update();
+}
+CliView.prototype = {
+ /**
+ * Create divs for completion, hints and output
+ */
+ createElements: function() {
+ var input = this.element;
+
+ this.element.spellcheck = false;
+
+ this.output = this.doc.getElementById('cockpitOutput');
+ this.popupOutput = (this.output == null);
+ if (!this.output) {
+ this.output = this.doc.createElement('div');
+ this.output.id = 'cockpitOutput';
+ this.output.className = 'cptOutput';
+ input.parentNode.insertBefore(this.output, input.nextSibling);
+
+ var setMaxOutputHeight = function() {
+ this.output.style.maxHeight = this.outputHeight.get() + 'px';
+ }.bind(this);
+ this.outputHeight.addEventListener('change', setMaxOutputHeight);
+ setMaxOutputHeight();
+ }
+
+ this.completer = this.doc.createElement('div');
+ this.completer.className = 'cptCompletion VALID';
+
+ this.completer.style.color = dom.computedStyle(input, "color");
+ this.completer.style.fontSize = dom.computedStyle(input, "fontSize");
+ this.completer.style.fontFamily = dom.computedStyle(input, "fontFamily");
+ this.completer.style.fontWeight = dom.computedStyle(input, "fontWeight");
+ this.completer.style.fontStyle = dom.computedStyle(input, "fontStyle");
+ input.parentNode.insertBefore(this.completer, input.nextSibling);
+
+ // Transfer background styling to the completer.
+ this.completer.style.backgroundColor = input.style.backgroundColor;
+ input.style.backgroundColor = 'transparent';
+
+ this.hinter = this.doc.createElement('div');
+ this.hinter.className = 'cptHints';
+ input.parentNode.insertBefore(this.hinter, input.nextSibling);
+
+ var resizer = this.resizer.bind(this);
+ event.addListener(this.win, 'resize', resizer);
+ this.hintDirection.addEventListener('change', resizer);
+ this.outputDirection.addEventListener('change', resizer);
+ resizer();
+
+ canon.addEventListener('output', function(ev) {
+ new RequestView(ev.request, this);
+ }.bind(this));
+ event.addCommandKeyListener(input, this.onCommandKey.bind(this));
+ event.addListener(input, 'keyup', this.onKeyUp.bind(this));
+
+ // cursor position affects hint severity. TODO: shortcuts for speed
+ event.addListener(input, 'mouseup', function(ev) {
+ this.isUpdating = true;
+ this.update();
+ this.isUpdating = false;
+ }.bind(this));
+
+ this.cli.addEventListener('argumentChange', this.onArgChange.bind(this));
+
+ event.addListener(input, "focus", function() {
+ dom.addCssClass(this.output, "cptFocusPopup");
+ dom.addCssClass(this.hinter, "cptFocusPopup");
+ }.bind(this));
+
+ function hideOutput() {
+ dom.removeCssClass(this.output, "cptFocusPopup");
+ dom.removeCssClass(this.hinter, "cptFocusPopup");
+ };
+ event.addListener(input, "blur", hideOutput.bind(this));
+ hideOutput.call(this);
+ },
+
+ /**
+ * We need to see the output of the latest command entered
+ */
+ scrollOutputToBottom: function() {
+ // Certain browsers have a bug such that scrollHeight is too small
+ // when content does not fill the client area of the element
+ var scrollHeight = Math.max(this.output.scrollHeight, this.output.clientHeight);
+ this.output.scrollTop = scrollHeight - this.output.clientHeight;
+ },
+
+ /**
+ * To be called on window resize or any time we want to align the elements
+ * with the input box.
+ */
+ resizer: function() {
+ var rect = this.element.getClientRects()[0];
+
+ this.completer.style.top = rect.top + 'px';
+ var height = rect.bottom - rect.top;
+ this.completer.style.height = height + 'px';
+ this.completer.style.lineHeight = height + 'px';
+ this.completer.style.left = rect.left + 'px';
+ var width = rect.right - rect.left;
+ this.completer.style.width = width + 'px';
+
+ if (this.hintDirection.get() === 'below') {
+ this.hinter.style.top = rect.bottom + 'px';
+ this.hinter.style.bottom = 'auto';
+ }
+ else {
+ this.hinter.style.top = 'auto';
+ this.hinter.style.bottom = (this.doc.documentElement.clientHeight - rect.top) + 'px';
+ }
+ this.hinter.style.left = (rect.left + 30) + 'px';
+ this.hinter.style.maxWidth = (width - 110) + 'px';
+
+ if (this.popupOutput) {
+ if (this.outputDirection.get() === 'below') {
+ this.output.style.top = rect.bottom + 'px';
+ this.output.style.bottom = 'auto';
+ }
+ else {
+ this.output.style.top = 'auto';
+ this.output.style.bottom = (this.doc.documentElement.clientHeight - rect.top) + 'px';
+ }
+ this.output.style.left = rect.left + 'px';
+ this.output.style.width = (width - 80) + 'px';
+ }
+ },
+
+ /**
+ * Ensure that TAB isn't handled by the browser
+ */
+onCommandKey: function(ev, hashId, keyCode) {
+ var stopEvent;
+ if (keyCode === keys.TAB ||
+ keyCode === keys.UP ||
+ keyCode === keys.DOWN) {
+ stopEvent = true;
+ } else if (hashId != 0 || keyCode != 0) {
+ stopEvent = canon.execKeyCommand(this.env, 'cli', hashId, keyCode);
+ }
+ stopEvent && event.stopEvent(ev);
+ },
+
+ /**
+ * The main keyboard processing loop
+ */
+ onKeyUp: function(ev) {
+ var handled;
+ /*
+ var handled = keyboardManager.processKeyEvent(ev, this, {
+ isCommandLine: true, isKeyUp: true
+ });
+ */
+
+ // RETURN does a special exec/highlight thing
+ if (ev.keyCode === keys.RETURN) {
+ var worst = this.cli.getWorstHint();
+ // Deny RETURN unless the command might work
+ if (worst.status === Status.VALID) {
+ this.cli.exec();
+ this.element.value = '';
+ }
+ else {
+ // If we've denied RETURN because the command was not VALID,
+ // select the part of the command line that is causing problems
+ // TODO: if there are 2 errors are we picking the right one?
+ dom.setSelectionStart(this.element, worst.start);
+ dom.setSelectionEnd(this.element, worst.end);
+ }
+ }
+
+ this.update();
+
+ // Special actions which delegate to the assignment
+ var current = this.cli.getAssignmentAt(dom.getSelectionStart(this.element));
+ if (current) {
+ // TAB does a special complete thing
+ if (ev.keyCode === keys.TAB) {
+ current.complete();
+ this.update();
+ }
+
+ // UP/DOWN look for some history
+ if (ev.keyCode === keys.UP) {
+ current.increment();
+ this.update();
+ }
+ if (ev.keyCode === keys.DOWN) {
+ current.decrement();
+ this.update();
+ }
+ }
+
+ return handled;
+ },
+
+ /**
+ * Actually parse the input and make sure we're all up to date
+ */
+ update: function() {
+ this.isUpdating = true;
+ var input = {
+ typed: this.element.value,
+ cursor: {
+ start: dom.getSelectionStart(this.element),
+ end: dom.getSelectionEnd(this.element.selectionEnd)
+ }
+ };
+ this.cli.update(input);
+
+ var display = this.cli.getAssignmentAt(input.cursor.start).getHint();
+
+ // 1. Update the completer with prompt/error marker/TAB info
+ dom.removeCssClass(this.completer, Status.VALID.toString());
+ dom.removeCssClass(this.completer, Status.INCOMPLETE.toString());
+ dom.removeCssClass(this.completer, Status.INVALID.toString());
+
+ var completion = '<span class="cptPrompt">&gt;</span> ';
+ if (this.element.value.length > 0) {
+ var scores = this.cli.getInputStatusMarkup();
+ completion += this.markupStatusScore(scores);
+ }
+
+ // Display the "-> prediction" at the end of the completer
+ if (this.element.value.length > 0 &&
+ display.predictions && display.predictions.length > 0) {
+ var tab = display.predictions[0];
+ completion += ' &nbsp;&#x21E5; ' + (tab.name ? tab.name : tab);
+ }
+ this.completer.innerHTML = completion;
+ dom.addCssClass(this.completer, this.cli.getWorstHint().status.toString());
+
+ // 2. Update the hint element
+ var hint = '';
+ if (this.element.value.length !== 0) {
+ hint += display.message;
+ if (display.predictions && display.predictions.length > 0) {
+ hint += ': [ ';
+ display.predictions.forEach(function(prediction) {
+ hint += (prediction.name ? prediction.name : prediction);
+ hint += ' | ';
+ }, this);
+ hint = hint.replace(/\| $/, ']');
+ }
+ }
+
+ this.hinter.innerHTML = hint;
+ if (hint.length === 0) {
+ dom.addCssClass(this.hinter, 'cptNoPopup');
+ }
+ else {
+ dom.removeCssClass(this.hinter, 'cptNoPopup');
+ }
+
+ this.isUpdating = false;
+ },
+
+ /**
+ * Markup an array of Status values with spans
+ */
+ markupStatusScore: function(scores) {
+ var completion = '';
+ // Create mark-up
+ var i = 0;
+ var lastStatus = -1;
+ while (true) {
+ if (lastStatus !== scores[i]) {
+ completion += '<span class=' + scores[i].toString() + '>';
+ lastStatus = scores[i];
+ }
+ completion += this.element.value[i];
+ i++;
+ if (i === this.element.value.length) {
+ completion += '</span>';
+ break;
+ }
+ if (lastStatus !== scores[i]) {
+ completion += '</span>';
+ }
+ }
+
+ return completion;
+ },
+
+ /**
+ * Update the input element to reflect the changed argument
+ */
+ onArgChange: function(ev) {
+ if (this.isUpdating) {
+ return;
+ }
+
+ var prefix = this.element.value.substring(0, ev.argument.start);
+ var suffix = this.element.value.substring(ev.argument.end);
+ var insert = typeof ev.text === 'string' ? ev.text : ev.text.name;
+ this.element.value = prefix + insert + suffix;
+ // Fix the cursor.
+ var insertEnd = (prefix + insert).length;
+ this.element.selectionStart = insertEnd;
+ this.element.selectionEnd = insertEnd;
+ }
+};
+exports.CliView = CliView;
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('cockpit/ui/request_view', ['require', 'exports', 'module' , 'pilot/dom', 'pilot/event', 'text!cockpit/ui/request_view.html', 'pilot/domtemplate', 'text!cockpit/ui/request_view.css'], function(require, exports, module) {
+
+var dom = require("pilot/dom");
+var event = require("pilot/event");
+var requestViewHtml = require("text!cockpit/ui/request_view.html");
+var Templater = require("pilot/domtemplate").Templater;
+
+var requestViewCss = require("text!cockpit/ui/request_view.css");
+dom.importCssString(requestViewCss);
+
+/**
+ * Pull the HTML into the DOM, but don't add it to the document
+ */
+var templates = document.createElement('div');
+templates.innerHTML = requestViewHtml;
+var row = templates.querySelector('.cptRow');
+
+/**
+ * Work out the path for images.
+ * TODO: This should probably live in some utility area somewhere
+ */
+function imageUrl(path) {
+ var dataUrl;
+ try {
+ dataUrl = require('text!cockpit/ui/' + path);
+ } catch (e) { }
+ if (dataUrl) {
+ return dataUrl;
+ }
+
+ var filename = module.id.split('/').pop() + '.js';
+ var imagePath;
+
+ if (module.uri.substr(-filename.length) !== filename) {
+ console.error('Can\'t work out path from module.uri/module.id');
+ return path;
+ }
+
+ if (module.uri) {
+ var end = module.uri.length - filename.length - 1;
+ return module.uri.substr(0, end) + "/" + path;
+ }
+
+ return filename + path;
+}
+
+
+/**
+ * Adds a row to the CLI output display
+ */
+function RequestView(request, cliView) {
+ this.request = request;
+ this.cliView = cliView;
+ this.imageUrl = imageUrl;
+
+ // Elements attached to this by the templater. For info only
+ this.rowin = null;
+ this.rowout = null;
+ this.output = null;
+ this.hide = null;
+ this.show = null;
+ this.duration = null;
+ this.throb = null;
+
+ new Templater().processNode(row.cloneNode(true), this);
+
+ this.cliView.output.appendChild(this.rowin);
+ this.cliView.output.appendChild(this.rowout);
+
+ this.request.addEventListener('output', this.onRequestChange.bind(this));
+};
+
+RequestView.prototype = {
+ /**
+ * A single click on an invocation line in the console copies the command to
+ * the command line
+ */
+ copyToInput: function() {
+ this.cliView.element.value = this.request.typed;
+ },
+
+ /**
+ * A double click on an invocation line in the console executes the command
+ */
+ executeRequest: function(ev) {
+ this.cliView.cli.update({
+ typed: this.request.typed,
+ cursor: { start:0, end:0 }
+ });
+ this.cliView.cli.exec();
+ },
+
+ hideOutput: function(ev) {
+ this.output.style.display = 'none';
+ dom.addCssClass(this.hide, 'cmd_hidden');
+ dom.removeCssClass(this.show, 'cmd_hidden');
+
+ event.stopPropagation(ev);
+ },
+
+ showOutput: function(ev) {
+ this.output.style.display = 'block';
+ dom.removeCssClass(this.hide, 'cmd_hidden');
+ dom.addCssClass(this.show, 'cmd_hidden');
+
+ event.stopPropagation(ev);
+ },
+
+ remove: function(ev) {
+ this.cliView.output.removeChild(this.rowin);
+ this.cliView.output.removeChild(this.rowout);
+ event.stopPropagation(ev);
+ },
+
+ onRequestChange: function(ev) {
+ this.duration.innerHTML = this.request.duration ?
+ 'completed in ' + (this.request.duration / 1000) + ' sec ' :
+ '';
+
+ this.output.innerHTML = '';
+ this.request.outputs.forEach(function(output) {
+ var node;
+ if (typeof output == 'string') {
+ node = document.createElement('p');
+ node.innerHTML = output;
+ } else {
+ node = output;
+ }
+ this.output.appendChild(node);
+ }, this);
+ this.cliView.scrollOutputToBottom();
+
+ dom.setCssClass(this.output, 'cmd_error', this.request.error);
+
+ this.throb.style.display = this.request.completed ? 'none' : 'block';
+ }
+};
+exports.RequestView = RequestView;
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is DomTemplate.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Joe Walker (jwalker@mozilla.com) (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('pilot/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+
+// WARNING: do not 'use_strict' without reading the notes in envEval;
+
+/**
+ * A templater that allows one to quickly template DOM nodes.
+ */
+function Templater() {
+ this.scope = [];
+};
+
+/**
+ * Recursive function to walk the tree processing the attributes as it goes.
+ * @param node the node to process. If you pass a string in instead of a DOM
+ * element, it is assumed to be an id for use with document.getElementById()
+ * @param data the data to use for node processing.
+ */
+Templater.prototype.processNode = function(node, data) {
+ if (typeof node === 'string') {
+ node = document.getElementById(node);
+ }
+ if (data === null || data === undefined) {
+ data = {};
+ }
+ this.scope.push(node.nodeName + (node.id ? '#' + node.id : ''));
+ try {
+ // Process attributes
+ if (node.attributes && node.attributes.length) {
+ // We need to handle 'foreach' and 'if' first because they might stop
+ // some types of processing from happening, and foreach must come first
+ // because it defines new data on which 'if' might depend.
+ if (node.hasAttribute('foreach')) {
+ this.processForEach(node, data);
+ return;
+ }
+ if (node.hasAttribute('if')) {
+ if (!this.processIf(node, data)) {
+ return;
+ }
+ }
+ // Only make the node available once we know it's not going away
+ data.__element = node;
+ // It's good to clean up the attributes when we've processed them,
+ // but if we do it straight away, we mess up the array index
+ var attrs = Array.prototype.slice.call(node.attributes);
+ for (var i = 0; i < attrs.length; i++) {
+ var value = attrs[i].value;
+ var name = attrs[i].name;
+ this.scope.push(name);
+ try {
+ if (name === 'save') {
+ // Save attributes are a setter using the node
+ value = this.stripBraces(value);
+ this.property(value, data, node);
+ node.removeAttribute('save');
+ } else if (name.substring(0, 2) === 'on') {
+ // Event registration relies on property doing a bind
+ value = this.stripBraces(value);
+ var func = this.property(value, data);
+ if (typeof func !== 'function') {
+ this.handleError('Expected ' + value +
+ ' to resolve to a function, but got ' + typeof func);
+ }
+ node.removeAttribute(name);
+ var capture = node.hasAttribute('capture' + name.substring(2));
+ node.addEventListener(name.substring(2), func, capture);
+ if (capture) {
+ node.removeAttribute('capture' + name.substring(2));
+ }
+ } else {
+ // Replace references in all other attributes
+ var self = this;
+ var newValue = value.replace(/\$\{[^}]*\}/g, function(path) {
+ return self.envEval(path.slice(2, -1), data, value);
+ });
+ // Remove '_' prefix of attribute names so the DOM won't try
+ // to use them before we've processed the template
+ if (name.charAt(0) === '_') {
+ node.removeAttribute(name);
+ node.setAttribute(name.substring(1), newValue);
+ } else if (value !== newValue) {
+ attrs[i].value = newValue;
+ }
+ }
+ } finally {
+ this.scope.pop();
+ }
+ }
+ }
+
+ // Loop through our children calling processNode. First clone them, so the
+ // set of nodes that we visit will be unaffected by additions or removals.
+ var childNodes = Array.prototype.slice.call(node.childNodes);
+ for (var j = 0; j < childNodes.length; j++) {
+ this.processNode(childNodes[j], data);
+ }
+
+ if (node.nodeType === Node.TEXT_NODE) {
+ this.processTextNode(node, data);
+ }
+ } finally {
+ this.scope.pop();
+ }
+};
+
+/**
+ * Handle <x if="${...}">
+ * @param node An element with an 'if' attribute
+ * @param data The data to use with envEval
+ * @returns true if processing should continue, false otherwise
+ */
+Templater.prototype.processIf = function(node, data) {
+ this.scope.push('if');
+ try {
+ var originalValue = node.getAttribute('if');
+ var value = this.stripBraces(originalValue);
+ var recurse = true;
+ try {
+ var reply = this.envEval(value, data, originalValue);
+ recurse = !!reply;
+ } catch (ex) {
+ this.handleError('Error with \'' + value + '\'', ex);
+ recurse = false;
+ }
+ if (!recurse) {
+ node.parentNode.removeChild(node);
+ }
+ node.removeAttribute('if');
+ return recurse;
+ } finally {
+ this.scope.pop();
+ }
+};
+
+/**
+ * Handle <x foreach="param in ${array}"> and the special case of
+ * <loop foreach="param in ${array}">
+ * @param node An element with a 'foreach' attribute
+ * @param data The data to use with envEval
+ */
+Templater.prototype.processForEach = function(node, data) {
+ this.scope.push('foreach');
+ try {
+ var originalValue = node.getAttribute('foreach');
+ var value = originalValue;
+
+ var paramName = 'param';
+ if (value.charAt(0) === '$') {
+ // No custom loop variable name. Use the default: 'param'
+ value = this.stripBraces(value);
+ } else {
+ // Extract the loop variable name from 'NAME in ${ARRAY}'
+ var nameArr = value.split(' in ');
+ paramName = nameArr[0].trim();
+ value = this.stripBraces(nameArr[1].trim());
+ }
+ node.removeAttribute('foreach');
+ try {
+ var self = this;
+ // Process a single iteration of a loop
+ var processSingle = function(member, clone, ref) {
+ ref.parentNode.insertBefore(clone, ref);
+ data[paramName] = member;
+ self.processNode(clone, data);
+ delete data[paramName];
+ };
+
+ // processSingle is no good for <loop> nodes where we want to work on
+ // the childNodes rather than the node itself
+ var processAll = function(scope, member) {
+ self.scope.push(scope);
+ try {
+ if (node.nodeName === 'LOOP') {
+ for (var i = 0; i < node.childNodes.length; i++) {
+ var clone = node.childNodes[i].cloneNode(true);
+ processSingle(member, clone, node);
+ }
+ } else {
+ var clone = node.cloneNode(true);
+ clone.removeAttribute('foreach');
+ processSingle(member, clone, node);
+ }
+ } finally {
+ self.scope.pop();
+ }
+ };
+
+ var reply = this.envEval(value, data, originalValue);
+ if (Array.isArray(reply)) {
+ reply.forEach(function(data, i) {
+ processAll('' + i, data);
+ }, this);
+ } else {
+ for (var param in reply) {
+ if (reply.hasOwnProperty(param)) {
+ processAll(param, param);
+ }
+ }
+ }
+ node.parentNode.removeChild(node);
+ } catch (ex) {
+ this.handleError('Error with \'' + value + '\'', ex);
+ }
+ } finally {
+ this.scope.pop();
+ }
+};
+
+/**
+ * Take a text node and replace it with another text node with the ${...}
+ * sections parsed out. We replace the node by altering node.parentNode but
+ * we could probably use a DOM Text API to achieve the same thing.
+ * @param node The Text node to work on
+ * @param data The data to use in calls to envEval
+ */
+Templater.prototype.processTextNode = function(node, data) {
+ // Replace references in other attributes
+ var value = node.data;
+ // We can't use the string.replace() with function trick (see generic
+ // attribute processing in processNode()) because we need to support
+ // functions that return DOM nodes, so we can't have the conversion to a
+ // string.
+ // Instead we process the string as an array of parts. In order to split
+ // the string up, we first replace '${' with '\uF001$' and '}' with '\uF002'
+ // We can then split using \uF001 or \uF002 to get an array of strings
+ // where scripts are prefixed with $.
+ // \uF001 and \uF002 are just unicode chars reserved for private use.
+ value = value.replace(/\$\{([^}]*)\}/g, '\uF001$$$1\uF002');
+ var parts = value.split(/\uF001|\uF002/);
+ if (parts.length > 1) {
+ parts.forEach(function(part) {
+ if (part === null || part === undefined || part === '') {
+ return;
+ }
+ if (part.charAt(0) === '$') {
+ part = this.envEval(part.slice(1), data, node.data);
+ }
+ // It looks like this was done a few lines above but see envEval
+ if (part === null) {
+ part = "null";
+ }
+ if (part === undefined) {
+ part = "undefined";
+ }
+ // if (isDOMElement(part)) { ... }
+ if (typeof part.cloneNode !== 'function') {
+ part = node.ownerDocument.createTextNode(part.toString());
+ }
+ node.parentNode.insertBefore(part, node);
+ }, this);
+ node.parentNode.removeChild(node);
+ }
+};
+
+/**
+ * Warn of string does not begin '${' and end '}'
+ * @param str the string to check.
+ * @return The string stripped of ${ and }, or untouched if it does not match
+ */
+Templater.prototype.stripBraces = function(str) {
+ if (!str.match(/\$\{.*\}/g)) {
+ this.handleError('Expected ' + str + ' to match ${...}');
+ return str;
+ }
+ return str.slice(2, -1);
+};
+
+/**
+ * Combined getter and setter that works with a path through some data set.
+ * For example:
+ * <ul>
+ * <li>property('a.b', { a: { b: 99 }}); // returns 99
+ * <li>property('a', { a: { b: 99 }}); // returns { b: 99 }
+ * <li>property('a', { a: { b: 99 }}, 42); // returns 99 and alters the
+ * input data to be { a: { b: 42 }}
+ * </ul>
+ * @param path An array of strings indicating the path through the data, or
+ * a string to be cut into an array using <tt>split('.')</tt>
+ * @param data An object to look in for the <tt>path</tt> argument
+ * @param newValue (optional) If defined, this value will replace the
+ * original value for the data at the path specified.
+ * @return The value pointed to by <tt>path</tt> before any
+ * <tt>newValue</tt> is applied.
+ */
+Templater.prototype.property = function(path, data, newValue) {
+ this.scope.push(path);
+ try {
+ if (typeof path === 'string') {
+ path = path.split('.');
+ }
+ var value = data[path[0]];
+ if (path.length === 1) {
+ if (newValue !== undefined) {
+ data[path[0]] = newValue;
+ }
+ if (typeof value === 'function') {
+ return function() {
+ return value.apply(data, arguments);
+ };
+ }
+ return value;
+ }
+ if (!value) {
+ this.handleError('Can\'t find path=' + path);
+ return null;
+ }
+ return this.property(path.slice(1), value, newValue);
+ } finally {
+ this.scope.pop();
+ }
+};
+
+/**
+ * Like eval, but that creates a context of the variables in <tt>env</tt> in
+ * which the script is evaluated.
+ * WARNING: This script uses 'with' which is generally regarded to be evil.
+ * The alternative is to create a Function at runtime that takes X parameters
+ * according to the X keys in the env object, and then call that function using
+ * the values in the env object. This is likely to be slow, but workable.
+ * @param script The string to be evaluated.
+ * @param env The environment in which to eval the script.
+ * @param context Optional debugging string in case of failure
+ * @return The return value of the script, or the error message if the script
+ * execution failed.
+ */
+Templater.prototype.envEval = function(script, env, context) {
+ with (env) {
+ try {
+ this.scope.push(context);
+ return eval(script);
+ } catch (ex) {
+ this.handleError('Template error evaluating \'' + script + '\'', ex);
+ return script;
+ } finally {
+ this.scope.pop();
+ }
+ }
+};
+
+/**
+ * A generic way of reporting errors, for easy overloading in different
+ * environments.
+ * @param message the error message to report.
+ * @param ex optional associated exception.
+ */
+Templater.prototype.handleError = function(message, ex) {
+ this.logError(message);
+ this.logError('In: ' + this.scope.join(' > '));
+ if (ex) {
+ this.logError(ex);
+ }
+};
+
+
+/**
+ * A generic way of reporting errors, for easy overloading in different
+ * environments.
+ * @param message the error message to report.
+ */
+Templater.prototype.logError = function(message) {
+ window.console && window.console.log && console.log(message);
+};
+
+exports.Templater = Templater;
+
+
+});
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Skywriter.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Skywriter Team (skywriter@mozilla.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define('cockpit/commands/basic', ['require', 'exports', 'module' , 'pilot/canon'], function(require, exports, module) {
+
+
+var canon = require('pilot/canon');
+
+/**
+ * '!' command
+ */
+var bangCommandSpec = {
+ name: 'sh',
+ description: 'Execute a system command (requires server support)',
+ params: [
+ {
+ name: 'command',
+ type: 'text',
+ description: 'The string to send to the os shell.'
+ }
+ ],
+ exec: function(env, args, request) {
+ var req = new XMLHttpRequest();
+ req.open('GET', '/exec?args=' + args.command, true);
+ req.onreadystatechange = function(ev) {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ request.done('<pre>' + req.responseText + '</pre>');
+ }
+ }
+ };
+ req.send(null);
+ }
+};
+
+var canon = require('pilot/canon');
+
+exports.startup = function(data, reason) {
+ canon.addCommand(bangCommandSpec);
+};
+
+exports.shutdown = function(data, reason) {
+ canon.removeCommand(bangCommandSpec);
+};
+
+
+});
+define("text!cockpit/ui/cli_view.css", [], "" +
+ "#cockpitInput { padding-left: 16px; }" +
+ "" +
+ ".cptOutput { overflow: auto; position: absolute; z-index: 999; display: none; }" +
+ "" +
+ ".cptCompletion { padding: 0; position: absolute; z-index: -1000; }" +
+ ".cptCompletion.VALID { background: #FFF; }" +
+ ".cptCompletion.INCOMPLETE { background: #DDD; }" +
+ ".cptCompletion.INVALID { background: #DDD; }" +
+ ".cptCompletion span { color: #FFF; }" +
+ ".cptCompletion span.INCOMPLETE { color: #DDD; border-bottom: 2px dotted #F80; }" +
+ ".cptCompletion span.INVALID { color: #DDD; border-bottom: 2px dotted #F00; }" +
+ "span.cptPrompt { color: #66F; font-weight: bold; }" +
+ "" +
+ "" +
+ ".cptHints {" +
+ " color: #000;" +
+ " position: absolute;" +
+ " border: 1px solid rgba(230, 230, 230, 0.8);" +
+ " background: rgba(250, 250, 250, 0.8);" +
+ " -moz-border-radius-topleft: 10px;" +
+ " -moz-border-radius-topright: 10px;" +
+ " border-top-left-radius: 10px; border-top-right-radius: 10px;" +
+ " z-index: 1000;" +
+ " padding: 8px;" +
+ " display: none;" +
+ "}" +
+ "" +
+ ".cptFocusPopup { display: block; }" +
+ ".cptFocusPopup.cptNoPopup { display: none; }" +
+ "" +
+ ".cptHints ul { margin: 0; padding: 0 15px; }" +
+ "" +
+ ".cptGt { font-weight: bold; font-size: 120%; }" +
+ "");
+
+define("text!cockpit/ui/request_view.css", [], "" +
+ ".cptRowIn {" +
+ " display: box; display: -moz-box; display: -webkit-box;" +
+ " box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-orient: horizontal;" +
+ " box-align: center; -moz-box-align: center; -webkit-box-align: center;" +
+ " color: #333;" +
+ " background-color: #EEE;" +
+ " width: 100%;" +
+ " font-family: consolas, courier, monospace;" +
+ "}" +
+ ".cptRowIn > * { padding-left: 2px; padding-right: 2px; }" +
+ ".cptRowIn > img { cursor: pointer; }" +
+ ".cptHover { display: none; }" +
+ ".cptRowIn:hover > .cptHover { display: block; }" +
+ ".cptRowIn:hover > .cptHover.cptHidden { display: none; }" +
+ ".cptOutTyped {" +
+ " box-flex: 1; -moz-box-flex: 1; -webkit-box-flex: 1;" +
+ " font-weight: bold; color: #000; font-size: 120%;" +
+ "}" +
+ ".cptRowOutput { padding-left: 10px; line-height: 1.2em; }" +
+ ".cptRowOutput strong," +
+ ".cptRowOutput b," +
+ ".cptRowOutput th," +
+ ".cptRowOutput h1," +
+ ".cptRowOutput h2," +
+ ".cptRowOutput h3 { color: #000; }" +
+ ".cptRowOutput a { font-weight: bold; color: #666; text-decoration: none; }" +
+ ".cptRowOutput a: hover { text-decoration: underline; cursor: pointer; }" +
+ ".cptRowOutput input[type=password]," +
+ ".cptRowOutput input[type=text]," +
+ ".cptRowOutput textarea {" +
+ " color: #000; font-size: 120%;" +
+ " background: transparent; padding: 3px;" +
+ " border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;" +
+ "}" +
+ ".cptRowOutput table," +
+ ".cptRowOutput td," +
+ ".cptRowOutput th { border: 0; padding: 0 2px; }" +
+ ".cptRowOutput .right { text-align: right; }" +
+ "");
+
+define("text!cockpit/ui/request_view.html", [], "" +
+ "<div class=cptRow>" +
+ " <!-- The div for the input (i.e. what was typed) -->" +
+ " <div class=\"cptRowIn\" save=\"${rowin}\"" +
+ " onclick=\"${copyToInput}\"" +
+ " ondblclick=\"${executeRequest}\">" +
+ "" +
+ " <!-- What the user actually typed -->" +
+ " <div class=\"cptGt\">&gt; </div>" +
+ " <div class=\"cptOutTyped\">${request.typed}</div>" +
+ "" +
+ " <!-- The extra details that appear on hover -->" +
+ " <div class=cptHover save=\"${duration}\"></div>" +
+ " <img class=cptHover onclick=\"${hideOutput}\" save=\"${hide}\"" +
+ " alt=\"Hide command output\" _src=\"${imageUrl('images/minus.png')}\"/>" +
+ " <img class=\"cptHover cptHidden\" onclick=\"${showOutput}\" save=\"${show}\"" +
+ " alt=\"Show command output\" _src=\"${imageUrl('images/plus.png')}\"/>" +
+ " <img class=cptHover onclick=\"${remove}\"" +
+ " alt=\"Remove this command from the history\"" +
+ " _src=\"${imageUrl('images/closer.png')}\"/>" +
+ "" +
+ " </div>" +
+ "" +
+ " <!-- The div for the command output -->" +
+ " <div class=\"cptRowOut\" save=\"${rowout}\">" +
+ " <div class=\"cptRowOutput\" save=\"${output}\"></div>" +
+ " <img _src=\"${imageUrl('images/throbber.gif')}\" save=\"${throb}\"/>" +
+ " </div>" +
+ "</div>" +
+ "");
+
+define("text!cockpit/ui/images/pinaction.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAClklEQVQ4EX1TXUhUQRQ+Z3Zmd+9uN1q2P3UpZaEwcikKekkqLKggKHJ96MHe9DmLkCDa9U198Id8kErICmIlRAN96UdE6QdBW/tBA5Uic7E0zN297L17p5mb1zYjD3eYc+d83zlnON8g5xzWNUSEdUBkHTJasRWySPP7fw3hfwkk2GoNsc0vOaJRHo1GV/GiMctkTIJRFlpZli8opK+htmf83gXeG63oteOtra0u25e7TYJIJELb26vYCACTgUe1lXV86BTn745l+MsyHqs53S/Aq4VEUa9Y6ko14eYY4u3AyM3HYwdKU35DZyblGR2+qq6W0X2Nnh07xynnVYpHORx/E1/GvvqaAZUayjMjdM2f/Lgr5E+fV93zR4u3zKCLughsZqKwAzAxaz6dPY6JgjLUF+eSP5OpjmAw2E8DvldHSvJMKPg08aRor1tc4BuALu6mOwGWdQC3mKIqRsC8mKd8wYfD78/earzSYzdMDW9QgKb0Is8CBY1mQXOiaXAHEpMDE5XTJqIq4EiyxUqKlpfkF0pyV1OTAoFAhmTmyCCoDsZNZvIkUjELQpipo0sQqYZAswZHwsEEE10M0pq2SSZY9HqNcDicJcNTpBvQJz40UbSOTh1B8bDpuY0w9Hb3kkn9lPAlBLfhfD39XTtX/blFJqiqrjbkTi63Hbofj2uL4GMsmzFgbDJ/vmMgv/lB4syJ0oXO7d3j++vio6GFsYmD6cHJreWc3/jRVVHhsOYvM8iZ36mtjPDBk/xDZE8CoHlbrlAssbTxDdDJvdb536L7I6S7Vy++6Gi4Xi9BsUthJRaLOYSPz4XALKI4j4iObd/e5UtDKUjZzYyYRyGAJv01Zj8kC5cbs5WY83hQnv0DzCXl+r8APElkq0RU6oMAAAAASUVORK5CYII=");
+
+define("text!cockpit/ui/images/throbber.gif", [], "data:image/gif;base64,R0lGODlh3AATAPQAAP///wAAAL6+vqamppycnLi4uLKyssjIyNjY2MTExNTU1Nzc3ODg4OTk5LCwsLy8vOjo6Ozs7MrKyvLy8vT09M7Ozvb29sbGxtDQ0O7u7tbW1sLCwqqqqvj4+KCgoJaWliH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFg8PwKIMHnLF63N2438f0mv1I2O8buXjvaOPtaHx7fn96goR4hmuId4qDdX95c4+RG4GCBoyAjpmQhZN0YGYFXitdZBIVGAoKoq4CG6Qaswi1CBtkcG6ytrYJubq8vbfAcMK9v7q7D8O1ycrHvsW6zcTKsczNz8HZw9vG3cjTsMIYqQgDLAQGCQoLDA0QCwUHqfYSFw/xEPz88/X38Onr14+Bp4ADCco7eC8hQYMAEe57yNCew4IVBU7EGNDiRn8Z831cGLHhSIgdE/9chIeBgDoB7gjaWUWTlYAFE3LqzDCTlc9WOHfm7PkTqNCh54rePDqB6M+lR536hCpUqs2gVZM+xbrTqtGoWqdy1emValeXKwgcWABB5y1acFNZmEvXwoJ2cGfJrTv3bl69Ffj2xZt3L1+/fw3XRVw4sGDGcR0fJhxZsF3KtBTThZxZ8mLMgC3fRatCLYMIFCzwLEprg84OsDus/tvqdezZf13Hvr2B9Szdu2X3pg18N+68xXn7rh1c+PLksI/Dhe6cuO3ow3NfV92bdArTqC2Ebc3A8vjf5QWf15Bg7Nz17c2fj69+fnq+8N2Lty+fuP78/eV2X13neIcCeBRwxorbZrAxAJoCDHbgoG8RTshahQ9iSKEEzUmYIYfNWViUhheCGJyIP5E4oom7WWjgCeBBAJNv1DVV01MZdJhhjdkplWNzO/5oXI846njjVEIqR2OS2B1pE5PVscajkxhMycqLJgxQCwT40PjfAV4GqNSXYdZXJn5gSkmmmmJu1aZYb14V51do+pTOCmA00AqVB4hG5IJ9PvYnhIFOxmdqhpaI6GeHCtpooisuutmg+Eg62KOMKuqoTaXgicQWoIYq6qiklmoqFV0UoeqqrLbq6quwxirrrLTWauutJ4QAACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAXHx/EoCzboAcdhcLDdgwJ6nua03YZ8PMFPoBMca215eg98G36IgYNvDgOGh4lqjHd7fXOTjYV9nItvhJaIfYF4jXuIf4CCbHmOBZySdoOtj5eja59wBmYFXitdHhwSFRgKxhobBgUPAmdoyxoI0tPJaM5+u9PaCQZzZ9gP2tPcdM7L4tLVznPn6OQb18nh6NV0fu3i5OvP8/nd1qjwaasHcIPAcf/gBSyAAMMwBANYEAhWYQGDBhAyLihwYJiEjx8fYMxIcsGDAxVA/yYIOZIkBAaGPIK8INJlRpgrPeasaRPmx5QgJfB0abLjz50tSeIM+pFmUo0nQQIV+vRlTJUSnNq0KlXCSq09ozIFexEBAYkeNiwgOaEtn2LFpGEQsKCtXbcSjOmVlqDuhAx3+eg1Jo3u37sZBA9GoMAw4MB5FyMwfLht4sh7G/utPGHlYAV8Nz9OnOBz4c2VFWem/Pivar0aKCP2LFn2XwhnVxBwsPbuBAQbEGiIFg1BggoWkidva5z4cL7IlStfkED48OIYoiufYIH68+cKPkqfnsB58ePjmZd3Dj199/XE20tv6/27XO3S6z9nPCz9BP3FISDefL/Bt192/uWmAv8BFzAQAQUWWFaaBgqA11hbHWTIXWIVXifNhRlq6FqF1sm1QQYhdiAhbNEYc2KKK1pXnAIvhrjhBh0KxxiINlqQAY4UXjdcjSJyeAx2G2BYJJD7NZQkjCPKuCORKnbAIXsuKhlhBxEomAIBBzgIYXIfHfmhAAyMR2ZkHk62gJoWlNlhi33ZJZ2cQiKTJoG05Wjcm3xith9dcOK5X51tLRenoHTuud2iMnaolp3KGXrdBo7eKYF5p/mXgJcogClmcgzAR5gCKymXYqlCgmacdhp2UCqL96mq4nuDBTmgBasaCFp4sHaQHHUsGvNRiiGyep1exyIra2mS7dprrtA5++z/Z8ZKYGuGsy6GqgTIDvupRGE+6CO0x3xI5Y2mOTkBjD4ySeGU79o44mcaSEClhglgsKyJ9S5ZTGY0Bnzrj+3SiKK9Rh5zjAALCywZBk/ayCWO3hYM5Y8Dn6qxxRFsgAGoJwwgDQRtYXAAragyQOmaLKNZKGaEuUlpyiub+ad/KtPqpntypvvnzR30DBtjMhNodK6Eqrl0zU0/GjTUgG43wdN6Ra2pAhGtAAZGE5Ta8TH6wknd2IytNKaiZ+Or79oR/tcvthIcAPe7DGAs9Edwk6r3qWoTaNzY2fb9HuHh2S343Hs1VIHhYtOt+Hh551rh24vP5YvXSGzh+eeghy76GuikU9FFEainrvrqrLfu+uuwxy777LTXfkIIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAWHB2l4CDZo9IDjcBja7UEhTV+3DXi3PJFA8xMcbHiDBgMPG31pgHBvg4Z9iYiBjYx7kWocb26OD398mI2EhoiegJlud4UFiZ5sm6Kdn2mBr5t7pJ9rlG0cHg5gXitdaxwFGArIGgoaGwYCZ3QFDwjU1AoIzdCQzdPV1c0bZ9vS3tUJBmjQaGXl1OB0feze1+faiBvk8wjnimn55e/o4OtWjp+4NPIKogsXjaA3g/fiGZBQAcEAFgQGOChgYEEDCCBBLihwQILJkxIe/3wMKfJBSQkJYJpUyRIkgwcVUJq8QLPmTYoyY6ZcyfJmTp08iYZc8MBkhZgxk9aEcPOlzp5FmwI9KdWn1qASurJkClRoWKwhq6IUqpJBAwQEMBYroAHkhLt3+RyzhgCDgAV48Wbgg+waAnoLMgTOm6DwQ8CLBzdGdvjw38V5JTg2lzhyTMeUEwBWHPgzZc4TSOM1bZia6LuqJxCmnOxv7NSsl1mGHHiw5tOuIWeAEHcFATwJME/ApgFBc3MVLEgPvE+Ddb4JokufPmFBAuvPXWu3MIF89wTOmxvOvp179evQtwf2nr6aApPyzVd3jn089e/8xdfeXe/xdZ9/d1ngHf98lbHH3V0LMrgPgsWpcFwBEFBgHmyNXWeYAgLc1UF5sG2wTHjIhNjBiIKZCN81GGyQwYq9uajeMiBOQGOLJ1KjTI40kmfBYNfc2NcGIpI4pI0vyrhjiT1WFqOOLEIZnjVOVpmajYfBiCSNLGbA5YdOkjdihSkQwIEEEWg4nQUmvYhYe+bFKaFodN5lp3rKvJYfnBKAJ+gGDMi3mmbwWYfng7IheuWihu5p32XcSWdSj+stkF95dp64jJ+RBipocHkCCp6PCiRQ6INookCAAwy0yd2CtNET3Yo7RvihBjFZAOaKDHT43DL4BQnsZMo8xx6uI1oQrHXXhHZrB28G62n/YSYxi+uzP2IrgbbHbiaer7hCiOxDFWhrbmGnLVuus5NFexhFuHLX6gkEECorlLpZo0CWJG4pLjIACykmBsp0eSSVeC15TDJeUhlkowlL+SWLNJpW2WEF87urXzNWSZ6JOEb7b8g1brZMjCg3ezBtWKKc4MvyEtwybPeaMAA1ECRoAQYHYLpbeYYCLfQ+mtL5c9CnfQpYpUtHOSejEgT9ogZ/GSqd0f2m+LR5WzOtHqlQX1pYwpC+WbXKqSYtpJ5Mt4a01lGzS3akF60AxkcTaLgAyRBPWCoDgHfJqwRuBuzdw/1ml3iCwTIeLUWJN0v4McMe7uasCTxseNWPSxc5RbvIgD7geZLbGrqCG3jepUmbbze63Y6fvjiOylbwOITPfIHEFsAHL/zwxBdvPBVdFKH88sw37/zz0Ecv/fTUV2/99SeEAAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2cw8BQEm3T6yHEYHHD4oKCuD9qGvNsxT6QTgAkcHHmFeX11fm17hXwPG35qgnhxbwMPkXaLhgZ9gWp3bpyegX4DcG+inY+Qn6eclpiZkHh6epetgLSUcBxlD2csXXdvBQrHGgoaGhsGaIkFDwjTCArTzX+QadHU3c1ofpHc3dcGG89/4+TYktvS1NYI7OHu3fEJ5tpqBu/k+HX7+nXDB06SuoHm0KXhR65cQT8P3FRAMIAFgVMPwDCAwLHjggIHJIgceeFBg44eC/+ITCCBZYKSJ1FCWPBgpE2YMmc+qNCypwScMmnaXAkUJYOaFVyKLOqx5tCXJnMelcBzJNSYKIX2ZPkzqsyjPLku9Zr1QciVErYxaICAgEUOBRJIgzChbt0MLOPFwyBggV27eCUcmxZvg9+/dfPGo5bg8N/Ag61ZM4w4seDF1fpWhizZmoa+GSortgcaMWd/fkP/HY0MgWbTipVV++wY8GhvqSG4XUEgoYTKE+Qh0OCvggULiBckWEZ4Ggbjx5HXVc58IPQJ0idQJ66XanTpFraTe348+XLizRNcz658eHMN3rNPT+C+G/nodqk3t6a+fN3j+u0Xn3nVTQPfdRPspkL/b+dEIN8EeMm2GAYbTNABdrbJ1hyFFv5lQYTodSZABhc+loCEyhxTYYkZopdMMiNeiBxyIFajV4wYHpfBBspUl8yKHu6ooV5APsZjQxyyeNeJ3N1IYod38cgdPBUid6GCKfRWgAYU4IccSyHew8B3doGJHmMLkGkZcynKk2Z50Ym0zJzLbDCmfBbI6eIyCdyJmJmoqZmnBAXy9+Z/yOlZDZpwYihnj7IZpuYEevrYJ5mJEuqiof4l+NYDEXQpXQcMnNjZNDx1oGqJ4S2nF3EsqWrhqqVWl6JIslpAK5MaIqDeqjJq56qN1aTaQaPbHTPYr8Be6Gsyyh6Da7OkmmqP/7GyztdrNVQBm5+pgw3X7aoYKhfZosb6hyUKBHCgQKij1rghkOAJuZg1SeYIIY+nIpDvf/sqm4yNG5CY64f87qdAwSXKGqFkhPH1ZHb2EgYtw3bpKGVkPz5pJAav+gukjB1UHE/HLNJobWcSX8jiuicMMBFd2OmKwQFs2tjXpDfnPE1j30V3c7iRHlrzBD2HONzODyZtsQJMI4r0AUNaE3XNHQw95c9GC001MpIxDacFQ+ulTNTZlU3O1eWVHa6vb/pnQUUrgHHSBKIuwG+bCPyEqbAg25gMVV1iOB/IGh5YOKLKIQ6xBAcUHmzjIcIqgajZ+Ro42DcvXl7j0U4WOUd+2IGu7DWjI1pt4DYq8BPm0entuGSQY/4tBi9Ss0HqfwngBQtHbCH88MQXb/zxyFfRRRHMN+/889BHL/301Fdv/fXYZ39CCAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2fAKXsKm7R6Q+Y43vABep0mGwwOPH7w2CT+gHZ3d3lyagl+CQNvg4yGh36LcHoGfHR/ZYOElQ9/a4ocmoRygIiRk5p8pYmZjXePaYBujHoOqp5qZHBlHAUFXitddg8PBg8KGsgayxvGkAkFDwgICtPTzX2mftHW3QnOpojG3dbYkNjk1waxsdDS1N7ga9zw1t/aifTk35fu6Qj3numL14fOuHTNECHqU4DDgQEsCCwidiHBAwYQMmpcUOCAhI8gJVzUuLGThAQnP/9abEAyI4MCIVOKZNnyJUqUJxNcGNlywYOQgHZirGkSJ8gHNEky+AkS58qWEJYC/bMzacmbQHkqNdlUJ1KoSz2i9COhmQYCEXtVrCBgwYS3cCf8qTcNQ9u4cFFOq2bPLV65Cf7dxZthbjW+CgbjnWtNgWPFcAsHdoxgWWK/iyV045sAc2S96SDn1exYw17REwpLQEYt2eW/qtPZRQAB7QoC61RW+GsBwYZ/CXb/XRCYLsAKFizEtUAc+G7lcZsjroscOvTmsoUvx15PwccJ0N8yL17N9PG/E7jv9S4hOV7pdIPDdZ+ePDzv2qMXn2b5+wTbKuAWnF3oZbABZY0lVmD/ApQd9thybxno2GGuCVDggaUpoyBsB1bGGgIYbJCBcuFJiOAyGohIInQSmmdeiBnMF2GHfNUlIoc1rncjYRjW6NgGf3VQGILWwNjBfxEZcAFbC7gHXQcfUYOYdwzQNxo5yUhQZXhvRYlMeVSuSOJHKJa5AQMQThBlZWZ6Bp4Fa1qzTAJbijcBlJrtxeaZ4lnnpZwpukWieGQmYx5ATXIplwTL8DdNZ07CtWYybNIJF4Ap4NZHe0920AEDk035kafieQrqXofK5ympn5JHKYjPrfoWcR8WWQGp4Ul32KPVgXdnqxM6OKqspjIYrGPDrlrsZtRIcOuR86nHFwbPvmes/6PH4frrqbvySh+mKGhaAARPzjjdhCramdoGGOhp44i+zogBkSDuWC5KlE4r4pHJkarXrj++Raq5iLmWLlxHBteavjG+6amJrUkJJI4Ro5sBv9AaOK+jAau77sbH7nspCwNIYIACffL7J4JtWQnen421nNzMcB6AqpRa9klonmBSiR4GNi+cJZpvwgX0ejj71W9yR+eIgaVvQgf0l/A8nWjUFhwtZYWC4hVnkZ3p/PJqNQ5NnwUQrQCGBBBMQIGTtL7abK+5JjAv1fi9bS0GLlJHgdjEgYzzARTwC1fgEWdJuKKBZzj331Y23qB3i9v5aY/rSUC4w7PaLeWXmr9NszMFoN79eeiM232o33EJAIzaSGwh++y012777bhT0UURvPfu++/ABy/88MQXb/zxyCd/QggAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEBY5nwCk7xIWNer0hO95wziC9Ttg5b4ND/+Y87IBqZAaEe29zGwmJigmDfHoGiImTjXiQhJEPdYyWhXwDmpuVmHwOoHZqjI6kZ3+MqhyemJKAdo6Ge3OKbEd4ZRwFBV4rc4MPrgYPChrMzAgbyZSJBcoI1tfQoYsJydfe2amT3d7W0OGp1OTl0YtqyQrq0Lt11PDk3KGoG+nxBpvTD9QhwCctm0BzbOyMIwdOUwEDEgawIOCB2oMLgB4wgMCx44IHBySIHClBY0ePfyT/JCB5weRJCAwejFw58kGDlzBTqqTZcuPLmCIBiWx58+VHmiRLFj0JVCVLl0xl7qSZwCbOo0lFWv0pdefQrVFDJtr5gMBEYBgxqBWwYILbtxPsqMPAFu7blfa81bUbN4HAvXAzyLWnoDBguHIRFF6m4LBbwQngMYPXuC3fldbyPrMcGLM3w5wRS1iWWUNlvnElKDZtz/EEwaqvYahQoexEfyILi4RrYYKFZwJ3810QWZ2ECrx9Ew+O3K6F5Yq9zXbb+y30a7olJJ+wnLC16W97Py+uwdtx1NcLWzs/3G9e07stVPc9kHJ0BcLtQp+c3ewKAgYkUAFpCaAmmHqKLSYA/18WHEiZPRhsQF1nlLFWmIR8ZbDBYs0YZuCGpGXWmG92aWiPMwhEOOEEHXRwIALlwXjhio+BeE15IzpnInaLbZBBhhti9x2GbnVQo2Y9ZuCfCgBeMCB+DJDIolt4iVhOaNSJdCOBUfIlkmkyMpPAAvKJ59aXzTQzJo0WoJnmQF36Jp6W1qC4gWW9GZladCiyJd+KnsHImgRRVjfnaDEKuiZvbcYWo5htzefbl5LFWNeSKQAo1QXasdhiiwwUl2B21H3aQaghXnPcp1NagCqYslXAqnV+zYWcpNwVp9l5eepJnHqL4SdBi56CGlmw2Zn6aaiZjZqfb8Y2m+Cz1O0n3f+tnvrGbF6kToApCgAWoNWPeh754JA0vmajiAr4iOuOW7abQXVGNriBWoRdOK8FxNqLwX3oluubhv8yluRbegqGb536ykesuoXhyJqPQJIGbLvQhkcwjKs1zBvBwSZIsbcsDCCBAAf4ya+UEhyQoIiEJtfoZ7oxUOafE2BwgMWMqUydfC1LVtiArk0QtGkWEopzlqM9aJrKHfw5c6wKjFkmXDrbhwFockodtMGFLWpXy9JdiXN1ZDNszV4WSLQCGBKoQYHUyonqrHa4ErewAgMmcAAF7f2baIoVzC2p3gUvJtLcvIWqloy6/R04mIpLwDhciI8qLOB5yud44pHPLbA83hFDWPjNbuk9KnySN57Av+TMBvgEAgzzNhJb5K777rz37vvvVHRRxPDEF2/88cgnr/zyzDfv/PPQnxACACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIUCwcMpO84OT2HDbm8GHLQjnn6wE3g83SA3DB55G3llfHxnfnZ4gglvew6Gf4ySgmYGlpCJknochWiId3kJcZZyDn93i6KPl4eniopwq6SIoZKxhpenbhtHZRxhXisDopwPgHkGDxrLGgjLG8mC0gkFDwjX2AgJ0bXJ2djbgNJsAtbfCNB2oOnn6MmKbeXt226K1fMGi6j359D69ua+QZskjd+3cOvY9XNgp4ABCQNYEDBl7EIeCQkeMIDAseOCBwckiBSZ4ILGjh4B/40kaXIjSggMHmBcifHky5gYE6zM2OAlzGM6Z5rs+fIjTZ0tfcYMSlLCUJ8fL47kCVXmTjwPiKJkUCDnyqc3CxzQmYeAxAEGLGJYiwCDgAUT4sqdgOebArdw507IUNfuW71xdZ7DC5iuhGsKErf9CxhPYgUaEhPWyzfBMgUIJDPW6zhb5M1y+R5GjFkBaLmCM0dOfHqvztXYJnMejaFCBQlmVxAYsEGkYnQV4lqYMNyCtnYSggNekAC58uJxmTufW5w55mwKkg+nLp105uTC53a/nhg88fMTmDfDVl65Xum/IZt/3/zaag3a5W63nll1dvfiWbaaZLmpQIABCVQA2f9lAhTG112PQWYadXE9+FtmEwKWwQYQJrZagxomsOCAGVImInsSbpCBhhwug6KKcXXQQYUcYuDMggrASFmNzjjzzIrh7cUhhhHqONeGpSEW2QYxHsmjhxpgUGAKB16g4IIbMNCkXMlhaJ8GWVJo2I3NyKclYF1GxgyYDEAnXHJrMpNAm/rFBSczPiYAlwXF8ZnmesvoOdyMbx7m4o0S5LWdn4bex2Z4xYmEzaEb5EUcnxbA+WWglqIn6aHPTInCgVbdlZyMqMrIQHMRSiaBBakS1903p04w434n0loBoQFOt1yu2YAnY68RXiNsqh2s2qqxuyKb7Imtmgcrqsp6h8D/fMSpapldx55nwayK/SfqCQd2hcFdAgDp5GMvqhvakF4mZuS710WGIYy30khekRkMu92GNu6bo7r/ttjqwLaua5+HOdrKq5Cl3dcwi+xKiLBwwwom4b0E6xvuYyqOa8IAEghwQAV45VvovpkxBl2mo0W7AKbCZXoAhgMmWnOkEqx2JX5nUufbgJHpXCfMOGu2QAd8eitpW1eaNrNeMGN27mNz0swziYnpSbXN19gYtstzfXrdYjNHtAIYGFVwwAEvR1dfxdjKxVzAP0twAAW/ir2w3nzTd3W4yQWO3t0DfleB4XYnEHCEhffdKgaA29p0eo4fHLng9qoG+OVyXz0gMeWGY7qq3xhiRIEAwayNxBawxy777LTXbjsVXRSh++689+7778AHL/zwxBdv/PEnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLD4BlwHGg0ubBpuzdm9Dk9eCTu+MTZkDb4PXYbeIIcHHxqf4F3gnqGY2kOdQmCjHCGfpCSjHhmh2N+knmEkJmKg3uHfgaaeY2qn6t2i4t7sKAPbwIJD2VhXisDCQZgDrKDBQ8aGgjKyhvDlJMJyAjV1gjCunkP1NfVwpRtk93e2ZVt5NfCk27jD97f0LPP7/Dr4pTp1veLgvrx7AL+Q/BM25uBegoYkDCABYFhEobhkUBRwoMGEDJqXPDgQMUEFC9c1LjxQUUJICX/iMRIEgIDkycrjmzJMSXFlDNJvkwJsmdOjQwKfDz5M+PLoSGLQqgZU6XSoB/voHxawGbFlS2XGktAwKEADB0xiEWAodqGBRPSqp1wx5qCamDRrp2Qoa3bagLkzrULF4GCvHPTglRAmKxZvWsHayBcliDitHUlvGWM97FgCdYWVw4c2e/kw4HZJlCwmDBhwHPrjraGYTHqtaoxVKggoesKAgd2SX5rbUMFCxOAC8cGDwHFwBYWJCgu4XfwtcqZV0grPHj0u2SnqwU+IXph3rK5b1fOu7Bx5+K7L6/2/Xhg8uyXnQ8dvfRiDe7TwyfNuzlybKYpgIFtKhAgwEKkKcOf/wChZbBBgMucRh1so5XH3wbI1WXafRJy9iCErmX4IWHNaIAhZ6uxBxeGHXQA24P3yYfBBhmgSBozESpwongWOBhggn/N1aKG8a1YY2oVAklgCgQUUwGJ8iXAgItrWUARbwpqIOWEal0ZoYJbzmWlZCWSlsAC6VkwZonNbMAAl5cpg+NiZwpnJ0Xylegmlc+tWY1mjnGnZnB4QukMA9UJRxGOf5r4ppqDjjmnfKilh2ejGiyJAgF1XNmYbC2GmhZ5AcJVgajcXecNqM9Rx8B6bingnlotviqdkB3YCg+rtOaapFsUhSrsq6axJ6sEwoZK7I/HWpCsr57FBxJ1w8LqV/81zbkoXK3LfVeNpic0KRQG4NHoIW/XEmZuaiN6tti62/moWbk18uhjqerWS6GFpe2YVotskVssWfBOAHACrZHoWcGQwQhlvmsdXBZ/F9YLMF2jzUuYBP4a7CLCnoEHrgkDSCDAARUILAGaVVqAwQHR8pZXomm9/ONhgjrbgc2lyYxmpIRK9uSNjrXs8gEbTrYyl2ryTJmsLCdKkWzFQl1lWlOXGmifal6p9VnbQfpyY2SZyXKVV7JmZkMrgIFSyrIeUJ2r7YKnXdivUg1kAgdQ8B7IzJjGsd9zKSdwyBL03WpwDGxwuOASEP5vriO2F3nLjQdIrpaRDxqcBdgIHGA74pKrZXiR2ZWuZt49m+o3pKMC3p4Av7SNxBa456777rz37jsVXRQh/PDEF2/88cgnr/zyzDfv/PMnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLDUPAMHGi0weEpbN7wI8cxTzsGj4R+n+DUxwaBeBt7hH1/gYIPhox+Y3Z3iwmGk36BkIN8egOIl3h8hBuOkAaZhQlna4BrpnyWa4mleZOFjrGKcXoFA2ReKwMJBgISDw6abwUPGggazc0bBqG0G8kI1tcIwZp51djW2nC03d7BjG8J49jl4cgP3t/RetLp1+vT6O7v5fKhAvnk0UKFogeP3zmCCIoZkDCABQFhChQYuKBHgkUJkxpA2MhxQYEDFhNcvPBAI8eNCx7/gMQYckPJkxsZPLhIM8FLmDJrYiRp8mTKkCwT8IQJwSPQkENhpgQpEunNkzlpWkwKdSbGihKocowqVSvKWQkIOBSgQOYFDBgQpI0oYMGEt3AzTLKm4BqGtnDjirxW95vbvG/nWlub8G9euRsiqqWLF/AEkRoiprX2wLDeDQgkW9PQGLDgyNc665WguK8C0XAnRY6oGPUEuRLsgk5g+a3cCxUqSBC7gsCBBXcVq6swwULx4hayvctGPK8FCwsSLE9A3Hje6NOrHzeOnW695sffRi/9HfDz7sIVSNB+XXrmugo0rHcM3X388o6jr44ceb51uNjF1xcC8zk3wXiS8aYC/wESaLABBs7ch0ECjr2WAGvLsLZBeHqVFl9kGxooV0T81TVhBo6NiOEyJ4p4IYnNRBQiYCN6x4wCG3ZAY2If8jXjYRcyk2FmG/5nXAY8wqhWAii+1YGOSGLoY4VRfqiAgikwmIeS1gjAgHkWYLQZf9m49V9gDWYWY5nmTYCRM2TS5pxxb8IZGV5nhplmhJyZadxzbrpnZ2d/6rnZgHIid5xIMDaDgJfbLdrgMkKW+Rygz1kEZz1mehabkBpgiQIByVikwGTqVfDkk2/Vxxqiqur4X3fksHccre8xlxerDLiHjQIVUAgXr77yFeyuOvYqXGbMrbrqBMqaFpFFzhL7qv9i1FX7ZLR0LUNdcc4e6Cus263KbV+inkAAHhJg0BeITR6WmHcaxhvXg/AJiKO9R77ILF1FwmVdAu6WBu+ZFua72mkZWMfqBElKu0G8rFZ5n4ATp5jkmvsOq+Nj7u63ZMMPv4bveyYy6fDH+C6brgnACHBABQUrkGirz2FwAHnM4Mmhzq9yijOrOi/MKabH6VwBiYwZdukEQAvILKTWXVq0ZvH5/CfUM7M29Zetthp1eht0eqkFYw8IKXKA6mzXfTeH7fZg9zW0AhgY0TwthUa6Ch9dBeIsbsFrYkRBfgTfiG0FhwMWnbsoq3cABUYOnu/ejU/A6uNeT8u4wMb1WnBCyJJTLjjnr8o3OeJrUcpc5oCiPqAEkz8tXuLkPeDL3Uhs4fvvwAcv/PDEU9FFEcgnr/zyzDfv/PPQRy/99NRXf0IIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIWCw/AoDziOtCHt8BQ28PjmzK57Hom8fo42+P8DeAkbeYQcfX9+gYOFg4d1bIGEjQmPbICClI9/YwaLjHAJdJeKmZOViGtpn3qOqZineoeJgG8CeWUbBV4rAwkGAhIVGL97hGACGsrKCAgbBoTRhLvN1c3PepnU1s2/oZO6AtzdBoPf4eMI3tIJyOnF0YwFD+nY8e3z7+Xfefnj9uz8cVsXCh89axgk7BrAggAwBQsYIChwQILFixIeNIDAseOCBwcSXMy2sSPHjxJE/6a0eEGjSY4MQGK86PIlypUJEmYsaTKmyJ8JW/Ls6HMkzaEn8YwMWtPkx4pGd76E4DMPRqFTY860OGhogwYagBFoKEABA46DEGBAoEBB0AUT4sqdIFKBNbcC4M6dkEEk22oYFOTdG9fvWrtsBxM23MytYL17666t9phwXwlum2lIDHmuSA2IGyuOLOHv38qLMbdFjHruZbWgRXeOe1nC2BUEDiyAMMHZuwoTLAQX3nvDOAUW5Vogru434d4JnAsnPmFB9NBshQXfa9104+Rxl8e13rZxN+CEydtVsFkd+vDjE7C/q52wOvb4s7+faz025frbxefWbSoQIAEDEUCwgf9j7bUlwHN9ZVaegxDK1xYzFMJH24L5saXABhlYxiEzHoKoIV8LYqAMaw9aZqFmJUK4YHuNfRjiXhmk+NcyJgaIolvM8BhiBx3IleN8lH1IWAcRgkZgCgYiaBGJojGgHHFTgtagAFYSZhF7/qnTpY+faVlNAnqJN0EHWa6ozAZjBtgmmBokwMB01LW5jAZwbqfmlNips4B4eOqJgDJ2+imXRZpthuigeC6XZTWIxilXmRo8iYKBCwiWmWkJVEAkfB0w8KI1IvlIpKnOkVpqdB5+h96o8d3lFnijrgprjbfGRSt0lH0nAZG5vsprWxYRW6Suq4UWqrLEsspWg8Io6yv/q6EhK0Fw0GLbjKYn5CZYBYht1laPrnEY67kyrhYbuyceiR28Pso7bYwiXjihjWsWuWF5p/H765HmNoiur3RJsGKNG/jq748XMrwmjhwCfO6QD9v7LQsDxPTAMKsFpthyJCdkmgYiw0VdXF/Om9dyv7YMWGXTLYpZg5wNR11C78oW3p8HSGgul4qyrJppgllJHJZHn0Y0yUwDXCXUNquFZNLKyYXBAVZvxtAKYIQEsmPgDacr0tltO1y/DMwYpkgUpJfTasLGzd3cdCN3gN3UWRcY3epIEPevfq+3njBxq/kqBoGBduvea8f393zICS63ivRBTqgFpgaWZEIUULdcK+frIfAAL2AjscXqrLfu+uuwx05FF0XUbvvtuOeu++689+7778AHL/wJIQAAOwAAAAAAAAAAAA==");
+
+define("text!cockpit/ui/images/closer.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAj9JREFUeNp0ks+LUlEUx7/vV1o8Z8wUx3IEHcQmiBiQlomjRNCiZpEuEqF/oEUwq/6EhvoHggmRcJUQBM1CRJAW0aLIaGQimZJxJsWxyV/P9/R1zzWlFl04vPvOPZ9z7rnnK5imidmKRCIq+zxgdoPZ1T/ut8xeM3tcKpW6s1hhBkaj0Qj7bDebTX+324WmadxvsVigqipcLleN/d4rFoulORiLxTZY8ItOp8MBCpYkiYPj8Xjus9vtlORWoVB4KcTjcQc732dLpSRXvCZaAws6Q4WDdqsO52kNH+oCRFGEz+f7ydwBKRgMPmTXi49GI1x2D/DsznesB06ws2eDbI7w9HYN6bVjvGss4KAjwDAMq81mM2SW5Wa/3weBbz42UL9uYnVpiO2Nr9ANHSGXib2Wgm9tCYIggGKJEVkvlwgi5/FQRmTLxO6hgJVzI1x0T/fJrBtHJxPeL6tI/fsZLA6ot8lkQi8HRVbw94gkWYI5MaHrOjcCGSNRxZosy9y5cErDzn0Dqx7gcwO8WtBp4PndI35GMYqiUMUvBL5yOBz8yRfFNpbPmqgcCFh/IuHa1nR/YXGM8+oUpFhihEQiwcdRLpfVRqOBtWXWq34Gra6AXq8Hp2piZcmKT4cKnE4nwuHwdByVSmWQz+d32WCTlHG/qaHHREN9kgi0sYQfv0R4PB4EAgESQDKXy72fSy6VSnHJVatVf71eR7vd5n66mtfrRSgU4pLLZrOlf7RKK51Ok8g3/yPyR5lMZi7y3wIMAME4EigHWgKnAAAAAElFTkSuQmCC");
+
+define("text!cockpit/ui/images/pinin.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQ4Ea2TPUsDQRCGZ89Eo4FACkULEQs1CH4Uamfjn7GxEYJFIFXgChFsbPwzNnZioREkaiHBQtEiEEiMRm/dZ8OEGAxR4sBxx877Pju7M2estTJIxLrNuVwuMxQEx0ZkzcFHyRtjXt02559RtB2GYanTYzoryOfz+6l4Nbszf2niwffKmpGRo9sVW22mDgqFwp5C2gDMm+P32a3JB1N+n5JifUGeP9JeNxGryPLYjcwMP8rJ07Q9fZltQzyAstOJ2vVu5sKc1ZZkRBrOcKeb+HexPidvkpCN5JUcllZtpZFc5DgBWc5M2eysZuMuofMBSA4NWjx4PUCsXefMlI0QY3ewRg4NWi4ZTQsgrjYXema+e4VqtEMK6KXvu+4B9Bklt90vVKMeD2BI6DOt4rZ/Gk7WyKFBi4fNPIAJY0joM61SCCZ9tI1o0OIB8D+DBIkYaJRbCBH9mZgNt+bb++ufSSF/eX8BYcDeAzuQJVUAAAAASUVORK5CYII=");
+
+define("text!cockpit/ui/images/plus.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4yFTwuJTkAAAH7SURBVCjPdZKxa1NRFMZ/956XZMgFyyMlCZRA4hBx6lBcQ00GoYi4tEstFPwLAs7iLDi7FWuHThaUggihBDI5OWRoQAmBQFISQgvvpbwX3rsOaR4K+o2H8zvfOZxPWWtZqVarGaAJPAEe3ZW/A1+Bd+1221v1qhW4vb1dA44mk0nZ8zyCIAAgk8lgjGF9fb0PHF5cXLQTsF6vP/c879P19TVBEJDJZBARAKIoSmpra2sYY561Wq3PqtFouMBgMBgYay3ZbJZ/yfd9tNaUSqUboOKISPPq6sqsVvZ9H4AvL34B8PTj/QSO45jpdHovn883Ha31znw+JwzDpCEMQx4UloM8zyOdTif3zudztNY7jog8DMMQpRRxHPPt5TCBAEZvxlyOFTsfykRRBICIlB2t9a21Nh3HMXEc8+d7VhJHWCwWyzcohdZaHBHpO46z6fs+IsLj94XECaD4unCHL8FsNouI/HRE5Nx13c3ZbIbWOnG5HKtl+53TSq7rIiLnand31wUGnU7HjEYjlFLJZN/3yRnL1FMYY8jlcmxtbd0AFel2u7dnZ2eXxpi9xWJBEASkUimstYgIQSSkUimKxSKVSgVjzN7p6emPJHL7+/s14KjX65WHwyGz2SxZbWNjg2q12gcOT05O2n9lFeDg4MAAr/4T8rfHx8dJyH8DvvbYGzKvWukAAAAASUVORK5CYII=");
+
+define("text!cockpit/ui/images/minus.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4xMrIJw5EAAAHcSURBVCjPhZIxSxtxGMZ/976XhJA/RA5EAyJcFksnp64hjUPBoXRyCYLQTyD0UxScu0nFwalCQSgFCVk7dXAwUAiBDA2RO4W7yN1x9+9gcyhU+pteHt4H3pfncay1LOl0OgY4BN4Ar/7KP4BvwNFwOIyWu87S2O12O8DxfD73oygiSRIAarUaxhhWV1fHwMFgMBiWxl6v9y6Koi+3t7ckSUKtVkNVAcjzvNRWVlYwxry9vLz86uzs7HjAZDKZGGstjUaDfxHHMSLC5ubmHdB2VfVwNpuZ5clxHPMcRVFwc3PTXFtbO3RFZHexWJCmabnweAaoVqvlv4vFAhHZdVX1ZZqmOI5DURR8fz/lxbp9Yrz+7bD72SfPcwBU1XdF5N5aWy2KgqIoeBzPEnWVLMseYnAcRERdVR27rrsdxzGqyutP6898+GBsNBqo6i9XVS88z9sOggAR4X94noeqXoiIHPm+H9XrdYIgIAxDwjAkTVPCMESzBy3LMprNJr7v34nIkV5dXd2fn59fG2P2siwjSRIqlQrWWlSVJFcqlQqtVot2u40xZu/s7OxnWbl+v98BjkejkT+dTgmCoDxtY2ODra2tMXBweno6fNJVgP39fQN8eKbkH09OTsqS/wHFRdHPfTSfjwAAAABJRU5ErkJggg==");
+
+define("text!cockpit/ui/images/dot_clear.gif", [], "data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAEBMgA7");
+
+define("text!cockpit/ui/images/pins.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGYklEQVRIDbVWe0yURxCf/R735o6DO0FBe0RFsaL4iLXGIKa2SY3P6JGa2GpjlJjUV9NosbU++tYUbEnaQIrVaKJBG7WiNFQFUWO1UUEsVg2CAgoeHHLewcH32O58cBdQsX9Y5+7LfrszOzO/2ZnZj1BKgTBiIwVGVvKd49OVVYunDlXn6wdBKh+ogXrv+DOz1melIb+3LM5fNv2XPYE5EHY+L3PJljN5zavHpJjsQNsA/JJEgyC2+WTjy3b0GfoJW8O4aoHtDwiHQrj5lw1LLyyb1bp5zAjJTus9klrVpdD6TqH2ngVO+0dsRJnp06cLIYU4fx7NnRI3bu7UIYOeJ/McnuY88q3k62gc0S4Dgf5qhICQtIXS2lqD7BhSduPk3YfyzXaANhBBJDxYdUqCywB2qS4RdyUuSkTF/VJxcbH5j8N7/75RuFrN3Zh8OS8zqf5m4UpPeenOyP42dbtBeuvVnCdkK1e4PfPouX03mo9se+c33M8wqDk5Ofqed8REUTicQhbySUxp9u3KlMSHTtrFU6Kyn03lz15PPpW25vsZeYSIKyiVURcqeZJOH9lTNZLfnxRjU/uwrjbEUBWsapcSO2Hq4k0VfZg9EzxdDNCEjDxgNqRDme9umz/btwlsHRIEePHgAf73RdnHZ6LTuIUBN7OBQ+c1Fdnp6cZ1BQUdeRuWZi97o3ktDQQkVeFFzqJARd1A5a0Vr7ta6Kp6TZjtZ+NTIOoKF6qDrL7e0QQIUCiqMMKk8Z1Q/SCSKvzocf2B6NEN0SQn/kTO6fKJ0zqjZUlQBSpJ0GjR77w0aoc1Pr6S5/kVJrNpakV5hR+LWKN4t7sLX+p0rx2vqSta64olIulUKUgCSXLWE1R4KPPSj+5vhm2hdDOG+CkQBmhhyyKq6SaFYWTn5bB3QJRNz54AuXKn8TJjhu0Wbv+wNEKQjVhnmKopjo4FxXmetCRnC4F7BhCiCUepqAepRh0TM/gjjzOOSK2NgWZPc05qampRWJHb7dbOffep2ednzLzgczlbrQA6gHYF9BYDh9GY+FjddMweHMscmMuep07gXlMQoqw9ALoYu5MJsak9QmJA2IvAgVmoCRciooyPujJtNCv1uHt3TmK9gegFKrG9kh6oXwZiIEAtBIjORGKNTWR/WeW8XVkbjuJepLAyloM8LmTN//njKZPbraATZaLjCHEww9Ei4FFiPg6Ja5gT6gxYgLgnRDHRQwJXbz2GOw0d4A3K4GXlUtMahJjYVxiYbrwOmxIS10bFnIBOSi6Tl9Jgs0zbOEX18wyEwgLPMrxD1Y4aCK8kmTpgYcpAF27Mzs42Hjx4kA8BICUlJfKArR7LcEvTB1xEC9AoEw9OPagWkVU/D1oesmK6U911zEczMVe01oZjiMggg6ux2Qk379qh4rYKet4GjrhhwEteBgBrH8BssoXEtbHzPpSBRRSpqlNpgAiUoxzHKxLRszoVuggIisxaDQWZqkQvQjAoax3NbDbLLGuUEABNGedXqSyLRupXgDT5JfAGZNLio9B0X8Uiwk4w77MDc1D4yejjWtykPS3DX01UDCY/GPQcVDe0QYT0CIxGFvUorfvBxZsRfVrUuWruMBAb/lXCUofoFNZfzGJtowXOX0vwUSFK4BgyMKm6P6s9wQUZld+jrYyMDC0iIQDaJdG4IyZQfL3RfbFcCBIlRgc+u3CjaTApuZ9KsANgG8PNzHlWWD3tCxd6kafNNiFp5HAalAkkJ0SCV2H3CgOD9Nc/FqrXuyb0Eocvfhq171p5eyuJ1omKJEP5rQGe/FOOnXtq335z8YmvYo9cHb2t8spIb3lVSseZW46FlGY/Sk9P50P2w20UlWJUkUHIushfc5PXGAzCo0PlD2pnpCYfCXga3lu+fPlevEhWrVrFyrN/Orfv87FOW9tlqb2Kc9pV8DzioMk3UNUbXM+8B/ATBr8C8CKdvGXWGD/9sqm3dkxtzA4McMjHMB8D2ftheYXo+qzt3pXvz8/PP/vk+v8537V+yYW87Zu+RZ1ZbrexoKAA/SBpaWn4+aL5w5zGk+/jW59JiMkESW5urpiVlWXENRb1H/Yf2I9txIxz5IdkX3TsraukpsbQjz6090yb4XsAvQoRE0YvJdamtIIbOnRoUVlZ2ftsLVQzIdEXHntsaZdimssVfCpFui109+BnWPsXaWLI/zactygAAAAASUVORK5CYII=");
+
+define("text!cockpit/ui/images/pinout.png", [], "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACyUlEQVQ4EW1TXUgUURQ+Z3ZmnVV3QV2xJbVSEIowQbAfLQx8McLoYX2qjB58MRSkP3vZppceYhGxgrZaIughlYpE7CHFWiiKyj9II0qxWmwlNh1Xtp2f27mz7GDlZX7uuXO+73zfuXeQMQYIgAyALppgyBtse32stsw86txkHhATn+FbfPfzxnPB+vR3RMJYuTwW6bbB4a6WS5O3Yu2VlXIesDiAamiQNKVlVXfx5I0GJ7DY7p0/+erU4dgeMJIA31WNxZmAgibOreXDqF55sY4SFUURqbi+nkjgwTyAbHhLX8yOLsSM2QRA3JRAAgd4RGPbVhkKEp8qeJ7PFyW3fw++YHtC7CkaD0amqyqihSwlMQQ0wa07IjPVI/vbexreIUrVaQV2D4RMQ/o7m12Mdfx4H3PfB9FNzTR1U2cO0Bi45aV6xNvFBNaoIAfbSiwLlqi9/hR/R3Nrhua+Oqi9TEKiB02C7YXz+Pba4MTDrpbLiMAxNgmXb+HpwVkZdoIrkn9isW7nRw/TZYaagZArAWyhfqsSDL/c9aTx7JUjGZCtYExRqCzAwGblwr6aFQ84nTo6qZ7XCeCVQNckE/KSWolvoQnxeoFFgIh8G/nA+kBAxxuQO5m9eFrwLIGJHgcyM63VFMhRSgNVyJr7og8y1vbTQpH8DIEVgxuYuexw0QECIalq5FYgEmpkgoFYltU/lnrqDz5osirSFpF7lrHAFKSWHYfEs+mY/82UnAStyMlW8sUPsVIciTZgz3jV1ebg0CEOpgPF22s1z1YQYKSXPJ1hbAhR8T26WdLhkuVfAzPR+YO1Ox5n58SmCcF6e3uzAoHA77RkevJdWH/3+f2O9TGf3w3fWQ2Hw5F/13mcsWAT+vv6DK4kFApJ/d3d1k+kJtbCrmxXHS3n8ER6b3CQbAqaEHVra6sGxcXW4SovLx+empxapS//FfwD9kpMJjMMBBAAAAAASUVORK5CYII=");
+
diff --git a/studio/static/js/ace/cockpit.js b/studio/static/js/ace/cockpit.js
new file mode 100644
index 0000000..ecdc74c
--- /dev/null
+++ b/studio/static/js/ace/cockpit.js
@@ -0,0 +1 @@
+define("cockpit/index",["require","exports","module","pilot/index","cockpit/cli","cockpit/ui/settings","cockpit/ui/cli_view","cockpit/commands/basic"],function(a,b,c){b.startup=function(b,c){a("pilot/index"),a("cockpit/cli").startup(b,c),a("cockpit/ui/settings").startup(b,c),a("cockpit/ui/cli_view").startup(b,c),a("cockpit/commands/basic").startup(b,c)}}),define("cockpit/cli",["require","exports","module","pilot/console","pilot/lang","pilot/oop","pilot/event_emitter","pilot/types","pilot/canon"],function(a,b,c){function r(a,b){q.call(this,a),b&&b.flags&&(this.flags=b.flags)}function q(a){this.env=a,this.commandAssignment=new o(p,this)}function o(a,b){this.param=a,this.requisition=b,this.setValue(a.defaultValue)}function n(a,b,c,d,e,f){this.emitter=a,this.setText(b),this.start=c,this.end=d,this.prefix=e,this.suffix=f}function m(a,b){this.status=a.status,this.message=a.message,b?(this.start=b.start,this.end=b.end):(this.start=0,this.end=0),this.predictions=a.predictions}function l(a,b,c,d,e){this.status=a,this.message=b;if(typeof c=="number")this.start=c,this.end=d,this.predictions=e;else{var f=c;this.start=f.start,this.end=f.end,this.predictions=f.predictions}}var d=a("pilot/console"),e=a("pilot/lang"),f=a("pilot/oop"),g=a("pilot/event_emitter").EventEmitter,h=a("pilot/types"),i=a("pilot/types").Status,j=a("pilot/types").Conversion,k=a("pilot/canon");b.startup=function(a,b){k.upgradeType("command",p)},l.prototype={},l.sort=function(a,b){b!==undefined&&a.forEach(function(a){a.start===n.AT_CURSOR?a.distance=0:b<a.start?a.distance=a.start-b:b>a.end?a.distance=b-a.end:a.distance=0},this),a.sort(function(a,c){if(b!==undefined){var d=a.distance-c.distance;if(d!=0)return d}return c.status-a.status}),b!==undefined&&a.forEach(function(a){delete a.distance},this);return a},b.Hint=l,f.inherits(m,l),n.prototype={merge:function(a){if(a.emitter!=this.emitter)throw new Error("Can't merge Arguments from different EventEmitters");return new n(this.emitter,this.text+this.suffix+a.prefix+a.text,this.start,a.end,this.prefix,a.suffix)},setText:function(a){if(a==null)throw new Error("Illegal text for Argument: "+a);var b={argument:this,oldText:this.text,text:a};this.text=a,this.emitter._dispatchEvent("argumentChange",b)},toString:function(){return this.prefix+this.text+this.suffix}},n.merge=function(a,b,c){b=b===undefined?0:b,c=c===undefined?a.length:c;var d;for(var e=b;e<c;e++){var f=a[e];d?d=d.merge(f):d=f}return d},n.AT_CURSOR=-1,o.prototype={param:undefined,conversion:undefined,value:undefined,arg:undefined,value:undefined,setValue:function(a){if(this.value!==a){if(a===undefined)this.value=this.param.defaultValue,this.conversion=this.param.getDefault?this.param.getDefault():this.param.type.getDefault(),this.arg=undefined;else{this.value=a,this.conversion=undefined;var b=a==null?"":this.param.type.stringify(a);this.arg&&this.arg.setText(b)}this.requisition._assignmentChanged(this)}},arg:undefined,setArgument:function(a){this.arg!==a&&(this.arg=a,this.conversion=this.param.type.parse(a.text),this.conversion.arg=a,this.value=this.conversion.value,this.requisition._assignmentChanged(this))},getHint:function(){if(this.param.getCustomHint&&this.value&&this.arg){var a=this.param.getCustomHint(this.value,this.arg);if(a)return a}var b="<strong>"+this.param.name+"</strong>: ";this.param.description&&(b+=this.param.description.trim(),b.charAt(b.length-1)!=="."&&(b+="."),b.charAt(b.length-1)!==" "&&(b+=" "));var c=i.VALID,d=this.arg?this.arg.start:n.AT_CURSOR,e=this.arg?this.arg.end:n.AT_CURSOR,f;this.conversion&&(c=this.conversion.status,this.conversion.message&&(b+=this.conversion.message),f=this.conversion.predictions);var g=this.arg&&this.arg.text!=="",h=this.value!==undefined||g;this.param.defaultValue===undefined&&!h&&(c=i.INVALID,b+="<strong>Required<strong>");return new l(c,b,d,e,f)},complete:function(){this.conversion&&this.conversion.predictions&&this.conversion.predictions.length>0&&this.setValue(this.conversion.predictions[0])},isPositionCaptured:function(a){if(!this.arg)return!1;if(this.arg.start===-1)return!1;if(a>this.arg.end)return!1;if(a===this.arg.end)return this.conversion.status!==i.VALID||this.conversion.predictions.length!==0;return!0},decrement:function(){var a=this.param.type.decrement(this.value);a!=null&&this.setValue(a)},increment:function(){var a=this.param.type.increment(this.value);a!=null&&this.setValue(a)},toString:function(){return this.arg?this.arg.toString():""}},b.Assignment=o;var p={name:"__command",type:"command",description:"The command to execute",getCustomHint:function(a,b){var c=[];c.push("<strong><tt> &gt; "),c.push(a.name),a.params&&a.params.length>0&&a.params.forEach(function(a){a.defaultValue===undefined?c.push(" ["+a.name+"]"):c.push(" <em>["+a.name+"]</em>")},this),c.push("</tt></strong><br/>"),c.push(a.description?a.description:"(No description)"),c.push("<br/>"),a.params&&a.params.length>0&&(c.push("<ul>"),a.params.forEach(function(a){c.push("<li>"),c.push("<strong><tt>"+a.name+"</tt></strong>: "),c.push(a.description?a.description:"(No description)"),a.defaultValue===undefined?c.push(" <em>[Required]</em>"):a.defaultValue===null?c.push(" <em>[Optional]</em>"):c.push(" <em>[Default: "+a.defaultValue+"]</em>"),c.push("</li>")},this),c.push("</ul>"));return new l(i.VALID,c.join(""),b)}};q.prototype={commandAssignment:undefined,assignmentCount:undefined,_assignments:undefined,_hints:undefined,_assignmentChanged:function(a){a.param.name==="__command"&&(this._assignments={},a.value&&a.value.params.forEach(function(a){this._assignments[a.name]=new o(a,this)},this),this.assignmentCount=Object.keys(this._assignments).length,this._dispatchEvent("commandChange",{command:a.value}))},getAssignment:function(a){var b=typeof a=="string"?a:Object.keys(this._assignments)[a];return this._assignments[b]},getParameterNames:function(){return Object.keys(this._assignments)},cloneAssignments:function(){return Object.keys(this._assignments).map(function(a){return this._assignments[a]},this)},_updateHints:function(){this.getAssignments(!0).forEach(function(a){this._hints.push(a.getHint())},this),l.sort(this._hints)},getWorstHint:function(){return this._hints[0]},getArgsObject:function(){var a={};this.getAssignments().forEach(function(b){a[b.param.name]=b.value},this);return a},getAssignments:function(a){var b=[];a===!0&&b.push(this.commandAssignment),Object.keys(this._assignments).forEach(function(a){b.push(this.getAssignment(a))},this);return b},setDefaultValues:function(){this.getAssignments().forEach(function(a){a.setValue(undefined)},this)},exec:function(){k.exec(this.commandAssignment.value,this.env,"cli",this.getArgsObject(),this.toCanonicalString())},toCanonicalString:function(){var a=[];a.push(this.commandAssignment.value.name),Object.keys(this._assignments).forEach(function(b){var c=this._assignments[b],d=c.param.type;c.value!==c.param.defaultValue&&(a.push(" "),a.push(d.stringify(c.value)))},this);return a.join("")}},f.implement(q.prototype,g),b.Requisition=q,f.inherits(r,q),function(){r.prototype.update=function(a){this.input=a,this._hints=[];var b=this._tokenize(a.typed);this._split(b),this.commandAssignment.value&&this._assign(b),this._updateHints()},r.prototype.getInputStatusMarkup=function(){var a=this.toString().split("").map(function(a){return i.VALID});this._hints.forEach(function(b){for(var c=b.start;c<=b.end;c++)b.status>a[c]&&(a[c]=b.status)},this);return a},r.prototype.toString=function(){return this.getAssignments(!0).map(function(a){return a.toString()},this).join("")};var a=r.prototype._updateHints;r.prototype._updateHints=function(){a.call(this);var b=this.input.cursor;this._hints.forEach(function(a){var c=b.start>=a.start&&b.start<=a.end,d=b.end>=a.start&&b.end<=a.end,e=c||d;!e&&a.status===i.INCOMPLETE&&(a.status=i.INVALID)},this),l.sort(this._hints)},r.prototype.getHints=function(){return this._hints},r.prototype.getAssignmentAt=function(a){var b=this.getAssignments(!0);for(var c=0;c<b.length;c++){var d=b[c];if(!d.arg)return d;if(d.isPositionCaptured(a))return d}return d},r.prototype._tokenize=function(a){function g(a){return a.replace(/\uF000/g," ").replace(/\uF001/g,"'").replace(/\uF002/g,'"')}if(a==null||a.length===0)return[new n(this,"",0,0,"","")];var b=1,c=2,d=3,e=4,f=b;a=a.replace(/\\\\/g,"\\").replace(/\\b/g,"\b").replace(/\\f/g,"\f").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\v/g," ").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\ /g,"").replace(/\\'/g,"").replace(/\\"/g,"");var h=0,i=0,j="",k=[];for(;;){if(h>=a.length){if(f!==b){var l=g(a.substring(i,h));k.push(new n(this,l,i,h,j,""))}else if(h!==i){var m=a.substring(i,h),o=k[k.length-1];o?o.suffix+=m:(o=new n(this,"",h,h,m,""),k.push(o))}break}var p=a[h];switch(f){case b:p==="'"?(j=a.substring(i,h+1),f=d,i=h+1):p==='"'?(j=a.substring(i,h+1),f=e,i=h+1):/ /.test(p)||(j=a.substring(i,h),f=c,i=h);break;case c:if(p===" "){var l=g(a.substring(i,h));k.push(new n(this,l,i,h,j,"")),f=b,i=h,j=""}break;case d:if(p==="'"){var l=g(a.substring(i,h));k.push(new n(this,l,i-1,h+1,j,p)),f=b,i=h+1,j=""}break;case e:if(p==='"'){var l=g(a.substring(i,h));k.push(new n(this,l,i-1,h+1,j,p)),f=b,i=h+1,j=""}}h++}return k},r.prototype._split=function(a){var b=1,c;while(b<=a.length){var c=n.merge(a,0,b);this.commandAssignment.setArgument(c);if(!this.commandAssignment.value)break;if(this.commandAssignment.value.exec){for(var d=0;d<b;d++)a.shift();break}b++}},r.prototype._assign=function(a){if(a.length===0)this.setDefaultValues();else{if(this.assignmentCount===0){this._hints.push(new l(i.INVALID,this.commandAssignment.value.name+" does not take any parameters",n.merge(a)));return}if(this.assignmentCount===1){var b=this.getAssignment(0);if(b.param.type.name==="text"){b.setArgument(n.merge(a));return}}var c=this.cloneAssignments(),d=this.getParameterNames(),f=[];c.forEach(function(b){var c="--"+b.name,f=0;for(;;){var g=a[f];if(c!==g.text){f++;if(f>=a.length)break;continue}b.param.type.name==="boolean"?b.setValue(!0):f+1<a.length?this._hints.push(new l(i.INCOMPLETE,"Missing value for: "+c,a[f])):(a.splice(f+1,1),b.setArgument(a[f+1])),e.arrayRemove(d,b.name),a.splice(f,1)}},this),d.forEach(function(b){var c=this.getAssignment(b);if(a.length===0)c.setValue(undefined);else{var d=a[0];a.splice(0,1),c.setArgument(d)}},this);if(a.length>0){var g=n.merge(a);this._hints.push(new l(i.INVALID,"Input '"+g.text+"' makes no sense.",g))}}}}(),b.CliRequisition=r}),define("cockpit/ui/settings",["require","exports","module","pilot/types","pilot/types/basic"],function(a,b,c){var d=a("pilot/types"),e=a("pilot/types/basic").SelectionType,f=new e({name:"direction",data:["above","below"]}),g={name:"hintDirection",description:"Are hints shown above or below the command line?",type:"direction",defaultValue:"above"},h={name:"outputDirection",description:"Is the output window shown above or below the command line?",type:"direction",defaultValue:"above"},i={name:"outputHeight",description:"What height should the output panel be?",type:"number",defaultValue:300};b.startup=function(a,b){d.registerType(f),a.env.settings.addSetting(g),a.env.settings.addSetting(h),a.env.settings.addSetting(i)},b.shutdown=function(a,b){d.unregisterType(f),a.env.settings.removeSetting(g),a.env.settings.removeSetting(h),a.env.settings.removeSetting(i)}}),define("cockpit/ui/cli_view",["require","exports","module","text!cockpit/ui/cli_view.css","pilot/event","pilot/dom","pilot/keys","pilot/canon","pilot/types","cockpit/cli","cockpit/ui/request_view"],function(a,b,c){function n(a,b){a.cliView=this,this.cli=a,this.doc=document,this.win=f.getParentWindow(this.doc),this.env=b,this.element=this.doc.getElementById("cockpitInput");!this.element||(this.settings=b.settings,this.hintDirection=this.settings.getSetting("hintDirection"),this.outputDirection=this.settings.getSetting("outputDirection"),this.outputHeight=this.settings.getSetting("outputHeight"),this.isUpdating=!1,this.createElements(),this.update())}var d=a("text!cockpit/ui/cli_view.css"),e=a("pilot/event"),f=a("pilot/dom");f.importCssString(d);var e=a("pilot/event"),g=a("pilot/keys"),h=a("pilot/canon"),i=a("pilot/types").Status,j=a("cockpit/cli").CliRequisition,k=a("cockpit/cli").Hint,l=a("cockpit/ui/request_view").RequestView,m=new k(i.VALID,"",0,0);b.startup=function(a,b){var c=new j(a.env),d=new n(c,a.env);a.env.cli=c},n.prototype={createElements:function(){function d(){f.removeCssClass(this.output,"cptFocusPopup"),f.removeCssClass(this.hinter,"cptFocusPopup")}var a=this.element;this.element.spellcheck=!1,this.output=this.doc.getElementById("cockpitOutput"),this.popupOutput=this.output==null;if(!this.output){this.output=this.doc.createElement("div"),this.output.id="cockpitOutput",this.output.className="cptOutput",a.parentNode.insertBefore(this.output,a.nextSibling);var b=function(){this.output.style.maxHeight=this.outputHeight.get()+"px"}.bind(this);this.outputHeight.addEventListener("change",b),b()}this.completer=this.doc.createElement("div"),this.completer.className="cptCompletion VALID",this.completer.style.color=f.computedStyle(a,"color"),this.completer.style.fontSize=f.computedStyle(a,"fontSize"),this.completer.style.fontFamily=f.computedStyle(a,"fontFamily"),this.completer.style.fontWeight=f.computedStyle(a,"fontWeight"),this.completer.style.fontStyle=f.computedStyle(a,"fontStyle"),a.parentNode.insertBefore(this.completer,a.nextSibling),this.completer.style.backgroundColor=a.style.backgroundColor,a.style.backgroundColor="transparent",this.hinter=this.doc.createElement("div"),this.hinter.className="cptHints",a.parentNode.insertBefore(this.hinter,a.nextSibling);var c=this.resizer.bind(this);e.addListener(this.win,"resize",c),this.hintDirection.addEventListener("change",c),this.outputDirection.addEventListener("change",c),c(),h.addEventListener("output",function(a){new l(a.request,this)}.bind(this)),e.addCommandKeyListener(a,this.onCommandKey.bind(this)),e.addListener(a,"keyup",this.onKeyUp.bind(this)),e.addListener(a,"mouseup",function(a){this.isUpdating=!0,this.update(),this.isUpdating=!1}.bind(this)),this.cli.addEventListener("argumentChange",this.onArgChange.bind(this)),e.addListener(a,"focus",function(){f.addCssClass(this.output,"cptFocusPopup"),f.addCssClass(this.hinter,"cptFocusPopup")}.bind(this)),e.addListener(a,"blur",d.bind(this)),d.call(this)},scrollOutputToBottom:function(){var a=Math.max(this.output.scrollHeight,this.output.clientHeight);this.output.scrollTop=a-this.output.clientHeight},resizer:function(){var a=this.element.getClientRects()[0];this.completer.style.top=a.top+"px";var b=a.bottom-a.top;this.completer.style.height=b+"px",this.completer.style.lineHeight=b+"px",this.completer.style.left=a.left+"px";var c=a.right-a.left;this.completer.style.width=c+"px",this.hintDirection.get()==="below"?(this.hinter.style.top=a.bottom+"px",this.hinter.style.bottom="auto"):(this.hinter.style.top="auto",this.hinter.style.bottom=this.doc.documentElement.clientHeight-a.top+"px"),this.hinter.style.left=a.left+30+"px",this.hinter.style.maxWidth=c-110+"px",this.popupOutput&&(this.outputDirection.get()==="below"?(this.output.style.top=a.bottom+"px",this.output.style.bottom="auto"):(this.output.style.top="auto",this.output.style.bottom=this.doc.documentElement.clientHeight-a.top+"px"),this.output.style.left=a.left+"px",this.output.style.width=c-80+"px")},onCommandKey:function(a,b,c){var d;if(c===g.TAB||c===g.UP||c===g.DOWN)d=!0;else if(b!=0||c!=0)d=h.execKeyCommand(this.env,"cli",b,c);d&&e.stopEvent(a)},onKeyUp:function(a){var b;if(a.keyCode===g.RETURN){var c=this.cli.getWorstHint();c.status===i.VALID?(this.cli.exec(),this.element.value=""):(f.setSelectionStart(this.element,c.start),f.setSelectionEnd(this.element,c.end))}this.update();var d=this.cli.getAssignmentAt(f.getSelectionStart(this.element));d&&(a.keyCode===g.TAB&&(d.complete(),this.update()),a.keyCode===g.UP&&(d.increment(),this.update()),a.keyCode===g.DOWN&&(d.decrement(),this.update()));return b},update:function(){this.isUpdating=!0;var a={typed:this.element.value,cursor:{start:f.getSelectionStart(this.element),end:f.getSelectionEnd(this.element.selectionEnd)}};this.cli.update(a);var b=this.cli.getAssignmentAt(a.cursor.start).getHint();f.removeCssClass(this.completer,i.VALID.toString()),f.removeCssClass(this.completer,i.INCOMPLETE.toString()),f.removeCssClass(this.completer,i.INVALID.toString());var c='<span class="cptPrompt">&gt;</span> ';if(this.element.value.length>0){var d=this.cli.getInputStatusMarkup();c+=this.markupStatusScore(d)}if(this.element.value.length>0&&b.predictions&&b.predictions.length>0){var e=b.predictions[0];c+=" &nbsp;&#x21E5; "+(e.name?e.name:e)}this.completer.innerHTML=c,f.addCssClass(this.completer,this.cli.getWorstHint().status.toString());var g="";this.element.value.length!==0&&(g+=b.message,b.predictions&&b.predictions.length>0&&(g+=": [ ",b.predictions.forEach(function(a){g+=a.name?a.name:a,g+=" | "},this),g=g.replace(/\| $/,"]"))),this.hinter.innerHTML=g,g.length===0?f.addCssClass(this.hinter,"cptNoPopup"):f.removeCssClass(this.hinter,"cptNoPopup"),this.isUpdating=!1},markupStatusScore:function(a){var b="",c=0,d=-1;for(;;){d!==a[c]&&(b+="<span class="+a[c].toString()+">",d=a[c]),b+=this.element.value[c],c++;if(c===this.element.value.length){b+="</span>";break}d!==a[c]&&(b+="</span>")}return b},onArgChange:function(a){if(!this.isUpdating){var b=this.element.value.substring(0,a.argument.start),c=this.element.value.substring(a.argument.end),d=typeof a.text=="string"?a.text:a.text.name;this.element.value=b+d+c;var e=(b+d).length;this.element.selectionStart=e,this.element.selectionEnd=e}}},b.CliView=n}),define("cockpit/ui/request_view",["require","exports","module","pilot/dom","pilot/event","text!cockpit/ui/request_view.html","pilot/domtemplate","text!cockpit/ui/request_view.css"],function(a,b,c){function l(a,b){this.request=a,this.cliView=b,this.imageUrl=k,this.rowin=null,this.rowout=null,this.output=null,this.hide=null,this.show=null,this.duration=null,this.throb=null,(new g).processNode(j.cloneNode(!0),this),this.cliView.output.appendChild(this.rowin),this.cliView.output.appendChild(this.rowout),this.request.addEventListener("output",this.onRequestChange.bind(this))}function k(b){var d;try{d=a("text!cockpit/ui/"+b)}catch(e){}if(d)return d;var f=c.id.split("/").pop()+".js",g;if(c.uri.substr(-f.length)!==f){console.error("Can't work out path from module.uri/module.id");return b}if(c.uri){var h=c.uri.length-f.length-1;return c.uri.substr(0,h)+"/"+b}return f+b}var d=a("pilot/dom"),e=a("pilot/event"),f=a("text!cockpit/ui/request_view.html"),g=a("pilot/domtemplate").Templater,h=a("text!cockpit/ui/request_view.css");d.importCssString(h);var i=document.createElement("div");i.innerHTML=f;var j=i.querySelector(".cptRow");l.prototype={copyToInput:function(){this.cliView.element.value=this.request.typed},executeRequest:function(a){this.cliView.cli.update({typed:this.request.typed,cursor:{start:0,end:0}}),this.cliView.cli.exec()},hideOutput:function(a){this.output.style.display="none",d.addCssClass(this.hide,"cmd_hidden"),d.removeCssClass(this.show,"cmd_hidden"),e.stopPropagation(a)},showOutput:function(a){this.output.style.display="block",d.removeCssClass(this.hide,"cmd_hidden"),d.addCssClass(this.show,"cmd_hidden"),e.stopPropagation(a)},remove:function(a){this.cliView.output.removeChild(this.rowin),this.cliView.output.removeChild(this.rowout),e.stopPropagation(a)},onRequestChange:function(a){this.duration.innerHTML=this.request.duration?"completed in "+this.request.duration/1e3+" sec ":"",this.output.innerHTML="",this.request.outputs.forEach(function(a){var b;typeof a=="string"?(b=document.createElement("p"),b.innerHTML=a):b=a,this.output.appendChild(b)},this),this.cliView.scrollOutputToBottom(),d.setCssClass(this.output,"cmd_error",this.request.error),this.throb.style.display=this.request.completed?"none":"block"}},b.RequestView=l}),define("pilot/domtemplate",["require","exports","module"],function(require,exports,module){function Templater(){this.scope=[]}Templater.prototype.processNode=function(a,b){typeof a=="string"&&(a=document.getElementById(a));if(b===null||b===undefined)b={};this.scope.push(a.nodeName+(a.id?"#"+a.id:""));try{if(a.attributes&&a.attributes.length){if(a.hasAttribute("foreach")){this.processForEach(a,b);return}if(a.hasAttribute("if")&&!this.processIf(a,b))return;b.__element=a;var c=Array.prototype.slice.call(a.attributes);for(var d=0;d<c.length;d++){var e=c[d].value,f=c[d].name;this.scope.push(f);try{if(f==="save")e=this.stripBraces(e),this.property(e,b,a),a.removeAttribute("save");else if(f.substring(0,2)==="on"){e=this.stripBraces(e);var g=this.property(e,b);typeof g!="function"&&this.handleError("Expected "+e+" to resolve to a function, but got "+typeof g),a.removeAttribute(f);var h=a.hasAttribute("capture"+f.substring(2));a.addEventListener(f.substring(2),g,h),h&&a.removeAttribute("capture"+f.substring(2))}else{var i=this,j=e.replace(/\$\{[^}]*\}/g,function(a){return i.envEval(a.slice(2,-1),b,e)});f.charAt(0)==="_"?(a.removeAttribute(f),a.setAttribute(f.substring(1),j)):e!==j&&(c[d].value=j)}}finally{this.scope.pop()}}}var k=Array.prototype.slice.call(a.childNodes);for(var l=0;l<k.length;l++)this.processNode(k[l],b);a.nodeType===Node.TEXT_NODE&&this.processTextNode(a,b)}finally{this.scope.pop()}},Templater.prototype.processIf=function(a,b){this.scope.push("if");try{var c=a.getAttribute("if"),d=this.stripBraces(c),e=!0;try{var f=this.envEval(d,b,c);e=!!f}catch(g){this.handleError("Error with '"+d+"'",g),e=!1}e||a.parentNode.removeChild(a),a.removeAttribute("if");return e}finally{this.scope.pop()}},Templater.prototype.processForEach=function(a,b){this.scope.push("foreach");try{var c=a.getAttribute("foreach"),d=c,e="param";if(d.charAt(0)==="$")d=this.stripBraces(d);else{var f=d.split(" in ");e=f[0].trim(),d=this.stripBraces(f[1].trim())}a.removeAttribute("foreach");try{var g=this,h=function(a,c,d){d.parentNode.insertBefore(c,d),b[e]=a,g.processNode(c,b),delete b[e]},i=function(b,c){g.scope.push(b);try{if(a.nodeName==="LOOP")for(var d=0;d<a.childNodes.length;d++){var e=a.childNodes[d].cloneNode(!0);h(c,e,a)}else{var e=a.cloneNode(!0);e.removeAttribute("foreach"),h(c,e,a)}}finally{g.scope.pop()}},j=this.envEval(d,b,c);if(Array.isArray(j))j.forEach(function(a,b){i(""+b,a)},this);else for(var k in j)j.hasOwnProperty(k)&&i(k,k);a.parentNode.removeChild(a)}catch(l){this.handleError("Error with '"+d+"'",l)}}finally{this.scope.pop()}},Templater.prototype.processTextNode=function(a,b){var c=a.data;c=c.replace(/\$\{([^}]*)\}/g,"$$$1");var d=c.split(/\uF001|\uF002/);d.length>1&&(d.forEach(function(c){c!==null&&c!==undefined&&c!==""&&(c.charAt(0)==="$"&&(c=this.envEval(c.slice(1),b,a.data)),c===null&&(c="null"),c===undefined&&(c="undefined"),typeof c.cloneNode!="function"&&(c=a.ownerDocument.createTextNode(c.toString())),a.parentNode.insertBefore(c,a))},this),a.parentNode.removeChild(a))},Templater.prototype.stripBraces=function(a){if(!a.match(/\$\{.*\}/g)){this.handleError("Expected "+a+" to match ${...}");return a}return a.slice(2,-1)},Templater.prototype.property=function(a,b,c){this.scope.push(a);try{typeof a=="string"&&(a=a.split("."));var d=b[a[0]];if(a.length===1){c!==undefined&&(b[a[0]]=c);if(typeof d=="function")return function(){return d.apply(b,arguments)};return d}if(!d){this.handleError("Can't find path="+a);return null}return this.property(a.slice(1),d,c)}finally{this.scope.pop()}},Templater.prototype.envEval=function(script,env,context){with(env)try{this.scope.push(context);return eval(script)}catch(ex){this.handleError("Template error evaluating '"+script+"'",ex);return script}finally{this.scope.pop()}},Templater.prototype.handleError=function(a,b){this.logError(a),this.logError("In: "+this.scope.join(" > ")),b&&this.logError(b)},Templater.prototype.logError=function(a){window.console&&window.console.log&&console.log(a)},exports.Templater=Templater}),define("cockpit/commands/basic",["require","exports","module","pilot/canon"],function(a,b,c){var d=a("pilot/canon"),e={name:"sh",description:"Execute a system command (requires server support)",params:[{name:"command",type:"text",description:"The string to send to the os shell."}],exec:function(a,b,c){var d=new XMLHttpRequest;d.open("GET","/exec?args="+b.command,!0),d.onreadystatechange=function(a){d.readyState==4&&d.status==200&&c.done("<pre>"+d.responseText+"</pre>")},d.send(null)}},d=a("pilot/canon");b.startup=function(a,b){d.addCommand(e)},b.shutdown=function(a,b){d.removeCommand(e)}}),define("text!cockpit/ui/cli_view.css",[],"#cockpitInput { padding-left: 16px; }.cptOutput { overflow: auto; position: absolute; z-index: 999; display: none; }.cptCompletion { padding: 0; position: absolute; z-index: -1000; }.cptCompletion.VALID { background: #FFF; }.cptCompletion.INCOMPLETE { background: #DDD; }.cptCompletion.INVALID { background: #DDD; }.cptCompletion span { color: #FFF; }.cptCompletion span.INCOMPLETE { color: #DDD; border-bottom: 2px dotted #F80; }.cptCompletion span.INVALID { color: #DDD; border-bottom: 2px dotted #F00; }span.cptPrompt { color: #66F; font-weight: bold; }.cptHints { color: #000; position: absolute; border: 1px solid rgba(230, 230, 230, 0.8); background: rgba(250, 250, 250, 0.8); -moz-border-radius-topleft: 10px; -moz-border-radius-topright: 10px; border-top-left-radius: 10px; border-top-right-radius: 10px; z-index: 1000; padding: 8px; display: none;}.cptFocusPopup { display: block; }.cptFocusPopup.cptNoPopup { display: none; }.cptHints ul { margin: 0; padding: 0 15px; }.cptGt { font-weight: bold; font-size: 120%; }"),define("text!cockpit/ui/request_view.css",[],".cptRowIn { display: box; display: -moz-box; display: -webkit-box; box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-orient: horizontal; box-align: center; -moz-box-align: center; -webkit-box-align: center; color: #333; background-color: #EEE; width: 100%; font-family: consolas, courier, monospace;}.cptRowIn > * { padding-left: 2px; padding-right: 2px; }.cptRowIn > img { cursor: pointer; }.cptHover { display: none; }.cptRowIn:hover > .cptHover { display: block; }.cptRowIn:hover > .cptHover.cptHidden { display: none; }.cptOutTyped { box-flex: 1; -moz-box-flex: 1; -webkit-box-flex: 1; font-weight: bold; color: #000; font-size: 120%;}.cptRowOutput { padding-left: 10px; line-height: 1.2em; }.cptRowOutput strong,.cptRowOutput b,.cptRowOutput th,.cptRowOutput h1,.cptRowOutput h2,.cptRowOutput h3 { color: #000; }.cptRowOutput a { font-weight: bold; color: #666; text-decoration: none; }.cptRowOutput a: hover { text-decoration: underline; cursor: pointer; }.cptRowOutput input[type=password],.cptRowOutput input[type=text],.cptRowOutput textarea { color: #000; font-size: 120%; background: transparent; padding: 3px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;}.cptRowOutput table,.cptRowOutput td,.cptRowOutput th { border: 0; padding: 0 2px; }.cptRowOutput .right { text-align: right; }"),define("text!cockpit/ui/request_view.html",[],'<div class=cptRow> <!-- The div for the input (i.e. what was typed) --> <div class="cptRowIn" save="${rowin}" onclick="${copyToInput}" ondblclick="${executeRequest}"> <!-- What the user actually typed --> <div class="cptGt">&gt; </div> <div class="cptOutTyped">${request.typed}</div> <!-- The extra details that appear on hover --> <div class=cptHover save="${duration}"></div> <img class=cptHover onclick="${hideOutput}" save="${hide}" alt="Hide command output" _src="${imageUrl(\'images/minus.png\')}"/> <img class="cptHover cptHidden" onclick="${showOutput}" save="${show}" alt="Show command output" _src="${imageUrl(\'images/plus.png\')}"/> <img class=cptHover onclick="${remove}" alt="Remove this command from the history" _src="${imageUrl(\'images/closer.png\')}"/> </div> <!-- The div for the command output --> <div class="cptRowOut" save="${rowout}"> <div class="cptRowOutput" save="${output}"></div> <img _src="${imageUrl(\'images/throbber.gif\')}" save="${throb}"/> </div></div>'),define("text!cockpit/ui/images/pinaction.png",[],"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAClklEQVQ4EX1TXUhUQRQ+Z3Zmd+9uN1q2P3UpZaEwcikKekkqLKggKHJ96MHe9DmLkCDa9U198Id8kErICmIlRAN96UdE6QdBW/tBA5Uic7E0zN297L17p5mb1zYjD3eYc+d83zlnON8g5xzWNUSEdUBkHTJasRWySPP7fw3hfwkk2GoNsc0vOaJRHo1GV/GiMctkTIJRFlpZli8opK+htmf83gXeG63oteOtra0u25e7TYJIJELb26vYCACTgUe1lXV86BTn745l+MsyHqs53S/Aq4VEUa9Y6ko14eYY4u3AyM3HYwdKU35DZyblGR2+qq6W0X2Nnh07xynnVYpHORx/E1/GvvqaAZUayjMjdM2f/Lgr5E+fV93zR4u3zKCLughsZqKwAzAxaz6dPY6JgjLUF+eSP5OpjmAw2E8DvldHSvJMKPg08aRor1tc4BuALu6mOwGWdQC3mKIqRsC8mKd8wYfD78/earzSYzdMDW9QgKb0Is8CBY1mQXOiaXAHEpMDE5XTJqIq4EiyxUqKlpfkF0pyV1OTAoFAhmTmyCCoDsZNZvIkUjELQpipo0sQqYZAswZHwsEEE10M0pq2SSZY9HqNcDicJcNTpBvQJz40UbSOTh1B8bDpuY0w9Hb3kkn9lPAlBLfhfD39XTtX/blFJqiqrjbkTi63Hbofj2uL4GMsmzFgbDJ/vmMgv/lB4syJ0oXO7d3j++vio6GFsYmD6cHJreWc3/jRVVHhsOYvM8iZ36mtjPDBk/xDZE8CoHlbrlAssbTxDdDJvdb536L7I6S7Vy++6Gi4Xi9BsUthJRaLOYSPz4XALKI4j4iObd/e5UtDKUjZzYyYRyGAJv01Zj8kC5cbs5WY83hQnv0DzCXl+r8APElkq0RU6oMAAAAASUVORK5CYII="),define("text!cockpit/ui/images/throbber.gif",[],"data:image/gif;base64,R0lGODlh3AATAPQAAP///wAAAL6+vqamppycnLi4uLKyssjIyNjY2MTExNTU1Nzc3ODg4OTk5LCwsLy8vOjo6Ozs7MrKyvLy8vT09M7Ozvb29sbGxtDQ0O7u7tbW1sLCwqqqqvj4+KCgoJaWliH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFg8PwKIMHnLF63N2438f0mv1I2O8buXjvaOPtaHx7fn96goR4hmuId4qDdX95c4+RG4GCBoyAjpmQhZN0YGYFXitdZBIVGAoKoq4CG6Qaswi1CBtkcG6ytrYJubq8vbfAcMK9v7q7D8O1ycrHvsW6zcTKsczNz8HZw9vG3cjTsMIYqQgDLAQGCQoLDA0QCwUHqfYSFw/xEPz88/X38Onr14+Bp4ADCco7eC8hQYMAEe57yNCew4IVBU7EGNDiRn8Z831cGLHhSIgdE/9chIeBgDoB7gjaWUWTlYAFE3LqzDCTlc9WOHfm7PkTqNCh54rePDqB6M+lR536hCpUqs2gVZM+xbrTqtGoWqdy1emValeXKwgcWABB5y1acFNZmEvXwoJ2cGfJrTv3bl69Ffj2xZt3L1+/fw3XRVw4sGDGcR0fJhxZsF3KtBTThZxZ8mLMgC3fRatCLYMIFCzwLEprg84OsDus/tvqdezZf13Hvr2B9Szdu2X3pg18N+68xXn7rh1c+PLksI/Dhe6cuO3ow3NfV92bdArTqC2Ebc3A8vjf5QWf15Bg7Nz17c2fj69+fnq+8N2Lty+fuP78/eV2X13neIcCeBRwxorbZrAxAJoCDHbgoG8RTshahQ9iSKEEzUmYIYfNWViUhheCGJyIP5E4oom7WWjgCeBBAJNv1DVV01MZdJhhjdkplWNzO/5oXI846njjVEIqR2OS2B1pE5PVscajkxhMycqLJgxQCwT40PjfAV4GqNSXYdZXJn5gSkmmmmJu1aZYb14V51do+pTOCmA00AqVB4hG5IJ9PvYnhIFOxmdqhpaI6GeHCtpooisuutmg+Eg62KOMKuqoTaXgicQWoIYq6qiklmoqFV0UoeqqrLbq6quwxirrrLTWauutJ4QAACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAXHx/EoCzboAcdhcLDdgwJ6nua03YZ8PMFPoBMca215eg98G36IgYNvDgOGh4lqjHd7fXOTjYV9nItvhJaIfYF4jXuIf4CCbHmOBZySdoOtj5eja59wBmYFXitdHhwSFRgKxhobBgUPAmdoyxoI0tPJaM5+u9PaCQZzZ9gP2tPcdM7L4tLVznPn6OQb18nh6NV0fu3i5OvP8/nd1qjwaasHcIPAcf/gBSyAAMMwBANYEAhWYQGDBhAyLihwYJiEjx8fYMxIcsGDAxVA/yYIOZIkBAaGPIK8INJlRpgrPeasaRPmx5QgJfB0abLjz50tSeIM+pFmUo0nQQIV+vRlTJUSnNq0KlXCSq09ozIFexEBAYkeNiwgOaEtn2LFpGEQsKCtXbcSjOmVlqDuhAx3+eg1Jo3u37sZBA9GoMAw4MB5FyMwfLht4sh7G/utPGHlYAV8Nz9OnOBz4c2VFWem/Pivar0aKCP2LFn2XwhnVxBwsPbuBAQbEGiIFg1BggoWkidva5z4cL7IlStfkED48OIYoiufYIH68+cKPkqfnsB58ePjmZd3Dj199/XE20tv6/27XO3S6z9nPCz9BP3FISDefL/Bt192/uWmAv8BFzAQAQUWWFaaBgqA11hbHWTIXWIVXifNhRlq6FqF1sm1QQYhdiAhbNEYc2KKK1pXnAIvhrjhBh0KxxiINlqQAY4UXjdcjSJyeAx2G2BYJJD7NZQkjCPKuCORKnbAIXsuKhlhBxEomAIBBzgIYXIfHfmhAAyMR2ZkHk62gJoWlNlhi33ZJZ2cQiKTJoG05Wjcm3xith9dcOK5X51tLRenoHTuud2iMnaolp3KGXrdBo7eKYF5p/mXgJcogClmcgzAR5gCKymXYqlCgmacdhp2UCqL96mq4nuDBTmgBasaCFp4sHaQHHUsGvNRiiGyep1exyIra2mS7dprrtA5++z/Z8ZKYGuGsy6GqgTIDvupRGE+6CO0x3xI5Y2mOTkBjD4ySeGU79o44mcaSEClhglgsKyJ9S5ZTGY0Bnzrj+3SiKK9Rh5zjAALCywZBk/ayCWO3hYM5Y8Dn6qxxRFsgAGoJwwgDQRtYXAAragyQOmaLKNZKGaEuUlpyiub+ad/KtPqpntypvvnzR30DBtjMhNodK6Eqrl0zU0/GjTUgG43wdN6Ra2pAhGtAAZGE5Ta8TH6wknd2IytNKaiZ+Or79oR/tcvthIcAPe7DGAs9Edwk6r3qWoTaNzY2fb9HuHh2S343Hs1VIHhYtOt+Hh551rh24vP5YvXSGzh+eeghy76GuikU9FFEainrvrqrLfu+uuwxy777LTXfkIIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAWHB2l4CDZo9IDjcBja7UEhTV+3DXi3PJFA8xMcbHiDBgMPG31pgHBvg4Z9iYiBjYx7kWocb26OD398mI2EhoiegJlud4UFiZ5sm6Kdn2mBr5t7pJ9rlG0cHg5gXitdaxwFGArIGgoaGwYCZ3QFDwjU1AoIzdCQzdPV1c0bZ9vS3tUJBmjQaGXl1OB0feze1+faiBvk8wjnimn55e/o4OtWjp+4NPIKogsXjaA3g/fiGZBQAcEAFgQGOChgYEEDCCBBLihwQILJkxIe/3wMKfJBSQkJYJpUyRIkgwcVUJq8QLPmTYoyY6ZcyfJmTp08iYZc8MBkhZgxk9aEcPOlzp5FmwI9KdWn1qASurJkClRoWKwhq6IUqpJBAwQEMBYroAHkhLt3+RyzhgCDgAV48Wbgg+waAnoLMgTOm6DwQ8CLBzdGdvjw38V5JTg2lzhyTMeUEwBWHPgzZc4TSOM1bZia6LuqJxCmnOxv7NSsl1mGHHiw5tOuIWeAEHcFATwJME/ApgFBc3MVLEgPvE+Ddb4JokufPmFBAuvPXWu3MIF89wTOmxvOvp179evQtwf2nr6aApPyzVd3jn089e/8xdfeXe/xdZ9/d1ngHf98lbHH3V0LMrgPgsWpcFwBEFBgHmyNXWeYAgLc1UF5sG2wTHjIhNjBiIKZCN81GGyQwYq9uajeMiBOQGOLJ1KjTI40kmfBYNfc2NcGIpI4pI0vyrhjiT1WFqOOLEIZnjVOVpmajYfBiCSNLGbA5YdOkjdihSkQwIEEEWg4nQUmvYhYe+bFKaFodN5lp3rKvJYfnBKAJ+gGDMi3mmbwWYfng7IheuWihu5p32XcSWdSj+stkF95dp64jJ+RBipocHkCCp6PCiRQ6INookCAAwy0yd2CtNET3Yo7RvihBjFZAOaKDHT43DL4BQnsZMo8xx6uI1oQrHXXhHZrB28G62n/YSYxi+uzP2IrgbbHbiaer7hCiOxDFWhrbmGnLVuus5NFexhFuHLX6gkEECorlLpZo0CWJG4pLjIACykmBsp0eSSVeC15TDJeUhlkowlL+SWLNJpW2WEF87urXzNWSZ6JOEb7b8g1brZMjCg3ezBtWKKc4MvyEtwybPeaMAA1ECRoAQYHYLpbeYYCLfQ+mtL5c9CnfQpYpUtHOSejEgT9ogZ/GSqd0f2m+LR5WzOtHqlQX1pYwpC+WbXKqSYtpJ5Mt4a01lGzS3akF60AxkcTaLgAyRBPWCoDgHfJqwRuBuzdw/1ml3iCwTIeLUWJN0v4McMe7uasCTxseNWPSxc5RbvIgD7geZLbGrqCG3jepUmbbze63Y6fvjiOylbwOITPfIHEFsAHL/zwxBdvPBVdFKH88sw37/zz0Ecv/fTUV2/99SeEAAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2cw8BQEm3T6yHEYHHD4oKCuD9qGvNsxT6QTgAkcHHmFeX11fm17hXwPG35qgnhxbwMPkXaLhgZ9gWp3bpyegX4DcG+inY+Qn6eclpiZkHh6epetgLSUcBxlD2csXXdvBQrHGgoaGhsGaIkFDwjTCArTzX+QadHU3c1ofpHc3dcGG89/4+TYktvS1NYI7OHu3fEJ5tpqBu/k+HX7+nXDB06SuoHm0KXhR65cQT8P3FRAMIAFgVMPwDCAwLHjggIHJIgceeFBg44eC/+ITCCBZYKSJ1FCWPBgpE2YMmc+qNCypwScMmnaXAkUJYOaFVyKLOqx5tCXJnMelcBzJNSYKIX2ZPkzqsyjPLku9Zr1QciVErYxaICAgEUOBRJIgzChbt0MLOPFwyBggV27eCUcmxZvg9+/dfPGo5bg8N/Ag61ZM4w4seDF1fpWhizZmoa+GSortgcaMWd/fkP/HY0MgWbTipVV++wY8GhvqSG4XUEgoYTKE+Qh0OCvggULiBckWEZ4Ggbjx5HXVc58IPQJ0idQJ66XanTpFraTe348+XLizRNcz658eHMN3rNPT+C+G/nodqk3t6a+fN3j+u0Xn3nVTQPfdRPspkL/b+dEIN8EeMm2GAYbTNABdrbJ1hyFFv5lQYTodSZABhc+loCEyhxTYYkZopdMMiNeiBxyIFajV4wYHpfBBspUl8yKHu6ooV5APsZjQxyyeNeJ3N1IYod38cgdPBUid6GCKfRWgAYU4IccSyHew8B3doGJHmMLkGkZcynKk2Z50Ym0zJzLbDCmfBbI6eIyCdyJmJmoqZmnBAXy9+Z/yOlZDZpwYihnj7IZpuYEevrYJ5mJEuqiof4l+NYDEXQpXQcMnNjZNDx1oGqJ4S2nF3EsqWrhqqVWl6JIslpAK5MaIqDeqjJq56qN1aTaQaPbHTPYr8Be6Gsyyh6Da7OkmmqP/7GyztdrNVQBm5+pgw3X7aoYKhfZosb6hyUKBHCgQKij1rghkOAJuZg1SeYIIY+nIpDvf/sqm4yNG5CY64f87qdAwSXKGqFkhPH1ZHb2EgYtw3bpKGVkPz5pJAav+gukjB1UHE/HLNJobWcSX8jiuicMMBFd2OmKwQFs2tjXpDfnPE1j30V3c7iRHlrzBD2HONzODyZtsQJMI4r0AUNaE3XNHQw95c9GC001MpIxDacFQ+ulTNTZlU3O1eWVHa6vb/pnQUUrgHHSBKIuwG+bCPyEqbAg25gMVV1iOB/IGh5YOKLKIQ6xBAcUHmzjIcIqgajZ+Ro42DcvXl7j0U4WOUd+2IGu7DWjI1pt4DYq8BPm0entuGSQY/4tBi9Ss0HqfwngBQtHbCH88MQXb/zxyFfRRRHMN+/889BHL/301Fdv/fXYZ39CCAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2fAKXsKm7R6Q+Y43vABep0mGwwOPH7w2CT+gHZ3d3lyagl+CQNvg4yGh36LcHoGfHR/ZYOElQ9/a4ocmoRygIiRk5p8pYmZjXePaYBujHoOqp5qZHBlHAUFXitddg8PBg8KGsgayxvGkAkFDwgICtPTzX2mftHW3QnOpojG3dbYkNjk1waxsdDS1N7ga9zw1t/aifTk35fu6Qj3numL14fOuHTNECHqU4DDgQEsCCwidiHBAwYQMmpcUOCAhI8gJVzUuLGThAQnP/9abEAyI4MCIVOKZNnyJUqUJxNcGNlywYOQgHZirGkSJ8gHNEky+AkS58qWEJYC/bMzacmbQHkqNdlUJ1KoSz2i9COhmQYCEXtVrCBgwYS3cCf8qTcNQ9u4cFFOq2bPLV65Cf7dxZthbjW+CgbjnWtNgWPFcAsHdoxgWWK/iyV045sAc2S96SDn1exYw17REwpLQEYt2eW/qtPZRQAB7QoC61RW+GsBwYZ/CXb/XRCYLsAKFizEtUAc+G7lcZsjroscOvTmsoUvx15PwccJ0N8yL17N9PG/E7jv9S4hOV7pdIPDdZ+ePDzv2qMXn2b5+wTbKuAWnF3oZbABZY0lVmD/ApQd9thybxno2GGuCVDggaUpoyBsB1bGGgIYbJCBcuFJiOAyGohIInQSmmdeiBnMF2GHfNUlIoc1rncjYRjW6NgGf3VQGILWwNjBfxEZcAFbC7gHXQcfUYOYdwzQNxo5yUhQZXhvRYlMeVSuSOJHKJa5AQMQThBlZWZ6Bp4Fa1qzTAJbijcBlJrtxeaZ4lnnpZwpukWieGQmYx5ATXIplwTL8DdNZ07CtWYybNIJF4Ap4NZHe0920AEDk035kafieQrqXofK5ympn5JHKYjPrfoWcR8WWQGp4Ul32KPVgXdnqxM6OKqspjIYrGPDrlrsZtRIcOuR86nHFwbPvmes/6PH4frrqbvySh+mKGhaAARPzjjdhCramdoGGOhp44i+zogBkSDuWC5KlE4r4pHJkarXrj++Raq5iLmWLlxHBteavjG+6amJrUkJJI4Ro5sBv9AaOK+jAau77sbH7nspCwNIYIACffL7J4JtWQnen421nNzMcB6AqpRa9klonmBSiR4GNi+cJZpvwgX0ejj71W9yR+eIgaVvQgf0l/A8nWjUFhwtZYWC4hVnkZ3p/PJqNQ5NnwUQrQCGBBBMQIGTtL7abK+5JjAv1fi9bS0GLlJHgdjEgYzzARTwC1fgEWdJuKKBZzj331Y23qB3i9v5aY/rSUC4w7PaLeWXmr9NszMFoN79eeiM232o33EJAIzaSGwh++y012777bhT0UURvPfu++/ABy/88MQXb/zxyCd/QggAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEBY5nwCk7xIWNer0hO95wziC9Ttg5b4ND/+Y87IBqZAaEe29zGwmJigmDfHoGiImTjXiQhJEPdYyWhXwDmpuVmHwOoHZqjI6kZ3+MqhyemJKAdo6Ge3OKbEd4ZRwFBV4rc4MPrgYPChrMzAgbyZSJBcoI1tfQoYsJydfe2amT3d7W0OGp1OTl0YtqyQrq0Lt11PDk3KGoG+nxBpvTD9QhwCctm0BzbOyMIwdOUwEDEgawIOCB2oMLgB4wgMCx44IHBySIHClBY0ePfyT/JCB5weRJCAwejFw58kGDlzBTqqTZcuPLmCIBiWx58+VHmiRLFj0JVCVLl0xl7qSZwCbOo0lFWv0pdefQrVFDJtr5gMBEYBgxqBWwYILbtxPsqMPAFu7blfa81bUbN4HAvXAzyLWnoDBguHIRFF6m4LBbwQngMYPXuC3fldbyPrMcGLM3w5wRS1iWWUNlvnElKDZtz/EEwaqvYahQoexEfyILi4RrYYKFZwJ3810QWZ2ECrx9Ew+O3K6F5Yq9zXbb+y30a7olJJ+wnLC16W97Py+uwdtx1NcLWzs/3G9e07stVPc9kHJ0BcLtQp+c3ewKAgYkUAFpCaAmmHqKLSYA/18WHEiZPRhsQF1nlLFWmIR8ZbDBYs0YZuCGpGXWmG92aWiPMwhEOOEEHXRwIALlwXjhio+BeE15IzpnInaLbZBBhhti9x2GbnVQo2Y9ZuCfCgBeMCB+DJDIolt4iVhOaNSJdCOBUfIlkmkyMpPAAvKJ59aXzTQzJo0WoJnmQF36Jp6W1qC4gWW9GZladCiyJd+KnsHImgRRVjfnaDEKuiZvbcYWo5htzefbl5LFWNeSKQAo1QXasdhiiwwUl2B21H3aQaghXnPcp1NagCqYslXAqnV+zYWcpNwVp9l5eepJnHqL4SdBi56CGlmw2Zn6aaiZjZqfb8Y2m+Cz1O0n3f+tnvrGbF6kToApCgAWoNWPeh754JA0vmajiAr4iOuOW7abQXVGNriBWoRdOK8FxNqLwX3oluubhv8yluRbegqGb536ykesuoXhyJqPQJIGbLvQhkcwjKs1zBvBwSZIsbcsDCCBAAf4ya+UEhyQoIiEJtfoZ7oxUOafE2BwgMWMqUydfC1LVtiArk0QtGkWEopzlqM9aJrKHfw5c6wKjFkmXDrbhwFockodtMGFLWpXy9JdiXN1ZDNszV4WSLQCGBKoQYHUyonqrHa4ErewAgMmcAAF7f2baIoVzC2p3gUvJtLcvIWqloy6/R04mIpLwDhciI8qLOB5yud44pHPLbA83hFDWPjNbuk9KnySN57Av+TMBvgEAgzzNhJb5K777rz37vvvVHRRxPDEF2/88cgnr/zyzDfv/PPQnxACACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIUCwcMpO84OT2HDbm8GHLQjnn6wE3g83SA3DB55G3llfHxnfnZ4gglvew6Gf4ySgmYGlpCJknochWiId3kJcZZyDn93i6KPl4eniopwq6SIoZKxhpenbhtHZRxhXisDopwPgHkGDxrLGgjLG8mC0gkFDwjX2AgJ0bXJ2djbgNJsAtbfCNB2oOnn6MmKbeXt226K1fMGi6j359D69ua+QZskjd+3cOvY9XNgp4ABCQNYEDBl7EIeCQkeMIDAseOCBwckiBSZ4ILGjh4B/40kaXIjSggMHmBcifHky5gYE6zM2OAlzGM6Z5rs+fIjTZ0tfcYMSlLCUJ8fL47kCVXmTjwPiKJkUCDnyqc3CxzQmYeAxAEGLGJYiwCDgAUT4sqdgOebArdw507IUNfuW71xdZ7DC5iuhGsKErf9CxhPYgUaEhPWyzfBMgUIJDPW6zhb5M1y+R5GjFkBaLmCM0dOfHqvztXYJnMejaFCBQlmVxAYsEGkYnQV4lqYMNyCtnYSggNekAC58uJxmTufW5w55mwKkg+nLp105uTC53a/nhg88fMTmDfDVl65Xum/IZt/3/zaag3a5W63nll1dvfiWbaaZLmpQIABCVQA2f9lAhTG112PQWYadXE9+FtmEwKWwQYQJrZagxomsOCAGVImInsSbpCBhhwug6KKcXXQQYUcYuDMggrASFmNzjjzzIrh7cUhhhHqONeGpSEW2QYxHsmjhxpgUGAKB16g4IIbMNCkXMlhaJ8GWVJo2I3NyKclYF1GxgyYDEAnXHJrMpNAm/rFBSczPiYAlwXF8ZnmesvoOdyMbx7m4o0S5LWdn4bex2Z4xYmEzaEb5EUcnxbA+WWglqIn6aHPTInCgVbdlZyMqMrIQHMRSiaBBakS1903p04w434n0loBoQFOt1yu2YAnY68RXiNsqh2s2qqxuyKb7Imtmgcrqsp6h8D/fMSpapldx55nwayK/SfqCQd2hcFdAgDp5GMvqhvakF4mZuS710WGIYy30khekRkMu92GNu6bo7r/ttjqwLaua5+HOdrKq5Cl3dcwi+xKiLBwwwom4b0E6xvuYyqOa8IAEghwQAV45VvovpkxBl2mo0W7AKbCZXoAhgMmWnOkEqx2JX5nUufbgJHpXCfMOGu2QAd8eitpW1eaNrNeMGN27mNz0swziYnpSbXN19gYtstzfXrdYjNHtAIYGFVwwAEvR1dfxdjKxVzAP0twAAW/ir2w3nzTd3W4yQWO3t0DfleB4XYnEHCEhffdKgaA29p0eo4fHLng9qoG+OVyXz0gMeWGY7qq3xhiRIEAwayNxBawxy777LTXbjsVXRSh++689+7778AHL/zwxBdv/PEnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLD4BlwHGg0ubBpuzdm9Dk9eCTu+MTZkDb4PXYbeIIcHHxqf4F3gnqGY2kOdQmCjHCGfpCSjHhmh2N+knmEkJmKg3uHfgaaeY2qn6t2i4t7sKAPbwIJD2VhXisDCQZgDrKDBQ8aGgjKyhvDlJMJyAjV1gjCunkP1NfVwpRtk93e2ZVt5NfCk27jD97f0LPP7/Dr4pTp1veLgvrx7AL+Q/BM25uBegoYkDCABYFhEobhkUBRwoMGEDJqXPDgQMUEFC9c1LjxQUUJICX/iMRIEgIDkycrjmzJMSXFlDNJvkwJsmdOjQwKfDz5M+PLoSGLQqgZU6XSoB/voHxawGbFlS2XGktAwKEADB0xiEWAodqGBRPSqp1wx5qCamDRrp2Qoa3bagLkzrULF4GCvHPTglRAmKxZvWsHayBcliDitHUlvGWM97FgCdYWVw4c2e/kw4HZJlCwmDBhwHPrjraGYTHqtaoxVKggoesKAgd2SX5rbUMFCxOAC8cGDwHFwBYWJCgu4XfwtcqZV0grPHj0u2SnqwU+IXph3rK5b1fOu7Bx5+K7L6/2/Xhg8uyXnQ8dvfRiDe7TwyfNuzlybKYpgIFtKhAgwEKkKcOf/wChZbBBgMucRh1so5XH3wbI1WXafRJy9iCErmX4IWHNaIAhZ6uxBxeGHXQA24P3yYfBBhmgSBozESpwongWOBhggn/N1aKG8a1YY2oVAklgCgQUUwGJ8iXAgItrWUARbwpqIOWEal0ZoYJbzmWlZCWSlsAC6VkwZonNbMAAl5cpg+NiZwpnJ0Xylegmlc+tWY1mjnGnZnB4QukMA9UJRxGOf5r4ppqDjjmnfKilh2ejGiyJAgF1XNmYbC2GmhZ5AcJVgajcXecNqM9Rx8B6bingnlotviqdkB3YCg+rtOaapFsUhSrsq6axJ6sEwoZK7I/HWpCsr57FBxJ1w8LqV/81zbkoXK3LfVeNpic0KRQG4NHoIW/XEmZuaiN6tti62/moWbk18uhjqerWS6GFpe2YVotskVssWfBOAHACrZHoWcGQwQhlvmsdXBZ/F9YLMF2jzUuYBP4a7CLCnoEHrgkDSCDAARUILAGaVVqAwQHR8pZXomm9/ONhgjrbgc2lyYxmpIRK9uSNjrXs8gEbTrYyl2ryTJmsLCdKkWzFQl1lWlOXGmifal6p9VnbQfpyY2SZyXKVV7JmZkMrgIFSyrIeUJ2r7YKnXdivUg1kAgdQ8B7IzJjGsd9zKSdwyBL03WpwDGxwuOASEP5vriO2F3nLjQdIrpaRDxqcBdgIHGA74pKrZXiR2ZWuZt49m+o3pKMC3p4Av7SNxBa456777rz37jsVXRQh/PDEF2/88cgnr/zyzDfv/PMnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLDUPAMHGi0weEpbN7wI8cxTzsGj4R+n+DUxwaBeBt7hH1/gYIPhox+Y3Z3iwmGk36BkIN8egOIl3h8hBuOkAaZhQlna4BrpnyWa4mleZOFjrGKcXoFA2ReKwMJBgISDw6abwUPGggazc0bBqG0G8kI1tcIwZp51djW2nC03d7BjG8J49jl4cgP3t/RetLp1+vT6O7v5fKhAvnk0UKFogeP3zmCCIoZkDCABQFhChQYuKBHgkUJkxpA2MhxQYEDFhNcvPBAI8eNCx7/gMQYckPJkxsZPLhIM8FLmDJrYiRp8mTKkCwT8IQJwSPQkENhpgQpEunNkzlpWkwKdSbGihKocowqVSvKWQkIOBSgQOYFDBgQpI0oYMGEt3AzTLKm4BqGtnDjirxW95vbvG/nWlub8G9euRsiqqWLF/AEkRoiprX2wLDeDQgkW9PQGLDgyNc665WguK8C0XAnRY6oGPUEuRLsgk5g+a3cCxUqSBC7gsCBBXcVq6swwULx4hayvctGPK8FCwsSLE9A3Hje6NOrHzeOnW695sffRi/9HfDz7sIVSNB+XXrmugo0rHcM3X388o6jr44ceb51uNjF1xcC8zk3wXiS8aYC/wESaLABBs7ch0ECjr2WAGvLsLZBeHqVFl9kGxooV0T81TVhBo6NiOEyJ4p4IYnNRBQiYCN6x4wCG3ZAY2If8jXjYRcyk2FmG/5nXAY8wqhWAii+1YGOSGLoY4VRfqiAgikwmIeS1gjAgHkWYLQZf9m49V9gDWYWY5nmTYCRM2TS5pxxb8IZGV5nhplmhJyZadxzbrpnZ2d/6rnZgHIid5xIMDaDgJfbLdrgMkKW+Rygz1kEZz1mehabkBpgiQIByVikwGTqVfDkk2/Vxxqiqur4X3fksHccre8xlxerDLiHjQIVUAgXr77yFeyuOvYqXGbMrbrqBMqaFpFFzhL7qv9i1FX7ZLR0LUNdcc4e6Cus263KbV+inkAAHhJg0BeITR6WmHcaxhvXg/AJiKO9R77ILF1FwmVdAu6WBu+ZFua72mkZWMfqBElKu0G8rFZ5n4ATp5jkmvsOq+Nj7u63ZMMPv4bveyYy6fDH+C6brgnACHBABQUrkGirz2FwAHnM4Mmhzq9yijOrOi/MKabH6VwBiYwZdukEQAvILKTWXVq0ZvH5/CfUM7M29Zetthp1eht0eqkFYw8IKXKA6mzXfTeH7fZg9zW0AhgY0TwthUa6Ch9dBeIsbsFrYkRBfgTfiG0FhwMWnbsoq3cABUYOnu/ejU/A6uNeT8u4wMb1WnBCyJJTLjjnr8o3OeJrUcpc5oCiPqAEkz8tXuLkPeDL3Uhs4fvvwAcv/PDEU9FFEcgnr/zyzDfv/PPQRy/99NRXf0IIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIWCw/AoDziOtCHt8BQ28PjmzK57Hom8fo42+P8DeAkbeYQcfX9+gYOFg4d1bIGEjQmPbICClI9/YwaLjHAJdJeKmZOViGtpn3qOqZineoeJgG8CeWUbBV4rAwkGAhIVGL97hGACGsrKCAgbBoTRhLvN1c3PepnU1s2/oZO6AtzdBoPf4eMI3tIJyOnF0YwFD+nY8e3z7+Xfefnj9uz8cVsXCh89axgk7BrAggAwBQsYIChwQILFixIeNIDAseOCBwcSXMy2sSPHjxJE/6a0eEGjSY4MQGK86PIlypUJEmYsaTKmyJ8JW/Ls6HMkzaEn8YwMWtPkx4pGd76E4DMPRqFTY860OGhogwYagBFoKEABA46DEGBAoEBB0AUT4sqdIFKBNbcC4M6dkEEk22oYFOTdG9fvWrtsBxM23MytYL17666t9phwXwlum2lIDHmuSA2IGyuOLOHv38qLMbdFjHruZbWgRXeOe1nC2BUEDiyAMMHZuwoTLAQX3nvDOAUW5Vogru434d4JnAsnPmFB9NBshQXfa9104+Rxl8e13rZxN+CEydtVsFkd+vDjE7C/q52wOvb4s7+faz025frbxefWbSoQIAEDEUCwgf9j7bUlwHN9ZVaegxDK1xYzFMJH24L5saXABhlYxiEzHoKoIV8LYqAMaw9aZqFmJUK4YHuNfRjiXhmk+NcyJgaIolvM8BhiBx3IleN8lH1IWAcRgkZgCgYiaBGJojGgHHFTgtagAFYSZhF7/qnTpY+faVlNAnqJN0EHWa6ozAZjBtgmmBokwMB01LW5jAZwbqfmlNips4B4eOqJgDJ2+imXRZpthuigeC6XZTWIxilXmRo8iYKBCwiWmWkJVEAkfB0w8KI1IvlIpKnOkVpqdB5+h96o8d3lFnijrgprjbfGRSt0lH0nAZG5vsprWxYRW6Suq4UWqrLEsspWg8Io6yv/q6EhK0Fw0GLbjKYn5CZYBYht1laPrnEY67kyrhYbuyceiR28Pso7bYwiXjihjWsWuWF5p/H765HmNoiur3RJsGKNG/jq748XMrwmjhwCfO6QD9v7LQsDxPTAMKsFpthyJCdkmgYiw0VdXF/Om9dyv7YMWGXTLYpZg5wNR11C78oW3p8HSGgul4qyrJppgllJHJZHn0Y0yUwDXCXUNquFZNLKyYXBAVZvxtAKYIQEsmPgDacr0tltO1y/DMwYpkgUpJfTasLGzd3cdCN3gN3UWRcY3epIEPevfq+3njBxq/kqBoGBduvea8f393zICS63ivRBTqgFpgaWZEIUULdcK+frIfAAL2AjscXqrLfu+uuwx05FF0XUbvvtuOeu++689+7778AHL/wJIQAAOwAAAAAAAAAAAA=="),define("text!cockpit/ui/images/closer.png",[],"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAj9JREFUeNp0ks+LUlEUx7/vV1o8Z8wUx3IEHcQmiBiQlomjRNCiZpEuEqF/oEUwq/6EhvoHggmRcJUQBM1CRJAW0aLIaGQimZJxJsWxyV/P9/R1zzWlFl04vPvOPZ9z7rnnK5imidmKRCIq+zxgdoPZ1T/ut8xeM3tcKpW6s1hhBkaj0Qj7bDebTX+324WmadxvsVigqipcLleN/d4rFoulORiLxTZY8ItOp8MBCpYkiYPj8Xjus9vtlORWoVB4KcTjcQc732dLpSRXvCZaAws6Q4WDdqsO52kNH+oCRFGEz+f7ydwBKRgMPmTXi49GI1x2D/DsznesB06ws2eDbI7w9HYN6bVjvGss4KAjwDAMq81mM2SW5Wa/3weBbz42UL9uYnVpiO2Nr9ANHSGXib2Wgm9tCYIggGKJEVkvlwgi5/FQRmTLxO6hgJVzI1x0T/fJrBtHJxPeL6tI/fsZLA6ot8lkQi8HRVbw94gkWYI5MaHrOjcCGSNRxZosy9y5cErDzn0Dqx7gcwO8WtBp4PndI35GMYqiUMUvBL5yOBz8yRfFNpbPmqgcCFh/IuHa1nR/YXGM8+oUpFhihEQiwcdRLpfVRqOBtWXWq34Gra6AXq8Hp2piZcmKT4cKnE4nwuHwdByVSmWQz+d32WCTlHG/qaHHREN9kgi0sYQfv0R4PB4EAgESQDKXy72fSy6VSnHJVatVf71eR7vd5n66mtfrRSgU4pLLZrOlf7RKK51Ok8g3/yPyR5lMZi7y3wIMAME4EigHWgKnAAAAAElFTkSuQmCC"),define("text!cockpit/ui/images/pinin.png",[],"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQ4Ea2TPUsDQRCGZ89Eo4FACkULEQs1CH4Uamfjn7GxEYJFIFXgChFsbPwzNnZioREkaiHBQtEiEEiMRm/dZ8OEGAxR4sBxx877Pju7M2estTJIxLrNuVwuMxQEx0ZkzcFHyRtjXt02559RtB2GYanTYzoryOfz+6l4Nbszf2niwffKmpGRo9sVW22mDgqFwp5C2gDMm+P32a3JB1N+n5JifUGeP9JeNxGryPLYjcwMP8rJ07Q9fZltQzyAstOJ2vVu5sKc1ZZkRBrOcKeb+HexPidvkpCN5JUcllZtpZFc5DgBWc5M2eysZuMuofMBSA4NWjx4PUCsXefMlI0QY3ewRg4NWi4ZTQsgrjYXema+e4VqtEMK6KXvu+4B9Bklt90vVKMeD2BI6DOt4rZ/Gk7WyKFBi4fNPIAJY0joM61SCCZ9tI1o0OIB8D+DBIkYaJRbCBH9mZgNt+bb++ufSSF/eX8BYcDeAzuQJVUAAAAASUVORK5CYII="),define("text!cockpit/ui/images/plus.png",[],"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4yFTwuJTkAAAH7SURBVCjPdZKxa1NRFMZ/956XZMgFyyMlCZRA4hBx6lBcQ00GoYi4tEstFPwLAs7iLDi7FWuHThaUggihBDI5OWRoQAmBQFISQgvvpbwX3rsOaR4K+o2H8zvfOZxPWWtZqVarGaAJPAEe3ZW/A1+Bd+1221v1qhW4vb1dA44mk0nZ8zyCIAAgk8lgjGF9fb0PHF5cXLQTsF6vP/c879P19TVBEJDJZBARAKIoSmpra2sYY561Wq3PqtFouMBgMBgYay3ZbJZ/yfd9tNaUSqUboOKISPPq6sqsVvZ9H4AvL34B8PTj/QSO45jpdHovn883Ha31znw+JwzDpCEMQx4UloM8zyOdTif3zudztNY7jog8DMMQpRRxHPPt5TCBAEZvxlyOFTsfykRRBICIlB2t9a21Nh3HMXEc8+d7VhJHWCwWyzcohdZaHBHpO46z6fs+IsLj94XECaD4unCHL8FsNouI/HRE5Nx13c3ZbIbWOnG5HKtl+53TSq7rIiLnand31wUGnU7HjEYjlFLJZN/3yRnL1FMYY8jlcmxtbd0AFel2u7dnZ2eXxpi9xWJBEASkUimstYgIQSSkUimKxSKVSgVjzN7p6emPJHL7+/s14KjX65WHwyGz2SxZbWNjg2q12gcOT05O2n9lFeDg4MAAr/4T8rfHx8dJyH8DvvbYGzKvWukAAAAASUVORK5CYII="),define("text!cockpit/ui/images/minus.png",[],"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4xMrIJw5EAAAHcSURBVCjPhZIxSxtxGMZ/976XhJA/RA5EAyJcFksnp64hjUPBoXRyCYLQTyD0UxScu0nFwalCQSgFCVk7dXAwUAiBDA2RO4W7yN1x9+9gcyhU+pteHt4H3pfncay1LOl0OgY4BN4Ar/7KP4BvwNFwOIyWu87S2O12O8DxfD73oygiSRIAarUaxhhWV1fHwMFgMBiWxl6v9y6Koi+3t7ckSUKtVkNVAcjzvNRWVlYwxry9vLz86uzs7HjAZDKZGGstjUaDfxHHMSLC5ubmHdB2VfVwNpuZ5clxHPMcRVFwc3PTXFtbO3RFZHexWJCmabnweAaoVqvlv4vFAhHZdVX1ZZqmOI5DURR8fz/lxbp9Yrz+7bD72SfPcwBU1XdF5N5aWy2KgqIoeBzPEnWVLMseYnAcRERdVR27rrsdxzGqyutP6898+GBsNBqo6i9XVS88z9sOggAR4X94noeqXoiIHPm+H9XrdYIgIAxDwjAkTVPCMESzBy3LMprNJr7v34nIkV5dXd2fn59fG2P2siwjSRIqlQrWWlSVJFcqlQqtVot2u40xZu/s7OxnWbl+v98BjkejkT+dTgmCoDxtY2ODra2tMXBweno6fNJVgP39fQN8eKbkH09OTsqS/wHFRdHPfTSfjwAAAABJRU5ErkJggg=="),define("text!cockpit/ui/images/dot_clear.gif",[],"data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAEBMgA7"),define("text!cockpit/ui/images/pins.png",[],"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGYklEQVRIDbVWe0yURxCf/R735o6DO0FBe0RFsaL4iLXGIKa2SY3P6JGa2GpjlJjUV9NosbU++tYUbEnaQIrVaKJBG7WiNFQFUWO1UUEsVg2CAgoeHHLewcH32O58cBdQsX9Y5+7LfrszOzO/2ZnZj1BKgTBiIwVGVvKd49OVVYunDlXn6wdBKh+ogXrv+DOz1melIb+3LM5fNv2XPYE5EHY+L3PJljN5zavHpJjsQNsA/JJEgyC2+WTjy3b0GfoJW8O4aoHtDwiHQrj5lw1LLyyb1bp5zAjJTus9klrVpdD6TqH2ngVO+0dsRJnp06cLIYU4fx7NnRI3bu7UIYOeJ/McnuY88q3k62gc0S4Dgf5qhICQtIXS2lqD7BhSduPk3YfyzXaANhBBJDxYdUqCywB2qS4RdyUuSkTF/VJxcbH5j8N7/75RuFrN3Zh8OS8zqf5m4UpPeenOyP42dbtBeuvVnCdkK1e4PfPouX03mo9se+c33M8wqDk5Ofqed8REUTicQhbySUxp9u3KlMSHTtrFU6Kyn03lz15PPpW25vsZeYSIKyiVURcqeZJOH9lTNZLfnxRjU/uwrjbEUBWsapcSO2Hq4k0VfZg9EzxdDNCEjDxgNqRDme9umz/btwlsHRIEePHgAf73RdnHZ6LTuIUBN7OBQ+c1Fdnp6cZ1BQUdeRuWZi97o3ktDQQkVeFFzqJARd1A5a0Vr7ta6Kp6TZjtZ+NTIOoKF6qDrL7e0QQIUCiqMMKk8Z1Q/SCSKvzocf2B6NEN0SQn/kTO6fKJ0zqjZUlQBSpJ0GjR77w0aoc1Pr6S5/kVJrNpakV5hR+LWKN4t7sLX+p0rx2vqSta64olIulUKUgCSXLWE1R4KPPSj+5vhm2hdDOG+CkQBmhhyyKq6SaFYWTn5bB3QJRNz54AuXKn8TJjhu0Wbv+wNEKQjVhnmKopjo4FxXmetCRnC4F7BhCiCUepqAepRh0TM/gjjzOOSK2NgWZPc05qampRWJHb7dbOffep2ednzLzgczlbrQA6gHYF9BYDh9GY+FjddMweHMscmMuep07gXlMQoqw9ALoYu5MJsak9QmJA2IvAgVmoCRciooyPujJtNCv1uHt3TmK9gegFKrG9kh6oXwZiIEAtBIjORGKNTWR/WeW8XVkbjuJepLAyloM8LmTN//njKZPbraATZaLjCHEww9Ei4FFiPg6Ja5gT6gxYgLgnRDHRQwJXbz2GOw0d4A3K4GXlUtMahJjYVxiYbrwOmxIS10bFnIBOSi6Tl9Jgs0zbOEX18wyEwgLPMrxD1Y4aCK8kmTpgYcpAF27Mzs42Hjx4kA8BICUlJfKArR7LcEvTB1xEC9AoEw9OPagWkVU/D1oesmK6U911zEczMVe01oZjiMggg6ux2Qk379qh4rYKet4GjrhhwEteBgBrH8BssoXEtbHzPpSBRRSpqlNpgAiUoxzHKxLRszoVuggIisxaDQWZqkQvQjAoax3NbDbLLGuUEABNGedXqSyLRupXgDT5JfAGZNLio9B0X8Uiwk4w77MDc1D4yejjWtykPS3DX01UDCY/GPQcVDe0QYT0CIxGFvUorfvBxZsRfVrUuWruMBAb/lXCUofoFNZfzGJtowXOX0vwUSFK4BgyMKm6P6s9wQUZld+jrYyMDC0iIQDaJdG4IyZQfL3RfbFcCBIlRgc+u3CjaTApuZ9KsANgG8PNzHlWWD3tCxd6kafNNiFp5HAalAkkJ0SCV2H3CgOD9Nc/FqrXuyb0Eocvfhq171p5eyuJ1omKJEP5rQGe/FOOnXtq335z8YmvYo9cHb2t8spIb3lVSseZW46FlGY/Sk9P50P2w20UlWJUkUHIushfc5PXGAzCo0PlD2pnpCYfCXga3lu+fPlevEhWrVrFyrN/Orfv87FOW9tlqb2Kc9pV8DzioMk3UNUbXM+8B/ATBr8C8CKdvGXWGD/9sqm3dkxtzA4McMjHMB8D2ftheYXo+qzt3pXvz8/PP/vk+v8537V+yYW87Zu+RZ1ZbrexoKAA/SBpaWn4+aL5w5zGk+/jW59JiMkESW5urpiVlWXENRb1H/Yf2I9txIxz5IdkX3TsraukpsbQjz6090yb4XsAvQoRE0YvJdamtIIbOnRoUVlZ2ftsLVQzIdEXHntsaZdimssVfCpFui109+BnWPsXaWLI/zactygAAAAASUVORK5CYII="),define("text!cockpit/ui/images/pinout.png",[],"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACyUlEQVQ4EW1TXUgUURQ+Z3ZmnVV3QV2xJbVSEIowQbAfLQx8McLoYX2qjB58MRSkP3vZppceYhGxgrZaIughlYpE7CHFWiiKyj9II0qxWmwlNh1Xtp2f27mz7GDlZX7uuXO+73zfuXeQMQYIgAyALppgyBtse32stsw86txkHhATn+FbfPfzxnPB+vR3RMJYuTwW6bbB4a6WS5O3Yu2VlXIesDiAamiQNKVlVXfx5I0GJ7DY7p0/+erU4dgeMJIA31WNxZmAgibOreXDqF55sY4SFUURqbi+nkjgwTyAbHhLX8yOLsSM2QRA3JRAAgd4RGPbVhkKEp8qeJ7PFyW3fw++YHtC7CkaD0amqyqihSwlMQQ0wa07IjPVI/vbexreIUrVaQV2D4RMQ/o7m12Mdfx4H3PfB9FNzTR1U2cO0Bi45aV6xNvFBNaoIAfbSiwLlqi9/hR/R3Nrhua+Oqi9TEKiB02C7YXz+Pba4MTDrpbLiMAxNgmXb+HpwVkZdoIrkn9isW7nRw/TZYaagZArAWyhfqsSDL/c9aTx7JUjGZCtYExRqCzAwGblwr6aFQ84nTo6qZ7XCeCVQNckE/KSWolvoQnxeoFFgIh8G/nA+kBAxxuQO5m9eFrwLIGJHgcyM63VFMhRSgNVyJr7og8y1vbTQpH8DIEVgxuYuexw0QECIalq5FYgEmpkgoFYltU/lnrqDz5osirSFpF7lrHAFKSWHYfEs+mY/82UnAStyMlW8sUPsVIciTZgz3jV1ebg0CEOpgPF22s1z1YQYKSXPJ1hbAhR8T26WdLhkuVfAzPR+YO1Ox5n58SmCcF6e3uzAoHA77RkevJdWH/3+f2O9TGf3w3fWQ2Hw5F/13mcsWAT+vv6DK4kFApJ/d3d1k+kJtbCrmxXHS3n8ER6b3CQbAqaEHVra6sGxcXW4SovLx+empxapS//FfwD9kpMJjMMBBAAAAAASUVORK5CYII=") \ No newline at end of file
diff --git a/studio/static/js/ace/keybinding-emacs.js b/studio/static/js/ace/keybinding-emacs.js
new file mode 100644
index 0000000..da4aa9d
--- /dev/null
+++ b/studio/static/js/ace/keybinding-emacs.js
@@ -0,0 +1 @@
+define("ace/keyboard/keybinding/emacs",["require","exports","module","ace/keyboard/state_handler"],function(a,b,c){var d=a("ace/keyboard/state_handler").StateHandler,e=a("ace/keyboard/state_handler").matchCharacterOnly,f={start:[{key:"ctrl-x",then:"c-x"},{regex:["(?:command-([0-9]*))*","(down|ctrl-n)"],exec:"golinedown",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{regex:["(?:command-([0-9]*))*","(right|ctrl-f)"],exec:"gotoright",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{regex:["(?:command-([0-9]*))*","(up|ctrl-p)"],exec:"golineup",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{regex:["(?:command-([0-9]*))*","(left|ctrl-b)"],exec:"gotoleft",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{comment:"This binding matches all printable characters except numbers as long as they are no numbers and print them n times.",regex:["(?:command-([0-9]*))","([^0-9]+)*"],match:e,exec:"inserttext",params:[{name:"times",match:1,type:"number",defaultValue:"1"},{name:"text",match:2}]},{comment:"This binding matches numbers as long as there is no meta_number in the buffer.",regex:["(command-[0-9]*)*","([0-9]+)"],match:e,disallowMatches:[1],exec:"inserttext",params:[{name:"text",match:2,type:"text"}]},{regex:["command-([0-9]*)","(command-[0-9]|[0-9])"],comment:"Stops execution if the regex /meta_[0-9]+/ matches to avoid resetting the buffer."}],"c-x":[{key:"ctrl-g",then:"start"},{key:"ctrl-s",exec:"save",then:"start"}]};b.Emacs=new d(f)}),define("ace/keyboard/state_handler",["require","exports","module"],function(a,b,c){function e(a){this.keymapping=this.$buildKeymappingRegex(a)}var d=!1;e.prototype={$buildKeymappingRegex:function(a){for(state in a)this.$buildBindingsRegex(a[state]);return a},$buildBindingsRegex:function(a){a.forEach(function(a){a.key?a.key=new RegExp("^"+a.key+"$"):Array.isArray(a.regex)?(a.key=new RegExp("^"+a.regex[1]+"$"),a.regex=new RegExp(a.regex.join("")+"$")):a.regex&&(a.regex=new RegExp(a.regex+"$"))})},$composeBuffer:function(a,b,c){if(a.state==null||a.buffer==null)a.state="start",a.buffer="";var d=[];b&1&&d.push("ctrl"),b&8&&d.push("command"),b&2&&d.push("option"),b&4&&d.push("shift"),c&&d.push(c);var e=d.join("-"),f=a.buffer+e;b!=2&&(a.buffer=f);return{bufferToUse:f,symbolicName:e}},$find:function(a,b,c,e,f){var g={};this.keymapping[a.state].some(function(h){var i;if(h.key&&!h.key.test(c))return!1;if(h.regex&&!(i=h.regex.exec(b)))return!1;if(h.match&&!h.match(b,e,f,c))return!1;if(h.disallowMatches)for(var j=0;j<h.disallowMatches.length;j++)if(!!i[h.disallowMatches[j]])return!1;if(h.exec){g.command=h.exec;if(h.params){var k;g.args={},h.params.forEach(function(a){a.match!=null&&i!=null?k=i[a.match]||a.defaultValue:k=a.defaultValue,a.type==="number"&&(k=parseInt(k)),g.args[a.name]=k})}a.buffer=""}h.then&&(a.state=h.then,a.buffer=""),g.command==null&&(g.command="null"),d&&console.log("KeyboardStateMapper#find",h);return!0});if(g.command)return g;a.buffer="";return!1},handleKeyboard:function(a,b,c){if(b!=0&&(c==""||c==String.fromCharCode(0)))return null;var e=this.$composeBuffer(a,b,c),f=e.bufferToUse,g=e.symbolicName;e=this.$find(a,f,g,b,c),d&&console.log("KeyboardStateMapper#match",f,g,e);return e}},b.matchCharacterOnly=function(a,b,c,d){return b==0?!0:b==4&&c.length==1?!0:!1},b.StateHandler=e}) \ No newline at end of file
diff --git a/studio/static/js/ace/keybinding-vim.js b/studio/static/js/ace/keybinding-vim.js
new file mode 100644
index 0000000..9f6ae87
--- /dev/null
+++ b/studio/static/js/ace/keybinding-vim.js
@@ -0,0 +1 @@
+define("ace/keyboard/keybinding/vim",["require","exports","module","ace/keyboard/state_handler"],function(a,b,c){var d=a("ace/keyboard/state_handler").StateHandler,e=a("ace/keyboard/state_handler").matchCharacterOnly,f={start:[{key:"i",then:"insertMode"},{key:"a",exec:"gotoright",then:"insertMode"},{key:"shift-i",exec:"gotolinestart",then:"insertMode"},{key:"shift-a",exec:"gotolineend",then:"insertMode"},{key:"shift-c",exec:"removetolineend",then:"insertMode"},{key:"shift-r",exec:"overwrite",then:"replaceMode"},{regex:["([0-9]*)","(k|up)"],exec:"golineup",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{regex:["([0-9]*)","(j|down|enter)"],exec:"golinedown",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{regex:["([0-9]*)","(l|right)"],exec:"gotoright",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{regex:["([0-9]*)","(h|left)"],exec:"gotoleft",params:[{name:"times",match:1,type:"number",defaultValue:1}]},{key:"shift-g",exec:"gotoend"},{key:"b",exec:"gotowordleft"},{key:"e",exec:"gotowordright"},{key:"x",exec:"del"},{key:"shift-x",exec:"backspace"},{key:"shift-d",exec:"removetolineend"},{comment:"Catch some keyboard input to stop it here",match:e}],insertMode:[{key:"esc",then:"start"}],replaceMode:[{key:"esc",exec:"overwrite",then:"start"}]};b.Vim=new d(f)}),define("ace/keyboard/state_handler",["require","exports","module"],function(a,b,c){function e(a){this.keymapping=this.$buildKeymappingRegex(a)}var d=!1;e.prototype={$buildKeymappingRegex:function(a){for(state in a)this.$buildBindingsRegex(a[state]);return a},$buildBindingsRegex:function(a){a.forEach(function(a){a.key?a.key=new RegExp("^"+a.key+"$"):Array.isArray(a.regex)?(a.key=new RegExp("^"+a.regex[1]+"$"),a.regex=new RegExp(a.regex.join("")+"$")):a.regex&&(a.regex=new RegExp(a.regex+"$"))})},$composeBuffer:function(a,b,c){if(a.state==null||a.buffer==null)a.state="start",a.buffer="";var d=[];b&1&&d.push("ctrl"),b&8&&d.push("command"),b&2&&d.push("option"),b&4&&d.push("shift"),c&&d.push(c);var e=d.join("-"),f=a.buffer+e;b!=2&&(a.buffer=f);return{bufferToUse:f,symbolicName:e}},$find:function(a,b,c,e,f){var g={};this.keymapping[a.state].some(function(h){var i;if(h.key&&!h.key.test(c))return!1;if(h.regex&&!(i=h.regex.exec(b)))return!1;if(h.match&&!h.match(b,e,f,c))return!1;if(h.disallowMatches)for(var j=0;j<h.disallowMatches.length;j++)if(!!i[h.disallowMatches[j]])return!1;if(h.exec){g.command=h.exec;if(h.params){var k;g.args={},h.params.forEach(function(a){a.match!=null&&i!=null?k=i[a.match]||a.defaultValue:k=a.defaultValue,a.type==="number"&&(k=parseInt(k)),g.args[a.name]=k})}a.buffer=""}h.then&&(a.state=h.then,a.buffer=""),g.command==null&&(g.command="null"),d&&console.log("KeyboardStateMapper#find",h);return!0});if(g.command)return g;a.buffer="";return!1},handleKeyboard:function(a,b,c){if(b!=0&&(c==""||c==String.fromCharCode(0)))return null;var e=this.$composeBuffer(a,b,c),f=e.bufferToUse,g=e.symbolicName;e=this.$find(a,f,g,b,c),d&&console.log("KeyboardStateMapper#match",f,g,e);return e}},b.matchCharacterOnly=function(a,b,c,d){return b==0?!0:b==4&&c.length==1?!0:!1},b.StateHandler=e}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-c_cpp.js b/studio/static/js/ace/mode-c_cpp.js
new file mode 100644
index 0000000..b19fc07
--- /dev/null
+++ b/studio/static/js/ace/mode-c_cpp.js
@@ -0,0 +1 @@
+define("ace/mode/c_cpp",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/c_cpp_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/c_cpp_highlight_rules").c_cppHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=a("ace/mode/behaviour/cstyle").CstyleBehaviour,k=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new j};d.inherits(k,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)\/\//;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"//")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}else if(a=="doc-start"){if(g=="start")return"";var h=b.match(/^\s*(\/?)\*/);h&&(h[1]&&(d+=" "),d+="* ")}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(k.prototype),b.Mode=k}),define("ace/mode/c_cpp_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("and|double|not_eq|throw|and_eq|dynamic_cast|operator|true|asm|else|or|try|auto|enum|or_eq|typedef|bitand|explicit|private|typeid|bitor|extern|protected|typename|bool|false|public|union|break|float|register|unsigned|case|fro|reinterpret-cast|using|catch|friend|return|virtual|char|goto|short|void|class|if|signed|volatile|compl|inline|sizeof|wchar_t|const|int|static|while|const-cast|long|static_cast|xor|continue|mutable|struct|xor_eq|default|namespace|switch|delete|new|template|do|not|this|for".split("|")),b=e.arrayToMap("NULL".split("|"));this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant",regex:"<[a-zA-Z0-9.]+>"},{token:"keyword",regex:"(?:#include|#pragma|#line|#define|#undef|#ifdef|#else|#elif|#endif|#ifndef)"},{token:function(c){return c=="this"?"variable.language":a.hasOwnProperty(c)?"keyword":b.hasOwnProperty(c)?"constant.language":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|==|=|!=|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.c_cppHighlightRules=h}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},{token:"comment.doc",regex:"\\s+"},{token:"comment.doc",regex:"TODO"},{token:"comment.doc",regex:"[^@\\*]+"},{token:"comment.doc",regex:"."}]}};d.inherits(f,e),function(){this.getStartRule=function(a){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:a}},this.getEndRule=function(a){return{token:"comment.doc",regex:"\\*\\/",next:a}}}.call(f.prototype),b.DocCommentHighlightRules=f}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-clojure.js b/studio/static/js/ace/mode-clojure.js
new file mode 100644
index 0000000..91338d2
--- /dev/null
+++ b/studio/static/js/ace/mode-clojure.js
@@ -0,0 +1 @@
+define("ace/mode/clojure",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/clojure_highlight_rules","ace/mode/matching_parens_outdent","ace/range"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/clojure_highlight_rules").ClojureHighlightRules,h=a("ace/mode/matching_parens_outdent").MatchingParensOutdent,i=a("ace/range").Range,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(j,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)#/;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,";")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=d,f=this.$tokenizer.getLineTokens(b,a),g=f.tokens,h=f.state;if(g.length&&g[g.length-1].type=="comment")return d;if(a=="start"){var i=b.match(/[\(\[]/);i&&(d+=" "),i=b.match(/[\)]/),i&&(d="")}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(j.prototype),b.Mode=j}),define("ace/mode/clojure_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){var a=e.arrayToMap("* *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *e *err* *file* *flush-on-newline* *in* *macro-meta* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *use-context-classloader* *warn-on-reflection* + - -> -&gt; ->> -&gt;&gt; .. / < &lt; <= &lt;= = == > &gt; >= &gt;= accessor aclone add-classpath add-watch agent agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* butlast byte byte-array bytes cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec decimal? declare definline defmacro defmethod defmulti defn defn- defonce defstruct delay delay? deliver deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall doc dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq eval even? every? false? ffirst file-seq filter find find-doc find-ns find-var first float float-array float? floats flush fn fn? fnext for force format future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator hash hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map? mapcat max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod name namespace neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext num number? odd? or parents partial partition pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-doc print-dup print-method print-namespace-doc print-simple print-special-doc print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string reduce ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure release-pending-sends rem remove remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest resultset-seq reverse reversible? rseq rsubseq second select-keys send send-off seq seq? seque sequence sequential? set set-validator! set? short short-array shorts shutdown-agents slurp some sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-form-anchor special-symbol? split-at split-with str stream? string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync syntax-symbol-anchor take take-last take-nth take-while test the-ns time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-dec unchecked-divide unchecked-inc unchecked-multiply unchecked-negate unchecked-remainder unchecked-subtract underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision xml-seq zero? zipmap ".split(" ")),b=e.arrayToMap("def do fn if let loop monitor-enter monitor-exit new quote recur set! throw try var".split(" ")),c=e.arrayToMap("true false nil".split(" "));this.$rules={start:[{token:"comment",regex:";.*$"},{token:"comment",regex:"^=begin$",next:"comment"},{token:"keyword",regex:"[\\(|\\)]"},{token:"keyword",regex:"[\\'\\(]"},{token:"keyword",regex:"[\\[|\\]]"},{token:"keyword",regex:"[\\{|\\}|\\#\\{|\\#\\}]"},{token:"keyword",regex:"[\\&]"},{token:"keyword",regex:"[\\#\\^\\{]"},{token:"keyword",regex:"[\\%]"},{token:"keyword",regex:"[@]"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language",regex:"[!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+||=|!=|<=|>=|<>|<|>|!|&&]"},{token:function(d){return b.hasOwnProperty(d)?"keyword":c.hasOwnProperty(d)?"constant.language":a.hasOwnProperty(d)?"support.function":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"[:](?:[a-zA-Z]|d)+"},{token:"string.regexp",regex:'/#"(?:.|(\\")|[^""\n])*"/g'}],comment:[{token:"comment",regex:"^=end$",next:"start"},{token:"comment",regex:".+"}]}};d.inherits(g,f),b.ClojureHighlightRules=g}),define("ace/mode/matching_parens_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\)/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\))/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingParensOutdent=e}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-coffee.js b/studio/static/js/ace/mode-coffee.js
new file mode 100644
index 0000000..ca47332
--- /dev/null
+++ b/studio/static/js/ace/mode-coffee.js
@@ -0,0 +1 @@
+define("ace/mode/coffee",["require","exports","module","ace/tokenizer","ace/mode/coffee_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/text","ace/worker/worker_client","pilot/oop"],function(a,b,c){function k(){this.$tokenizer=new d((new e).getRules()),this.$outdent=new f}var d=a("ace/tokenizer").Tokenizer,e=a("ace/mode/coffee_highlight_rules").CoffeeHighlightRules,f=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,g=a("ace/range").Range,h=a("ace/mode/text").Mode,i=a("ace/worker/worker_client").WorkerClient,j=a("pilot/oop");j.inherits(k,h),function(){var a=/(?:[({[=:]|[-=]>|\b(?:else|switch|try|catch(?:\s*[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)?|finally))\s*$/,b=/^(\s*)#/,c=/^\s*###(?!#)/,d=/^\s*/;this.getNextLineIndent=function(b,c,d){var e=this.$getIndent(c),f=this.$tokenizer.getLineTokens(c,b).tokens;(!f.length||f[f.length-1].type!=="comment")&&b==="start"&&a.test(c)&&(e+=d);return e},this.toggleCommentLines=function(a,e,f,h){console.log("toggle");var i=new g(0,0,0,0);for(var j=f;j<=h;++j){var k=e.getLine(j);if(c.test(k))continue;b.test(k)?k=k.replace(b,"$1"):k=k.replace(d,"$&#"),i.end.row=i.start.row=j,i.end.column=k.length+1,e.replace(i,k)}},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){var b=a.getDocument(),c=new i(["ace","pilot"],"worker-coffee.js","ace/mode/coffee_worker","Worker");c.call("setValue",[b.getValue()]),b.on("change",function(a){a.range={start:a.data.range.start,end:a.data.range.end},c.emit("change",a)}),c.on("error",function(b){a.setAnnotations([b.data])}),c.on("ok",function(b){a.clearAnnotations()})}}.call(k.prototype),b.Mode=k}),define("ace/mode/coffee_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){function d(){var a="[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*",b="(?![$\\w]|\\s*:)",c={token:"string",regex:".+"};this.$rules={start:[{token:"identifier",regex:"(?:@|(?:\\.|::)\\s*)"+a},{token:"keyword",regex:"(?:t(?:h(?:is|row|en)|ry|ypeof)|s(?:uper|witch)|return|b(?:reak|y)|c(?:ontinue|atch|lass)|i(?:n(?:stanceof)?|s(?:nt)?|f)|e(?:lse|xtends)|f(?:or (?:own)?|inally|unction)|wh(?:ile|en)|n(?:ew|ot?)|d(?:e(?:lete|bugger)|o)|loop|o(?:ff?|[rn])|un(?:less|til)|and|yes)"+b},{token:"constant.language",regex:"(?:true|false|null|undefined)"+b},{token:"invalid.illegal",regex:"(?:c(?:ase|onst)|default|function|v(?:ar|oid)|with|e(?:num|xport)|i(?:mplements|nterface)|let|p(?:ackage|r(?:ivate|otected)|ublic)|static|yield|__(?:hasProp|extends|slice|bind|indexOf))"+b},{token:"language.support.class",regex:"(?:Array|Boolean|Date|Function|Number|Object|R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|TypeError|URIError)"+b},{token:"language.support.function",regex:"(?:Math|JSON|is(?:NaN|Finite)|parse(?:Int|Float)|encodeURI(?:Component)?|decodeURI(?:Component)?)"+b},{token:"identifier",regex:a},{token:"constant.numeric",regex:"(?:0x[\\da-fA-F]+|(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:[eE][+-]?\\d+)?)"},{token:"string",regex:"'''",next:"qdoc"},{token:"string",regex:'"""',next:"qqdoc"},{token:"string",regex:"'",next:"qstring"},{token:"string",regex:'"',next:"qqstring"},{token:"string",regex:"`",next:"js"},{token:"string.regex",regex:"///",next:"heregex"},{token:"string.regex",regex:"/(?!\\s)[^[/\\n\\\\]*(?: (?:\\\\.|\\[[^\\]\\n\\\\]*(?:\\\\.[^\\]\\n\\\\]*)*\\])[^[/\\n\\\\]*)*/[imgy]{0,4}(?!\\w)"},{token:"comment",regex:"###(?!#)",next:"comment"},{token:"comment",regex:"#.*"},{token:"lparen",regex:"[({[]"},{token:"rparen",regex:"[\\]})]"},{token:"keyword.operator",regex:"\\S+"},{token:"text",regex:"\\s+"}],qdoc:[{token:"string",regex:".*?'''",next:"start"},c],qqdoc:[{token:"string",regex:'.*?"""',next:"start"},c],qstring:[{token:"string",regex:"[^\\\\']*(?:\\\\.[^\\\\']*)*'",next:"start"},c],qqstring:[{token:"string",regex:'[^\\\\"]*(?:\\\\.[^\\\\"]*)*"',next:"start"},c],js:[{token:"string",regex:"[^\\\\`]*(?:\\\\.[^\\\\`]*)*`",next:"start"},c],heregex:[{token:"string.regex",regex:".*?///[imgy]{0,4}",next:"start"},{token:"comment.regex",regex:"\\s+(?:#.*)?"},{token:"string.regex",regex:"\\S+"}],comment:[{token:"comment",regex:".*?###",next:"start"},{token:"comment",regex:".+"}]}}a("pilot/oop").inherits(d,a("ace/mode/text_highlight_rules").TextHighlightRules),b.CoffeeHighlightRules=d}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/worker/worker_client",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=function(b,c,d,e){this.callbacks=[];if(a.packaged)var f=this.$guessBasePath(),g=this.$worker=new Worker(f+c);else{var h=a.nameToUrl("ace/worker/worker",null,"_"),g=this.$worker=new Worker(h),i={};for(var j=0;j<b.length;j++){var k=b[j];i[k]=a.nameToUrl(k,null,"_").replace(/.js$/,"")}}this.$worker.postMessage({init:!0,tlns:i,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){window.console&&console.log&&console.log(a);throw a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._dispatchEvent(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$guessBasePath=function(){if(a.aceBaseUrl)return a.aceBaseUrl;var b=document.getElementsByTagName("script");for(var c=0;c<b.length;c++){var d=b[c].src||b[c].getAttribute("src");if(!d)continue;var e=d.match(/^(?:(.*\/)ace\.js|(.*\/)ace-uncompressed\.js)(?:\?|$)/);if(e)return e[1]||e[2]}return""},this.terminate=function(){this._dispatchEvent("terminate",{}),this.$worker.terminate()},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){this.$worker.postMessage({event:a,data:b})}}).call(f.prototype),b.WorkerClient=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-csharp.js b/studio/static/js/ace/mode-csharp.js
new file mode 100644
index 0000000..4c3c12d
--- /dev/null
+++ b/studio/static/js/ace/mode-csharp.js
@@ -0,0 +1 @@
+define("ace/mode/csharp",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/csharp_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/csharp_highlight_rules").CSharpHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/mode/behaviour/cstyle").CstyleBehaviour,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new i};d.inherits(j,e),function(){this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){return null}}.call(j.prototype),b.Mode=j}),define("ace/mode/csharp_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("abstract|event|new|struct|as|explicit|null|switch|base|extern|object|this|bool|false|operator|throw|break|finally|out|true|byte|fixed|override|try|case|float|params|typeof|catch|for|private|uint|char|foreach|protected|ulong|checked|goto|public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|continue|in|return|using|decimal|int|sbyte|virtual|default|interface|sealed|volatile|delegate|internal|short|void|do|is|sizeof|while|double|lock|stackalloc|else|long|static|enum|namespace|string|var|dynamic".split("|")),b=e.arrayToMap("null|true|false".split("|"));this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"comment",regex:"\\/\\*\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:function(c){return c=="this"?"variable.language":a.hasOwnProperty(c)?"keyword":b.hasOwnProperty(c)?"constant.language":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.CSharpHighlightRules=h}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},{token:"comment.doc",regex:"\\s+"},{token:"comment.doc",regex:"TODO"},{token:"comment.doc",regex:"[^@\\*]+"},{token:"comment.doc",regex:"."}]}};d.inherits(f,e),function(){this.getStartRule=function(a){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:a}},this.getEndRule=function(a){return{token:"comment.doc",regex:"\\*\\/",next:a}}}.call(f.prototype),b.DocCommentHighlightRules=f}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-css.js b/studio/static/js/ace/mode-css.js
new file mode 100644
index 0000000..c27ad88
--- /dev/null
+++ b/studio/static/js/ace/mode-css.js
@@ -0,0 +1 @@
+define("ace/mode/css",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/css_highlight_rules").CssHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/worker/worker_client").WorkerClient,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(j,e),function(){this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a).tokens;if(e.length&&e[e.length-1].type=="comment")return d;var f=b.match(/^.*\{\s*$/);f&&(d+=c);return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){var b=a.getDocument(),c=new i(["ace","pilot"],"worker-css.js","ace/mode/css_worker","Worker");c.call("setValue",[b.getValue()]),b.on("change",function(a){a.range={start:a.data.range.start,end:a.data.range.end},c.emit("change",a)}),c.on("csslint",function(b){var c=[];b.data.forEach(function(a){c.push({row:a.line-1,column:a.col-1,text:a.message,type:a.type,lint:a})}),a.setAnnotations(c)})}}.call(j.prototype),b.Mode=j}),define("ace/mode/css_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){function g(a){var b=[],c=a.split("");for(var d=0;d<c.length;d++)b.push("[",c[d].toLowerCase(),c[d].toUpperCase(),"]");return b.join("")}var a=e.arrayToMap("-moz-box-sizing|-webkit-box-sizing|appearance|azimuth|background-attachment|background-color|background-image|background-position|background-repeat|background|border-bottom-color|border-bottom-style|border-bottom-width|border-bottom|border-collapse|border-color|border-left-color|border-left-style|border-left-width|border-left|border-right-color|border-right-style|border-right-width|border-right|border-spacing|border-style|border-top-color|border-top-style|border-top-width|border-top|border-width|border|bottom|box-sizing|caption-side|clear|clip|color|content|counter-increment|counter-reset|cue-after|cue-before|cue|cursor|direction|display|elevation|empty-cells|float|font-family|font-size-adjust|font-size|font-stretch|font-style|font-variant|font-weight|font|height|left|letter-spacing|line-height|list-style-image|list-style-position|list-style-type|list-style|margin-bottom|margin-left|margin-right|margin-top|marker-offset|margin|marks|max-height|max-width|min-height|min-width|-moz-border-radius|opacity|orphans|outline-color|outline-style|outline-width|outline|overflow|overflow-x|overflow-y|padding-bottom|padding-left|padding-right|padding-top|padding|page-break-after|page-break-before|page-break-inside|page|pause-after|pause-before|pause|pitch-range|pitch|play-during|position|quotes|richness|right|size|speak-header|speak-numeral|speak-punctuation|speech-rate|speak|stress|table-layout|text-align|text-decoration|text-indent|text-shadow|text-transform|top|unicode-bidi|vertical-align|visibility|voice-family|volume|white-space|widows|width|word-spacing|z-index".split("|")),b=e.arrayToMap("rgb|rgba|url|attr|counter|counters".split("|")),c=e.arrayToMap("absolute|all-scroll|always|armenian|auto|baseline|below|bidi-override|block|bold|bolder|border-box|both|bottom|break-all|break-word|capitalize|center|char|circle|cjk-ideographic|col-resize|collapse|content-box|crosshair|dashed|decimal-leading-zero|decimal|default|disabled|disc|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ellipsis|fixed|georgian|groove|hand|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|inactive|inherit|inline-block|inline|inset|inside|inter-ideograph|inter-word|italic|justify|katakana-iroha|katakana|keep-all|left|lighter|line-edge|line-through|line|list-item|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|medium|middle|move|n-resize|ne-resize|newspaper|no-drop|no-repeat|nw-resize|none|normal|not-allowed|nowrap|oblique|outset|outside|overline|pointer|progress|relative|repeat-x|repeat-y|repeat|right|ridge|row-resize|rtl|s-resize|scroll|se-resize|separate|small-caps|solid|square|static|strict|super|sw-resize|table-footer-group|table-header-group|tb-rl|text-bottom|text-top|text|thick|thin|top|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|zero".split("|")),d=e.arrayToMap("aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow".split("|")),f="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=[{token:"comment",regex:"\\/\\*",next:"ruleset_comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:f+g("em")},{token:"constant.numeric",regex:f+g("ex")},{token:"constant.numeric",regex:f+g("px")},{token:"constant.numeric",regex:f+g("cm")},{token:"constant.numeric",regex:f+g("mm")},{token:"constant.numeric",regex:f+g("in")},{token:"constant.numeric",regex:f+g("pt")},{token:"constant.numeric",regex:f+g("pc")},{token:"constant.numeric",regex:f+g("deg")},{token:"constant.numeric",regex:f+g("rad")},{token:"constant.numeric",regex:f+g("grad")},{token:"constant.numeric",regex:f+g("ms")},{token:"constant.numeric",regex:f+g("s")},{token:"constant.numeric",regex:f+g("hz")},{token:"constant.numeric",regex:f+g("khz")},{token:"constant.numeric",regex:f+"%"},{token:"constant.numeric",regex:f},{token:"constant.numeric",regex:"#[a-fA-F0-9]{6}"},{token:"constant.numeric",regex:"#[a-fA-F0-9]{3}"},{token:function(e){return a.hasOwnProperty(e.toLowerCase())?"support.type":b.hasOwnProperty(e.toLowerCase())?"support.function":c.hasOwnProperty(e.toLowerCase())?"support.constant":d.hasOwnProperty(e.toLowerCase())?"support.constant.color":"text"},regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"}],i=e.copyArray(h);i.unshift({token:"rparen",regex:"}",next:"start"});var j=e.copyArray(h);j.unshift({token:"rparen",regex:"}",next:"media"});var k=[{token:"comment",regex:".+"}],l=e.copyArray(k);l.unshift({token:"comment",regex:".*?\\*\\/",next:"start"});var m=e.copyArray(k);m.unshift({token:"comment",regex:".*?\\*\\/",next:"media"});var n=e.copyArray(k);n.unshift({token:"comment",regex:".*?\\*\\/",next:"ruleset"}),this.$rules={start:[{token:"comment",regex:"\\/\\*",next:"comment"},{token:"lparen",regex:"{",next:"ruleset"},{token:"string",regex:"@media.*?{",next:"media"},{token:"keyword",regex:"#[a-zA-Z0-9-_]+"},{token:"variable",regex:"\\.[a-zA-Z0-9-_]+"},{token:"string",regex:":[a-zA-Z0-9-_]+"},{token:"constant",regex:"[a-zA-Z0-9-_]+"}],media:[{token:"comment",regex:"\\/\\*",next:"media_comment"},{token:"lparen",regex:"{",next:"media_ruleset"},{token:"string",regex:"}",next:"start"},{token:"keyword",regex:"#[a-zA-Z0-9-_]+"},{token:"variable",regex:"\\.[a-zA-Z0-9-_]+"},{token:"string",regex:":[a-zA-Z0-9-_]+"},{token:"constant",regex:"[a-zA-Z0-9-_]+"}],comment:l,ruleset:i,ruleset_comment:n,media_ruleset:j,media_comment:m}};d.inherits(g,f),b.CssHighlightRules=g}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/worker/worker_client",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=function(b,c,d,e){this.callbacks=[];if(a.packaged)var f=this.$guessBasePath(),g=this.$worker=new Worker(f+c);else{var h=a.nameToUrl("ace/worker/worker",null,"_"),g=this.$worker=new Worker(h),i={};for(var j=0;j<b.length;j++){var k=b[j];i[k]=a.nameToUrl(k,null,"_").replace(/.js$/,"")}}this.$worker.postMessage({init:!0,tlns:i,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){window.console&&console.log&&console.log(a);throw a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._dispatchEvent(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$guessBasePath=function(){if(a.aceBaseUrl)return a.aceBaseUrl;var b=document.getElementsByTagName("script");for(var c=0;c<b.length;c++){var d=b[c].src||b[c].getAttribute("src");if(!d)continue;var e=d.match(/^(?:(.*\/)ace\.js|(.*\/)ace-uncompressed\.js)(?:\?|$)/);if(e)return e[1]||e[2]}return""},this.terminate=function(){this._dispatchEvent("terminate",{}),this.$worker.terminate()},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){this.$worker.postMessage({event:a,data:b})}}).call(f.prototype),b.WorkerClient=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-html.js b/studio/static/js/ace/mode-html.js
new file mode 100644
index 0000000..526a1cb
--- /dev/null
+++ b/studio/static/js/ace/mode-html.js
@@ -0,0 +1 @@
+define("ace/mode/html",["require","exports","module","pilot/oop","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/tokenizer","ace/mode/html_highlight_rules","ace/mode/behaviour/xml"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/mode/javascript").Mode,g=a("ace/mode/css").Mode,h=a("ace/tokenizer").Tokenizer,i=a("ace/mode/html_highlight_rules").HtmlHighlightRules,j=a("ace/mode/behaviour/xml").XmlBehaviour,k=function(){var a=new i;this.$tokenizer=new h(a.getRules()),this.$behaviour=new j,this.$embeds=a.getEmbeds(),this.createModeDelegates({"js-":f,"css-":g})};d.inherits(k,e),function(){this.toggleCommentLines=function(a,b,c,d){return 0},this.getNextLineIndent=function(a,b,c){return this.$getIndent(b)},this.checkOutdent=function(a,b,c){return!1}}.call(k.prototype),b.Mode=k}),define("ace/mode/javascript",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/javascript_highlight_rules").JavaScriptHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=a("ace/worker/worker_client").WorkerClient,k=a("ace/mode/behaviour/cstyle").CstyleBehaviour,l=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new k};d.inherits(l,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)\/\//;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"//")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}else if(a=="doc-start"){if(g=="start")return"";var h=b.match(/^\s*(\/?)\*/);h&&(h[1]&&(d+=" "),d+="* ")}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){var b=a.getDocument(),c=new j(["ace","pilot"],"worker-javascript.js","ace/mode/javascript_worker","JavaScriptWorker");c.call("setValue",[b.getValue()]),b.on("change",function(a){a.range={start:a.data.range.start,end:a.data.range.end},c.emit("change",a)}),c.on("jslint",function(b){var c=[];for(var d=0;d<b.data.length;d++){var e=b.data[d];e&&c.push({row:e.line-1,column:e.character-1,text:e.reason,type:"warning",lint:e})}a.setAnnotations(c)}),c.on("narcissus",function(b){a.setAnnotations([b.data])}),c.on("terminate",function(){a.clearAnnotations()});return c}}.call(l.prototype),b.Mode=l}),define("ace/mode/javascript_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|const|yield|import|get|set".split("|")),b=e.arrayToMap("null|Infinity|NaN|undefined".split("|")),c=e.arrayToMap("class|enum|extends|super|export|implements|private|public|interface|package|protected|static".split("|"));this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:function(d){return d=="this"?"variable.language":a.hasOwnProperty(d)?"keyword":b.hasOwnProperty(d)?"constant.language":c.hasOwnProperty(d)?"invalid.illegal":d=="debugger"?"invalid.deprecated":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"comment",regex:"^#!.*$"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.JavaScriptHighlightRules=h}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},{token:"comment.doc",regex:"\\s+"},{token:"comment.doc",regex:"TODO"},{token:"comment.doc",regex:"[^@\\*]+"},{token:"comment.doc",regex:"."}]}};d.inherits(f,e),function(){this.getStartRule=function(a){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:a}},this.getEndRule=function(a){return{token:"comment.doc",regex:"\\*\\/",next:a}}}.call(f.prototype),b.DocCommentHighlightRules=f}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/worker/worker_client",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=function(b,c,d,e){this.callbacks=[];if(a.packaged)var f=this.$guessBasePath(),g=this.$worker=new Worker(f+c);else{var h=a.nameToUrl("ace/worker/worker",null,"_"),g=this.$worker=new Worker(h),i={};for(var j=0;j<b.length;j++){var k=b[j];i[k]=a.nameToUrl(k,null,"_").replace(/.js$/,"")}}this.$worker.postMessage({init:!0,tlns:i,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){window.console&&console.log&&console.log(a);throw a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._dispatchEvent(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$guessBasePath=function(){if(a.aceBaseUrl)return a.aceBaseUrl;var b=document.getElementsByTagName("script");for(var c=0;c<b.length;c++){var d=b[c].src||b[c].getAttribute("src");if(!d)continue;var e=d.match(/^(?:(.*\/)ace\.js|(.*\/)ace-uncompressed\.js)(?:\?|$)/);if(e)return e[1]||e[2]}return""},this.terminate=function(){this._dispatchEvent("terminate",{}),this.$worker.terminate()},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){this.$worker.postMessage({event:a,data:b})}}).call(f.prototype),b.WorkerClient=f}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}),define("ace/mode/css",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/css_highlight_rules").CssHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/worker/worker_client").WorkerClient,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(j,e),function(){this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a).tokens;if(e.length&&e[e.length-1].type=="comment")return d;var f=b.match(/^.*\{\s*$/);f&&(d+=c);return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){var b=a.getDocument(),c=new i(["ace","pilot"],"worker-css.js","ace/mode/css_worker","Worker");c.call("setValue",[b.getValue()]),b.on("change",function(a){a.range={start:a.data.range.start,end:a.data.range.end},c.emit("change",a)}),c.on("csslint",function(b){var c=[];b.data.forEach(function(a){c.push({row:a.line-1,column:a.col-1,text:a.message,type:a.type,lint:a})}),a.setAnnotations(c)})}}.call(j.prototype),b.Mode=j}),define("ace/mode/css_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){function g(a){var b=[],c=a.split("");for(var d=0;d<c.length;d++)b.push("[",c[d].toLowerCase(),c[d].toUpperCase(),"]");return b.join("")}var a=e.arrayToMap("-moz-box-sizing|-webkit-box-sizing|appearance|azimuth|background-attachment|background-color|background-image|background-position|background-repeat|background|border-bottom-color|border-bottom-style|border-bottom-width|border-bottom|border-collapse|border-color|border-left-color|border-left-style|border-left-width|border-left|border-right-color|border-right-style|border-right-width|border-right|border-spacing|border-style|border-top-color|border-top-style|border-top-width|border-top|border-width|border|bottom|box-sizing|caption-side|clear|clip|color|content|counter-increment|counter-reset|cue-after|cue-before|cue|cursor|direction|display|elevation|empty-cells|float|font-family|font-size-adjust|font-size|font-stretch|font-style|font-variant|font-weight|font|height|left|letter-spacing|line-height|list-style-image|list-style-position|list-style-type|list-style|margin-bottom|margin-left|margin-right|margin-top|marker-offset|margin|marks|max-height|max-width|min-height|min-width|-moz-border-radius|opacity|orphans|outline-color|outline-style|outline-width|outline|overflow|overflow-x|overflow-y|padding-bottom|padding-left|padding-right|padding-top|padding|page-break-after|page-break-before|page-break-inside|page|pause-after|pause-before|pause|pitch-range|pitch|play-during|position|quotes|richness|right|size|speak-header|speak-numeral|speak-punctuation|speech-rate|speak|stress|table-layout|text-align|text-decoration|text-indent|text-shadow|text-transform|top|unicode-bidi|vertical-align|visibility|voice-family|volume|white-space|widows|width|word-spacing|z-index".split("|")),b=e.arrayToMap("rgb|rgba|url|attr|counter|counters".split("|")),c=e.arrayToMap("absolute|all-scroll|always|armenian|auto|baseline|below|bidi-override|block|bold|bolder|border-box|both|bottom|break-all|break-word|capitalize|center|char|circle|cjk-ideographic|col-resize|collapse|content-box|crosshair|dashed|decimal-leading-zero|decimal|default|disabled|disc|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ellipsis|fixed|georgian|groove|hand|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|inactive|inherit|inline-block|inline|inset|inside|inter-ideograph|inter-word|italic|justify|katakana-iroha|katakana|keep-all|left|lighter|line-edge|line-through|line|list-item|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|medium|middle|move|n-resize|ne-resize|newspaper|no-drop|no-repeat|nw-resize|none|normal|not-allowed|nowrap|oblique|outset|outside|overline|pointer|progress|relative|repeat-x|repeat-y|repeat|right|ridge|row-resize|rtl|s-resize|scroll|se-resize|separate|small-caps|solid|square|static|strict|super|sw-resize|table-footer-group|table-header-group|tb-rl|text-bottom|text-top|text|thick|thin|top|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|zero".split("|")),d=e.arrayToMap("aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow".split("|")),f="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=[{token:"comment",regex:"\\/\\*",next:"ruleset_comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:f+g("em")},{token:"constant.numeric",regex:f+g("ex")},{token:"constant.numeric",regex:f+g("px")},{token:"constant.numeric",regex:f+g("cm")},{token:"constant.numeric",regex:f+g("mm")},{token:"constant.numeric",regex:f+g("in")},{token:"constant.numeric",regex:f+g("pt")},{token:"constant.numeric",regex:f+g("pc")},{token:"constant.numeric",regex:f+g("deg")},{token:"constant.numeric",regex:f+g("rad")},{token:"constant.numeric",regex:f+g("grad")},{token:"constant.numeric",regex:f+g("ms")},{token:"constant.numeric",regex:f+g("s")},{token:"constant.numeric",regex:f+g("hz")},{token:"constant.numeric",regex:f+g("khz")},{token:"constant.numeric",regex:f+"%"},{token:"constant.numeric",regex:f},{token:"constant.numeric",regex:"#[a-fA-F0-9]{6}"},{token:"constant.numeric",regex:"#[a-fA-F0-9]{3}"},{token:function(e){return a.hasOwnProperty(e.toLowerCase())?"support.type":b.hasOwnProperty(e.toLowerCase())?"support.function":c.hasOwnProperty(e.toLowerCase())?"support.constant":d.hasOwnProperty(e.toLowerCase())?"support.constant.color":"text"},regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"}],i=e.copyArray(h);i.unshift({token:"rparen",regex:"}",next:"start"});var j=e.copyArray(h);j.unshift({token:"rparen",regex:"}",next:"media"});var k=[{token:"comment",regex:".+"}],l=e.copyArray(k);l.unshift({token:"comment",regex:".*?\\*\\/",next:"start"});var m=e.copyArray(k);m.unshift({token:"comment",regex:".*?\\*\\/",next:"media"});var n=e.copyArray(k);n.unshift({token:"comment",regex:".*?\\*\\/",next:"ruleset"}),this.$rules={start:[{token:"comment",regex:"\\/\\*",next:"comment"},{token:"lparen",regex:"{",next:"ruleset"},{token:"string",regex:"@media.*?{",next:"media"},{token:"keyword",regex:"#[a-zA-Z0-9-_]+"},{token:"variable",regex:"\\.[a-zA-Z0-9-_]+"},{token:"string",regex:":[a-zA-Z0-9-_]+"},{token:"constant",regex:"[a-zA-Z0-9-_]+"}],media:[{token:"comment",regex:"\\/\\*",next:"media_comment"},{token:"lparen",regex:"{",next:"media_ruleset"},{token:"string",regex:"}",next:"start"},{token:"keyword",regex:"#[a-zA-Z0-9-_]+"},{token:"variable",regex:"\\.[a-zA-Z0-9-_]+"},{token:"string",regex:":[a-zA-Z0-9-_]+"},{token:"constant",regex:"[a-zA-Z0-9-_]+"}],comment:l,ruleset:i,ruleset_comment:n,media_ruleset:j,media_comment:m}};d.inherits(g,f),b.CssHighlightRules=g}),define("ace/mode/html_highlight_rules",["require","exports","module","pilot/oop","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/css_highlight_rules").CssHighlightRules,f=a("ace/mode/javascript_highlight_rules").JavaScriptHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){function b(a,b){return[{token:"string",regex:".*"+a,next:b},{token:"string",regex:".+"}]}function a(a){return[{token:"string",regex:'".*?"'},{token:"string",regex:'["].*$',next:a+"-qqstring"},{token:"string",regex:"'.*?'"},{token:"string",regex:"['].*$",next:a+"-qstring"}]}this.$rules={start:[{token:"text",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:"xml_pe",regex:"<\\?.*?\\?>"},{token:"comment",regex:"<\\!--",next:"comment"},{token:"text",regex:"<(?=s*script)",next:"script"},{token:"text",regex:"<(?=s*style)",next:"css"},{token:"text",regex:"<\\/?",next:"tag"},{token:"text",regex:"\\s+"},{token:"text",regex:"[^<]+"}],script:[{token:"text",regex:">",next:"js-start"},{token:"keyword",regex:"[-_a-zA-Z0-9:]+"},{token:"text",regex:"\\s+"}].concat(a("script")),css:[{token:"text",regex:">",next:"css-start"},{token:"keyword",regex:"[-_a-zA-Z0-9:]+"},{token:"text",regex:"\\s+"}].concat(a("style")),tag:[{token:"text",regex:">",next:"start"},{token:"keyword",regex:"[-_a-zA-Z0-9:]+"},{token:"text",regex:"\\s+"}].concat(a("tag")),"style-qstring":b("'","style"),"style-qqstring":b('"',"style"),"script-qstring":b("'","script"),"script-qqstring":b('"',"script"),"tag-qstring":b("'","tag"),"tag-qqstring":b('"',"tag"),cdata:[{token:"text",regex:"\\]\\]>",next:"start"},{token:"text",regex:"\\s+"},{token:"text",regex:".+"}],comment:[{token:"comment",regex:".*?-->",next:"start"},{token:"comment",regex:".+"}]},this.embedRules(f,"js-",[{token:"comment",regex:"\\/\\/.*(?=<\\/script>)",next:"tag"},{token:"text",regex:"<\\/(?=script)",next:"tag"}]),this.embedRules(e,"css-",[{token:"text",regex:"<\\/(?=style)",next:"tag"}])};d.inherits(h,g),b.HtmlHighlightRules=h}),define("ace/mode/behaviour/xml",["require","exports","module","pilot/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=a("ace/mode/behaviour/cstyle").CstyleBehaviour,g=function(){this.inherit(f,["string_dquotes"]),this.add("brackets","insertion",function(a,b,c,d,e){if(e=="<"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?!1:{text:"<>",selection:[1,1]}}if(e==">"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==">")return{text:"",selection:[1,1]}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),k=i.substring(h.column,h.column+2);if(k=="</"){var l=this.$getIndent(d.doc.getLine(h.row))+d.getTabString(),m=this.$getIndent(d.doc.getLine(h.row));return{text:"\n"+l+"\n"+m,selection:[1,l.length,1,l.length]}}}return!1})};d.inherits(g,e),b.XmlBehaviour=g}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-java.js b/studio/static/js/ace/mode-java.js
new file mode 100644
index 0000000..93fc3b9
--- /dev/null
+++ b/studio/static/js/ace/mode-java.js
@@ -0,0 +1 @@
+define("ace/mode/java",["require","exports","module","pilot/oop","ace/mode/javascript","ace/tokenizer","ace/mode/java_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/javascript").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/java_highlight_rules").JavaHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/mode/behaviour/cstyle").CstyleBehaviour,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new i};d.inherits(j,e),function(){this.createWorker=function(a){return null}}.call(j.prototype),b.Mode=j}),define("ace/mode/javascript",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/javascript_highlight_rules").JavaScriptHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=a("ace/worker/worker_client").WorkerClient,k=a("ace/mode/behaviour/cstyle").CstyleBehaviour,l=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new k};d.inherits(l,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)\/\//;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"//")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}else if(a=="doc-start"){if(g=="start")return"";var h=b.match(/^\s*(\/?)\*/);h&&(h[1]&&(d+=" "),d+="* ")}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){var b=a.getDocument(),c=new j(["ace","pilot"],"worker-javascript.js","ace/mode/javascript_worker","JavaScriptWorker");c.call("setValue",[b.getValue()]),b.on("change",function(a){a.range={start:a.data.range.start,end:a.data.range.end},c.emit("change",a)}),c.on("jslint",function(b){var c=[];for(var d=0;d<b.data.length;d++){var e=b.data[d];e&&c.push({row:e.line-1,column:e.character-1,text:e.reason,type:"warning",lint:e})}a.setAnnotations(c)}),c.on("narcissus",function(b){a.setAnnotations([b.data])}),c.on("terminate",function(){a.clearAnnotations()});return c}}.call(l.prototype),b.Mode=l}),define("ace/mode/javascript_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|const|yield|import|get|set".split("|")),b=e.arrayToMap("null|Infinity|NaN|undefined".split("|")),c=e.arrayToMap("class|enum|extends|super|export|implements|private|public|interface|package|protected|static".split("|"));this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:function(d){return d=="this"?"variable.language":a.hasOwnProperty(d)?"keyword":b.hasOwnProperty(d)?"constant.language":c.hasOwnProperty(d)?"invalid.illegal":d=="debugger"?"invalid.deprecated":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"comment",regex:"^#!.*$"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.JavaScriptHighlightRules=h}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},{token:"comment.doc",regex:"\\s+"},{token:"comment.doc",regex:"TODO"},{token:"comment.doc",regex:"[^@\\*]+"},{token:"comment.doc",regex:"."}]}};d.inherits(f,e),function(){this.getStartRule=function(a){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:a}},this.getEndRule=function(a){return{token:"comment.doc",regex:"\\*\\/",next:a}}}.call(f.prototype),b.DocCommentHighlightRules=f}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/worker/worker_client",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=function(b,c,d,e){this.callbacks=[];if(a.packaged)var f=this.$guessBasePath(),g=this.$worker=new Worker(f+c);else{var h=a.nameToUrl("ace/worker/worker",null,"_"),g=this.$worker=new Worker(h),i={};for(var j=0;j<b.length;j++){var k=b[j];i[k]=a.nameToUrl(k,null,"_").replace(/.js$/,"")}}this.$worker.postMessage({init:!0,tlns:i,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){window.console&&console.log&&console.log(a);throw a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._dispatchEvent(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$guessBasePath=function(){if(a.aceBaseUrl)return a.aceBaseUrl;var b=document.getElementsByTagName("script");for(var c=0;c<b.length;c++){var d=b[c].src||b[c].getAttribute("src");if(!d)continue;var e=d.match(/^(?:(.*\/)ace\.js|(.*\/)ace-uncompressed\.js)(?:\?|$)/);if(e)return e[1]||e[2]}return""},this.terminate=function(){this._dispatchEvent("terminate",{}),this.$worker.terminate()},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){this.$worker.postMessage({event:a,data:b})}}).call(f.prototype),b.WorkerClient=f}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}),define("ace/mode/java_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while".split("|")),b=e.arrayToMap("null|Infinity|NaN|undefined".split("|")),c=e.arrayToMap("AbstractMethodError|AssertionError|ClassCircularityError|ClassFormatError|Deprecated|EnumConstantNotPresentException|ExceptionInInitializerError|IllegalAccessError|IllegalThreadStateException|InstantiationError|InternalError|NegativeArraySizeException|NoSuchFieldError|Override|Process|ProcessBuilder|SecurityManager|StringIndexOutOfBoundsException|SuppressWarnings|TypeNotPresentException|UnknownError|UnsatisfiedLinkError|UnsupportedClassVersionError|VerifyError|InstantiationException|IndexOutOfBoundsException|ArrayIndexOutOfBoundsException|CloneNotSupportedException|NoSuchFieldException|IllegalArgumentException|NumberFormatException|SecurityException|Void|InheritableThreadLocal|IllegalStateException|InterruptedException|NoSuchMethodException|IllegalAccessException|UnsupportedOperationException|Enum|StrictMath|Package|Compiler|Readable|Runtime|StringBuilder|Math|IncompatibleClassChangeError|NoSuchMethodError|ThreadLocal|RuntimePermission|ArithmeticException|NullPointerException|Long|Integer|Short|Byte|Double|Number|Float|Character|Boolean|StackTraceElement|Appendable|StringBuffer|Iterable|ThreadGroup|Runnable|Thread|IllegalMonitorStateException|StackOverflowError|OutOfMemoryError|VirtualMachineError|ArrayStoreException|ClassCastException|LinkageError|NoClassDefFoundError|ClassNotFoundException|RuntimeException|Exception|ThreadDeath|Error|Throwable|System|ClassLoader|Cloneable|Class|CharSequence|Comparable|String|Object".split("|")),d=e.arrayToMap("".split("|"));this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"comment",regex:"\\/\\*\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:function(e){return e=="this"?"variable.language":a.hasOwnProperty(e)?"keyword":c.hasOwnProperty(e)?"support.function":d.hasOwnProperty(e)?"support.function":b.hasOwnProperty(e)?"constant.language":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.JavaHighlightRules=h}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-javascript.js b/studio/static/js/ace/mode-javascript.js
new file mode 100644
index 0000000..f365db4
--- /dev/null
+++ b/studio/static/js/ace/mode-javascript.js
@@ -0,0 +1 @@
+define("ace/mode/javascript",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/javascript_highlight_rules").JavaScriptHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=a("ace/worker/worker_client").WorkerClient,k=a("ace/mode/behaviour/cstyle").CstyleBehaviour,l=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new k};d.inherits(l,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)\/\//;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"//")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}else if(a=="doc-start"){if(g=="start")return"";var h=b.match(/^\s*(\/?)\*/);h&&(h[1]&&(d+=" "),d+="* ")}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){var b=a.getDocument(),c=new j(["ace","pilot"],"worker-javascript.js","ace/mode/javascript_worker","JavaScriptWorker");c.call("setValue",[b.getValue()]),b.on("change",function(a){a.range={start:a.data.range.start,end:a.data.range.end},c.emit("change",a)}),c.on("jslint",function(b){var c=[];for(var d=0;d<b.data.length;d++){var e=b.data[d];e&&c.push({row:e.line-1,column:e.character-1,text:e.reason,type:"warning",lint:e})}a.setAnnotations(c)}),c.on("narcissus",function(b){a.setAnnotations([b.data])}),c.on("terminate",function(){a.clearAnnotations()});return c}}.call(l.prototype),b.Mode=l}),define("ace/mode/javascript_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|const|yield|import|get|set".split("|")),b=e.arrayToMap("null|Infinity|NaN|undefined".split("|")),c=e.arrayToMap("class|enum|extends|super|export|implements|private|public|interface|package|protected|static".split("|"));this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:function(d){return d=="this"?"variable.language":a.hasOwnProperty(d)?"keyword":b.hasOwnProperty(d)?"constant.language":c.hasOwnProperty(d)?"invalid.illegal":d=="debugger"?"invalid.deprecated":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"comment",regex:"^#!.*$"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.JavaScriptHighlightRules=h}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},{token:"comment.doc",regex:"\\s+"},{token:"comment.doc",regex:"TODO"},{token:"comment.doc",regex:"[^@\\*]+"},{token:"comment.doc",regex:"."}]}};d.inherits(f,e),function(){this.getStartRule=function(a){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:a}},this.getEndRule=function(a){return{token:"comment.doc",regex:"\\*\\/",next:a}}}.call(f.prototype),b.DocCommentHighlightRules=f}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/worker/worker_client",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=function(b,c,d,e){this.callbacks=[];if(a.packaged)var f=this.$guessBasePath(),g=this.$worker=new Worker(f+c);else{var h=a.nameToUrl("ace/worker/worker",null,"_"),g=this.$worker=new Worker(h),i={};for(var j=0;j<b.length;j++){var k=b[j];i[k]=a.nameToUrl(k,null,"_").replace(/.js$/,"")}}this.$worker.postMessage({init:!0,tlns:i,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){window.console&&console.log&&console.log(a);throw a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._dispatchEvent(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$guessBasePath=function(){if(a.aceBaseUrl)return a.aceBaseUrl;var b=document.getElementsByTagName("script");for(var c=0;c<b.length;c++){var d=b[c].src||b[c].getAttribute("src");if(!d)continue;var e=d.match(/^(?:(.*\/)ace\.js|(.*\/)ace-uncompressed\.js)(?:\?|$)/);if(e)return e[1]||e[2]}return""},this.terminate=function(){this._dispatchEvent("terminate",{}),this.$worker.terminate()},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){this.$worker.postMessage({event:a,data:b})}}).call(f.prototype),b.WorkerClient=f}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-json.js b/studio/static/js/ace/mode-json.js
new file mode 100644
index 0000000..b4561f6
--- /dev/null
+++ b/studio/static/js/ace/mode-json.js
@@ -0,0 +1 @@
+define("ace/mode/json",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/json_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/json_highlight_rules").JsonHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=a("ace/mode/behaviour/cstyle").CstyleBehaviour,k=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new j};d.inherits(k,e),function(){this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(k.prototype),b.Mode=k}),define("ace/mode/json_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){this.$rules={start:[{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:"invalid.illegal",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"invalid.illegal",regex:"\\/\\/.*$"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}]}};d.inherits(g,f),b.JsonHighlightRules=g}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-perl.js b/studio/static/js/ace/mode-perl.js
new file mode 100644
index 0000000..3ec8330
--- /dev/null
+++ b/studio/static/js/ace/mode-perl.js
@@ -0,0 +1 @@
+define("ace/mode/perl",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/perl_highlight_rules","ace/mode/matching_brace_outdent","ace/range"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/perl_highlight_rules").PerlHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(j,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)#/;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"#")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[\:]\s*$/);h&&(d+=c)}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(j.prototype),b.Mode=j}),define("ace/mode/perl_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){var a=e.arrayToMap("base|constant|continue|else|elsif|for|foreach|format|goto|if|last|local|my|next|no|package|parent|redo|require|scalar|sub|unless|until|while|use|vars".split("|")),b=e.arrayToMap("ARGV|ENV|INC|SIG".split("|")),c=e.arrayToMap("getprotobynumber|getprotobyname|getservbyname|gethostbyaddr|gethostbyname|getservbyport|getnetbyaddr|getnetbyname|getsockname|getpeername|setpriority|getprotoent|setprotoent|getpriority|endprotoent|getservent|setservent|endservent|sethostent|socketpair|getsockopt|gethostent|endhostent|setsockopt|setnetent|quotemeta|localtime|prototype|getnetent|endnetent|rewinddir|wantarray|getpwuid|closedir|getlogin|readlink|endgrent|getgrgid|getgrnam|shmwrite|shutdown|readline|endpwent|setgrent|readpipe|formline|truncate|dbmclose|syswrite|setpwent|getpwnam|getgrent|getpwent|ucfirst|sysread|setpgrp|shmread|sysseek|sysopen|telldir|defined|opendir|connect|lcfirst|getppid|binmode|syscall|sprintf|getpgrp|readdir|seekdir|waitpid|reverse|unshift|symlink|dbmopen|semget|msgrcv|rename|listen|chroot|msgsnd|shmctl|accept|unpack|exists|fileno|shmget|system|unlink|printf|gmtime|msgctl|semctl|values|rindex|substr|splice|length|msgget|select|socket|return|caller|delete|alarm|ioctl|index|undef|lstat|times|srand|chown|fcntl|close|write|umask|rmdir|study|sleep|chomp|untie|print|utime|mkdir|atan2|split|crypt|flock|chmod|BEGIN|bless|chdir|semop|shift|reset|link|stat|chop|grep|fork|dump|join|open|tell|pipe|exit|glob|warn|each|bind|sort|pack|eval|push|keys|getc|kill|seek|sqrt|send|wait|rand|tied|read|time|exec|recv|eof|chr|int|ord|exp|pos|pop|sin|log|abs|oct|hex|tie|cos|vec|END|ref|map|die|uc|lc|do".split("|"));this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0x[0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:function(d){return a.hasOwnProperty(d)?"keyword":b.hasOwnProperty(d)?"constant.language":c.hasOwnProperty(d)?"support.function":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\.\\.\\.|\\|\\|=|>>=|<<=|<=>|&&=|=>|!~|\\^=|&=|\\|=|\\.=|x=|%=|\\/=|\\*=|\\-=|\\+=|=~|\\*\\*|\\-\\-|\\.\\.|\\|\\||&&|\\+\\+|\\->|!=|==|>=|<=|>>|<<|,|=|\\?\\:|\\^|\\||x|%|\\/|\\*|<|&|\\\\|~|!|>|\\.|\\-|\\+|\\-C|\\-b|\\-S|\\-u|\\-t|\\-p|\\-l|\\-d|\\-f|\\-g|\\-s|\\-z|\\-k|\\-e|\\-O|\\-T|\\-B|\\-M|\\-A|\\-X|\\-W|\\-c|\\-R|\\-o|\\-x|\\-w|\\-r|\\b(?:and|cmp|eq|ge|gt|le|lt|ne|not|or|xor)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]}};d.inherits(g,f),b.PerlHighlightRules=g}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-php.js b/studio/static/js/ace/mode-php.js
new file mode 100644
index 0000000..161648b
--- /dev/null
+++ b/studio/static/js/ace/mode-php.js
@@ -0,0 +1 @@
+define("ace/mode/php",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/php_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/php_highlight_rules").PhpHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=a("ace/mode/behaviour/cstyle").CstyleBehaviour,k=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new j};d.inherits(k,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)#/;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"#")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[\:]\s*$/);h&&(d+=c)}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(k.prototype),b.Mode=k}),define("ace/mode/php_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("abs|acos|acosh|addcslashes|addslashes|aggregate|aggregate_info|aggregate_methods|aggregate_methods_by_list|aggregate_methods_by_regexp|aggregate_properties|aggregate_properties_by_list|aggregate_properties_by_regexp|aggregation_info|apache_child_terminate|apache_get_modules|apache_get_version|apache_getenv|apache_lookup_uri|apache_note|apache_request_headers|apache_response_headers|apache_setenv|array|array_change_key_case|array_chunk|array_combine|array_count_values|array_diff|array_diff_assoc|array_diff_uassoc|array_fill|array_filter|array_flip|array_intersect|array_intersect_assoc|array_key_exists|array_keys|array_map|array_merge|array_merge_recursive|array_multisort|array_pad|array_pop|array_push|array_rand|array_reduce|array_reverse|array_search|array_shift|array_slice|array_splice|array_sum|array_udiff|array_udiff_assoc|array_udiff_uassoc|array_unique|array_unshift|array_values|array_walk|arsort|ascii2ebcdic|asin|asinh|asort|aspell_check|aspell_check_raw|aspell_new|aspell_suggest|assert|assert_options|atan|atan2|atanh|base64_decode|base64_encode|base_convert|basename|bcadd|bccomp|bcdiv|bcmod|bcmul|bcpow|bcpowmod|bcscale|bcsqrt|bcsub|bin2hex|bind_textdomain_codeset|bindec|bindtextdomain|bzclose|bzcompress|bzdecompress|bzerrno|bzerror|bzerrstr|bzflush|bzopen|bzread|bzwrite|cal_days_in_month|cal_from_jd|cal_info|cal_to_jd|call_user_func|call_user_func_array|call_user_method|call_user_method_array|ccvs_add|ccvs_auth|ccvs_command|ccvs_count|ccvs_delete|ccvs_done|ccvs_init|ccvs_lookup|ccvs_new|ccvs_report|ccvs_return|ccvs_reverse|ccvs_sale|ccvs_status|ccvs_textvalue|ccvs_void|ceil|chdir|checkdate|checkdnsrr|chgrp|chmod|chop|chown|chr|chroot|chunk_split|class_exists|clearstatcache|closedir|closelog|com|com_addref|com_get|com_invoke|com_isenum|com_load|com_load_typelib|com_propget|com_propput|com_propset|com_release|com_set|compact|connection_aborted|connection_status|connection_timeout|constant|convert_cyr_string|copy|cos|cosh|count|count_chars|cpdf_add_annotation|cpdf_add_outline|cpdf_arc|cpdf_begin_text|cpdf_circle|cpdf_clip|cpdf_close|cpdf_closepath|cpdf_closepath_fill_stroke|cpdf_closepath_stroke|cpdf_continue_text|cpdf_curveto|cpdf_end_text|cpdf_fill|cpdf_fill_stroke|cpdf_finalize|cpdf_finalize_page|cpdf_global_set_document_limits|cpdf_import_jpeg|cpdf_lineto|cpdf_moveto|cpdf_newpath|cpdf_open|cpdf_output_buffer|cpdf_page_init|cpdf_place_inline_image|cpdf_rect|cpdf_restore|cpdf_rlineto|cpdf_rmoveto|cpdf_rotate|cpdf_rotate_text|cpdf_save|cpdf_save_to_file|cpdf_scale|cpdf_set_action_url|cpdf_set_char_spacing|cpdf_set_creator|cpdf_set_current_page|cpdf_set_font|cpdf_set_font_directories|cpdf_set_font_map_file|cpdf_set_horiz_scaling|cpdf_set_keywords|cpdf_set_leading|cpdf_set_page_animation|cpdf_set_subject|cpdf_set_text_matrix|cpdf_set_text_pos|cpdf_set_text_rendering|cpdf_set_text_rise|cpdf_set_title|cpdf_set_viewer_preferences|cpdf_set_word_spacing|cpdf_setdash|cpdf_setflat|cpdf_setgray|cpdf_setgray_fill|cpdf_setgray_stroke|cpdf_setlinecap|cpdf_setlinejoin|cpdf_setlinewidth|cpdf_setmiterlimit|cpdf_setrgbcolor|cpdf_setrgbcolor_fill|cpdf_setrgbcolor_stroke|cpdf_show|cpdf_show_xy|cpdf_stringwidth|cpdf_stroke|cpdf_text|cpdf_translate|crack_check|crack_closedict|crack_getlastmessage|crack_opendict|crc32|create_function|crypt|ctype_alnum|ctype_alpha|ctype_cntrl|ctype_digit|ctype_graph|ctype_lower|ctype_print|ctype_punct|ctype_space|ctype_upper|ctype_xdigit|curl_close|curl_errno|curl_error|curl_exec|curl_getinfo|curl_init|curl_multi_add_handle|curl_multi_close|curl_multi_exec|curl_multi_getcontent|curl_multi_info_read|curl_multi_init|curl_multi_remove_handle|curl_multi_select|curl_setopt|curl_version|current|cybercash_base64_decode|cybercash_base64_encode|cybercash_decr|cybercash_encr|cyrus_authenticate|cyrus_bind|cyrus_close|cyrus_connect|cyrus_query|cyrus_unbind|date|dba_close|dba_delete|dba_exists|dba_fetch|dba_firstkey|dba_handlers|dba_insert|dba_key_split|dba_list|dba_nextkey|dba_open|dba_optimize|dba_popen|dba_replace|dba_sync|dbase_add_record|dbase_close|dbase_create|dbase_delete_record|dbase_get_header_info|dbase_get_record|dbase_get_record_with_names|dbase_numfields|dbase_numrecords|dbase_open|dbase_pack|dbase_replace_record|dblist|dbmclose|dbmdelete|dbmexists|dbmfetch|dbmfirstkey|dbminsert|dbmnextkey|dbmopen|dbmreplace|dbplus_add|dbplus_aql|dbplus_chdir|dbplus_close|dbplus_curr|dbplus_errcode|dbplus_errno|dbplus_find|dbplus_first|dbplus_flush|dbplus_freealllocks|dbplus_freelock|dbplus_freerlocks|dbplus_getlock|dbplus_getunique|dbplus_info|dbplus_last|dbplus_lockrel|dbplus_next|dbplus_open|dbplus_prev|dbplus_rchperm|dbplus_rcreate|dbplus_rcrtexact|dbplus_rcrtlike|dbplus_resolve|dbplus_restorepos|dbplus_rkeys|dbplus_ropen|dbplus_rquery|dbplus_rrename|dbplus_rsecindex|dbplus_runlink|dbplus_rzap|dbplus_savepos|dbplus_setindex|dbplus_setindexbynumber|dbplus_sql|dbplus_tcl|dbplus_tremove|dbplus_undo|dbplus_undoprepare|dbplus_unlockrel|dbplus_unselect|dbplus_update|dbplus_xlockrel|dbplus_xunlockrel|dbx_close|dbx_compare|dbx_connect|dbx_error|dbx_escape_string|dbx_fetch_row|dbx_query|dbx_sort|dcgettext|dcngettext|deaggregate|debug_backtrace|debug_print_backtrace|debugger_off|debugger_on|decbin|dechex|decoct|define|define_syslog_variables|defined|deg2rad|delete|dgettext|die|dio_close|dio_fcntl|dio_open|dio_read|dio_seek|dio_stat|dio_tcsetattr|dio_truncate|dio_write|dir|dirname|disk_free_space|disk_total_space|diskfreespace|dl|dngettext|dns_check_record|dns_get_mx|dns_get_record|domxml_new_doc|domxml_open_file|domxml_open_mem|domxml_version|domxml_xmltree|domxml_xslt_stylesheet|domxml_xslt_stylesheet_doc|domxml_xslt_stylesheet_file|dotnet_load|doubleval|each|easter_date|easter_days|ebcdic2ascii|echo|empty|end|ereg|ereg_replace|eregi|eregi_replace|error_log|error_reporting|escapeshellarg|escapeshellcmd|eval|exec|exif_imagetype|exif_read_data|exif_thumbnail|exit|exp|explode|expm1|extension_loaded|extract|ezmlm_hash|fam_cancel_monitor|fam_close|fam_monitor_collection|fam_monitor_directory|fam_monitor_file|fam_next_event|fam_open|fam_pending|fam_resume_monitor|fam_suspend_monitor|fbsql_affected_rows|fbsql_autocommit|fbsql_blob_size|fbsql_change_user|fbsql_clob_size|fbsql_close|fbsql_commit|fbsql_connect|fbsql_create_blob|fbsql_create_clob|fbsql_create_db|fbsql_data_seek|fbsql_database|fbsql_database_password|fbsql_db_query|fbsql_db_status|fbsql_drop_db|fbsql_errno|fbsql_error|fbsql_fetch_array|fbsql_fetch_assoc|fbsql_fetch_field|fbsql_fetch_lengths|fbsql_fetch_object|fbsql_fetch_row|fbsql_field_flags|fbsql_field_len|fbsql_field_name|fbsql_field_seek|fbsql_field_table|fbsql_field_type|fbsql_free_result|fbsql_get_autostart_info|fbsql_hostname|fbsql_insert_id|fbsql_list_dbs|fbsql_list_fields|fbsql_list_tables|fbsql_next_result|fbsql_num_fields|fbsql_num_rows|fbsql_password|fbsql_pconnect|fbsql_query|fbsql_read_blob|fbsql_read_clob|fbsql_result|fbsql_rollback|fbsql_select_db|fbsql_set_lob_mode|fbsql_set_password|fbsql_set_transaction|fbsql_start_db|fbsql_stop_db|fbsql_tablename|fbsql_username|fbsql_warnings|fclose|fdf_add_doc_javascript|fdf_add_template|fdf_close|fdf_create|fdf_enum_values|fdf_errno|fdf_error|fdf_get_ap|fdf_get_attachment|fdf_get_encoding|fdf_get_file|fdf_get_flags|fdf_get_opt|fdf_get_status|fdf_get_value|fdf_get_version|fdf_header|fdf_next_field_name|fdf_open|fdf_open_string|fdf_remove_item|fdf_save|fdf_save_string|fdf_set_ap|fdf_set_encoding|fdf_set_file|fdf_set_flags|fdf_set_javascript_action|fdf_set_opt|fdf_set_status|fdf_set_submit_form_action|fdf_set_target_frame|fdf_set_value|fdf_set_version|feof|fflush|fgetc|fgetcsv|fgets|fgetss|file|file_exists|file_get_contents|file_put_contents|fileatime|filectime|filegroup|fileinode|filemtime|fileowner|fileperms|filepro|filepro_fieldcount|filepro_fieldname|filepro_fieldtype|filepro_fieldwidth|filepro_retrieve|filepro_rowcount|filesize|filetype|floatval|flock|floor|flush|fmod|fnmatch|fopen|fpassthru|fprintf|fputs|fread|frenchtojd|fribidi_log2vis|fscanf|fseek|fsockopen|fstat|ftell|ftok|ftp_alloc|ftp_cdup|ftp_chdir|ftp_chmod|ftp_close|ftp_connect|ftp_delete|ftp_exec|ftp_fget|ftp_fput|ftp_get|ftp_get_option|ftp_login|ftp_mdtm|ftp_mkdir|ftp_nb_continue|ftp_nb_fget|ftp_nb_fput|ftp_nb_get|ftp_nb_put|ftp_nlist|ftp_pasv|ftp_put|ftp_pwd|ftp_quit|ftp_raw|ftp_rawlist|ftp_rename|ftp_rmdir|ftp_set_option|ftp_site|ftp_size|ftp_ssl_connect|ftp_systype|ftruncate|func_get_arg|func_get_args|func_num_args|function_exists|fwrite|gd_info|get_browser|get_cfg_var|get_class|get_class_methods|get_class_vars|get_current_user|get_declared_classes|get_declared_interfaces|get_defined_constants|get_defined_functions|get_defined_vars|get_extension_funcs|get_headers|get_html_translation_table|get_include_path|get_included_files|get_loaded_extensions|get_magic_quotes_gpc|get_magic_quotes_runtime|get_meta_tags|get_object_vars|get_parent_class|get_required_files|get_resource_type|getallheaders|getcwd|getdate|getenv|gethostbyaddr|gethostbyname|gethostbynamel|getimagesize|getlastmod|getmxrr|getmygid|getmyinode|getmypid|getmyuid|getopt|getprotobyname|getprotobynumber|getrandmax|getrusage|getservbyname|getservbyport|gettext|gettimeofday|gettype|glob|gmdate|gmmktime|gmp_abs|gmp_add|gmp_and|gmp_clrbit|gmp_cmp|gmp_com|gmp_div|gmp_div_q|gmp_div_qr|gmp_div_r|gmp_divexact|gmp_fact|gmp_gcd|gmp_gcdext|gmp_hamdist|gmp_init|gmp_intval|gmp_invert|gmp_jacobi|gmp_legendre|gmp_mod|gmp_mul|gmp_neg|gmp_or|gmp_perfect_square|gmp_popcount|gmp_pow|gmp_powm|gmp_prob_prime|gmp_random|gmp_scan0|gmp_scan1|gmp_setbit|gmp_sign|gmp_sqrt|gmp_sqrtrem|gmp_strval|gmp_sub|gmp_xor|gmstrftime|gregoriantojd|gzclose|gzcompress|gzdeflate|gzencode|gzeof|gzfile|gzgetc|gzgets|gzgetss|gzinflate|gzopen|gzpassthru|gzputs|gzread|gzrewind|gzseek|gztell|gzuncompress|gzwrite|header|headers_list|headers_sent|hebrev|hebrevc|hexdec|highlight_file|highlight_string|html_entity_decode|htmlentities|htmlspecialchars|http_build_query|hw_api_attribute|hw_api_content|hw_api_object|hw_array2objrec|hw_changeobject|hw_children|hw_childrenobj|hw_close|hw_connect|hw_connection_info|hw_cp|hw_deleteobject|hw_docbyanchor|hw_docbyanchorobj|hw_document_attributes|hw_document_bodytag|hw_document_content|hw_document_setcontent|hw_document_size|hw_dummy|hw_edittext|hw_error|hw_errormsg|hw_free_document|hw_getanchors|hw_getanchorsobj|hw_getandlock|hw_getchildcoll|hw_getchildcollobj|hw_getchilddoccoll|hw_getchilddoccollobj|hw_getobject|hw_getobjectbyquery|hw_getobjectbyquerycoll|hw_getobjectbyquerycollobj|hw_getobjectbyqueryobj|hw_getparents|hw_getparentsobj|hw_getrellink|hw_getremote|hw_getremotechildren|hw_getsrcbydestobj|hw_gettext|hw_getusername|hw_identify|hw_incollections|hw_info|hw_inscoll|hw_insdoc|hw_insertanchors|hw_insertdocument|hw_insertobject|hw_mapid|hw_modifyobject|hw_mv|hw_new_document|hw_objrec2array|hw_output_document|hw_pconnect|hw_pipedocument|hw_root|hw_setlinkroot|hw_stat|hw_unlock|hw_who|hwapi_hgcsp|hypot|ibase_add_user|ibase_affected_rows|ibase_backup|ibase_blob_add|ibase_blob_cancel|ibase_blob_close|ibase_blob_create|ibase_blob_echo|ibase_blob_get|ibase_blob_import|ibase_blob_info|ibase_blob_open|ibase_close|ibase_commit|ibase_commit_ret|ibase_connect|ibase_db_info|ibase_delete_user|ibase_drop_db|ibase_errcode|ibase_errmsg|ibase_execute|ibase_fetch_assoc|ibase_fetch_object|ibase_fetch_row|ibase_field_info|ibase_free_event_handler|ibase_free_query|ibase_free_result|ibase_gen_id|ibase_maintain_db|ibase_modify_user|ibase_name_result|ibase_num_fields|ibase_num_params|ibase_param_info|ibase_pconnect|ibase_prepare|ibase_query|ibase_restore|ibase_rollback|ibase_rollback_ret|ibase_server_info|ibase_service_attach|ibase_service_detach|ibase_set_event_handler|ibase_timefmt|ibase_trans|ibase_wait_event|iconv|iconv_get_encoding|iconv_mime_decode|iconv_mime_decode_headers|iconv_mime_encode|iconv_set_encoding|iconv_strlen|iconv_strpos|iconv_strrpos|iconv_substr|idate|ifx_affected_rows|ifx_blobinfile_mode|ifx_byteasvarchar|ifx_close|ifx_connect|ifx_copy_blob|ifx_create_blob|ifx_create_char|ifx_do|ifx_error|ifx_errormsg|ifx_fetch_row|ifx_fieldproperties|ifx_fieldtypes|ifx_free_blob|ifx_free_char|ifx_free_result|ifx_get_blob|ifx_get_char|ifx_getsqlca|ifx_htmltbl_result|ifx_nullformat|ifx_num_fields|ifx_num_rows|ifx_pconnect|ifx_prepare|ifx_query|ifx_textasvarchar|ifx_update_blob|ifx_update_char|ifxus_close_slob|ifxus_create_slob|ifxus_free_slob|ifxus_open_slob|ifxus_read_slob|ifxus_seek_slob|ifxus_tell_slob|ifxus_write_slob|ignore_user_abort|image2wbmp|image_type_to_mime_type|imagealphablending|imageantialias|imagearc|imagechar|imagecharup|imagecolorallocate|imagecolorallocatealpha|imagecolorat|imagecolorclosest|imagecolorclosestalpha|imagecolorclosesthwb|imagecolordeallocate|imagecolorexact|imagecolorexactalpha|imagecolormatch|imagecolorresolve|imagecolorresolvealpha|imagecolorset|imagecolorsforindex|imagecolorstotal|imagecolortransparent|imagecopy|imagecopymerge|imagecopymergegray|imagecopyresampled|imagecopyresized|imagecreate|imagecreatefromgd|imagecreatefromgd2|imagecreatefromgd2part|imagecreatefromgif|imagecreatefromjpeg|imagecreatefrompng|imagecreatefromstring|imagecreatefromwbmp|imagecreatefromxbm|imagecreatefromxpm|imagecreatetruecolor|imagedashedline|imagedestroy|imageellipse|imagefill|imagefilledarc|imagefilledellipse|imagefilledpolygon|imagefilledrectangle|imagefilltoborder|imagefilter|imagefontheight|imagefontwidth|imageftbbox|imagefttext|imagegammacorrect|imagegd|imagegd2|imagegif|imageinterlace|imageistruecolor|imagejpeg|imagelayereffect|imageline|imageloadfont|imagepalettecopy|imagepng|imagepolygon|imagepsbbox|imagepscopyfont|imagepsencodefont|imagepsextendfont|imagepsfreefont|imagepsloadfont|imagepsslantfont|imagepstext|imagerectangle|imagerotate|imagesavealpha|imagesetbrush|imagesetpixel|imagesetstyle|imagesetthickness|imagesettile|imagestring|imagestringup|imagesx|imagesy|imagetruecolortopalette|imagettfbbox|imagettftext|imagetypes|imagewbmp|imagexbm|imap_8bit|imap_alerts|imap_append|imap_base64|imap_binary|imap_body|imap_bodystruct|imap_check|imap_clearflag_full|imap_close|imap_createmailbox|imap_delete|imap_deletemailbox|imap_errors|imap_expunge|imap_fetch_overview|imap_fetchbody|imap_fetchheader|imap_fetchstructure|imap_get_quota|imap_get_quotaroot|imap_getacl|imap_getmailboxes|imap_getsubscribed|imap_header|imap_headerinfo|imap_headers|imap_last_error|imap_list|imap_listmailbox|imap_listscan|imap_listsubscribed|imap_lsub|imap_mail|imap_mail_compose|imap_mail_copy|imap_mail_move|imap_mailboxmsginfo|imap_mime_header_decode|imap_msgno|imap_num_msg|imap_num_recent|imap_open|imap_ping|imap_qprint|imap_renamemailbox|imap_reopen|imap_rfc822_parse_adrlist|imap_rfc822_parse_headers|imap_rfc822_write_address|imap_scanmailbox|imap_search|imap_set_quota|imap_setacl|imap_setflag_full|imap_sort|imap_status|imap_subscribe|imap_thread|imap_timeout|imap_uid|imap_undelete|imap_unsubscribe|imap_utf7_decode|imap_utf7_encode|imap_utf8|implode|import_request_variables|in_array|ingres_autocommit|ingres_close|ingres_commit|ingres_connect|ingres_fetch_array|ingres_fetch_object|ingres_fetch_row|ingres_field_length|ingres_field_name|ingres_field_nullable|ingres_field_precision|ingres_field_scale|ingres_field_type|ingres_num_fields|ingres_num_rows|ingres_pconnect|ingres_query|ingres_rollback|ini_alter|ini_get|ini_get_all|ini_restore|ini_set|intval|ip2long|iptcembed|iptcparse|ircg_channel_mode|ircg_disconnect|ircg_fetch_error_msg|ircg_get_username|ircg_html_encode|ircg_ignore_add|ircg_ignore_del|ircg_invite|ircg_is_conn_alive|ircg_join|ircg_kick|ircg_list|ircg_lookup_format_messages|ircg_lusers|ircg_msg|ircg_nick|ircg_nickname_escape|ircg_nickname_unescape|ircg_notice|ircg_oper|ircg_part|ircg_pconnect|ircg_register_format_messages|ircg_set_current|ircg_set_file|ircg_set_on_die|ircg_topic|ircg_who|ircg_whois|is_a|is_array|is_bool|is_callable|is_dir|is_double|is_executable|is_file|is_finite|is_float|is_infinite|is_int|is_integer|is_link|is_long|is_nan|is_null|is_numeric|is_object|is_readable|is_real|is_resource|is_scalar|is_soap_fault|is_string|is_subclass_of|is_uploaded_file|is_writable|is_writeable|isset|java_last_exception_clear|java_last_exception_get|jddayofweek|jdmonthname|jdtofrench|jdtogregorian|jdtojewish|jdtojulian|jdtounix|jewishtojd|join|jpeg2wbmp|juliantojd|key|krsort|ksort|lcg_value|ldap_8859_to_t61|ldap_add|ldap_bind|ldap_close|ldap_compare|ldap_connect|ldap_count_entries|ldap_delete|ldap_dn2ufn|ldap_err2str|ldap_errno|ldap_error|ldap_explode_dn|ldap_first_attribute|ldap_first_entry|ldap_first_reference|ldap_free_result|ldap_get_attributes|ldap_get_dn|ldap_get_entries|ldap_get_option|ldap_get_values|ldap_get_values_len|ldap_list|ldap_mod_add|ldap_mod_del|ldap_mod_replace|ldap_modify|ldap_next_attribute|ldap_next_entry|ldap_next_reference|ldap_parse_reference|ldap_parse_result|ldap_read|ldap_rename|ldap_search|ldap_set_option|ldap_set_rebind_proc|ldap_sort|ldap_start_tls|ldap_t61_to_8859|ldap_unbind|levenshtein|link|linkinfo|list|localeconv|localtime|log|log10|log1p|long2ip|lstat|ltrim|lzf_compress|lzf_decompress|lzf_optimized_for|mail|mailparse_determine_best_xfer_encoding|mailparse_msg_create|mailparse_msg_extract_part|mailparse_msg_extract_part_file|mailparse_msg_free|mailparse_msg_get_part|mailparse_msg_get_part_data|mailparse_msg_get_structure|mailparse_msg_parse|mailparse_msg_parse_file|mailparse_rfc822_parse_addresses|mailparse_stream_encode|mailparse_uudecode_all|main|max|mb_convert_case|mb_convert_encoding|mb_convert_kana|mb_convert_variables|mb_decode_mimeheader|mb_decode_numericentity|mb_detect_encoding|mb_detect_order|mb_encode_mimeheader|mb_encode_numericentity|mb_ereg|mb_ereg_match|mb_ereg_replace|mb_ereg_search|mb_ereg_search_getpos|mb_ereg_search_getregs|mb_ereg_search_init|mb_ereg_search_pos|mb_ereg_search_regs|mb_ereg_search_setpos|mb_eregi|mb_eregi_replace|mb_get_info|mb_http_input|mb_http_output|mb_internal_encoding|mb_language|mb_output_handler|mb_parse_str|mb_preferred_mime_name|mb_regex_encoding|mb_regex_set_options|mb_send_mail|mb_split|mb_strcut|mb_strimwidth|mb_strlen|mb_strpos|mb_strrpos|mb_strtolower|mb_strtoupper|mb_strwidth|mb_substitute_character|mb_substr|mb_substr_count|mcal_append_event|mcal_close|mcal_create_calendar|mcal_date_compare|mcal_date_valid|mcal_day_of_week|mcal_day_of_year|mcal_days_in_month|mcal_delete_calendar|mcal_delete_event|mcal_event_add_attribute|mcal_event_init|mcal_event_set_alarm|mcal_event_set_category|mcal_event_set_class|mcal_event_set_description|mcal_event_set_end|mcal_event_set_recur_daily|mcal_event_set_recur_monthly_mday|mcal_event_set_recur_monthly_wday|mcal_event_set_recur_none|mcal_event_set_recur_weekly|mcal_event_set_recur_yearly|mcal_event_set_start|mcal_event_set_title|mcal_expunge|mcal_fetch_current_stream_event|mcal_fetch_event|mcal_is_leap_year|mcal_list_alarms|mcal_list_events|mcal_next_recurrence|mcal_open|mcal_popen|mcal_rename_calendar|mcal_reopen|mcal_snooze|mcal_store_event|mcal_time_valid|mcal_week_of_year|mcrypt_cbc|mcrypt_cfb|mcrypt_create_iv|mcrypt_decrypt|mcrypt_ecb|mcrypt_enc_get_algorithms_name|mcrypt_enc_get_block_size|mcrypt_enc_get_iv_size|mcrypt_enc_get_key_size|mcrypt_enc_get_modes_name|mcrypt_enc_get_supported_key_sizes|mcrypt_enc_is_block_algorithm|mcrypt_enc_is_block_algorithm_mode|mcrypt_enc_is_block_mode|mcrypt_enc_self_test|mcrypt_encrypt|mcrypt_generic|mcrypt_generic_deinit|mcrypt_generic_end|mcrypt_generic_init|mcrypt_get_block_size|mcrypt_get_cipher_name|mcrypt_get_iv_size|mcrypt_get_key_size|mcrypt_list_algorithms|mcrypt_list_modes|mcrypt_module_close|mcrypt_module_get_algo_block_size|mcrypt_module_get_algo_key_size|mcrypt_module_get_supported_key_sizes|mcrypt_module_is_block_algorithm|mcrypt_module_is_block_algorithm_mode|mcrypt_module_is_block_mode|mcrypt_module_open|mcrypt_module_self_test|mcrypt_ofb|mcve_adduser|mcve_adduserarg|mcve_bt|mcve_checkstatus|mcve_chkpwd|mcve_chngpwd|mcve_completeauthorizations|mcve_connect|mcve_connectionerror|mcve_deleteresponse|mcve_deletetrans|mcve_deleteusersetup|mcve_deluser|mcve_destroyconn|mcve_destroyengine|mcve_disableuser|mcve_edituser|mcve_enableuser|mcve_force|mcve_getcell|mcve_getcellbynum|mcve_getcommadelimited|mcve_getheader|mcve_getuserarg|mcve_getuserparam|mcve_gft|mcve_gl|mcve_gut|mcve_initconn|mcve_initengine|mcve_initusersetup|mcve_iscommadelimited|mcve_liststats|mcve_listusers|mcve_maxconntimeout|mcve_monitor|mcve_numcolumns|mcve_numrows|mcve_override|mcve_parsecommadelimited|mcve_ping|mcve_preauth|mcve_preauthcompletion|mcve_qc|mcve_responseparam|mcve_return|mcve_returncode|mcve_returnstatus|mcve_sale|mcve_setblocking|mcve_setdropfile|mcve_setip|mcve_setssl|mcve_setssl_files|mcve_settimeout|mcve_settle|mcve_text_avs|mcve_text_code|mcve_text_cv|mcve_transactionauth|mcve_transactionavs|mcve_transactionbatch|mcve_transactioncv|mcve_transactionid|mcve_transactionitem|mcve_transactionssent|mcve_transactiontext|mcve_transinqueue|mcve_transnew|mcve_transparam|mcve_transsend|mcve_ub|mcve_uwait|mcve_verifyconnection|mcve_verifysslcert|mcve_void|md5|md5_file|mdecrypt_generic|memory_get_usage|metaphone|method_exists|mhash|mhash_count|mhash_get_block_size|mhash_get_hash_name|mhash_keygen_s2k|microtime|mime_content_type|min|ming_setcubicthreshold|ming_setscale|ming_useswfversion|mkdir|mktime|money_format|move_uploaded_file|msession_connect|msession_count|msession_create|msession_destroy|msession_disconnect|msession_find|msession_get|msession_get_array|msession_getdata|msession_inc|msession_list|msession_listvar|msession_lock|msession_plugin|msession_randstr|msession_set|msession_set_array|msession_setdata|msession_timeout|msession_uniq|msession_unlock|msg_get_queue|msg_receive|msg_remove_queue|msg_send|msg_set_queue|msg_stat_queue|msql|msql|msql_affected_rows|msql_close|msql_connect|msql_create_db|msql_createdb|msql_data_seek|msql_dbname|msql_drop_db|msql_error|msql_fetch_array|msql_fetch_field|msql_fetch_object|msql_fetch_row|msql_field_flags|msql_field_len|msql_field_name|msql_field_seek|msql_field_table|msql_field_type|msql_fieldflags|msql_fieldlen|msql_fieldname|msql_fieldtable|msql_fieldtype|msql_free_result|msql_list_dbs|msql_list_fields|msql_list_tables|msql_num_fields|msql_num_rows|msql_numfields|msql_numrows|msql_pconnect|msql_query|msql_regcase|msql_result|msql_select_db|msql_tablename|mssql_bind|mssql_close|mssql_connect|mssql_data_seek|mssql_execute|mssql_fetch_array|mssql_fetch_assoc|mssql_fetch_batch|mssql_fetch_field|mssql_fetch_object|mssql_fetch_row|mssql_field_length|mssql_field_name|mssql_field_seek|mssql_field_type|mssql_free_result|mssql_free_statement|mssql_get_last_message|mssql_guid_string|mssql_init|mssql_min_error_severity|mssql_min_message_severity|mssql_next_result|mssql_num_fields|mssql_num_rows|mssql_pconnect|mssql_query|mssql_result|mssql_rows_affected|mssql_select_db|mt_getrandmax|mt_rand|mt_srand|muscat_close|muscat_get|muscat_give|muscat_setup|muscat_setup_net|mysql_affected_rows|mysql_change_user|mysql_client_encoding|mysql_close|mysql_connect|mysql_create_db|mysql_data_seek|mysql_db_name|mysql_db_query|mysql_drop_db|mysql_errno|mysql_error|mysql_escape_string|mysql_fetch_array|mysql_fetch_assoc|mysql_fetch_field|mysql_fetch_lengths|mysql_fetch_object|mysql_fetch_row|mysql_field_flags|mysql_field_len|mysql_field_name|mysql_field_seek|mysql_field_table|mysql_field_type|mysql_free_result|mysql_get_client_info|mysql_get_host_info|mysql_get_proto_info|mysql_get_server_info|mysql_info|mysql_insert_id|mysql_list_dbs|mysql_list_fields|mysql_list_processes|mysql_list_tables|mysql_num_fields|mysql_num_rows|mysql_pconnect|mysql_ping|mysql_query|mysql_real_escape_string|mysql_result|mysql_select_db|mysql_stat|mysql_tablename|mysql_thread_id|mysql_unbuffered_query|mysqli_affected_rows|mysqli_autocommit|mysqli_bind_param|mysqli_bind_result|mysqli_change_user|mysqli_character_set_name|mysqli_client_encoding|mysqli_close|mysqli_commit|mysqli_connect|mysqli_connect_errno|mysqli_connect_error|mysqli_data_seek|mysqli_debug|mysqli_disable_reads_from_master|mysqli_disable_rpl_parse|mysqli_dump_debug_info|mysqli_embedded_connect|mysqli_enable_reads_from_master|mysqli_enable_rpl_parse|mysqli_errno|mysqli_error|mysqli_escape_string|mysqli_execute|mysqli_fetch|mysqli_fetch_array|mysqli_fetch_assoc|mysqli_fetch_field|mysqli_fetch_field_direct|mysqli_fetch_fields|mysqli_fetch_lengths|mysqli_fetch_object|mysqli_fetch_row|mysqli_field_count|mysqli_field_seek|mysqli_field_tell|mysqli_free_result|mysqli_get_client_info|mysqli_get_client_version|mysqli_get_host_info|mysqli_get_metadata|mysqli_get_proto_info|mysqli_get_server_info|mysqli_get_server_version|mysqli_info|mysqli_init|mysqli_insert_id|mysqli_kill|mysqli_master_query|mysqli_more_results|mysqli_multi_query|mysqli_next_result|mysqli_num_fields|mysqli_num_rows|mysqli_options|mysqli_param_count|mysqli_ping|mysqli_prepare|mysqli_query|mysqli_real_connect|mysqli_real_escape_string|mysqli_real_query|mysqli_report|mysqli_rollback|mysqli_rpl_parse_enabled|mysqli_rpl_probe|mysqli_rpl_query_type|mysqli_select_db|mysqli_send_long_data|mysqli_send_query|mysqli_server_end|mysqli_server_init|mysqli_set_opt|mysqli_sqlstate|mysqli_ssl_set|mysqli_stat|mysqli_stmt_init|mysqli_stmt_affected_rows|mysqli_stmt_bind_param|mysqli_stmt_bind_result|mysqli_stmt_close|mysqli_stmt_data_seek|mysqli_stmt_errno|mysqli_stmt_error|mysqli_stmt_execute|mysqli_stmt_fetch|mysqli_stmt_free_result|mysqli_stmt_num_rows|mysqli_stmt_param_count|mysqli_stmt_prepare|mysqli_stmt_result_metadata|mysqli_stmt_send_long_data|mysqli_stmt_sqlstate|mysqli_stmt_store_result|mysqli_store_result|mysqli_thread_id|mysqli_thread_safe|mysqli_use_result|mysqli_warning_count|natcasesort|natsort|ncurses_addch|ncurses_addchnstr|ncurses_addchstr|ncurses_addnstr|ncurses_addstr|ncurses_assume_default_colors|ncurses_attroff|ncurses_attron|ncurses_attrset|ncurses_baudrate|ncurses_beep|ncurses_bkgd|ncurses_bkgdset|ncurses_border|ncurses_bottom_panel|ncurses_can_change_color|ncurses_cbreak|ncurses_clear|ncurses_clrtobot|ncurses_clrtoeol|ncurses_color_content|ncurses_color_set|ncurses_curs_set|ncurses_def_prog_mode|ncurses_def_shell_mode|ncurses_define_key|ncurses_del_panel|ncurses_delay_output|ncurses_delch|ncurses_deleteln|ncurses_delwin|ncurses_doupdate|ncurses_echo|ncurses_echochar|ncurses_end|ncurses_erase|ncurses_erasechar|ncurses_filter|ncurses_flash|ncurses_flushinp|ncurses_getch|ncurses_getmaxyx|ncurses_getmouse|ncurses_getyx|ncurses_halfdelay|ncurses_has_colors|ncurses_has_ic|ncurses_has_il|ncurses_has_key|ncurses_hide_panel|ncurses_hline|ncurses_inch|ncurses_init|ncurses_init_color|ncurses_init_pair|ncurses_insch|ncurses_insdelln|ncurses_insertln|ncurses_insstr|ncurses_instr|ncurses_isendwin|ncurses_keyok|ncurses_keypad|ncurses_killchar|ncurses_longname|ncurses_meta|ncurses_mouse_trafo|ncurses_mouseinterval|ncurses_mousemask|ncurses_move|ncurses_move_panel|ncurses_mvaddch|ncurses_mvaddchnstr|ncurses_mvaddchstr|ncurses_mvaddnstr|ncurses_mvaddstr|ncurses_mvcur|ncurses_mvdelch|ncurses_mvgetch|ncurses_mvhline|ncurses_mvinch|ncurses_mvvline|ncurses_mvwaddstr|ncurses_napms|ncurses_new_panel|ncurses_newpad|ncurses_newwin|ncurses_nl|ncurses_nocbreak|ncurses_noecho|ncurses_nonl|ncurses_noqiflush|ncurses_noraw|ncurses_pair_content|ncurses_panel_above|ncurses_panel_below|ncurses_panel_window|ncurses_pnoutrefresh|ncurses_prefresh|ncurses_putp|ncurses_qiflush|ncurses_raw|ncurses_refresh|ncurses_replace_panel|ncurses_reset_prog_mode|ncurses_reset_shell_mode|ncurses_resetty|ncurses_savetty|ncurses_scr_dump|ncurses_scr_init|ncurses_scr_restore|ncurses_scr_set|ncurses_scrl|ncurses_show_panel|ncurses_slk_attr|ncurses_slk_attroff|ncurses_slk_attron|ncurses_slk_attrset|ncurses_slk_clear|ncurses_slk_color|ncurses_slk_init|ncurses_slk_noutrefresh|ncurses_slk_refresh|ncurses_slk_restore|ncurses_slk_set|ncurses_slk_touch|ncurses_standend|ncurses_standout|ncurses_start_color|ncurses_termattrs|ncurses_termname|ncurses_timeout|ncurses_top_panel|ncurses_typeahead|ncurses_ungetch|ncurses_ungetmouse|ncurses_update_panels|ncurses_use_default_colors|ncurses_use_env|ncurses_use_extended_names|ncurses_vidattr|ncurses_vline|ncurses_waddch|ncurses_waddstr|ncurses_wattroff|ncurses_wattron|ncurses_wattrset|ncurses_wborder|ncurses_wclear|ncurses_wcolor_set|ncurses_werase|ncurses_wgetch|ncurses_whline|ncurses_wmouse_trafo|ncurses_wmove|ncurses_wnoutrefresh|ncurses_wrefresh|ncurses_wstandend|ncurses_wstandout|ncurses_wvline|next|ngettext|nl2br|nl_langinfo|notes_body|notes_copy_db|notes_create_db|notes_create_note|notes_drop_db|notes_find_note|notes_header_info|notes_list_msgs|notes_mark_read|notes_mark_unread|notes_nav_create|notes_search|notes_unread|notes_version|nsapi_request_headers|nsapi_response_headers|nsapi_virtual|number_format|ob_clean|ob_end_clean|ob_end_flush|ob_flush|ob_get_clean|ob_get_contents|ob_get_flush|ob_get_length|ob_get_level|ob_get_status|ob_gzhandler|ob_iconv_handler|ob_implicit_flush|ob_list_handlers|ob_start|ob_tidyhandler|oci_bind_by_name|oci_cancel|oci_close|oci_commit|oci_connect|oci_define_by_name|oci_error|oci_execute|oci_fetch|oci_fetch_all|oci_fetch_array|oci_fetch_assoc|oci_fetch_object|oci_fetch_row|oci_field_is_null|oci_field_name|oci_field_precision|oci_field_scale|oci_field_size|oci_field_type|oci_field_type_raw|oci_free_statement|oci_internal_debug|oci_lob_copy|oci_lob_is_equal|oci_new_collection|oci_new_connect|oci_new_cursor|oci_new_descriptor|oci_num_fields|oci_num_rows|oci_parse|oci_password_change|oci_pconnect|oci_result|oci_rollback|oci_server_version|oci_set_prefetch|oci_statement_type|ocibindbyname|ocicancel|ocicloselob|ocicollappend|ocicollassign|ocicollassignelem|ocicollgetelem|ocicollmax|ocicollsize|ocicolltrim|ocicolumnisnull|ocicolumnname|ocicolumnprecision|ocicolumnscale|ocicolumnsize|ocicolumntype|ocicolumntyperaw|ocicommit|ocidefinebyname|ocierror|ociexecute|ocifetch|ocifetchinto|ocifetchstatement|ocifreecollection|ocifreecursor|ocifreedesc|ocifreestatement|ociinternaldebug|ociloadlob|ocilogoff|ocilogon|ocinewcollection|ocinewcursor|ocinewdescriptor|ocinlogon|ocinumcols|ociparse|ociplogon|ociresult|ocirollback|ocirowcount|ocisavelob|ocisavelobfile|ociserverversion|ocisetprefetch|ocistatementtype|ociwritelobtofile|ociwritetemporarylob|octdec|odbc_autocommit|odbc_binmode|odbc_close|odbc_close_all|odbc_columnprivileges|odbc_columns|odbc_commit|odbc_connect|odbc_cursor|odbc_data_source|odbc_do|odbc_error|odbc_errormsg|odbc_exec|odbc_execute|odbc_fetch_array|odbc_fetch_into|odbc_fetch_object|odbc_fetch_row|odbc_field_len|odbc_field_name|odbc_field_num|odbc_field_precision|odbc_field_scale|odbc_field_type|odbc_foreignkeys|odbc_free_result|odbc_gettypeinfo|odbc_longreadlen|odbc_next_result|odbc_num_fields|odbc_num_rows|odbc_pconnect|odbc_prepare|odbc_primarykeys|odbc_procedurecolumns|odbc_procedures|odbc_result|odbc_result_all|odbc_rollback|odbc_setoption|odbc_specialcolumns|odbc_statistics|odbc_tableprivileges|odbc_tables|opendir|openlog|openssl_csr_export|openssl_csr_export_to_file|openssl_csr_new|openssl_csr_sign|openssl_error_string|openssl_free_key|openssl_get_privatekey|openssl_get_publickey|openssl_open|openssl_pkcs7_decrypt|openssl_pkcs7_encrypt|openssl_pkcs7_sign|openssl_pkcs7_verify|openssl_pkey_export|openssl_pkey_export_to_file|openssl_pkey_get_private|openssl_pkey_get_public|openssl_pkey_new|openssl_private_decrypt|openssl_private_encrypt|openssl_public_decrypt|openssl_public_encrypt|openssl_seal|openssl_sign|openssl_verify|openssl_x509_check_private_key|openssl_x509_checkpurpose|openssl_x509_export|openssl_x509_export_to_file|openssl_x509_free|openssl_x509_parse|openssl_x509_read|ora_bind|ora_close|ora_columnname|ora_columnsize|ora_columntype|ora_commit|ora_commitoff|ora_commiton|ora_do|ora_error|ora_errorcode|ora_exec|ora_fetch|ora_fetch_into|ora_getcolumn|ora_logoff|ora_logon|ora_numcols|ora_numrows|ora_open|ora_parse|ora_plogon|ora_rollback|ord|output_add_rewrite_var|output_reset_rewrite_vars|overload|ovrimos_close|ovrimos_commit|ovrimos_connect|ovrimos_cursor|ovrimos_exec|ovrimos_execute|ovrimos_fetch_into|ovrimos_fetch_row|ovrimos_field_len|ovrimos_field_name|ovrimos_field_num|ovrimos_field_type|ovrimos_free_result|ovrimos_longreadlen|ovrimos_num_fields|ovrimos_num_rows|ovrimos_prepare|ovrimos_result|ovrimos_result_all|ovrimos_rollback|pack|parse_ini_file|parse_str|parse_url|passthru|pathinfo|pclose|pcntl_alarm|pcntl_exec|pcntl_fork|pcntl_getpriority|pcntl_setpriority|pcntl_signal|pcntl_wait|pcntl_waitpid|pcntl_wexitstatus|pcntl_wifexited|pcntl_wifsignaled|pcntl_wifstopped|pcntl_wstopsig|pcntl_wtermsig|pdf_add_annotation|pdf_add_bookmark|pdf_add_launchlink|pdf_add_locallink|pdf_add_note|pdf_add_outline|pdf_add_pdflink|pdf_add_thumbnail|pdf_add_weblink|pdf_arc|pdf_arcn|pdf_attach_file|pdf_begin_page|pdf_begin_pattern|pdf_begin_template|pdf_circle|pdf_clip|pdf_close|pdf_close_image|pdf_close_pdi|pdf_close_pdi_page|pdf_closepath|pdf_closepath_fill_stroke|pdf_closepath_stroke|pdf_concat|pdf_continue_text|pdf_curveto|pdf_delete|pdf_end_page|pdf_end_pattern|pdf_end_template|pdf_endpath|pdf_fill|pdf_fill_stroke|pdf_findfont|pdf_get_buffer|pdf_get_font|pdf_get_fontname|pdf_get_fontsize|pdf_get_image_height|pdf_get_image_width|pdf_get_majorversion|pdf_get_minorversion|pdf_get_parameter|pdf_get_pdi_parameter|pdf_get_pdi_value|pdf_get_value|pdf_initgraphics|pdf_lineto|pdf_makespotcolor|pdf_moveto|pdf_new|pdf_open|pdf_open_ccitt|pdf_open_file|pdf_open_gif|pdf_open_image|pdf_open_image_file|pdf_open_jpeg|pdf_open_memory_image|pdf_open_pdi|pdf_open_pdi_page|pdf_open_png|pdf_open_tiff|pdf_place_image|pdf_place_pdi_page|pdf_rect|pdf_restore|pdf_rotate|pdf_save|pdf_scale|pdf_set_border_color|pdf_set_border_dash|pdf_set_border_style|pdf_set_char_spacing|pdf_set_duration|pdf_set_font|pdf_set_horiz_scaling|pdf_set_info|pdf_set_info_author|pdf_set_info_creator|pdf_set_info_keywords|pdf_set_info_subject|pdf_set_info_title|pdf_set_leading|pdf_set_parameter|pdf_set_text_matrix|pdf_set_text_pos|pdf_set_text_rendering|pdf_set_text_rise|pdf_set_value|pdf_set_word_spacing|pdf_setcolor|pdf_setdash|pdf_setflat|pdf_setfont|pdf_setgray|pdf_setgray_fill|pdf_setgray_stroke|pdf_setlinecap|pdf_setlinejoin|pdf_setlinewidth|pdf_setmatrix|pdf_setmiterlimit|pdf_setpolydash|pdf_setrgbcolor|pdf_setrgbcolor_fill|pdf_setrgbcolor_stroke|pdf_show|pdf_show_boxed|pdf_show_xy|pdf_skew|pdf_stringwidth|pdf_stroke|pdf_translate|pfpro_cleanup|pfpro_init|pfpro_process|pfpro_process_raw|pfpro_version|pfsockopen|pg_affected_rows|pg_cancel_query|pg_client_encoding|pg_close|pg_connect|pg_connection_busy|pg_connection_reset|pg_connection_status|pg_convert|pg_copy_from|pg_copy_to|pg_dbname|pg_delete|pg_end_copy|pg_escape_bytea|pg_escape_string|pg_fetch_all|pg_fetch_array|pg_fetch_assoc|pg_fetch_object|pg_fetch_result|pg_fetch_row|pg_field_is_null|pg_field_name|pg_field_num|pg_field_prtlen|pg_field_size|pg_field_type|pg_free_result|pg_get_notify|pg_get_pid|pg_get_result|pg_host|pg_insert|pg_last_error|pg_last_notice|pg_last_oid|pg_lo_close|pg_lo_create|pg_lo_export|pg_lo_import|pg_lo_open|pg_lo_read|pg_lo_read_all|pg_lo_seek|pg_lo_tell|pg_lo_unlink|pg_lo_write|pg_meta_data|pg_num_fields|pg_num_rows|pg_options|pg_pconnect|pg_ping|pg_port|pg_put_line|pg_query|pg_result_error|pg_result_seek|pg_result_status|pg_select|pg_send_query|pg_set_client_encoding|pg_trace|pg_tty|pg_unescape_bytea|pg_untrace|pg_update|php_ini_scanned_files|php_logo_guid|php_sapi_name|php_uname|phpcredits|phpinfo|phpversion|pi|png2wbmp|popen|pos|posix_ctermid|posix_get_last_error|posix_getcwd|posix_getegid|posix_geteuid|posix_getgid|posix_getgrgid|posix_getgrnam|posix_getgroups|posix_getlogin|posix_getpgid|posix_getpgrp|posix_getpid|posix_getppid|posix_getpwnam|posix_getpwuid|posix_getrlimit|posix_getsid|posix_getuid|posix_isatty|posix_kill|posix_mkfifo|posix_setegid|posix_seteuid|posix_setgid|posix_setpgid|posix_setsid|posix_setuid|posix_strerror|posix_times|posix_ttyname|posix_uname|pow|preg_grep|preg_match|preg_match_all|preg_quote|preg_replace|preg_replace_callback|preg_split|prev|print|print_r|printer_abort|printer_close|printer_create_brush|printer_create_dc|printer_create_font|printer_create_pen|printer_delete_brush|printer_delete_dc|printer_delete_font|printer_delete_pen|printer_draw_bmp|printer_draw_chord|printer_draw_elipse|printer_draw_line|printer_draw_pie|printer_draw_rectangle|printer_draw_roundrect|printer_draw_text|printer_end_doc|printer_end_page|printer_get_option|printer_list|printer_logical_fontheight|printer_open|printer_select_brush|printer_select_font|printer_select_pen|printer_set_option|printer_start_doc|printer_start_page|printer_write|printf|proc_close|proc_get_status|proc_nice|proc_open|proc_terminate|pspell_add_to_personal|pspell_add_to_session|pspell_check|pspell_clear_session|pspell_config_create|pspell_config_ignore|pspell_config_mode|pspell_config_personal|pspell_config_repl|pspell_config_runtogether|pspell_config_save_repl|pspell_new|pspell_new_config|pspell_new_personal|pspell_save_wordlist|pspell_store_replacement|pspell_suggest|putenv|qdom_error|qdom_tree|quoted_printable_decode|quotemeta|rad2deg|rand|range|rawurldecode|rawurlencode|read_exif_data|readdir|readfile|readgzfile|readline|readline_add_history|readline_clear_history|readline_completion_function|readline_info|readline_list_history|readline_read_history|readline_write_history|readlink|realpath|recode|recode_file|recode_string|register_shutdown_function|register_tick_function|rename|reset|restore_error_handler|restore_include_path|rewind|rewinddir|rmdir|round|rsort|rtrim|scandir|sem_acquire|sem_get|sem_release|sem_remove|serialize|sesam_affected_rows|sesam_commit|sesam_connect|sesam_diagnostic|sesam_disconnect|sesam_errormsg|sesam_execimm|sesam_fetch_array|sesam_fetch_result|sesam_fetch_row|sesam_field_array|sesam_field_name|sesam_free_result|sesam_num_fields|sesam_query|sesam_rollback|sesam_seek_row|sesam_settransaction|session_cache_expire|session_cache_limiter|session_commit|session_decode|session_destroy|session_encode|session_get_cookie_params|session_id|session_is_registered|session_module_name|session_name|session_regenerate_id|session_register|session_save_path|session_set_cookie_params|session_set_save_handler|session_start|session_unregister|session_unset|session_write_close|set_error_handler|set_file_buffer|set_include_path|set_magic_quotes_runtime|set_time_limit|setcookie|setlocale|setrawcookie|settype|sha1|sha1_file|shell_exec|shm_attach|shm_detach|shm_get_var|shm_put_var|shm_remove|shm_remove_var|shmop_close|shmop_delete|shmop_open|shmop_read|shmop_size|shmop_write|show_source|shuffle|similar_text|simplexml_import_dom|simplexml_load_file|simplexml_load_string|sin|sinh|sizeof|sleep|snmp_get_quick_print|snmp_set_quick_print|snmpget|snmprealwalk|snmpset|snmpwalk|snmpwalkoid|socket_accept|socket_bind|socket_clear_error|socket_close|socket_connect|socket_create|socket_create_listen|socket_create_pair|socket_get_option|socket_get_status|socket_getpeername|socket_getsockname|socket_iovec_add|socket_iovec_alloc|socket_iovec_delete|socket_iovec_fetch|socket_iovec_free|socket_iovec_set|socket_last_error|socket_listen|socket_read|socket_readv|socket_recv|socket_recvfrom|socket_recvmsg|socket_select|socket_send|socket_sendmsg|socket_sendto|socket_set_block|socket_set_blocking|socket_set_nonblock|socket_set_option|socket_set_timeout|socket_shutdown|socket_strerror|socket_write|socket_writev|sort|soundex|split|spliti|sprintf|sql_regcase|sqlite_array_query|sqlite_busy_timeout|sqlite_changes|sqlite_close|sqlite_column|sqlite_create_aggregate|sqlite_create_function|sqlite_current|sqlite_error_string|sqlite_escape_string|sqlite_fetch_array|sqlite_fetch_single|sqlite_fetch_string|sqlite_field_name|sqlite_has_more|sqlite_last_error|sqlite_last_insert_rowid|sqlite_libencoding|sqlite_libversion|sqlite_next|sqlite_num_fields|sqlite_num_rows|sqlite_open|sqlite_popen|sqlite_query|sqlite_rewind|sqlite_seek|sqlite_udf_decode_binary|sqlite_udf_encode_binary|sqlite_unbuffered_query|sqrt|srand|sscanf|stat|str_ireplace|str_pad|str_repeat|str_replace|str_rot13|str_shuffle|str_split|str_word_count|strcasecmp|strchr|strcmp|strcoll|strcspn|stream_context_create|stream_context_get_options|stream_context_set_option|stream_context_set_params|stream_copy_to_stream|stream_filter_append|stream_filter_prepend|stream_filter_register|stream_get_contents|stream_get_filters|stream_get_line|stream_get_meta_data|stream_get_transports|stream_get_wrappers|stream_register_wrapper|stream_select|stream_set_blocking|stream_set_timeout|stream_set_write_buffer|stream_socket_accept|stream_socket_client|stream_socket_get_name|stream_socket_recvfrom|stream_socket_sendto|stream_socket_server|stream_wrapper_register|strftime|strip_tags|stripcslashes|stripos|stripslashes|stristr|strlen|strnatcasecmp|strnatcmp|strncasecmp|strncmp|strpos|strrchr|strrev|strripos|strrpos|strspn|strstr|strtok|strtolower|strtotime|strtoupper|strtr|strval|substr|substr_compare|substr_count|substr_replace|swf_actiongeturl|swf_actiongotoframe|swf_actiongotolabel|swf_actionnextframe|swf_actionplay|swf_actionprevframe|swf_actionsettarget|swf_actionstop|swf_actiontogglequality|swf_actionwaitforframe|swf_addbuttonrecord|swf_addcolor|swf_closefile|swf_definebitmap|swf_definefont|swf_defineline|swf_definepoly|swf_definerect|swf_definetext|swf_endbutton|swf_enddoaction|swf_endshape|swf_endsymbol|swf_fontsize|swf_fontslant|swf_fonttracking|swf_getbitmapinfo|swf_getfontinfo|swf_getframe|swf_labelframe|swf_lookat|swf_modifyobject|swf_mulcolor|swf_nextid|swf_oncondition|swf_openfile|swf_ortho|swf_ortho2|swf_perspective|swf_placeobject|swf_polarview|swf_popmatrix|swf_posround|swf_pushmatrix|swf_removeobject|swf_rotate|swf_scale|swf_setfont|swf_setframe|swf_shapearc|swf_shapecurveto|swf_shapecurveto3|swf_shapefillbitmapclip|swf_shapefillbitmaptile|swf_shapefilloff|swf_shapefillsolid|swf_shapelinesolid|swf_shapelineto|swf_shapemoveto|swf_showframe|swf_startbutton|swf_startdoaction|swf_startshape|swf_startsymbol|swf_textwidth|swf_translate|swf_viewport|swfaction|swfbitmap|swfbutton|swfbutton_keypress|swfdisplayitem|swffill|swffont|swfgradient|swfmorph|swfmovie|swfshape|swfsprite|swftext|swftextfield|sybase_affected_rows|sybase_close|sybase_connect|sybase_data_seek|sybase_deadlock_retry_count|sybase_fetch_array|sybase_fetch_assoc|sybase_fetch_field|sybase_fetch_object|sybase_fetch_row|sybase_field_seek|sybase_free_result|sybase_get_last_message|sybase_min_client_severity|sybase_min_error_severity|sybase_min_message_severity|sybase_min_server_severity|sybase_num_fields|sybase_num_rows|sybase_pconnect|sybase_query|sybase_result|sybase_select_db|sybase_set_message_handler|sybase_unbuffered_query|symlink|syslog|system|tan|tanh|tcpwrap_check|tempnam|textdomain|tidy_access_count|tidy_clean_repair|tidy_config_count|tidy_diagnose|tidy_error_count|tidy_get_body|tidy_get_config|tidy_get_error_buffer|tidy_get_head|tidy_get_html|tidy_get_html_ver|tidy_get_output|tidy_get_release|tidy_get_root|tidy_get_status|tidy_getopt|tidy_is_xhtml|tidy_is_xml|tidy_load_config|tidy_parse_file|tidy_parse_string|tidy_repair_file|tidy_repair_string|tidy_reset_config|tidy_save_config|tidy_set_encoding|tidy_setopt|tidy_warning_count|time|tmpfile|token_get_all|token_name|touch|trigger_error|trim|uasort|ucfirst|ucwords|udm_add_search_limit|udm_alloc_agent|udm_alloc_agent_array|udm_api_version|udm_cat_list|udm_cat_path|udm_check_charset|udm_check_stored|udm_clear_search_limits|udm_close_stored|udm_crc32|udm_errno|udm_error|udm_find|udm_free_agent|udm_free_ispell_data|udm_free_res|udm_get_doc_count|udm_get_res_field|udm_get_res_param|udm_hash32|udm_load_ispell_data|udm_open_stored|udm_set_agent_param|uksort|umask|uniqid|unixtojd|unlink|unpack|unregister_tick_function|unserialize|unset|urldecode|urlencode|user_error|usleep|usort|utf8_decode|utf8_encode|var_dump|var_export|variant|version_compare|virtual|vpopmail_add_alias_domain|vpopmail_add_alias_domain_ex|vpopmail_add_domain|vpopmail_add_domain_ex|vpopmail_add_user|vpopmail_alias_add|vpopmail_alias_del|vpopmail_alias_del_domain|vpopmail_alias_get|vpopmail_alias_get_all|vpopmail_auth_user|vpopmail_del_domain|vpopmail_del_domain_ex|vpopmail_del_user|vpopmail_error|vpopmail_passwd|vpopmail_set_user_quota|vprintf|vsprintf|w32api_deftype|w32api_init_dtype|w32api_invoke_function|w32api_register_function|w32api_set_call_method|wddx_add_vars|wddx_deserialize|wddx_packet_end|wddx_packet_start|wddx_serialize_value|wddx_serialize_vars|wordwrap|xdiff_file_diff|xdiff_file_diff_binary|xdiff_file_merge3|xdiff_file_patch|xdiff_file_patch_binary|xdiff_string_diff|xdiff_string_diff_binary|xdiff_string_merge3|xdiff_string_patch|xdiff_string_patch_binary|xml_error_string|xml_get_current_byte_index|xml_get_current_column_number|xml_get_current_line_number|xml_get_error_code|xml_parse|xml_parse_into_struct|xml_parser_create|xml_parser_create_ns|xml_parser_free|xml_parser_get_option|xml_parser_set_option|xml_set_character_data_handler|xml_set_default_handler|xml_set_element_handler|xml_set_end_namespace_decl_handler|xml_set_external_entity_ref_handler|xml_set_notation_decl_handler|xml_set_object|xml_set_processing_instruction_handler|xml_set_start_namespace_decl_handler|xml_set_unparsed_entity_decl_handler|xmlrpc_decode|xmlrpc_decode_request|xmlrpc_encode|xmlrpc_encode_request|xmlrpc_get_type|xmlrpc_parse_method_descriptions|xmlrpc_server_add_introspection_data|xmlrpc_server_call_method|xmlrpc_server_create|xmlrpc_server_destroy|xmlrpc_server_register_introspection_callback|xmlrpc_server_register_method|xmlrpc_set_type|xpath_eval|xpath_eval_expression|xpath_new_context|xptr_eval|xptr_new_context|xsl_xsltprocessor_get_parameter|xsl_xsltprocessor_has_exslt_support|xsl_xsltprocessor_import_stylesheet|xsl_xsltprocessor_register_php_functions|xsl_xsltprocessor_remove_parameter|xsl_xsltprocessor_set_parameter|xsl_xsltprocessor_transform_to_doc|xsl_xsltprocessor_transform_to_uri|xsl_xsltprocessor_transform_to_xml|xslt_create|xslt_errno|xslt_error|xslt_free|xslt_process|xslt_set_base|xslt_set_encoding|xslt_set_error_handler|xslt_set_log|xslt_set_sax_handler|xslt_set_sax_handlers|xslt_set_scheme_handler|xslt_set_scheme_handlers|yaz_addinfo|yaz_ccl_conf|yaz_ccl_parse|yaz_close|yaz_connect|yaz_database|yaz_element|yaz_errno|yaz_error|yaz_es_result|yaz_get_option|yaz_hits|yaz_itemorder|yaz_present|yaz_range|yaz_record|yaz_scan|yaz_scan_result|yaz_schema|yaz_search|yaz_set_option|yaz_sort|yaz_syntax|yaz_wait|yp_all|yp_cat|yp_err_string|yp_errno|yp_first|yp_get_default_domain|yp_master|yp_match|yp_next|yp_order|zend_logo_guid|zend_version|zip_close|zip_entry_close|zip_entry_compressedsize|zip_entry_compressionmethod|zip_entry_filesize|zip_entry_name|zip_entry_open|zip_entry_read|zip_open|zip_read|zlib_get_coding_type".split("|")),b=e.arrayToMap("abstract|and|array|as|break|case|catch|cfunction|class|clone|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|final|for|foreach|function|include|include_once|global|goto|if|implements|interface|instanceof|namespace|new|old_function|or|private|protected|public|return|require|require_once|static|switch|throw|try|use|var|while|xor".split("|")),c=e.arrayToMap("true|false|null|__FILE__|__LINE__|__METHOD__|__FUNCTION__|__CLASS__".split("|")),d=e.arrayToMap("$GLOBALS|$_SERVER|$_GET|$_POST|$_FILES|$_REQUEST|$_SESSION|$_ENV|$_COOKIE|$php_errormsg|$HTTP_RAW_POST_DATA|$http_response_header|$argc|$argv".split("|")),g=e.arrayToMap([]);this.$rules={start:[{token:"support",regex:"<\\?(?:php|\\=)"},{token:"support",regex:"\\?>"},{token:"comment",regex:"\\/\\/.*$"},{token:"comment",regex:"#.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/][gimy]*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language",regex:"\\b(?:DEFAULT_INCLUDE_PATH|E_(?:ALL|CO(?:MPILE_(?:ERROR|WARNING)|RE_(?:ERROR|WARNING))|ERROR|NOTICE|PARSE|STRICT|USER_(?:ERROR|NOTICE|WARNING)|WARNING)|P(?:EAR_(?:EXTENSION_DIR|INSTALL_DIR)|HP_(?:BINDIR|CONFIG_FILE_(?:PATH|SCAN_DIR)|DATADIR|E(?:OL|XTENSION_DIR)|INT_(?:MAX|SIZE)|L(?:IBDIR|OCALSTATEDIR)|O(?:S|UTPUT_HANDLER_(?:CONT|END|START))|PREFIX|S(?:API|HLIB_SUFFIX|YSCONFDIR)|VERSION))|__COMPILER_HALT_OFFSET__)\\b"},{token:"constant.language",regex:"\\b(?:A(?:B(?:DAY_(?:1|2|3|4|5|6|7)|MON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9))|LT_DIGITS|M_STR|SSERT_(?:ACTIVE|BAIL|CALLBACK|QUIET_EVAL|WARNING))|C(?:ASE_(?:LOWER|UPPER)|HAR_MAX|O(?:DESET|NNECTION_(?:ABORTED|NORMAL|TIMEOUT)|UNT_(?:NORMAL|RECURSIVE))|R(?:EDITS_(?:ALL|DOCS|FULLPAGE|G(?:ENERAL|ROUP)|MODULES|QA|SAPI)|NCYSTR|YPT_(?:BLOWFISH|EXT_DES|MD5|S(?:ALT_LENGTH|TD_DES)))|URRENCY_SYMBOL)|D(?:AY_(?:1|2|3|4|5|6|7)|ECIMAL_POINT|IRECTORY_SEPARATOR|_(?:FMT|T_FMT))|E(?:NT_(?:COMPAT|NOQUOTES|QUOTES)|RA(?:_(?:D_(?:FMT|T_FMT)|T_FMT|YEAR)|)|XTR_(?:IF_EXISTS|OVERWRITE|PREFIX_(?:ALL|I(?:F_EXISTS|NVALID)|SAME)|SKIP))|FRAC_DIGITS|GROUPING|HTML_(?:ENTITIES|SPECIALCHARS)|IN(?:FO_(?:ALL|C(?:ONFIGURATION|REDITS)|ENVIRONMENT|GENERAL|LICENSE|MODULES|VARIABLES)|I_(?:ALL|PERDIR|SYSTEM|USER)|T_(?:CURR_SYMBOL|FRAC_DIGITS))|L(?:C_(?:ALL|C(?:OLLATE|TYPE)|M(?:ESSAGES|ONETARY)|NUMERIC|TIME)|O(?:CK_(?:EX|NB|SH|UN)|G_(?:A(?:LERT|UTH(?:PRIV|))|C(?:ONS|R(?:IT|ON))|D(?:AEMON|EBUG)|E(?:MERG|RR)|INFO|KERN|L(?:OCAL(?:0|1|2|3|4|5|6|7)|PR)|MAIL|N(?:DELAY|EWS|O(?:TICE|WAIT))|ODELAY|P(?:ERROR|ID)|SYSLOG|U(?:SER|UCP)|WARNING)))|M(?:ON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9|DECIMAL_POINT|GROUPING|THOUSANDS_SEP)|_(?:1_PI|2_(?:PI|SQRTPI)|E|L(?:N(?:10|2)|OG(?:10E|2E))|PI(?:_(?:2|4)|)|SQRT(?:1_2|2)))|N(?:EGATIVE_SIGN|O(?:EXPR|STR)|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|P(?:ATH(?:INFO_(?:BASENAME|DIRNAME|EXTENSION)|_SEPARATOR)|M_STR|OSITIVE_SIGN|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|RADIXCHAR|S(?:EEK_(?:CUR|END|SET)|ORT_(?:ASC|DESC|NUMERIC|REGULAR|STRING)|TR_PAD_(?:BOTH|LEFT|RIGHT))|T(?:HOUS(?:ANDS_SEP|EP)|_FMT(?:_AMPM|))|YES(?:EXPR|STR)|STD(?:IN|OUT|ERR))\\b"},{token:function(e){if(b.hasOwnProperty(e))return"keyword";if(c.hasOwnProperty(e))return"constant.language";if(d.hasOwnProperty(e))return"variable.language";if(g.hasOwnProperty(e))return"invalid.illegal";if(a.hasOwnProperty(e))return"support.function";if(e=="debugger")return"invalid.deprecated";if(e.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|self|parent)$/))return"variable";return"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.PhpHighlightRules=h}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},{token:"comment.doc",regex:"\\s+"},{token:"comment.doc",regex:"TODO"},{token:"comment.doc",regex:"[^@\\*]+"},{token:"comment.doc",regex:"."}]}};d.inherits(f,e),function(){this.getStartRule=function(a){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:a}},this.getEndRule=function(a){return{token:"comment.doc",regex:"\\*\\/",next:a}}}.call(f.prototype),b.DocCommentHighlightRules=f}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-python.js b/studio/static/js/ace/mode-python.js
new file mode 100644
index 0000000..4c680fa
--- /dev/null
+++ b/studio/static/js/ace/mode-python.js
@@ -0,0 +1 @@
+define("ace/mode/python",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/python_highlight_rules","ace/mode/matching_brace_outdent","ace/range"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/python_highlight_rules").PythonHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(j,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)#/;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"#")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[\:]\s*$/);h&&(d+=c)}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(j.prototype),b.Mode=j}),define("ace/mode/python_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){var a=e.arrayToMap("and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield".split("|")),b=e.arrayToMap("True|False|None|NotImplemented|Ellipsis|__debug__".split("|")),c=e.arrayToMap("abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|float|list|raw_input|unichr|callable|format|locals|reduce|unicode|chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|__import__|complex|hash|min|set|apply|delattr|help|next|setattr|buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern".split("|")),d=e.arrayToMap("".split("|")),f="(?:r|u|ur|R|U|UR|Ur|uR)?",g="(?:(?:[1-9]\\d*)|(?:0))",h="(?:0[oO]?[0-7]+)",i="(?:0[xX][\\dA-Fa-f]+)",j="(?:0[bB][01]+)",k="(?:"+g+"|"+h+"|"+i+"|"+j+")",l="(?:[eE][+-]?\\d+)",m="(?:\\.\\d+)",n="(?:\\d+)",o="(?:(?:"+n+"?"+m+")|(?:"+n+"\\.))",p="(?:(?:"+o+"|"+n+")"+l+")",q="(?:"+p+"|"+o+")";this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"string",regex:f+'"{3}(?:[^\\\\]|\\\\.)*?"{3}'},{token:"string",regex:f+'"{3}.*$',next:"qqstring"},{token:"string",regex:f+'"(?:[^\\\\]|\\\\.)*?"'},{token:"string",regex:f+"'{3}(?:[^\\\\]|\\\\.)*?'{3}"},{token:"string",regex:f+"'{3}.*$",next:"qstring"},{token:"string",regex:f+"'(?:[^\\\\]|\\\\.)*?'"},{token:"constant.numeric",regex:"(?:"+q+"|\\d+)[jJ]\\b"},{token:"constant.numeric",regex:q},{token:"constant.numeric",regex:k+"[lL]\\b"},{token:"constant.numeric",regex:k+"\\b"},{token:function(e){return a.hasOwnProperty(e)?"keyword":b.hasOwnProperty(e)?"constant.language":d.hasOwnProperty(e)?"invalid.illegal":c.hasOwnProperty(e)?"support.function":e=="debugger"?"invalid.deprecated":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"lparen",regex:"[\\[\\(\\{]"},{token:"rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"}],qqstring:[{token:"string",regex:'(?:[^\\\\]|\\\\.)*?"{3}',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:[^\\\\]|\\\\.)*?'{3}",next:"start"},{token:"string",regex:".+"}]}};d.inherits(g,f),b.PythonHighlightRules=g}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-ruby.js b/studio/static/js/ace/mode-ruby.js
new file mode 100644
index 0000000..7b185e0
--- /dev/null
+++ b/studio/static/js/ace/mode-ruby.js
@@ -0,0 +1 @@
+define("ace/mode/ruby",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/ruby_highlight_rules","ace/mode/matching_brace_outdent","ace/range"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/ruby_highlight_rules").RubyHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(j,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)#/;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"#")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(j.prototype),b.Mode=j}),define("ace/mode/ruby_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){var a=e.arrayToMap("abort|Array|assert|assert_equal|assert_not_equal|assert_same|assert_not_same|assert_nil|assert_not_nil|assert_match|assert_no_match|assert_in_delta|assert_throws|assert_raise|assert_nothing_raised|assert_instance_of|assert_kind_of|assert_respond_to|assert_operator|assert_send|assert_difference|assert_no_difference|assert_recognizes|assert_generates|assert_response|assert_redirected_to|assert_template|assert_select|assert_select_email|assert_select_rjs|assert_select_encoded|css_select|at_exit|attr|attr_writer|attr_reader|attr_accessor|attr_accessible|autoload|binding|block_given?|callcc|caller|catch|chomp|chomp!|chop|chop!|defined?|delete_via_redirect|eval|exec|exit|exit!|fail|Float|flunk|follow_redirect!|fork|form_for|form_tag|format|gets|global_variables|gsub|gsub!|get_via_redirect|h|host!|https?|https!|include|Integer|lambda|link_to|link_to_unless_current|link_to_function|link_to_remote|load|local_variables|loop|open|open_session|p|print|printf|proc|putc|puts|post_via_redirect|put_via_redirect|raise|rand|raw|readline|readlines|redirect?|request_via_redirect|require|scan|select|set_trace_func|sleep|split|sprintf|srand|String|stylesheet_link_tag|syscall|system|sub|sub!|test|throw|trace_var|trap|untrace_var|atan2|cos|exp|frexp|ldexp|log|log10|sin|sqrt|tan|render|javascript_include_tag|csrf_meta_tag|label_tag|text_field_tag|submit_tag|check_box_tag|content_tag|radio_button_tag|text_area_tag|password_field_tag|hidden_field_tag|fields_for|select_tag|options_for_select|options_from_collection_for_select|collection_select|time_zone_select|select_date|select_time|select_datetime|date_select|time_select|datetime_select|select_year|select_month|select_day|select_hour|select_minute|select_second|file_field_tag|file_field|respond_to|skip_before_filter|around_filter|after_filter|verify|protect_from_forgery|rescue_from|helper_method|redirect_to|before_filter|send_data|send_file|validates_presence_of|validates_uniqueness_of|validates_length_of|validates_format_of|validates_acceptance_of|validates_associated|validates_exclusion_of|validates_inclusion_of|validates_numericality_of|validates_with|validates_each|authenticate_or_request_with_http_basic|authenticate_or_request_with_http_digest|filter_parameter_logging|match|get|post|resources|redirect|scope|assert_routing|translate|localize|extract_locale_from_tld|t|l|caches_page|expire_page|caches_action|expire_action|cache|expire_fragment|expire_cache_for|observe|cache_sweeper|has_many|has_one|belongs_to|has_and_belongs_to_many".split("|")),b=e.arrayToMap("alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|__FILE__|finally|for|gem|if|in|__LINE__|module|next|not|or|private|protected|public|redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield".split("|")),c=e.arrayToMap("true|TRUE|false|FALSE|nil|NIL|ARGF|ARGV|DATA|ENV|RUBY_PLATFORM|RUBY_RELEASE_DATE|RUBY_VERSION|STDERR|STDIN|STDOUT|TOPLEVEL_BINDING".split("|")),d=e.arrayToMap("$DEBUG|$defout|$FILENAME|$LOAD_PATH|$SAFE|$stdin|$stdout|$stderr|$VERBOSE|$!|root_url|flash|session|cookies|params|request|response|logger".split("|"));this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"comment",regex:"^=begin$",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"[`](?:(?:\\\\.)|(?:[^'\\\\]))*?[`]"},{token:"variable.instancce",regex:"@{1,2}(?:[a-zA-Z_]|d)+"},{token:"variable.class",regex:"[A-Z](?:[a-zA-Z_]|d)+"},{token:"string",regex:"[:](?:[a-zA-Z]|d)+"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:function(e){return e=="self"?"variable.language":b.hasOwnProperty(e)?"keyword":c.hasOwnProperty(e)?"constant.language":d.hasOwnProperty(e)?"variable.language":a.hasOwnProperty(e)?"support.function":e=="debugger"?"invalid.deprecated":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:"^=end$",next:"start"},{token:"comment",regex:".+"}]}};d.inherits(g,f),b.RubyHighlightRules=g}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-scss.js b/studio/static/js/ace/mode-scss.js
new file mode 100644
index 0000000..49a9eb1
--- /dev/null
+++ b/studio/static/js/ace/mode-scss.js
@@ -0,0 +1 @@
+define("ace/mode/scss",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/scss_highlight_rules","ace/mode/matching_brace_outdent"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/scss_highlight_rules").ScssHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(i,e),function(){this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a).tokens;if(e.length&&e[e.length-1].type=="comment")return d;var f=b.match(/^.*\{\s*$/);f&&(d+=c);return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(i.prototype),b.Mode=i}),define("ace/mode/scss_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/text_highlight_rules").TextHighlightRules,g=function(){function i(a){var b=[],c=a.split("");for(var d=0;d<c.length;d++)b.push("[",c[d].toLowerCase(),c[d].toUpperCase(),"]");return b.join("")}var a=e.arrayToMap(function(){var a="-webkit-|-moz-|-o-|-ms-|-svg-|-pie-|-khtml-".split("|"),b="appearance|background-clip|background-inline-policy|background-origin|background-size|binding|border-bottom-colors|border-left-colors|border-right-colors|border-top-colors|border-end|border-end-color|border-end-style|border-end-width|border-image|border-start|border-start-color|border-start-style|border-start-width|box-align|box-direction|box-flex|box-flexgroup|box-ordinal-group|box-orient|box-pack|box-sizing|column-count|column-gap|column-width|column-rule|column-rule-width|column-rule-style|column-rule-color|float-edge|font-feature-settings|font-language-override|force-broken-image-icon|image-region|margin-end|margin-start|opacity|outline|outline-color|outline-offset|outline-radius|outline-radius-bottomleft|outline-radius-bottomright|outline-radius-topleft|outline-radius-topright|outline-style|outline-width|padding-end|padding-start|stack-sizing|tab-size|text-blink|text-decoration-color|text-decoration-line|text-decoration-style|transform|transform-origin|transition|transition-delay|transition-duration|transition-property|transition-timing-function|user-focus|user-input|user-modify|user-select|window-shadow|border-radius".split("|"),c="azimuth|background-attachment|background-color|background-image|background-position|background-repeat|background|border-bottom-color|border-bottom-style|border-bottom-width|border-bottom|border-collapse|border-color|border-left-color|border-left-style|border-left-width|border-left|border-right-color|border-right-style|border-right-width|border-right|border-spacing|border-style|border-top-color|border-top-style|border-top-width|border-top|border-width|border|bottom|box-sizing|caption-side|clear|clip|color|content|counter-increment|counter-reset|cue-after|cue-before|cue|cursor|direction|display|elevation|empty-cells|float|font-family|font-size-adjust|font-size|font-stretch|font-style|font-variant|font-weight|font|height|left|letter-spacing|line-height|list-style-image|list-style-position|list-style-type|list-style|margin-bottom|margin-left|margin-right|margin-top|marker-offset|margin|marks|max-height|max-width|min-height|min-width|opacity|orphans|outline-color|outline-style|outline-width|outline|overflow|overflow-x|overflow-y|padding-bottom|padding-left|padding-right|padding-top|padding|page-break-after|page-break-before|page-break-inside|page|pause-after|pause-before|pause|pitch-range|pitch|play-during|position|quotes|richness|right|size|speak-header|speak-numeral|speak-punctuation|speech-rate|speak|stress|table-layout|text-align|text-decoration|text-indent|text-shadow|text-transform|top|unicode-bidi|vertical-align|visibility|voice-family|volume|white-space|widows|width|word-spacing|z-index".split("|"),d=[];for(var e=0,f=a.length;e<f;e++)Array.prototype.push.apply(d,(a[e]+b.join("|"+a[e])).split("|"));Array.prototype.push.apply(d,b),Array.prototype.push.apply(d,c);return d}()),b=e.arrayToMap("hsl|hsla|rgb|rgba|url|attr|counter|counters|abs|adjust_color|adjust_hue|alpha|join|blue|ceil|change_color|comparable|complement|darken|desaturate|floor|grayscale|green|hue|if|invert|join|length|lighten|lightness|mix|nth|opacify|opacity|percentage|quote|red|round|saturate|saturation|scale_color|transparentize|type_of|unit|unitless|unqoute".split("|")),c=e.arrayToMap("absolute|all-scroll|always|armenian|auto|baseline|below|bidi-override|block|bold|bolder|border-box|both|bottom|break-all|break-word|capitalize|center|char|circle|cjk-ideographic|col-resize|collapse|content-box|crosshair|dashed|decimal-leading-zero|decimal|default|disabled|disc|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ellipsis|fixed|georgian|groove|hand|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|inactive|inherit|inline-block|inline|inset|inside|inter-ideograph|inter-word|italic|justify|katakana-iroha|katakana|keep-all|left|lighter|line-edge|line-through|line|list-item|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|medium|middle|move|n-resize|ne-resize|newspaper|no-drop|no-repeat|nw-resize|none|normal|not-allowed|nowrap|oblique|outset|outside|overline|pointer|progress|relative|repeat-x|repeat-y|repeat|right|ridge|row-resize|rtl|s-resize|scroll|se-resize|separate|small-caps|solid|square|static|strict|super|sw-resize|table-footer-group|table-header-group|tb-rl|text-bottom|text-top|text|thick|thin|top|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|zero".split("|")),d=e.arrayToMap("aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow".split("|")),f=e.arrayToMap("@mixin|@extend|@include|@import|@media|@debug|@warn|@if|@for|@each|@while|@else|@font-face|@-webkit-keyframes|if|and|!default|module|def|end|declare".split("|")),g=e.arrayToMap("a|abbr|acronym|address|applet|area|article|aside|audio|b|base|basefont|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|img|input|ins|keygen|kbd|label|legend|li|link|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|u|ul|var|video|wbr|xmp".split("|")),h="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))";this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:h+i("em")},{token:"constant.numeric",regex:h+i("ex")},{token:"constant.numeric",regex:h+i("px")},{token:"constant.numeric",regex:h+i("cm")},{token:"constant.numeric",regex:h+i("mm")},{token:"constant.numeric",regex:h+i("in")},{token:"constant.numeric",regex:h+i("pt")},{token:"constant.numeric",regex:h+i("pc")},{token:"constant.numeric",regex:h+i("deg")},{token:"constant.numeric",regex:h+i("rad")},{token:"constant.numeric",regex:h+i("grad")},{token:"constant.numeric",regex:h+i("ms")},{token:"constant.numeric",regex:h+i("s")},{token:"constant.numeric",regex:h+i("hz")},{token:"constant.numeric",regex:h+i("khz")},{token:"constant.numeric",regex:h+"%"},{token:"constant.numeric",regex:"#[a-fA-F0-9]{6}"},{token:"constant.numeric",regex:"#[a-fA-F0-9]{3}"},{token:"constant.numeric",regex:h},{token:function(e){if(a.hasOwnProperty(e.toLowerCase()))return"support.type";return f.hasOwnProperty(e)?"keyword":c.hasOwnProperty(e)?"constant.language":b.hasOwnProperty(e)?"support.function":d.hasOwnProperty(e.toLowerCase())?"support.constant.color":g.hasOwnProperty(e.toLowerCase())?"variable.language":"text"},regex:"\\-?[@a-zA-Z_][@a-zA-Z0-9_\\-]*"},{token:"variable",regex:"[a-zA-Z_\\-$][a-zA-Z0-9_\\-$]*\\b"},{token:"variable.language",regex:"#[a-zA-Z0-9-_]+"},{token:"variable.language",regex:"\\.[a-zA-Z0-9-_]+"},{token:"variable.language",regex:":[a-zA-Z0-9-_]+"},{token:"constant",regex:"[a-zA-Z0-9-_]+"},{token:"keyword.operator",regex:"<|>|<=|>=|==|!=|-|%|#|\\+|\\$|\\+|\\*"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]}};d.inherits(g,f),b.ScssHighlightRules=g}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-svg.js b/studio/static/js/ace/mode-svg.js
new file mode 100644
index 0000000..700e4bb
--- /dev/null
+++ b/studio/static/js/ace/mode-svg.js
@@ -0,0 +1 @@
+define("ace/mode/svg",["require","exports","module","pilot/oop","ace/mode/text","ace/mode/javascript","ace/tokenizer","ace/mode/svg_highlight_rules","ace/mode/behaviour/xml"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/mode/javascript").Mode,g=a("ace/tokenizer").Tokenizer,h=a("ace/mode/svg_highlight_rules").SvgHighlightRules,i=a("ace/mode/behaviour/xml").XmlBehaviour,j=function(){this.highlighter=new h,this.$tokenizer=new g(this.highlighter.getRules()),this.$behaviour=new i,this.$embeds=this.highlighter.getEmbeds(),this.createModeDelegates({"js-":f})};d.inherits(j,e),function(){this.toggleCommentLines=function(a,b,c,d){return 0},this.getNextLineIndent=function(a,b,c){return this.$getIndent(b)},this.checkOutdent=function(a,b,c){return!1}}.call(j.prototype),b.Mode=j}),define("ace/mode/javascript",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/javascript_highlight_rules").JavaScriptHighlightRules,h=a("ace/mode/matching_brace_outdent").MatchingBraceOutdent,i=a("ace/range").Range,j=a("ace/worker/worker_client").WorkerClient,k=a("ace/mode/behaviour/cstyle").CstyleBehaviour,l=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h,this.$behaviour=new k};d.inherits(l,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\s*)\/\//;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,"//")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type=="comment")return d;if(a=="start"){var h=b.match(/^.*[\{\(\[]\s*$/);h&&(d+=c)}else if(a=="doc-start"){if(g=="start")return"";var h=b.match(/^\s*(\/?)\*/);h&&(h[1]&&(d+=" "),d+="* ")}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)},this.createWorker=function(a){var b=a.getDocument(),c=new j(["ace","pilot"],"worker-javascript.js","ace/mode/javascript_worker","JavaScriptWorker");c.call("setValue",[b.getValue()]),b.on("change",function(a){a.range={start:a.data.range.start,end:a.data.range.end},c.emit("change",a)}),c.on("jslint",function(b){var c=[];for(var d=0;d<b.data.length;d++){var e=b.data[d];e&&c.push({row:e.line-1,column:e.character-1,text:e.reason,type:"warning",lint:e})}a.setAnnotations(c)}),c.on("narcissus",function(b){a.setAnnotations([b.data])}),c.on("terminate",function(){a.clearAnnotations()});return c}}.call(l.prototype),b.Mode=l}),define("ace/mode/javascript_highlight_rules",["require","exports","module","pilot/oop","pilot/lang","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/lang"),f=a("ace/mode/doc_comment_highlight_rules").DocCommentHighlightRules,g=a("ace/mode/text_highlight_rules").TextHighlightRules,h=function(){var a=e.arrayToMap("break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|const|yield|import|get|set".split("|")),b=e.arrayToMap("null|Infinity|NaN|undefined".split("|")),c=e.arrayToMap("class|enum|extends|super|export|implements|private|public|interface|package|protected|static".split("|"));this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},(new f).getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string.regexp",regex:"[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:'["].*\\\\$',next:"qqstring"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"string",regex:"['].*\\\\$",next:"qstring"},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:function(d){return d=="this"?"variable.language":a.hasOwnProperty(d)?"keyword":b.hasOwnProperty(d)?"constant.language":c.hasOwnProperty(d)?"invalid.illegal":d=="debugger"?"invalid.deprecated":"identifier"},regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"lparen",regex:"[[({]"},{token:"rparen",regex:"[\\])}]"},{token:"comment",regex:"^#!.*$"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}],qqstring:[{token:"string",regex:'(?:(?:\\\\.)|(?:[^"\\\\]))*?"',next:"start"},{token:"string",regex:".+"}],qstring:[{token:"string",regex:"(?:(?:\\\\.)|(?:[^'\\\\]))*?'",next:"start"},{token:"string",regex:".+"}]},this.embedRules(f,"doc-",[(new f).getEndRule("start")])};d.inherits(h,g),b.JavaScriptHighlightRules=h}),define("ace/mode/doc_comment_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},{token:"comment.doc",regex:"\\s+"},{token:"comment.doc",regex:"TODO"},{token:"comment.doc",regex:"[^@\\*]+"},{token:"comment.doc",regex:"."}]}};d.inherits(f,e),function(){this.getStartRule=function(a){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:a}},this.getEndRule=function(a){return{token:"comment.doc",regex:"\\*\\/",next:a}}}.call(f.prototype),b.DocCommentHighlightRules=f}),define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(a,b,c){var d=a("ace/range").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\s+$/.test(a))return!1;return/^\s*\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\s*\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\s+)/);if(b)return b[1];return""}}).call(e.prototype),b.MatchingBraceOutdent=e}),define("ace/worker/worker_client",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=function(b,c,d,e){this.callbacks=[];if(a.packaged)var f=this.$guessBasePath(),g=this.$worker=new Worker(f+c);else{var h=a.nameToUrl("ace/worker/worker",null,"_"),g=this.$worker=new Worker(h),i={};for(var j=0;j<b.length;j++){var k=b[j];i[k]=a.nameToUrl(k,null,"_").replace(/.js$/,"")}}this.$worker.postMessage({init:!0,tlns:i,module:d,classname:e}),this.callbackId=1,this.callbacks={};var l=this;this.$worker.onerror=function(a){window.console&&console.log&&console.log(a);throw a},this.$worker.onmessage=function(a){var b=a.data;switch(b.type){case"log":window.console&&console.log&&console.log(b.data);break;case"event":l._dispatchEvent(b.name,{data:b.data});break;case"call":var c=l.callbacks[b.id];c&&(c(b.data),delete l.callbacks[b.id])}}};(function(){d.implement(this,e),this.$guessBasePath=function(){if(a.aceBaseUrl)return a.aceBaseUrl;var b=document.getElementsByTagName("script");for(var c=0;c<b.length;c++){var d=b[c].src||b[c].getAttribute("src");if(!d)continue;var e=d.match(/^(?:(.*\/)ace\.js|(.*\/)ace-uncompressed\.js)(?:\?|$)/);if(e)return e[1]||e[2]}return""},this.terminate=function(){this._dispatchEvent("terminate",{}),this.$worker.terminate()},this.send=function(a,b){this.$worker.postMessage({command:a,args:b})},this.call=function(a,b,c){if(c){var d=this.callbackId++;this.callbacks[d]=c,b.push(d)}this.send(a,b)},this.emit=function(a,b){this.$worker.postMessage({event:a,data:b})}}).call(f.prototype),b.WorkerClient=f}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}),define("ace/mode/svg_highlight_rules",["require","exports","module","pilot/oop","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/javascript_highlight_rules").JavaScriptHighlightRules,f=a("ace/mode/xml_highlight_rules").XmlHighlightRules,g=function(){f.call(this),this.$rules.start.splice(3,0,{token:"text",regex:"<(?=s*script)",next:"script"}),this.$rules.script=[{token:"text",regex:">",next:"js-start"},{token:"keyword",regex:"[-_a-zA-Z0-9:]+"},{token:"text",regex:"\\s+"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"}],this.embedRules(e,"js-",[{token:"comment",regex:"\\/\\/.*(?=<\\/script>)",next:"tag"},{token:"text",regex:"<\\/(?=script)",next:"tag"}])};d.inherits(g,f),b.SvgHighlightRules=g}),define("ace/mode/xml_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"text",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:"xml_pe",regex:"<\\?.*?\\?>"},{token:"comment",regex:"<\\!--",next:"comment"},{token:"text",regex:"<\\/?",next:"tag"},{token:"text",regex:"\\s+"},{token:"text",regex:"[^<]+"}],tag:[{token:"text",regex:">",next:"start"},{token:"keyword",regex:"[-_a-zA-Z0-9:]+"},{token:"text",regex:"\\s+"},{token:"string",regex:'".*?"'},{token:"string",regex:'["].*$',next:"qqstring"},{token:"string",regex:"'.*?'"},{token:"string",regex:"['].*$",next:"qstring"}],qstring:[{token:"string",regex:".*'",next:"tag"},{token:"string",regex:".+"}],qqstring:[{token:"string",regex:'.*"',next:"tag"},{token:"string",regex:".+"}],cdata:[{token:"text",regex:"\\]\\]>",next:"start"},{token:"text",regex:"\\s+"},{token:"text",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment",regex:".*?-->",next:"start"},{token:"comment",regex:".+"}]}};d.inherits(f,e),b.XmlHighlightRules=f}),define("ace/mode/behaviour/xml",["require","exports","module","pilot/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=a("ace/mode/behaviour/cstyle").CstyleBehaviour,g=function(){this.inherit(f,["string_dquotes"]),this.add("brackets","insertion",function(a,b,c,d,e){if(e=="<"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?!1:{text:"<>",selection:[1,1]}}if(e==">"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==">")return{text:"",selection:[1,1]}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),k=i.substring(h.column,h.column+2);if(k=="</"){var l=this.$getIndent(d.doc.getLine(h.row))+d.getTabString(),m=this.$getIndent(d.doc.getLine(h.row));return{text:"\n"+l+"\n"+m,selection:[1,l.length,1,l.length]}}}return!1})};d.inherits(g,e),b.XmlBehaviour=g}) \ No newline at end of file
diff --git a/studio/static/js/ace/mode-xml.js b/studio/static/js/ace/mode-xml.js
new file mode 100644
index 0000000..bca8943
--- /dev/null
+++ b/studio/static/js/ace/mode-xml.js
@@ -0,0 +1 @@
+define("ace/mode/xml",["require","exports","module","pilot/oop","ace/mode/text","ace/tokenizer","ace/mode/xml_highlight_rules","ace/mode/behaviour/xml"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text").Mode,f=a("ace/tokenizer").Tokenizer,g=a("ace/mode/xml_highlight_rules").XmlHighlightRules,h=a("ace/mode/behaviour/xml").XmlBehaviour,i=function(){this.$tokenizer=new f((new g).getRules()),this.$behaviour=new h};d.inherits(i,e),function(){this.getNextLineIndent=function(a,b,c){return this.$getIndent(b)}}.call(i.prototype),b.Mode=i}),define("ace/mode/xml_highlight_rules",["require","exports","module","pilot/oop","ace/mode/text_highlight_rules"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/text_highlight_rules").TextHighlightRules,f=function(){this.$rules={start:[{token:"text",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:"xml_pe",regex:"<\\?.*?\\?>"},{token:"comment",regex:"<\\!--",next:"comment"},{token:"text",regex:"<\\/?",next:"tag"},{token:"text",regex:"\\s+"},{token:"text",regex:"[^<]+"}],tag:[{token:"text",regex:">",next:"start"},{token:"keyword",regex:"[-_a-zA-Z0-9:]+"},{token:"text",regex:"\\s+"},{token:"string",regex:'".*?"'},{token:"string",regex:'["].*$',next:"qqstring"},{token:"string",regex:"'.*?'"},{token:"string",regex:"['].*$",next:"qstring"}],qstring:[{token:"string",regex:".*'",next:"tag"},{token:"string",regex:".+"}],qqstring:[{token:"string",regex:'.*"',next:"tag"},{token:"string",regex:".+"}],cdata:[{token:"text",regex:"\\]\\]>",next:"start"},{token:"text",regex:"\\s+"},{token:"text",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment",regex:".*?-->",next:"start"},{token:"comment",regex:".+"}]}};d.inherits(f,e),b.XmlHighlightRules=f}),define("ace/mode/behaviour/xml",["require","exports","module","pilot/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=a("ace/mode/behaviour/cstyle").CstyleBehaviour,g=function(){this.inherit(f,["string_dquotes"]),this.add("brackets","insertion",function(a,b,c,d,e){if(e=="<"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?!1:{text:"<>",selection:[1,1]}}if(e==">"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==">")return{text:"",selection:[1,1]}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),k=i.substring(h.column,h.column+2);if(k=="</"){var l=this.$getIndent(d.doc.getLine(h.row))+d.getTabString(),m=this.$getIndent(d.doc.getLine(h.row));return{text:"\n"+l+"\n"+m,selection:[1,l.length,1,l.length]}}}return!1})};d.inherits(g,e),b.XmlBehaviour=g}),define("ace/mode/behaviour/cstyle",["require","exports","module","pilot/oop","ace/mode/behaviour"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/mode/behaviour").Behaviour,f=function(){this.add("braces","insertion",function(a,b,c,d,e){if(e=="{"){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"{"+g+"}",selection:!1}:{text:"{}",selection:[1,1]}}if(e=="}"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var k=d.$findOpeningBracket("}",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}else if(e=="\n"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j=="}"){var l=d.findMatchingBracket({row:h.row,column:h.column}),m=this.getNextLineIndent(a,i.substring(0,i.length-1),d.getTabString()),n=this.$getIndent(d.doc.getLine(l.row));return{text:"\n"+m+"\n"+n,selection:[1,m.length,1,m.length]}}}return!1}),this.add("braces","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="{"){var g=d.doc.getLine(e.start.row),h=g.substring(e.end.column,e.end.column+1);if(h=="}")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("parens","insertion",function(a,b,c,d,e){if(e=="("){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);return g!==""?{text:"("+g+")",selection:!1}:{text:"()",selection:[1,1]}}if(e==")"){var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column,h.column+1);if(j==")"){var k=d.$findOpeningBracket(")",{column:h.column+1,row:h.row});if(k!==null)return{text:"",selection:[1,1]}}}return!1}),this.add("parens","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=="("){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h==")")return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1}),this.add("string_dquotes","insertion",function(a,b,c,d,e){if(e=='"'){var f=c.getSelectionRange(),g=d.doc.getTextRange(f);if(g!=="")return{text:'"'+g+'"',selection:!1};var h=c.getCursorPosition(),i=d.doc.getLine(h.row),j=i.substring(h.column-1,h.column);if(j=="\\")return!1;var k=d.getTokens(f.start.row,f.start.row)[0].tokens,l=0,m,n=-1;for(var o=0;o<k.length;o++){m=k[o],m.type=="string"?n=-1:n<0&&(n=m.value.indexOf('"'));if(m.value.length+l>f.start.column)break;l+=k[o].value.length}if(!m||n<0&&m.type!=="comment"&&(m.type!=="string"||f.start.column!==m.value.length+l-1&&m.value.lastIndexOf('"')===m.value.length-1))return{text:'""',selection:[1,1]};if(m&&m.type==="string"){var p=i.substring(h.column,h.column+1);if(p=='"')return{text:"",selection:[1,1]}}}return!1}),this.add("string_dquotes","deletion",function(a,b,c,d,e){var f=d.doc.getTextRange(e);if(!e.isMultiLine()&&f=='"'){var g=d.doc.getLine(e.start.row),h=g.substring(e.start.column+1,e.start.column+2);if(h=='"')return new Range(e.start.row,e.start.column,e.start.row,e.end.column+1)}return!1})};d.inherits(f,e),b.CstyleBehaviour=f}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-clouds.js b/studio/static/js/ace/theme-clouds.js
new file mode 100644
index 0000000..c14d3cb
--- /dev/null
+++ b/studio/static/js/ace/theme-clouds.js
@@ -0,0 +1 @@
+define("ace/theme/clouds",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-clouds .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-clouds .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-clouds .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-clouds .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-clouds .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-clouds .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-clouds .ace_scroller {\n background-color: #FFFFFF;\n}\n\n.ace-clouds .ace_text-layer {\n cursor: text;\n color: #000000;\n}\n\n.ace-clouds .ace_cursor {\n border-left: 2px solid #000000;\n}\n\n.ace-clouds .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #000000;\n}\n \n.ace-clouds .ace_marker-layer .ace_selection {\n background: #BDD5FC;\n}\n\n.ace-clouds .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-clouds .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #BFBFBF;\n}\n\n.ace-clouds .ace_marker-layer .ace_active_line {\n background: #FFFBD1;\n}\n\n \n.ace-clouds .ace_invisible {\n color: #BFBFBF;\n}\n\n.ace-clouds .ace_keyword {\n color:#AF956F;\n}\n\n.ace-clouds .ace_keyword.ace_operator {\n color:#484848;\n}\n\n.ace-clouds .ace_constant {\n \n}\n\n.ace-clouds .ace_constant.ace_language {\n color:#39946A;\n}\n\n.ace-clouds .ace_constant.ace_library {\n \n}\n\n.ace-clouds .ace_constant.ace_numeric {\n color:#46A609;\n}\n\n.ace-clouds .ace_invalid {\n background-color:#FF002A;\n}\n\n.ace-clouds .ace_invalid.ace_illegal {\n \n}\n\n.ace-clouds .ace_invalid.ace_deprecated {\n \n}\n\n.ace-clouds .ace_support {\n \n}\n\n.ace-clouds .ace_support.ace_function {\n color:#C52727;\n}\n\n.ace-clouds .ace_function.ace_buildin {\n \n}\n\n.ace-clouds .ace_string {\n color:#5D90CD;\n}\n\n.ace-clouds .ace_string.ace_regexp {\n \n}\n\n.ace-clouds .ace_comment {\n color:#BCC8BA;\n}\n\n.ace-clouds .ace_comment.ace_doc {\n \n}\n\n.ace-clouds .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-clouds .ace_variable {\n \n}\n\n.ace-clouds .ace_variable.ace_language {\n \n}\n\n.ace-clouds .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-clouds"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-clouds_midnight.js b/studio/static/js/ace/theme-clouds_midnight.js
new file mode 100644
index 0000000..52137fb
--- /dev/null
+++ b/studio/static/js/ace/theme-clouds_midnight.js
@@ -0,0 +1 @@
+define("ace/theme/clouds_midnight",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-clouds-midnight .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-clouds-midnight .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-clouds-midnight .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-clouds-midnight .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-clouds-midnight .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-clouds-midnight .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-clouds-midnight .ace_scroller {\n background-color: #191919;\n}\n\n.ace-clouds-midnight .ace_text-layer {\n cursor: text;\n color: #929292;\n}\n\n.ace-clouds-midnight .ace_cursor {\n border-left: 2px solid #7DA5DC;\n}\n\n.ace-clouds-midnight .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #7DA5DC;\n}\n \n.ace-clouds-midnight .ace_marker-layer .ace_selection {\n background: #000000;\n}\n\n.ace-clouds-midnight .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-clouds-midnight .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #BFBFBF;\n}\n\n.ace-clouds-midnight .ace_marker-layer .ace_active_line {\n background: rgba(215, 215, 215, 0.031);\n}\n\n \n.ace-clouds-midnight .ace_invisible {\n color: #BFBFBF;\n}\n\n.ace-clouds-midnight .ace_keyword {\n color:#927C5D;\n}\n\n.ace-clouds-midnight .ace_keyword.ace_operator {\n color:#4B4B4B;\n}\n\n.ace-clouds-midnight .ace_constant {\n \n}\n\n.ace-clouds-midnight .ace_constant.ace_language {\n color:#39946A;\n}\n\n.ace-clouds-midnight .ace_constant.ace_library {\n \n}\n\n.ace-clouds-midnight .ace_constant.ace_numeric {\n color:#46A609;\n}\n\n.ace-clouds-midnight .ace_invalid {\n color:#FFFFFF;\nbackground-color:#E92E2E;\n}\n\n.ace-clouds-midnight .ace_invalid.ace_illegal {\n \n}\n\n.ace-clouds-midnight .ace_invalid.ace_deprecated {\n \n}\n\n.ace-clouds-midnight .ace_support {\n \n}\n\n.ace-clouds-midnight .ace_support.ace_function {\n color:#E92E2E;\n}\n\n.ace-clouds-midnight .ace_function.ace_buildin {\n \n}\n\n.ace-clouds-midnight .ace_string {\n color:#5D90CD;\n}\n\n.ace-clouds-midnight .ace_string.ace_regexp {\n \n}\n\n.ace-clouds-midnight .ace_comment {\n color:#3C403B;\n}\n\n.ace-clouds-midnight .ace_comment.ace_doc {\n \n}\n\n.ace-clouds-midnight .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-clouds-midnight .ace_variable {\n \n}\n\n.ace-clouds-midnight .ace_variable.ace_language {\n \n}\n\n.ace-clouds-midnight .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-clouds-midnight"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-cobalt.js b/studio/static/js/ace/theme-cobalt.js
new file mode 100644
index 0000000..51be7b2
--- /dev/null
+++ b/studio/static/js/ace/theme-cobalt.js
@@ -0,0 +1 @@
+define("ace/theme/cobalt",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-cobalt .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-cobalt .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-cobalt .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-cobalt .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-cobalt .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-cobalt .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-cobalt .ace_scroller {\n background-color: #002240;\n}\n\n.ace-cobalt .ace_text-layer {\n cursor: text;\n color: #FFFFFF;\n}\n\n.ace-cobalt .ace_cursor {\n border-left: 2px solid #FFFFFF;\n}\n\n.ace-cobalt .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #FFFFFF;\n}\n \n.ace-cobalt .ace_marker-layer .ace_selection {\n background: rgba(179, 101, 57, 0.75);\n}\n\n.ace-cobalt .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-cobalt .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgba(255, 255, 255, 0.15);\n}\n\n.ace-cobalt .ace_marker-layer .ace_active_line {\n background: rgba(0, 0, 0, 0.35);\n}\n\n \n.ace-cobalt .ace_invisible {\n color: rgba(255, 255, 255, 0.15);\n}\n\n.ace-cobalt .ace_keyword {\n color:#FF9D00;\n}\n\n.ace-cobalt .ace_keyword.ace_operator {\n \n}\n\n.ace-cobalt .ace_constant {\n color:#FF628C;\n}\n\n.ace-cobalt .ace_constant.ace_language {\n \n}\n\n.ace-cobalt .ace_constant.ace_library {\n \n}\n\n.ace-cobalt .ace_constant.ace_numeric {\n \n}\n\n.ace-cobalt .ace_invalid {\n color:#F8F8F8;\nbackground-color:#800F00;\n}\n\n.ace-cobalt .ace_invalid.ace_illegal {\n \n}\n\n.ace-cobalt .ace_invalid.ace_deprecated {\n \n}\n\n.ace-cobalt .ace_support {\n color:#80FFBB;\n}\n\n.ace-cobalt .ace_support.ace_function {\n color:#FFB054;\n}\n\n.ace-cobalt .ace_function.ace_buildin {\n \n}\n\n.ace-cobalt .ace_string {\n \n}\n\n.ace-cobalt .ace_string.ace_regexp {\n color:#80FFC2;\n}\n\n.ace-cobalt .ace_comment {\n font-style:italic;\ncolor:#0088FF;\n}\n\n.ace-cobalt .ace_comment.ace_doc {\n \n}\n\n.ace-cobalt .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-cobalt .ace_variable {\n color:#CCCCCC;\n}\n\n.ace-cobalt .ace_variable.ace_language {\n color:#FF80E1;\n}\n\n.ace-cobalt .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-cobalt"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-dawn.js b/studio/static/js/ace/theme-dawn.js
new file mode 100644
index 0000000..3b98416
--- /dev/null
+++ b/studio/static/js/ace/theme-dawn.js
@@ -0,0 +1 @@
+define("ace/theme/dawn",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-dawn .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-dawn .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-dawn .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-dawn .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-dawn .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-dawn .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-dawn .ace_scroller {\n background-color: #F9F9F9;\n}\n\n.ace-dawn .ace_text-layer {\n cursor: text;\n color: #080808;\n}\n\n.ace-dawn .ace_cursor {\n border-left: 2px solid #000000;\n}\n\n.ace-dawn .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #000000;\n}\n \n.ace-dawn .ace_marker-layer .ace_selection {\n background: rgba(39, 95, 255, 0.30);\n}\n\n.ace-dawn .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-dawn .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgba(75, 75, 126, 0.50);\n}\n\n.ace-dawn .ace_marker-layer .ace_active_line {\n background: rgba(36, 99, 180, 0.12);\n}\n\n \n.ace-dawn .ace_invisible {\n color: rgba(75, 75, 126, 0.50);\n}\n\n.ace-dawn .ace_keyword {\n color:#794938;\n}\n\n.ace-dawn .ace_keyword.ace_operator {\n \n}\n\n.ace-dawn .ace_constant {\n color:#811F24;\n}\n\n.ace-dawn .ace_constant.ace_language {\n \n}\n\n.ace-dawn .ace_constant.ace_library {\n \n}\n\n.ace-dawn .ace_constant.ace_numeric {\n \n}\n\n.ace-dawn .ace_invalid {\n \n}\n\n.ace-dawn .ace_invalid.ace_illegal {\n text-decoration:underline;\nfont-style:italic;\ncolor:#F8F8F8;\nbackground-color:#B52A1D;\n}\n\n.ace-dawn .ace_invalid.ace_deprecated {\n text-decoration:underline;\nfont-style:italic;\ncolor:#B52A1D;\n}\n\n.ace-dawn .ace_support {\n color:#691C97;\n}\n\n.ace-dawn .ace_support.ace_function {\n color:#693A17;\n}\n\n.ace-dawn .ace_function.ace_buildin {\n \n}\n\n.ace-dawn .ace_string {\n color:#0B6125;\n}\n\n.ace-dawn .ace_string.ace_regexp {\n color:#CF5628;\n}\n\n.ace-dawn .ace_comment {\n font-style:italic;\ncolor:#5A525F;\n}\n\n.ace-dawn .ace_comment.ace_doc {\n \n}\n\n.ace-dawn .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-dawn .ace_variable {\n color:#234A97;\n}\n\n.ace-dawn .ace_variable.ace_language {\n \n}\n\n.ace-dawn .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-dawn"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-eclipse.js b/studio/static/js/ace/theme-eclipse.js
new file mode 100644
index 0000000..e74e173
--- /dev/null
+++ b/studio/static/js/ace/theme-eclipse.js
@@ -0,0 +1 @@
+define("ace/theme/eclipse",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-eclipse .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-eclipse .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-eclipse .ace_gutter {\n width: 50px;\n background: rgb(227, 227, 227);\n border-right: 1px solid rgb(159, 159, 159);\t \n color: rgb(136, 136, 136);\n}\n\n.ace-eclipse .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-eclipse .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-eclipse .ace_text-layer {\n cursor: text;\n}\n\n.ace-eclipse .ace_cursor {\n border-left: 1px solid black;\n}\n\n.ace-eclipse .ace_line .ace_keyword, .ace-eclipse .ace_line .ace_variable {\n color: rgb(127, 0, 85);\n}\n\n.ace-eclipse .ace_line .ace_constant.ace_buildin {\n color: rgb(88, 72, 246);\n}\n\n.ace-eclipse .ace_line .ace_constant.ace_library {\n color: rgb(6, 150, 14);\n}\n\n.ace-eclipse .ace_line .ace_function {\n color: rgb(60, 76, 114);\n}\n\n.ace-eclipse .ace_line .ace_string {\n color: rgb(42, 0, 255);\n}\n\n.ace-eclipse .ace_line .ace_comment {\n color: rgb(63, 127, 95);\n}\n\n.ace-eclipse .ace_line .ace_comment.ace_doc {\n color: rgb(63, 95, 191);\n}\n\n.ace-eclipse .ace_line .ace_comment.ace_doc.ace_tag {\n color: rgb(127, 159, 191);\n}\n\n.ace-eclipse .ace_line .ace_constant.ace_numeric {\n}\n\n.ace-eclipse .ace_line .ace_tag {\n\tcolor: rgb(63, 127, 127);\n}\n\n.ace-eclipse .ace_line .ace_xml_pe {\n color: rgb(104, 104, 91);\n}\n\n.ace-eclipse .ace_marker-layer .ace_selection {\n background: rgb(181, 213, 255);\n}\n\n.ace-eclipse .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgb(192, 192, 192);\n}\n\n.ace-eclipse .ace_marker-layer .ace_active_line {\n background: rgb(232, 242, 254);\n}";d.importCssString(e),b.cssClass="ace-eclipse"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-idle_fingers.js b/studio/static/js/ace/theme-idle_fingers.js
new file mode 100644
index 0000000..40c7b95
--- /dev/null
+++ b/studio/static/js/ace/theme-idle_fingers.js
@@ -0,0 +1 @@
+define("ace/theme/idle_fingers",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-idle-fingers .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-idle-fingers .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-idle-fingers .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-idle-fingers .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-idle-fingers .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-idle-fingers .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-idle-fingers .ace_scroller {\n background-color: #323232;\n}\n\n.ace-idle-fingers .ace_text-layer {\n cursor: text;\n color: #FFFFFF;\n}\n\n.ace-idle-fingers .ace_cursor {\n border-left: 2px solid #91FF00;\n}\n\n.ace-idle-fingers .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #91FF00;\n}\n \n.ace-idle-fingers .ace_marker-layer .ace_selection {\n background: rgba(90, 100, 126, 0.88);\n}\n\n.ace-idle-fingers .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-idle-fingers .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #404040;\n}\n\n.ace-idle-fingers .ace_marker-layer .ace_active_line {\n background: #353637;\n}\n\n \n.ace-idle-fingers .ace_invisible {\n color: #404040;\n}\n\n.ace-idle-fingers .ace_keyword {\n color:#CC7833;\n}\n\n.ace-idle-fingers .ace_keyword.ace_operator {\n \n}\n\n.ace-idle-fingers .ace_constant {\n color:#6C99BB;\n}\n\n.ace-idle-fingers .ace_constant.ace_language {\n \n}\n\n.ace-idle-fingers .ace_constant.ace_library {\n \n}\n\n.ace-idle-fingers .ace_constant.ace_numeric {\n \n}\n\n.ace-idle-fingers .ace_invalid {\n color:#FFFFFF;\nbackground-color:#FF0000;\n}\n\n.ace-idle-fingers .ace_invalid.ace_illegal {\n \n}\n\n.ace-idle-fingers .ace_invalid.ace_deprecated {\n \n}\n\n.ace-idle-fingers .ace_support {\n \n}\n\n.ace-idle-fingers .ace_support.ace_function {\n color:#B83426;\n}\n\n.ace-idle-fingers .ace_function.ace_buildin {\n \n}\n\n.ace-idle-fingers .ace_string {\n color:#A5C261;\n}\n\n.ace-idle-fingers .ace_string.ace_regexp {\n color:#CCCC33;\n}\n\n.ace-idle-fingers .ace_comment {\n font-style:italic;\ncolor:#BC9458;\n}\n\n.ace-idle-fingers .ace_comment.ace_doc {\n \n}\n\n.ace-idle-fingers .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-idle-fingers .ace_variable {\n \n}\n\n.ace-idle-fingers .ace_variable.ace_language {\n \n}\n\n.ace-idle-fingers .ace_xml_pe {\n \n}\n\n.ace-idle-fingers .ace_collab.ace_user1 {\n color:#323232;\nbackground-color:#FFF980; \n}";d.importCssString(e),b.cssClass="ace-idle-fingers"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-kr_theme.js b/studio/static/js/ace/theme-kr_theme.js
new file mode 100644
index 0000000..2fac8ba
--- /dev/null
+++ b/studio/static/js/ace/theme-kr_theme.js
@@ -0,0 +1 @@
+define("ace/theme/kr_theme",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-kr-theme .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-kr-theme .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-kr-theme .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-kr-theme .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-kr-theme .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-kr-theme .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-kr-theme .ace_scroller {\n background-color: #0B0A09;\n}\n\n.ace-kr-theme .ace_text-layer {\n cursor: text;\n color: #FCFFE0;\n}\n\n.ace-kr-theme .ace_cursor {\n border-left: 2px solid #FF9900;\n}\n\n.ace-kr-theme .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #FF9900;\n}\n \n.ace-kr-theme .ace_marker-layer .ace_selection {\n background: rgba(170, 0, 255, 0.45);\n}\n\n.ace-kr-theme .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-kr-theme .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgba(255, 177, 111, 0.32);\n}\n\n.ace-kr-theme .ace_marker-layer .ace_active_line {\n background: #38403D;\n}\n\n \n.ace-kr-theme .ace_invisible {\n color: rgba(255, 177, 111, 0.32);\n}\n\n.ace-kr-theme .ace_keyword {\n color:#949C8B;\n}\n\n.ace-kr-theme .ace_keyword.ace_operator {\n \n}\n\n.ace-kr-theme .ace_constant {\n color:rgba(210, 117, 24, 0.76);\n}\n\n.ace-kr-theme .ace_constant.ace_language {\n \n}\n\n.ace-kr-theme .ace_constant.ace_library {\n \n}\n\n.ace-kr-theme .ace_constant.ace_numeric {\n \n}\n\n.ace-kr-theme .ace_invalid {\n color:#F8F8F8;\nbackground-color:#A41300;\n}\n\n.ace-kr-theme .ace_invalid.ace_illegal {\n \n}\n\n.ace-kr-theme .ace_invalid.ace_deprecated {\n \n}\n\n.ace-kr-theme .ace_support {\n color:#9FC28A;\n}\n\n.ace-kr-theme .ace_support.ace_function {\n color:#85873A;\n}\n\n.ace-kr-theme .ace_function.ace_buildin {\n \n}\n\n.ace-kr-theme .ace_string {\n \n}\n\n.ace-kr-theme .ace_string.ace_regexp {\n color:rgba(125, 255, 192, 0.65);\n}\n\n.ace-kr-theme .ace_comment {\n font-style:italic;\ncolor:#706D5B;\n}\n\n.ace-kr-theme .ace_comment.ace_doc {\n \n}\n\n.ace-kr-theme .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-kr-theme .ace_variable {\n color:#D1A796;\n}\n\n.ace-kr-theme .ace_variable.ace_language {\n color:#FF80E1;\n}\n\n.ace-kr-theme .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-kr-theme"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-merbivore.js b/studio/static/js/ace/theme-merbivore.js
new file mode 100644
index 0000000..407e6f6
--- /dev/null
+++ b/studio/static/js/ace/theme-merbivore.js
@@ -0,0 +1 @@
+define("ace/theme/merbivore",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-merbivore .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-merbivore .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-merbivore .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-merbivore .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-merbivore .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-merbivore .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-merbivore .ace_scroller {\n background-color: #161616;\n}\n\n.ace-merbivore .ace_text-layer {\n cursor: text;\n color: #E6E1DC;\n}\n\n.ace-merbivore .ace_cursor {\n border-left: 2px solid #FFFFFF;\n}\n\n.ace-merbivore .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #FFFFFF;\n}\n \n.ace-merbivore .ace_marker-layer .ace_selection {\n background: #454545;\n}\n\n.ace-merbivore .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-merbivore .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #FCE94F;\n}\n\n.ace-merbivore .ace_marker-layer .ace_active_line {\n background: #333435;\n}\n\n \n.ace-merbivore .ace_invisible {\n color: #404040;\n}\n\n.ace-merbivore .ace_keyword {\n color:#FC6F09;\n}\n\n.ace-merbivore .ace_keyword.ace_operator {\n \n}\n\n.ace-merbivore .ace_constant {\n color:#1EDAFB;\n}\n\n.ace-merbivore .ace_constant.ace_language {\n color:#FDC251;\n}\n\n.ace-merbivore .ace_constant.ace_library {\n color:#8DFF0A;\n}\n\n.ace-merbivore .ace_constant.ace_numeric {\n color:#58C554;\n}\n\n.ace-merbivore .ace_invalid {\n color:#FFFFFF;\n background-color:#990000;\n}\n\n.ace-merbivore .ace_invalid.ace_illegal {\n \n}\n\n.ace-merbivore .ace_invalid.ace_deprecated {\n color:#FFFFFF;\n background-color:#990000;\n}\n\n.ace-merbivore .ace_support {\n \n}\n\n.ace-merbivore .ace_support.ace_function {\n color:#FC6F09;\n}\n\n.ace-merbivore .ace_function.ace_buildin {\n \n}\n\n.ace-merbivore .ace_string {\n color:#8DFF0A;\n}\n\n.ace-merbivore .ace_string.ace_regexp {\n \n}\n\n.ace-merbivore .ace_comment {\n color:#AD2EA4;\n}\n\n.ace-merbivore .ace_comment.ace_doc {\n \n}\n\n.ace-merbivore .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-merbivore .ace_variable {\n \n}\n\n.ace-merbivore .ace_variable.ace_language {\n \n}\n\n.ace-merbivore .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-merbivore"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-merbivore_soft.js b/studio/static/js/ace/theme-merbivore_soft.js
new file mode 100644
index 0000000..5be28c5
--- /dev/null
+++ b/studio/static/js/ace/theme-merbivore_soft.js
@@ -0,0 +1 @@
+define("ace/theme/merbivore_soft",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-merbivore-soft .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-merbivore-soft .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-merbivore-soft .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-merbivore-soft .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-merbivore-soft .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-merbivore-soft .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-merbivore-soft .ace_scroller {\n background-color: #1C1C1C;\n}\n\n.ace-merbivore-soft .ace_text-layer {\n cursor: text;\n color: #E6E1DC;\n}\n\n.ace-merbivore-soft .ace_cursor {\n border-left: 2px solid #FFFFFF;\n}\n\n.ace-merbivore-soft .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #FFFFFF;\n}\n \n.ace-merbivore-soft .ace_marker-layer .ace_selection {\n background: #494949;\n}\n\n.ace-merbivore-soft .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-merbivore-soft .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #FCE94F;\n}\n\n.ace-merbivore-soft .ace_marker-layer .ace_active_line {\n background: #333435;\n}\n\n \n.ace-merbivore-soft .ace_invisible {\n color: #404040;\n}\n\n.ace-merbivore-soft .ace_keyword {\n color:#FC803A;\n}\n\n.ace-merbivore-soft .ace_keyword.ace_operator {\n \n}\n\n.ace-merbivore-soft .ace_constant {\n color:#68C1D8;\n}\n\n.ace-merbivore-soft .ace_constant.ace_language {\n color:#E1C582;\n}\n\n.ace-merbivore-soft .ace_constant.ace_library {\n color:#8EC65F;\n}\n\n.ace-merbivore-soft .ace_constant.ace_numeric {\n color:#7FC578;\n}\n\n.ace-merbivore-soft .ace_invalid {\n color:#FFFFFF;\n background-color:#FE3838;\n}\n\n.ace-merbivore-soft .ace_invalid.ace_illegal {\n \n}\n\n.ace-merbivore-soft .ace_invalid.ace_deprecated {\n color:#FFFFFF;\n background-color:#FE3838;\n}\n\n.ace-merbivore-soft .ace_support {\n \n}\n\n.ace-merbivore-soft .ace_support.ace_function {\n color:#FC803A;\n}\n\n.ace-merbivore-soft .ace_function.ace_buildin {\n \n}\n\n.ace-merbivore-soft .ace_string {\n color:#8EC65F;\n}\n\n.ace-merbivore-soft .ace_string.ace_regexp {\n \n}\n\n.ace-merbivore-soft .ace_comment {\n color:#AC4BB8;\n}\n\n.ace-merbivore-soft .ace_comment.ace_doc {\n \n}\n\n.ace-merbivore-soft .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-merbivore-soft .ace_variable {\n \n}\n\n.ace-merbivore-soft .ace_variable.ace_language {\n \n}\n\n.ace-merbivore-soft .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-merbivore-soft"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-mono_industrial.js b/studio/static/js/ace/theme-mono_industrial.js
new file mode 100644
index 0000000..354de6b
--- /dev/null
+++ b/studio/static/js/ace/theme-mono_industrial.js
@@ -0,0 +1 @@
+define("ace/theme/mono_industrial",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-mono-industrial .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-mono-industrial .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-mono-industrial .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-mono-industrial .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-mono-industrial .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-mono-industrial .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-mono-industrial .ace_scroller {\n background-color: #222C28;\n}\n\n.ace-mono-industrial .ace_text-layer {\n cursor: text;\n color: #FFFFFF;\n}\n\n.ace-mono-industrial .ace_cursor {\n border-left: 2px solid #FFFFFF;\n}\n\n.ace-mono-industrial .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #FFFFFF;\n}\n \n.ace-mono-industrial .ace_marker-layer .ace_selection {\n background: rgba(145, 153, 148, 0.40);\n}\n\n.ace-mono-industrial .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-mono-industrial .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgba(102, 108, 104, 0.50);\n}\n\n.ace-mono-industrial .ace_marker-layer .ace_active_line {\n background: rgba(12, 13, 12, 0.25);\n}\n\n \n.ace-mono-industrial .ace_invisible {\n color: rgba(102, 108, 104, 0.50);\n}\n\n.ace-mono-industrial .ace_keyword {\n color:#A39E64;\n}\n\n.ace-mono-industrial .ace_keyword.ace_operator {\n color:#A8B3AB;\n}\n\n.ace-mono-industrial .ace_constant {\n color:#E98800;\n}\n\n.ace-mono-industrial .ace_constant.ace_language {\n \n}\n\n.ace-mono-industrial .ace_constant.ace_library {\n \n}\n\n.ace-mono-industrial .ace_constant.ace_numeric {\n color:#E98800;\n}\n\n.ace-mono-industrial .ace_invalid {\n color:#FFFFFF;\nbackground-color:rgba(153, 0, 0, 0.68);\n}\n\n.ace-mono-industrial .ace_invalid.ace_illegal {\n \n}\n\n.ace-mono-industrial .ace_invalid.ace_deprecated {\n \n}\n\n.ace-mono-industrial .ace_support {\n \n}\n\n.ace-mono-industrial .ace_support.ace_function {\n color:#588E60;\n}\n\n.ace-mono-industrial .ace_function.ace_buildin {\n \n}\n\n.ace-mono-industrial .ace_string {\n \n}\n\n.ace-mono-industrial .ace_string.ace_regexp {\n \n}\n\n.ace-mono-industrial .ace_comment {\n color:#666C68;\nbackground-color:#151C19;\n}\n\n.ace-mono-industrial .ace_comment.ace_doc {\n \n}\n\n.ace-mono-industrial .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-mono-industrial .ace_variable {\n \n}\n\n.ace-mono-industrial .ace_variable.ace_language {\n color:#648BD2;\n}\n\n.ace-mono-industrial .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-mono-industrial"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-monokai.js b/studio/static/js/ace/theme-monokai.js
new file mode 100644
index 0000000..398195f
--- /dev/null
+++ b/studio/static/js/ace/theme-monokai.js
@@ -0,0 +1 @@
+define("ace/theme/monokai",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-monokai .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-monokai .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-monokai .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-monokai .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-monokai .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-monokai .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-monokai .ace_scroller {\n background-color: #272822;\n}\n\n.ace-monokai .ace_text-layer {\n cursor: text;\n color: #F8F8F2;\n}\n\n.ace-monokai .ace_cursor {\n border-left: 2px solid #F8F8F0;\n}\n\n.ace-monokai .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #F8F8F0;\n}\n \n.ace-monokai .ace_marker-layer .ace_selection {\n background: #49483E;\n}\n\n.ace-monokai .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-monokai .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #49483E;\n}\n\n.ace-monokai .ace_marker-layer .ace_active_line {\n background: #49483E;\n}\n\n \n.ace-monokai .ace_invisible {\n color: #49483E;\n}\n\n.ace-monokai .ace_keyword {\n color:#F92672;\n}\n\n.ace-monokai .ace_keyword.ace_operator {\n \n}\n\n.ace-monokai .ace_constant {\n \n}\n\n.ace-monokai .ace_constant.ace_language {\n color:#AE81FF;\n}\n\n.ace-monokai .ace_constant.ace_library {\n \n}\n\n.ace-monokai .ace_constant.ace_numeric {\n color:#AE81FF;\n}\n\n.ace-monokai .ace_invalid {\n color:#F8F8F0;\nbackground-color:#F92672;\n}\n\n.ace-monokai .ace_invalid.ace_illegal {\n \n}\n\n.ace-monokai .ace_invalid.ace_deprecated {\n color:#F8F8F0;\nbackground-color:#AE81FF;\n}\n\n.ace-monokai .ace_support {\n \n}\n\n.ace-monokai .ace_support.ace_function {\n color:#66D9EF;\n}\n\n.ace-monokai .ace_function.ace_buildin {\n \n}\n\n.ace-monokai .ace_string {\n color:#E6DB74;\n}\n\n.ace-monokai .ace_string.ace_regexp {\n \n}\n\n.ace-monokai .ace_comment {\n color:#75715E;\n}\n\n.ace-monokai .ace_comment.ace_doc {\n \n}\n\n.ace-monokai .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-monokai .ace_variable {\n \n}\n\n.ace-monokai .ace_variable.ace_language {\n \n}\n\n.ace-monokai .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-monokai"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-pastel_on_dark.js b/studio/static/js/ace/theme-pastel_on_dark.js
new file mode 100644
index 0000000..79cdb10
--- /dev/null
+++ b/studio/static/js/ace/theme-pastel_on_dark.js
@@ -0,0 +1 @@
+define("ace/theme/pastel_on_dark",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-pastel-on-dark .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-pastel-on-dark .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-pastel-on-dark .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-pastel-on-dark .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-pastel-on-dark .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-pastel-on-dark .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-pastel-on-dark .ace_scroller {\n background-color: #2c2828;\n}\n\n.ace-pastel-on-dark .ace_text-layer {\n cursor: text;\n color: #8f938f;\n}\n\n.ace-pastel-on-dark .ace_cursor {\n border-left: 2px solid #A7A7A7;\n}\n\n.ace-pastel-on-dark .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #A7A7A7;\n}\n \n.ace-pastel-on-dark .ace_marker-layer .ace_selection {\n background: rgba(221, 240, 255, 0.20);\n}\n\n.ace-pastel-on-dark .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-pastel-on-dark .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgba(255, 255, 255, 0.25);\n}\n\n.ace-pastel-on-dark .ace_marker-layer .ace_active_line {\n background: rgba(255, 255, 255, 0.031);\n}\n\n \n.ace-pastel-on-dark .ace_invisible {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.ace-pastel-on-dark .ace_keyword {\n color:#757ad8;\n}\n\n.ace-pastel-on-dark .ace_keyword.ace_operator {\n color:#797878;\n}\n\n.ace-pastel-on-dark .ace_constant {\n color:#4fb7c5;\n}\n\n.ace-pastel-on-dark .ace_constant.ace_language {\n \n}\n\n.ace-pastel-on-dark .ace_constant.ace_library {\n \n}\n\n.ace-pastel-on-dark .ace_constant.ace_numeric {\n \n}\n\n.ace-pastel-on-dark .ace_invalid {\n \n}\n\n.ace-pastel-on-dark .ace_invalid.ace_illegal {\n color:#F8F8F8;\nbackground-color:rgba(86, 45, 86, 0.75);\n}\n\n.ace-pastel-on-dark .ace_invalid.ace_deprecated {\n text-decoration:underline;\nfont-style:italic;\ncolor:#D2A8A1;\n}\n\n.ace-pastel-on-dark .ace_support {\n color:#9a9a9a;\n}\n\n.ace-pastel-on-dark .ace_support.ace_function {\n color:#aeb2f8;\n}\n\n.ace-pastel-on-dark .ace_function.ace_buildin {\n \n}\n\n.ace-pastel-on-dark .ace_string {\n color:#66a968;\n}\n\n.ace-pastel-on-dark .ace_string.ace_regexp {\n color:#E9C062;\n}\n\n.ace-pastel-on-dark .ace_comment {\n color:#656865;\n}\n\n.ace-pastel-on-dark .ace_comment.ace_doc {\n color:A6C6FF;\n}\n\n.ace-pastel-on-dark .ace_comment.ace_doc.ace_tag {\n color:A6C6FF;\n}\n\n.ace-pastel-on-dark .ace_variable {\n color:#bebf55;\n}\n\n.ace-pastel-on-dark .ace_variable.ace_language {\n color:#bebf55;\n}\n\n.ace-pastel-on-dark .ace_xml_pe {\n color:#494949;\n}";d.importCssString(e),b.cssClass="ace-pastel-on-dark"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-twilight.js b/studio/static/js/ace/theme-twilight.js
new file mode 100644
index 0000000..4072462
--- /dev/null
+++ b/studio/static/js/ace/theme-twilight.js
@@ -0,0 +1 @@
+define("ace/theme/twilight",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-twilight .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-twilight .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-twilight .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-twilight .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-twilight .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-twilight .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-twilight .ace_scroller {\n background-color: #141414;\n}\n\n.ace-twilight .ace_text-layer {\n cursor: text;\n color: #F8F8F8;\n}\n\n.ace-twilight .ace_cursor {\n border-left: 2px solid #A7A7A7;\n}\n\n.ace-twilight .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #A7A7A7;\n}\n \n.ace-twilight .ace_marker-layer .ace_selection {\n background: rgba(221, 240, 255, 0.20);\n}\n\n.ace-twilight .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-twilight .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgba(255, 255, 255, 0.25);\n}\n\n.ace-twilight .ace_marker-layer .ace_active_line {\n background: rgba(255, 255, 255, 0.031);\n}\n\n \n.ace-twilight .ace_invisible {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.ace-twilight .ace_keyword {\n color:#CDA869;\n}\n\n.ace-twilight .ace_keyword.ace_operator {\n \n}\n\n.ace-twilight .ace_constant {\n color:#CF6A4C;\n}\n\n.ace-twilight .ace_constant.ace_language {\n \n}\n\n.ace-twilight .ace_constant.ace_library {\n \n}\n\n.ace-twilight .ace_constant.ace_numeric {\n \n}\n\n.ace-twilight .ace_invalid {\n \n}\n\n.ace-twilight .ace_invalid.ace_illegal {\n color:#F8F8F8;\nbackground-color:rgba(86, 45, 86, 0.75);\n}\n\n.ace-twilight .ace_invalid.ace_deprecated {\n text-decoration:underline;\nfont-style:italic;\ncolor:#D2A8A1;\n}\n\n.ace-twilight .ace_support {\n color:#9B859D;\n}\n\n.ace-twilight .ace_support.ace_function {\n color:#DAD085;\n}\n\n.ace-twilight .ace_function.ace_buildin {\n \n}\n\n.ace-twilight .ace_string {\n color:#8F9D6A;\n}\n\n.ace-twilight .ace_string.ace_regexp {\n color:#E9C062;\n}\n\n.ace-twilight .ace_comment {\n font-style:italic;\ncolor:#5F5A60;\n}\n\n.ace-twilight .ace_comment.ace_doc {\n \n}\n\n.ace-twilight .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-twilight .ace_variable {\n color:#7587A6;\n}\n\n.ace-twilight .ace_variable.ace_language {\n \n}\n\n.ace-twilight .ace_xml_pe {\n color:#494949;\n}";d.importCssString(e),b.cssClass="ace-twilight"}) \ No newline at end of file
diff --git a/studio/static/js/ace/theme-vibrant_ink.js b/studio/static/js/ace/theme-vibrant_ink.js
new file mode 100644
index 0000000..ed6ae9e
--- /dev/null
+++ b/studio/static/js/ace/theme-vibrant_ink.js
@@ -0,0 +1 @@
+define("ace/theme/vibrant_ink",["require","exports","module"],function(a,b,c){var d=a("pilot/dom"),e=".ace-vibrant-ink .ace_editor {\n border: 2px solid rgb(159, 159, 159);\n}\n\n.ace-vibrant-ink .ace_editor.ace_focus {\n border: 2px solid #327fbd;\n}\n\n.ace-vibrant-ink .ace_gutter {\n width: 50px;\n background: #e8e8e8;\n color: #333;\n overflow : hidden;\n}\n\n.ace-vibrant-ink .ace_gutter-layer {\n width: 100%;\n text-align: right;\n}\n\n.ace-vibrant-ink .ace_gutter-layer .ace_gutter-cell {\n padding-right: 6px;\n}\n\n.ace-vibrant-ink .ace_print_margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-vibrant-ink .ace_scroller {\n background-color: #0F0F0F;\n}\n\n.ace-vibrant-ink .ace_text-layer {\n cursor: text;\n color: #FFFFFF;\n}\n\n.ace-vibrant-ink .ace_cursor {\n border-left: 2px solid #FFFFFF;\n}\n\n.ace-vibrant-ink .ace_cursor.ace_overwrite {\n border-left: 0px;\n border-bottom: 1px solid #FFFFFF;\n}\n \n.ace-vibrant-ink .ace_marker-layer .ace_selection {\n background: #6699CC;\n}\n\n.ace-vibrant-ink .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174);\n}\n\n.ace-vibrant-ink .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #99CC99;\n}\n\n.ace-vibrant-ink .ace_marker-layer .ace_active_line {\n background: #333333;\n}\n\n \n.ace-vibrant-ink .ace_invisible {\n color: #404040;\n}\n\n.ace-vibrant-ink .ace_keyword {\n color:#FF6600;\n}\n\n.ace-vibrant-ink .ace_keyword.ace_operator {\n \n}\n\n.ace-vibrant-ink .ace_constant {\n \n}\n\n.ace-vibrant-ink .ace_constant.ace_language {\n color:#339999;\n}\n\n.ace-vibrant-ink .ace_constant.ace_library {\n \n}\n\n.ace-vibrant-ink .ace_constant.ace_numeric {\n color:#99CC99;\n}\n\n.ace-vibrant-ink .ace_invalid {\n color:#CCFF33;\n background-color:#000000;\n}\n\n.ace-vibrant-ink .ace_invalid.ace_illegal {\n \n}\n\n.ace-vibrant-ink .ace_invalid.ace_deprecated {\n color:#CCFF33;\n background-color:#000000;\n}\n\n.ace-vibrant-ink .ace_support {\n \n}\n\n.ace-vibrant-ink .ace_support.ace_function {\n color:#FFCC00;\n}\n\n.ace-vibrant-ink .ace_function.ace_buildin {\n \n}\n\n.ace-vibrant-ink .ace_string {\n color:#66FF00;\n}\n\n.ace-vibrant-ink .ace_string.ace_regexp {\n \n}\n\n.ace-vibrant-ink .ace_comment {\n color:#9933CC;\n}\n\n.ace-vibrant-ink .ace_comment.ace_doc {\n \n}\n\n.ace-vibrant-ink .ace_comment.ace_doc.ace_tag {\n \n}\n\n.ace-vibrant-ink .ace_variable {\n \n}\n\n.ace-vibrant-ink .ace_variable.ace_language {\n \n}\n\n.ace-vibrant-ink .ace_xml_pe {\n \n}";d.importCssString(e),b.cssClass="ace-vibrant-ink"}) \ No newline at end of file
diff --git a/studio/static/js/ace/worker-coffee.js b/studio/static/js/ace/worker-coffee.js
new file mode 100644
index 0000000..1ffa65f
--- /dev/null
+++ b/studio/static/js/ace/worker-coffee.js
@@ -0,0 +1 @@
+function initSender(){var a=require("pilot/event_emitter").EventEmitter,b=require("pilot/oop"),c=function(){};(function(){b.implement(this,a),this.callback=function(a,b){postMessage({type:"call",id:b,data:a})},this.emit=function(a,b){postMessage({type:"event",name:a,data:b})}}).call(c.prototype);return new c}function initBaseUrls(a){require.tlns=a}var console={log:function(a){postMessage({type:"log",data:a})}},window={console:console},require=function(a){var b=require.modules[a];if(b){b.initialized||(b.exports=b.factory().exports,b.initialized=!0);return b.exports}var c=a.split("/");c[0]=require.tlns[c[0]]||c[0],path=c.join("/")+".js",require.id=a,importScripts(path);return require(a)};require.modules={},require.tlns={};var define=function(a,b,c){arguments.length==2?c=b:arguments.length==1&&(c=a,a=require.id);a.indexOf("text/")!==0&&(require.modules[a]={factory:function(){var a={exports:{}},b=c(require,a.exports,a);b&&(a.exports=b);return a}})},main,sender;onmessage=function(a){var b=a.data;if(b.command)main[b.command].apply(main,b.args);else if(b.init){initBaseUrls(b.tlns),require("pilot/fixoldbrowsers"),sender=initSender();var c=require(b.module)[b.classname];main=new c(sender)}else b.event&&sender&&sender._dispatchEvent(b.event,b.data)},define("pilot/fixoldbrowsers",["require","exports","module"],function(a,b,c){if(!Function.prototype.bind){var d=Array.prototype.slice;Function.prototype.bind=function(b){var c=this;if(typeof c.apply!="function"||typeof c.call!="function")return new TypeError;var e=d.call(arguments),f=function a(){if(this instanceof a){var b=Object.create(c.prototype);c.apply(b,e.concat(d.call(arguments)));return b}return c.call.apply(c,e.concat(d.call(arguments)))};f.length=typeof c=="function"?Math.max(c.length-e.length,0):0;return f}}var e=Function.prototype.call,f=Array.prototype,g=Object.prototype,h=e.bind(g.hasOwnProperty),i,j,k,l,m;if(m=h(g,"__defineGetter__"))i=e.bind(g.__defineGetter__),j=e.bind(g.__defineSetter__),k=e.bind(g.__lookupGetter__),l=e.bind(g.__lookupSetter__);Array.isArray||(Array.isArray=function(b){return Object.prototype.toString.call(b)==="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(b,c){var d=+this.length;for(var e=0;e<d;e++)e in this&&b.call(c,this[e],e,this)}),Array.prototype.map||(Array.prototype.map=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;var d=Array(c),e=arguments[1];for(var f=0;f<c;f++)f in this&&(d[f]=b.call(e,this[f],f,this));return d}),Array.prototype.filter||(Array.prototype.filter=function(b){var c=[],d=arguments[1];for(var e=0;e<this.length;e++)b.call(d,this[e])&&c.push(this[e]);return c}),Array.prototype.every||(Array.prototype.every=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(!b.call(c,this[d]))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(b.call(c,this[d]))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=0;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d++];break}if(++d>=c)throw new TypeError}while(!0);for(;d<c;d++)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=c-1;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d--];break}if(--d<0)throw new TypeError}while(!0);for(;d>=0;d--)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.indexOf||(Array.prototype.indexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||0;if(d>=c)return-1;d<0&&(d+=c);for(;d<c;d++){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||c;d<0&&(d+=c),d=Math.min(d,c-1);for(;d>=0;d--){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(b){return b.__proto__||b.constructor.prototype});if(!Object.getOwnPropertyDescriptor){var n="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(b,c){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(n+b);if(!h(b,c))return undefined;var d,e,f;d={enumerable:!0,configurable:!0};if(m){var i=b.__proto__;b.__proto__=g;var e=k(b,c),f=l(b,c);b.__proto__=i;if(e||f){e&&(descriptor.get=e),f&&(descriptor.set=f);return descriptor}}descriptor.value=b[c];return descriptor}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(b){return Object.keys(b)}),Object.create||(Object.create=function(b,c){var d;if(b===null)d={"__proto__":null};else{if(typeof b!="object")throw new TypeError("typeof prototype["+typeof b+"] != 'object'");var e=function(){};e.prototype=b,d=new e,d.__proto__=b}typeof c!="undefined"&&Object.defineProperties(d,c);return d});if(!Object.defineProperty){var o="Property description must be an object: ",p="Object.defineProperty called on non-object: ",q="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(b,c,d){if(typeof b!="object"&&typeof b!="function")throw new TypeError(p+b);if(typeof b!="object"||b===null)throw new TypeError(o+d);if(h(d,"value"))if(m&&(k(b,c)||l(b,c))){var e=b.__proto__;b.__proto__=g,delete b[c],b[c]=d.value,b.prototype}else b[c]=d.value;else{if(!m)throw new TypeError(q);h(d,"get")&&i(b,c,d.get),h(d,"set")&&j(b,c,d.set)}return b}}Object.defineProperties||(Object.defineProperties=function(b,c){for(var d in c)h(c,d)&&Object.defineProperty(b,d,c[d]);return b}),Object.seal||(Object.seal=function(b){return b}),Object.freeze||(Object.freeze=function(b){return b});try{Object.freeze(function(){})}catch(r){Object.freeze=function(b){return function(c){return typeof c=="function"?c:b(c)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(b){return b}),Object.isSealed||(Object.isSealed=function(b){return!1}),Object.isFrozen||(Object.isFrozen=function(b){return!1}),Object.isExtensible||(Object.isExtensible=function(b){return!0});if(!Object.keys){var s=!0,t=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],u=t.length;for(var v in{toString:null})s=!1;Object.keys=function a(b){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError("Object.keys called on a non-object");var a=[];for(var c in b)h(b,c)&&a.push(c);if(s)for(var d=0,e=u;d<e;d++){var f=t[d];h(b,f)&&a.push(f)}return a}}Date.prototype.toISOString||(Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+(this.getUTCMonth()+1)+"-"+this.getUTCDate()+"T"+this.getUTCHours()+":"+this.getUTCMinutes()+":"+this.getUTCSeconds()+"Z"}),Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(b){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),isNaN(Date.parse("T00:00"))&&(Date=function(a){var b=function(c,d,e,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j===1&&String(c)===c?new a(b.parse(c)):j>=7?new a(c,d,e,f,g,h,i):j>=6?new a(c,d,e,f,g,h):j>=5?new a(c,d,e,f,g):j>=4?new a(c,d,e,f):j>=3?new a(c,d,e):j>=2?new a(c,d):j>=1?new a(c):new a;k.constructor=b;return k}return a.apply(this,arguments)},c=new RegExp("^(?:((?:[+-]\\d\\d)?\\d\\d\\d\\d)(?:-(\\d\\d)(?:-(\\d\\d))?)?)?(?:T(\\d\\d):(\\d\\d)(?::(\\d\\d)(?:\\.(\\d\\d\\d))?)?)?(?:Z|([+-])(\\d\\d):(\\d\\d))?$");for(var d in a)b[d]=a[d];b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(d){var e=c.exec(d);if(e){e.shift();var f=e[0]===undefined;for(var g=0;g<10;g++){if(g===7)continue;e[g]=+(e[g]||(g<3?1:0)),g===1&&e[g]--}if(f)return((e[3]*60+e[4])*60+e[5])*1e3+e[6];var h=(e[8]*60+e[9])*60*1e3;e[6]==="-"&&(h=-h);return a.UTC.apply(this,e.slice(0,7))+h}return a.parse.apply(this,arguments)};return b}(Date));if(!String.prototype.trim){var w=/^\s\s*/,x=/\s\s*$/;String.prototype.trim=function(){return String(this).replace(w,"").replace(x,"")}}}),define("pilot/event_emitter",["require","exports","module"],function(a,b,c){var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c&&!!c.length){var b=b||{};b.type=a;for(var d=0;d<c.length;d++)c[d](b)}},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c){var d=c.indexOf(b);d!==-1&&c.splice(d,1)}},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define("pilot/oop",["require","exports","module"],function(a,b,c){b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define("ace/mode/coffee_worker",["require","exports","module","pilot/oop","ace/worker/mirror","ace/mode/coffee/coffee-script"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/worker/mirror").Mirror,f=a("ace/mode/coffee/coffee-script");window.addEventListener=function(){};var g=b.Worker=function(a){e.call(this,a),this.setTimeout(200)};d.inherits(g,e),function(){this.onUpdate=function(){var a=this.doc.getValue();try{f.parse(a)}catch(b){var c=b.message.match(/Parse error on line (\d+): (.*)/);if(c){this.sender.emit("error",{row:parseInt(c[1])-1,column:null,text:c[2],type:"error"});return}if(b instanceof SyntaxError){var c=b.message.match(/ on line (\d+)/);c&&this.sender.emit("error",{row:parseInt(c[1])-1,column:null,text:b.message.replace(c[0],""),type:"error"})}return}this.sender.emit("ok")}}.call(g.prototype)}),define("ace/worker/mirror",["require","exports","module","ace/document","pilot/lang"],function(a,b,c){var d=a("ace/document").Document,e=a("pilot/lang"),f=b.Mirror=function(a){this.sender=a;var b=this.doc=new d(""),c=this.deferredUpdate=e.deferredCall(this.onUpdate.bind(this)),f=this;a.on("change",function(a){b.applyDeltas([a.data]),c.schedule(f.$timeout)})};(function(){this.$timeout=500,this.setTimeout=function(a){this.$timeout=a},this.setValue=function(a){this.doc.setValue(a),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(a){this.sender.callback(this.doc.getValue(),a)},this.onUpdate=function(){}}).call(f.prototype)}),define("ace/document",["require","exports","module","pilot/oop","pilot/event_emitter","ace/range","ace/anchor"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=a("ace/range").Range,g=a("ace/anchor").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[""]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},"aaa".split(/a/).length==0?this.$split=function(a){return a.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(a){return a.split(/\r\n|\r|\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r?\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(a){this.$newLineMode!==a&&(this.$newLineMode=a)},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a=="\r\n"||a=="\r"||a=="\n"},this.getLine=function(a){return this.$lines[a]||""},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column));return b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length);return a},this.insert=function(a,b){if(b.length==0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];this._dispatchEvent("changeStart"),a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||"")),this._dispatchEvent("changeEnd");return a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c),this._dispatchEvent("changeStart");var d=new f(a,0,a+b.length,0),e={action:"insertLines",range:d,lines:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:"insertText",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:d}),this._dispatchEvent("changeEnd");return c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:"insertText",range:f.fromPoints(a,d),text:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;this._dispatchEvent("changeStart");if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);this._dispatchEvent("changeEnd");return a.start},this.removeInLine=function(a,b,c){if(b!=c){var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this._dispatchEvent("changeStart"),this.$lines.splice(a,1,h);var i={action:"removeText",range:d,text:g};this._dispatchEvent("change",{data:i}),this._dispatchEvent("changeEnd");return d.start}},this.removeLines=function(a,b){this._dispatchEvent("changeStart");var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:"removeLines",range:c,nl:this.getNewLineCharacter(),lines:d};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this._dispatchEvent("changeStart"),this.$lines.splice(a,2,e);var g={action:"removeText",range:d,text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:g}),this._dispatchEvent("changeEnd")},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this._dispatchEvent("changeStart"),this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;this._dispatchEvent("changeEnd");return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.insertLines(d.start.row,c.lines):c.action=="insertText"?this.insert(d.start,c.text):c.action=="removeLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="removeText"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="insertText"?this.remove(d):c.action=="removeLines"?this.insertLines(d.start.row,c.lines):c.action=="removeText"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define("ace/range",["require","exports","module"],function(a,b,c){var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(a,b){return this.compare(a,b)==0},this.compareRange=function(a){var b,c=a.end,d=a.start;b=this.compare(c.row,c.column);if(b==1){b=this.compare(d.row,d.column);return b==1?2:b==0?1:0}if(b==-1)return-2;b=this.compare(d.row,d.column);return b==-1?-1:b==1?42:0},this.containsRange=function(a){var b=this.compareRange(a);return b==-1||b==0||b==1},this.isEnd=function(a,b){return this.end.row==a&&this.end.column==b},this.isStart=function(a,b){return this.start.row==a&&this.start.column==b},this.setStart=function(a,b){typeof a=="object"?(this.start.column=a.column,this.start.row=a.row):(this.start.row=a,this.start.column=b)},this.setEnd=function(a,b){typeof a=="object"?(this.end.column=a.column,this.end.row=a.row):(this.end.row=a,this.end.column=b)},this.inside=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)||this.isStart(a,b)?!1:!0;return!1},this.insideStart=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)?!1:!0;return!1},this.insideEnd=function(a,b){if(this.compare(a,b)==0)return this.isStart(a,b)?!1:!0;return!1},this.compare=function(a,b){if(!this.isMultiLine()&&a===this.start.row)return b<this.start.column?-1:b>this.end.column?1:0;if(a<this.start.row)return-1;if(a>this.end.row)return 1;if(this.start.row===a)return b>=this.start.column?0:-1;if(this.end.row===a)return b<=this.end.column?0:1;return 0},this.compareStart=function(a,b){return this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.compareEnd=function(a,b){return this.end.row==a&&this.end.column==b?1:this.compare(a,b)},this.compareInside=function(a,b){return this.end.row==a&&this.end.column==b?1:this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define("ace/anchor",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c=="undefined"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on("change",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row!=c.end.row||c.start.row==this.row){if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action==="insertText"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action==="insertLines"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action=="removeText"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action=="removeLines"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)}},this.setPosition=function(a,b,c){c?pos={row:a,column:b}:pos=this.$clipPositionToDocument(a,b);if(this.row!=pos.row||this.column!=pos.column){var d={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._dispatchEvent("change",{old:d,value:pos})}},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0);return c}}).call(f.prototype)}),define("pilot/lang",["require","exports","module"],function(a,b,c){b.stringReverse=function(a){return a.split("").reverse().join("")},b.stringRepeat=function(a,b){return Array(b+1).join(a)};var d=/^\s\s*/,e=/\s\s*$/;b.stringTrimLeft=function(a){return a.replace(d,"")},b.stringTrimRight=function(a){return a.replace(e,"")},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.copyArray=function(a){var b=[];for(i=0,l=a.length;i<l;i++)a[i]&&typeof a[i]=="object"?b[i]=this.copyObject(a[i]):b[i]=a[i];return b},b.deepCopy=function(a){if(typeof a!="object")return a;var b=a.constructor();for(var c in a)typeof a[c]=="object"?b[c]=this.deepCopy(a[c]):b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){b||(b=setTimeout(c,a||0));return d};d.schedule=d,d.call=function(){this.cancel(),a();return d},d.cancel=function(){clearTimeout(b),b=null;return d};return d}}),define("ace/mode/coffee/coffee-script",["require","exports","module","ace/mode/coffee/lexer","ace/mode/coffee/parser","ace/mode/coffee/nodes"],function(a,b,c){var d=a("ace/mode/coffee/lexer").Lexer,e=a("ace/mode/coffee/parser"),f=new d;e.lexer={lex:function(){var a,b;b=this.tokens[this.pos++]||[""],a=b[0],this.yytext=b[1],this.yylineno=b[2];return a},setInput:function(a){this.tokens=a;return this.pos=0},upcomingInput:function(){return""}},e.yy=a("ace/mode/coffee/nodes"),b.parse=function(a){return e.parse(f.tokenize(a))}}),define("ace/mode/coffee/lexer",["require","exports","module","ace/mode/coffee/rewriter","ace/mode/coffee/helpers"],function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y=Array.prototype.indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]===a)return b;return-1};M=a("ace/mode/coffee/rewriter").Rewriter,X=a("ace/mode/coffee/helpers"),T=X.count,W=X.starts,S=X.compact,V=X.last,b.Lexer=A=function(){function a(){}a.prototype.tokenize=function(a,b){var c;b==null&&(b={}),R.test(a)&&(a="\n"+a),a=a.replace(/\r/g,"").replace(P,""),this.code=a,this.line=b.line||0,this.indent=0,this.indebt=0,this.outdebt=0,this.indents=[],this.tokens=[],c=0;while(this.chunk=a.slice(c))c+=this.identifierToken()||this.commentToken()||this.whitespaceToken()||this.lineToken()||this.heredocToken()||this.stringToken()||this.numberToken()||this.regexToken()||this.jsToken()||this.literalToken();this.closeIndentation();if(b.rewrite===!1)return this.tokens;return(new M).rewrite(this.tokens)},a.prototype.identifierToken=function(){var a,b,c,d,e,f,g,k,l;if(!(e=s.exec(this.chunk)))return 0;d=e[0],c=e[1],a=e[2];if(c==="own"&&this.tag()==="FOR"){this.token("OWN",c);return c.length}b=a||(f=V(this.tokens))&&((k=f[0])==="."||k==="?."||k==="::"||!f.spaced&&f[0]==="@"),g="IDENTIFIER";if(Y.call(w,c)>=0||!b&&Y.call(j,c)>=0)g=c.toUpperCase(),g==="WHEN"&&(l=this.tag(),Y.call(x,l)>=0)?g="LEADING_WHEN":g==="FOR"?this.seenFor=!0:g==="UNLESS"?g="IF":Y.call(Q,g)>=0?g="UNARY":Y.call(K,g)>=0&&(g!=="INSTANCEOF"&&this.seenFor?(g="FOR"+g,this.seenFor=!1):(g="RELATION",this.value()==="!"&&(this.tokens.pop(),c="!"+c)));Y.call(v,c)>=0&&(b?(g="IDENTIFIER",c=new String(c),c.reserved=!0):Y.call(L,c)>=0&&this.identifierError(c)),b||(Y.call(h,c)>=0&&(c=i[c]),g=function(){switch(c){case"!":return"UNARY";case"==":case"!=":return"COMPARE";case"&&":case"||":return"LOGIC";case"true":case"false":case"null":case"undefined":return"BOOL";case"break":case"continue":case"debugger":return"STATEMENT";default:return g}}()),this.token(g,c),a&&this.token(":",":");return d.length},a.prototype.numberToken=function(){var a,b;if(!(a=H.exec(this.chunk)))return 0;b=a[0],this.token("NUMBER",b);return b.length},a.prototype.stringToken=function(){var a,b;switch(this.chunk.charAt(0)){case"'":if(!(a=O.exec(this.chunk)))return 0;this.token("STRING",(b=a[0]).replace(C,"\\\n"));break;case'"':if(!(b=this.balancedString(this.chunk,'"')))return 0;0<b.indexOf("#{",1)?this.interpolateString(b.slice(1,-1)):this.token("STRING",this.escapeLines(b));break;default:return 0}this.line+=T(b,"\n");return b.length},a.prototype.heredocToken=function(){var a,b,c,d;if(!(c=n.exec(this.chunk)))return 0;b=c[0],d=b.charAt(0),a=this.sanitizeHeredoc(c[2],{quote:d,indent:null}),d==='"'&&0<=a.indexOf("#{")?this.interpolateString(a,{heredoc:!0}):this.token("STRING",this.makeString(a,d,!0)),this.line+=T(b,"\n");return b.length},a.prototype.commentToken=function(){var a,b,c;if(!(c=this.chunk.match(k)))return 0;a=c[0],b=c[1],b&&(this.token("HERECOMMENT",this.sanitizeHeredoc(b,{herecomment:!0,indent:Array(this.indent+1).join(" ")})),this.token("TERMINATOR","\n")),this.line+=T(a,"\n");return a.length},a.prototype.jsToken=function(){var a,b;if(this.chunk.charAt(0)!=="`"||!(a=u.exec(this.chunk)))return 0;this.token("JS",(b=a[0]).slice(1,-1));return b.length},a.prototype.regexToken=function(){var a,b,c,d;if(this.chunk.charAt(0)!=="/")return 0;if(a=q.exec(this.chunk)){this.line+=T(a[0],"\n");return this.heregexToken(a)}b=V(this.tokens);if(b&&(d=b[0],Y.call(b.spaced?E:F,d)>=0))return 0;if(!(a=J.exec(this.chunk)))return 0;c=a[0],this.token("REGEX",c==="//"?"/(?:)/":c);return c.length},a.prototype.heregexToken=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n;d=a[0],b=a[1],c=a[2];if(0>b.indexOf("#{")){e=b.replace(r,"").replace(/\//g,"\\/"),this.token("REGEX","/"+(e||"(?:)")+"/"+c);return d.length}this.token("IDENTIFIER","RegExp"),this.tokens.push(["CALL_START","("]),g=[],k=this.interpolateString(b,{regex:!0});for(i=0,j=k.length;i<j;i++){l=k[i],f=l[0],h=l[1];if(f==="TOKENS")g.push.apply(g,h);else{if(!(h=h.replace(r,"")))continue;h=h.replace(/\\/g,"\\\\"),g.push(["STRING",this.makeString(h,'"',!0)])}g.push(["+","+"])}g.pop(),((m=g[0])!=null?m[0]:void 0)!=="STRING"&&this.tokens.push(["STRING",'""'],["+","+"]),(n=this.tokens).push.apply(n,g),c&&this.tokens.push([",",","],["STRING",'"'+c+'"']),this.token(")",")");return d.length},a.prototype.lineToken=function(){var a,b,c,d,e,f;if(!(c=D.exec(this.chunk)))return 0;b=c[0],this.line+=T(b,"\n"),e=V(this.tokens,1),f=b.length-1-b.lastIndexOf("\n"),d=this.unfinished();if(f-this.indebt===this.indent){d?this.suppressNewlines():this.newlineToken();return b.length}if(f>this.indent){if(d){this.indebt=f-this.indent,this.suppressNewlines();return b.length}a=f-this.indent+this.outdebt,this.token("INDENT",a),this.indents.push(a),this.outdebt=this.indebt=0}else this.indebt=0,this.outdentToken(this.indent-f,d);this.indent=f;return b.length},a.prototype.outdentToken=function(a,b,c){var d,e;while(a>0)e=this.indents.length-1,this.indents[e]===void 0?a=0:this.indents[e]===this.outdebt?(a-=this.outdebt,this.outdebt=0):this.indents[e]<this.outdebt?(this.outdebt-=this.indents[e],a-=this.indents[e]):(d=this.indents.pop()-this.outdebt,a-=d,this.outdebt=0,this.token("OUTDENT",d));d&&(this.outdebt-=a),this.tag()!=="TERMINATOR"&&!b&&this.token("TERMINATOR","\n");return this},a.prototype.whitespaceToken=function(){var a,b,c;if(!(a=R.exec(this.chunk))&&!(b=this.chunk.charAt(0)==="\n"))return 0;c=V(this.tokens),c&&(c[a?"spaced":"newLine"]=!0);return a?a[0].length:0},a.prototype.newlineToken=function(){this.tag()!=="TERMINATOR"&&this.token("TERMINATOR","\n");return this},a.prototype.suppressNewlines=function(){this.value()==="\\"&&this.tokens.pop();return this},a.prototype.literalToken=function(){var a,b,c,d,e,h,i,j;(a=I.exec(this.chunk))?(d=a[0],g.test(d)&&this.tagParameters()):d=this.chunk.charAt(0),c=d,b=V(this.tokens);if(d==="="&&b){!b[1].reserved&&(e=b[1],Y.call(v,e)>=0)&&this.assignmentError();if((h=b[1])==="||"||h==="&&"){b[0]="COMPOUND_ASSIGN",b[1]+="=";return d.length}}if(d===";")c="TERMINATOR";else if(Y.call(B,d)>=0)c="MATH";else if(Y.call(l,d)>=0)c="COMPARE";else if(Y.call(m,d)>=0)c="COMPOUND_ASSIGN";else if(Y.call(Q,d)>=0)c="UNARY";else if(Y.call(N,d)>=0)c="SHIFT";else if(Y.call(z,d)>=0||d==="?"&&(b!=null?b.spaced:void 0))c="LOGIC";else if(b&&!b.spaced)if(d==="("&&(i=b[0],Y.call(f,i)>=0))b[0]==="?"&&(b[0]="FUNC_EXIST"),c="CALL_START";else if(d==="["&&(j=b[0],Y.call(t,j)>=0)){c="INDEX_START";switch(b[0]){case"?":b[0]="INDEX_SOAK";break;case"::":b[0]="INDEX_PROTO"}}this.token(c,d);return d.length},a.prototype.sanitizeHeredoc=function(a,b){var c,d,e,f,g;e=b.indent,d=b.herecomment;if(d){if(o.test(a))throw new Error('block comment cannot contain "*/", starting on line '+(this.line+1));if(a.indexOf("\n")<=0)return a}else while(f=p.exec(a)){c=f[1];if(e===null||0<(g=c.length)&&g<e.length)e=c}e&&(a=a.replace(RegExp("\\n"+e,"g"),"\n")),d||(a=a.replace(/^\n/,""));return a},a.prototype.tagParameters=function(){var a,b,c,d;if(this.tag()!==")")return this;b=[],d=this.tokens,a=d.length,d[--a][0]="PARAM_END";while(c=d[--a])switch(c[0]){case")":b.push(c);break;case"(":case"CALL_START":if(b.length)b.pop();else if(c[0]==="("){c[0]="PARAM_START";return this}}return this},a.prototype.closeIndentation=function(){return this.outdentToken(this.indent)},a.prototype.identifierError=function(a){throw SyntaxError('Reserved word "'+a+'" on line '+(this.line+1))},a.prototype.assignmentError=function(){throw SyntaxError('Reserved word "'+this.value()+'" on line '+(this.line+1)+" can't be assigned")},a.prototype.balancedString=function(a,b){var c,d,e,f,g;f=[b];for(c=1,g=a.length;1<=g?c<g:c>g;1<=g?c++:c--){switch(d=a.charAt(c)){case"\\":c++;continue;case b:f.pop();if(!f.length)return a.slice(0,c+1);b=f[f.length-1];continue}b!=="}"||d!=='"'&&d!=="'"?b==="}"&&d==="{"?f.push(b="}"):b==='"'&&e==="#"&&d==="{"&&f.push(b="}"):f.push(b=d),e=d}throw new Error("missing "+f.pop()+", starting on line "+(this.line+1))},a.prototype.interpolateString=function(b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t;c==null&&(c={}),e=c.heredoc,m=c.regex,o=[],l=0,f=-1;while(j=b.charAt(f+=1)){if(j==="\\"){f+=1;continue}if(j!=="#"||b.charAt(f+1)!=="{"||!(d=this.balancedString(b.slice(f+1),"}")))continue;l<f&&o.push(["NEOSTRING",b.slice(l,f)]),g=d.slice(1,-1);if(g.length){k=(new a).tokenize(g,{line:this.line,rewrite:!1}),k.pop(),((r=k[0])!=null?r[0]:void 0)==="TERMINATOR"&&k.shift();if(i=k.length)i>1&&(k.unshift(["(","("]),k.push([")",")"])),o.push(["TOKENS",k])}f+=d.length,l=f+1}f>l&&l<b.length&&o.push(["NEOSTRING",b.slice(l)]);if(m)return o;if(!o.length)return this.token("STRING",'""');o[0][0]!=="NEOSTRING"&&o.unshift(["",""]),(h=o.length>1)&&this.token("(","(");for(f=0,q=o.length;f<q;f++)s=o[f],n=s[0],p=s[1],f&&this.token("+","+"),n==="TOKENS"?(t=this.tokens).push.apply(t,p):this.token("STRING",this.makeString(p,'"',e));h&&this.token(")",")");return o},a.prototype.token=function(a,b){return this.tokens.push([a,b,this.line])},a.prototype.tag=function(a,b){var c;return(c=V(this.tokens,a))&&(b?c[0]=b:c[0])},a.prototype.value=function(a,b){var c;return(c=V(this.tokens,a))&&(b?c[1]=b:c[1])},a.prototype.unfinished=function(){var a,b;return y.test(this.chunk)||(a=V(this.tokens,1))&&a[0]!=="."&&(b=this.value())&&!b.reserved&&G.test(b)&&!g.test(b)&&!d.test(this.chunk)},a.prototype.escapeLines=function(a,b){return a.replace(C,b?"\\n":"")},a.prototype.makeString=function(a,b,c){if(!a)return b+b;a=a.replace(/\\([\s\S])/g,function(a,c){return c==="\n"||c===b?c:a}),a=a.replace(RegExp(""+b,"g"),"\\$&");return b+this.escapeLines(a,c)+b};return a}(),w=["true","false","null","this","new","delete","typeof","in","instanceof","return","throw","break","continue","debugger","if","else","switch","for","while","do","try","catch","finally","class","extends","super"],j=["undefined","then","unless","until","loop","of","by","when"],i={and:"&&",or:"||",is:"==",isnt:"!=",not:"!",yes:"true",no:"false",on:"true",off:"false"},h=function(){var a;a=[];for(U in i)a.push(U);return a}(),j=j.concat(h),L=["case","default","function","var","void","with","const","let","enum","export","import","native","__hasProp","__extends","__slice","__bind","__indexOf"],v=w.concat(L),b.RESERVED=L.concat(w).concat(j),s=/^([$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*)([^\n\S]*:(?!:))?/,H=/^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i,n=/^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/,I=/^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/,R=/^[^\n\S]+/,k=/^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/,g=/^[-=]>/,D=/^(?:\n[^\n\S]*)+/,O=/^'[^\\']*(?:\\.[^\\']*)*'/,u=/^`[^\\`]*(?:\\.[^\\`]*)*`/,J=/^\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/[imgy]{0,4}(?!\w)/,q=/^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/,r=/\s+(?:#.*)?/g,C=/\n/g,p=/\n+([^\n\S]*)/g,o=/\*\//,d=/^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/,y=/^\s*(?:,|\??\.(?![.\d])|::)/,P=/\s+$/,G=/^(?:[-+*&|\/%=<>!.\\][<>=&|]*|and|or|is(?:nt)?|n(?:ot|ew)|delete|typeof|instanceof)$/,m=["-=","+=","/=","*=","%=","||=","&&=","?=","<<=",">>=",">>>=","&=","^=","|="],Q=["!","~","NEW","TYPEOF","DELETE","DO"],z=["&&","||","&","|","^"],N=["<<",">>",">>>"],l=["==","!=","<",">","<=",">="],B=["*","/","%"],K=["IN","OF","INSTANCEOF"],e=["TRUE","FALSE","NULL","UNDEFINED"],E=["NUMBER","REGEX","BOOL","++","--","]"],F=E.concat(")","}","THIS","IDENTIFIER","STRING"),f=["IDENTIFIER","STRING","REGEX",")","]","}","?","::","@","THIS","SUPER"],t=f.concat("NUMBER","BOOL"),x=["INDENT","OUTDENT","TERMINATOR"]}),define("ace/mode/coffee/rewriter",["require","exports","module"],function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v=Array.prototype.indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]===a)return b;return-1},w=Array.prototype.slice;b.Rewriter=function(){function a(){}a.prototype.rewrite=function(a){this.tokens=a,this.removeLeadingNewlines(),this.removeMidExpressionNewlines(),this.closeOpenCalls(),this.closeOpenIndexes(),this.addImplicitIndentation(),this.tagPostfixConditionals(),this.addImplicitBraces(),this.addImplicitParentheses(),this.ensureBalance(d),this.rewriteClosingParens();return this.tokens},a.prototype.scanTokens=function(a){var b,c,d;d=this.tokens,b=0;while(c=d[b])b+=a.call(this,c,b,d);return!0},a.prototype.detectEnd=function(a,b,c){var d,e,h,i,j;h=this.tokens,d=0;while(e=h[a]){if(d===0&&b.call(this,e,a))return c.call(this,e,a);if(!e||d<0)return c.call(this,e,a-1);if(i=e[0],v.call(g,i)>=0)d+=1;else if(j=e[0],v.call(f,j)>=0)d-=1;a+=1}return a-1},a.prototype.removeLeadingNewlines=function(){var a,b,c,d;d=this.tokens;for(a=0,c=d.length;a<c;a++){b=d[a][0];if(b!=="TERMINATOR")break}if(a)return this.tokens.splice(0,a)},a.prototype.removeMidExpressionNewlines=function(){return this.scanTokens(function(a,b,c){var d;if(!(a[0]==="TERMINATOR"&&(d=this.tag(b+1),v.call(e,d)>=0)))return 1;c.splice(b,1);return 0})},a.prototype.closeOpenCalls=function(){var a,b;b=function(a,b){var c;return(c=a[0])===")"||c==="CALL_END"||a[0]==="OUTDENT"&&this.tag(b-1)===")"},a=function(a,b){return this.tokens[a[0]==="OUTDENT"?b-1:b][0]="CALL_END"};return this.scanTokens(function(c,d){c[0]==="CALL_START"&&this.detectEnd(d+1,b,a);return 1})},a.prototype.closeOpenIndexes=function(){var a,b;b=function(a,b){var c;return(c=a[0])==="]"||c==="INDEX_END"},a=function(a,b){return a[0]="INDEX_END"};return this.scanTokens(function(c,d){c[0]==="INDEX_START"&&this.detectEnd(d+1,b,a);return 1})},a.prototype.addImplicitBraces=function(){var a,b,c,d,e;c=[],d=null,e=0,b=function(a,b){var c,d,e,f,g,h;g=this.tokens.slice(b+1,b+3+1||9e9),c=g[0],f=g[1],e=g[2];if("HERECOMMENT"===(c!=null?c[0]:void 0))return!1;d=a[0];return(d==="TERMINATOR"||d==="OUTDENT")&&(f!=null?f[0]:void 0)!==":"&&((c!=null?c[0]:void 0)!=="@"||(e!=null?e[0]:void 0)!==":")||d===","&&c&&(h=c[0])!=="IDENTIFIER"&&h!=="NUMBER"&&h!=="STRING"&&h!=="@"&&h!=="TERMINATOR"&&h!=="OUTDENT"},a=function(a,b){var c;c=["}","}",a[2]],c.generated=!0;return this.tokens.splice(b,0,c)};return this.scanTokens(function(e,h,i){var j,k,l,m,n,o,p;if(o=l=e[0],v.call(g,o)>=0){c.push([l==="INDENT"&&this.tag(h-1)==="{"?"{":l,h]);return 1}if(v.call(f,l)>=0){d=c.pop();return 1}if(l!==":"||(j=this.tag(h-2))!==":"&&((p=c[c.length-1])!=null?p[0]:void 0)==="{")return 1;c.push(["{"]),k=j==="@"?h-2:h-1;while(this.tag(k-2)==="HERECOMMENT")k-=2;n=new String("{"),n.generated=!0,m=["{",n,e[2]],m.generated=!0,i.splice(k,0,m),this.detectEnd(h+2,b,a);return 2})},a.prototype.addImplicitParentheses=function(){var a,b;b=!1,a=function(a,b){var c;c=a[0]==="OUTDENT"?b+1:b;return this.tokens.splice(c,0,["CALL_END",")",a[2]])};return this.scanTokens(function(c,d,e){var f,g,m,o,p,q,r,s,t,u;r=c[0];if(r==="CLASS"||r==="IF")b=!0;s=e.slice(d-1,d+1+1||9e9),o=s[0],g=s[1],m=s[2],f=!b&&r==="INDENT"&&m&&m.generated&&m[0]==="{"&&o&&(t=o[0],v.call(k,t)>=0),q=!1,p=!1,v.call(n,r)>=0&&(b=!1),o&&!o.spaced&&r==="?"&&(c.call=!0);if(c.fromThen)return 1;if(!(f||(o!=null?o.spaced:void 0)&&(o.call||(u=o[0],v.call(k,u)>=0))&&(v.call(i,r)>=0||!c.spaced&&!c.newLine&&v.call(l,r)>=0)))return 1;e.splice(d,0,["CALL_START","(",c[2]]),this.detectEnd(d+1,function(a,b){var c,d;r=a[0];if(!q&&a.fromThen)return!0;if(r==="IF"||r==="ELSE"||r==="CATCH"||r==="->"||r==="=>")q=!0;if(r==="IF"||r==="ELSE"||r==="SWITCH"||r==="TRY")p=!0;if((r==="."||r==="?."||r==="::")&&this.tag(b-1)==="OUTDENT")return!0;return!a.generated&&this.tag(b-1)!==","&&(v.call(j,r)>=0||r==="INDENT"&&!p)&&(r!=="INDENT"||this.tag(b-2)!=="CLASS"&&(d=this.tag(b-1),v.call(h,d)<0)&&(!(c=this.tokens[b+1])||!c.generated||c[0]!=="{"))},a),o[0]==="?"&&(o[0]="FUNC_EXIST");return 2})},a.prototype.addImplicitIndentation=function(){return this.scanTokens(function(a,b,c){var d,e,f,g,h,i,j,k;i=a[0];if(i==="TERMINATOR"&&this.tag(b+1)==="THEN"){c.splice(b,1);return 0}if(i==="ELSE"&&this.tag(b-1)!=="OUTDENT"){c.splice.apply(c,[b,0].concat(w.call(this.indentation(a))));return 2}if(i==="CATCH"&&((j=this.tag(b+2))==="OUTDENT"||j==="TERMINATOR"||j==="FINALLY")){c.splice.apply(c,[b+2,0].concat(w.call(this.indentation(a))));return 4}if(v.call(p,i)>=0&&this.tag(b+1)!=="INDENT"&&(i!=="ELSE"||this.tag(b+1)!=="IF")){h=i,k=this.indentation(a),f=k[0],g=k[1],h==="THEN"&&(f.fromThen=!0),f.generated=g.generated=!0,c.splice(b+1,0,f),e=function(a,b){var c;return a[1]!==";"&&(c=a[0],v.call(o,c)>=0)&&(a[0]!=="ELSE"||h==="IF"||h==="THEN")},d=function(a,b){return this.tokens.splice(this.tag(b-1)===","?b-1:b,0,g)},this.detectEnd(b+2,e,d),i==="THEN"&&c.splice(b,1);return 1}return 1})},a.prototype.tagPostfixConditionals=function(){var a;a=function(a,b){var c;return(c=a[0])==="TERMINATOR"||c==="INDENT"};return this.scanTokens(function(b,c){var d;if(b[0]!=="IF")return 1;d=b,this.detectEnd(c+1,a,function(a,b){if(a[0]!=="INDENT")return d[0]="POST_"+d[0]});return 1})},a.prototype.ensureBalance=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n;d={},f={},m=this.tokens;for(i=0,k=m.length;i<k;i++){h=m[i],g=h[0];for(j=0,l=a.length;j<l;j++){n=a[j],e=n[0],b=n[1],d[e]|=0;if(g===e)d[e]++===0&&(f[e]=h[2]);else if(g===b&&--d[e]<0)throw Error("too many "+h[1]+" on line "+(h[2]+1))}}for(e in d){c=d[e];if(c>0)throw Error("unclosed "+e+" on line "+(f[e]+1))}return this},a.prototype.rewriteClosingParens=function(){var a,b,c;c=[],a={};for(b in m)a[b]=0;return this.scanTokens(function(b,d,e){var h,i,j,k,l,n,o;if(o=l=b[0],v.call(g,o)>=0){c.push(b);return 1}if(v.call(f,l)<0)return 1;if(a[h=m[l]]>0){a[h]-=1,e.splice(d,1);return 0}i=c.pop(),j=i[0],k=m[j];if(l===k)return 1;a[j]+=1,n=[k,j==="INDENT"?i[1]:k],this.tag(d+2)===j?(e.splice(d+3,0,n),c.push(i)):e.splice(d,0,n);return 1})},a.prototype.indentation=function(a){return[["INDENT",2,a[2]],["OUTDENT",2,a[2]]]},a.prototype.tag=function(a){var b;return(b=this.tokens[a])!=null?b[0]:void 0};return a}(),d=[["(",")"],["[","]"],["{","}"],["INDENT","OUTDENT"],["CALL_START","CALL_END"],["PARAM_START","PARAM_END"],["INDEX_START","INDEX_END"]],m={},g=[],f=[];for(s=0,t=d.length;s<t;s++)u=d[s],q=u[0],r=u[1],g.push(m[r]=q),f.push(m[q]=r);e=["CATCH","WHEN","ELSE","FINALLY"].concat(f),k=["IDENTIFIER","SUPER",")","CALL_END","]","INDEX_END","@","THIS"],i=["IDENTIFIER","NUMBER","STRING","JS","REGEX","NEW","PARAM_START","CLASS","IF","TRY","SWITCH","THIS","BOOL","UNARY","SUPER","@","->","=>","[","(","{","--","++"],l=["+","-"],h=["->","=>","{","[",","],j=["POST_IF","FOR","WHILE","UNTIL","WHEN","BY","LOOP","TERMINATOR"],p=["ELSE","->","=>","TRY","FINALLY","THEN"],o=["TERMINATOR","CATCH","FINALLY","ELSE","OUTDENT","LEADING_WHEN"],n=["TERMINATOR","INDENT","OUTDENT"]}),define("ace/mode/coffee/helpers",["require","exports","module"],function(a,b,c){var d,e;b.starts=function(a,b,c){return b===a.substr(c,b.length)},b.ends=function(a,b,c){var d;d=b.length;return b===a.substr(a.length-d-(c||0),d)},b.compact=function(a){var b,c,d,e;e=[];for(c=0,d=a.length;c<d;c++)b=a[c],b&&e.push(b);return e},b.count=function(a,b){var c,d;c=d=0;if(!b.length)return 1/0;while(d=1+a.indexOf(b,d))c++;return c},b.merge=function(a,b){return d(d({},a),b)},d=b.extend=function(a,b){var c,d;for(c in b)d=b[c],a[c]=d;return a},b.flatten=e=function(a){var b,c,d,f;c=[];for(d=0,f=a.length;d<f;d++)b=a[d],b instanceof Array?c=c.concat(e(b)):c.push(b);return c},b.del=function(a,b){var c;c=a[b],delete a[b];return c},b.last=function(a,b){return a[a.length-(b||0)-1]}}),define("ace/mode/coffee/parser",["require","exports","module"],function(a,b,c){var d={trace:function(){},yy:{},symbols_:{error:2,Root:3,Body:4,Block:5,TERMINATOR:6,Line:7,Expression:8,Statement:9,Return:10,Throw:11,Comment:12,STATEMENT:13,Value:14,Invocation:15,Code:16,Operation:17,Assign:18,If:19,Try:20,While:21,For:22,Switch:23,Class:24,INDENT:25,OUTDENT:26,Identifier:27,IDENTIFIER:28,AlphaNumeric:29,NUMBER:30,STRING:31,Literal:32,JS:33,REGEX:34,BOOL:35,Assignable:36,"=":37,AssignObj:38,ObjAssignable:39,":":40,ThisProperty:41,RETURN:42,HERECOMMENT:43,PARAM_START:44,ParamList:45,PARAM_END:46,FuncGlyph:47,"->":48,"=>":49,OptComma:50,",":51,Param:52,ParamVar:53,"...":54,Array:55,Object:56,Splat:57,SimpleAssignable:58,Accessor:59,Parenthetical:60,Range:61,This:62,".":63,"?.":64,"::":65,Index:66,INDEX_START:67,IndexValue:68,INDEX_END:69,INDEX_SOAK:70,INDEX_PROTO:71,Slice:72,"{":73,AssignList:74,"}":75,CLASS:76,EXTENDS:77,OptFuncExist:78,Arguments:79,SUPER:80,FUNC_EXIST:81,CALL_START:82,CALL_END:83,ArgList:84,THIS:85,"@":86,"[":87,"]":88,RangeDots:89,"..":90,Arg:91,SimpleArgs:92,TRY:93,Catch:94,FINALLY:95,CATCH:96,THROW:97,"(":98,")":99,WhileSource:100,WHILE:101,WHEN:102,UNTIL:103,Loop:104,LOOP:105,ForBody:106,FOR:107,ForStart:108,ForSource:109,ForVariables:110,OWN:111,ForValue:112,FORIN:113,FOROF:114,BY:115,SWITCH:116,Whens:117,ELSE:118,When:119,LEADING_WHEN:120,IfBlock:121,IF:122,POST_IF:123,UNARY:124,"-":125,"+":126,"--":127,"++":128,"?":129,MATH:130,SHIFT:131,COMPARE:132,LOGIC:133,RELATION:134,COMPOUND_ASSIGN:135,$accept:0,$end:1},terminals_:{2:"error",6:"TERMINATOR",13:"STATEMENT",25:"INDENT",26:"OUTDENT",28:"IDENTIFIER",30:"NUMBER",31:"STRING",33:"JS",34:"REGEX",35:"BOOL",37:"=",40:":",42:"RETURN",43:"HERECOMMENT",44:"PARAM_START",46:"PARAM_END",48:"->",49:"=>",51:",",54:"...",63:".",64:"?.",65:"::",67:"INDEX_START",69:"INDEX_END",70:"INDEX_SOAK",71:"INDEX_PROTO",73:"{",75:"}",76:"CLASS",77:"EXTENDS",80:"SUPER",81:"FUNC_EXIST",82:"CALL_START",83:"CALL_END",85:"THIS",86:"@",87:"[",88:"]",90:"..",93:"TRY",95:"FINALLY",96:"CATCH",97:"THROW",98:"(",99:")",101:"WHILE",102:"WHEN",103:"UNTIL",105:"LOOP",107:"FOR",111:"OWN",113:"FORIN",114:"FOROF",115:"BY",116:"SWITCH",118:"ELSE",120:"LEADING_WHEN",122:"IF",123:"POST_IF",124:"UNARY",125:"-",126:"+",127:"--",128:"++",129:"?",130:"MATH",131:"SHIFT",132:"COMPARE",133:"LOGIC",134:"RELATION",135:"COMPOUND_ASSIGN"},productions_:[0,[3,0],[3,1],[3,2],[4,1],[4,3],[4,2],[7,1],[7,1],[9,1],[9,1],[9,1],[9,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[5,2],[5,3],[27,1],[29,1],[29,1],[32,1],[32,1],[32,1],[32,1],[18,3],[18,5],[38,1],[38,3],[38,5],[38,1],[39,1],[39,1],[39,1],[10,2],[10,1],[12,1],[16,5],[16,2],[47,1],[47,1],[50,0],[50,1],[45,0],[45,1],[45,3],[52,1],[52,2],[52,3],[53,1],[53,1],[53,1],[53,1],[57,2],[58,1],[58,2],[58,2],[58,1],[36,1],[36,1],[36,1],[14,1],[14,1],[14,1],[14,1],[14,1],[59,2],[59,2],[59,2],[59,1],[59,1],[66,3],[66,2],[66,2],[68,1],[68,1],[56,4],[74,0],[74,1],[74,3],[74,4],[74,6],[24,1],[24,2],[24,3],[24,4],[24,2],[24,3],[24,4],[24,5],[15,3],[15,3],[15,1],[15,2],[78,0],[78,1],[79,2],[79,4],[62,1],[62,1],[41,2],[55,2],[55,4],[89,1],[89,1],[61,5],[72,3],[72,2],[72,2],[84,1],[84,3],[84,4],[84,4],[84,6],[91,1],[91,1],[92,1],[92,3],[20,2],[20,3],[20,4],[20,5],[94,3],[11,2],[60,3],[60,5],[100,2],[100,4],[100,2],[100,4],[21,2],[21,2],[21,2],[21,1],[104,2],[104,2],[22,2],[22,2],[22,2],[106,2],[106,2],[108,2],[108,3],[112,1],[112,1],[112,1],[110,1],[110,3],[109,2],[109,2],[109,4],[109,4],[109,4],[109,6],[109,6],[23,5],[23,7],[23,4],[23,6],[117,1],[117,2],[119,3],[119,4],[121,3],[121,5],[19,1],[19,3],[19,3],[19,3],[17,2],[17,2],[17,2],[17,2],[17,2],[17,2],[17,2],[17,2],[17,3],[17,3],[17,3],[17,3],[17,3],[17,3],[17,3],[17,3],[17,5],[17,3]],performAction:function(b,c,d,e,f,g,h){var i=g.length-1;switch(f){case 1:return this.$=new e.Block;case 2:return this.$=g[i];case 3:return this.$=g[i-1];case 4:this.$=e.Block.wrap([g[i]]);break;case 5:this.$=g[i-2].push(g[i]);break;case 6:this.$=g[i-1];break;case 7:this.$=g[i];break;case 8:this.$=g[i];break;case 9:this.$=g[i];break;case 10:this.$=g[i];break;case 11:this.$=g[i];break;case 12:this.$=new e.Literal(g[i]);break;case 13:this.$=g[i];break;case 14:this.$=g[i];break;case 15:this.$=g[i];break;case 16:this.$=g[i];break;case 17:this.$=g[i];break;case 18:this.$=g[i];break;case 19:this.$=g[i];break;case 20:this.$=g[i];break;case 21:this.$=g[i];break;case 22:this.$=g[i];break;case 23:this.$=g[i];break;case 24:this.$=new e.Block;break;case 25:this.$=g[i-1];break;case 26:this.$=new e.Literal(g[i]);break;case 27:this.$=new e.Literal(g[i]);break;case 28:this.$=new e.Literal(g[i]);break;case 29:this.$=g[i];break;case 30:this.$=new e.Literal(g[i]);break;case 31:this.$=new e.Literal(g[i]);break;case 32:this.$=function(){var a;a=new e.Literal(g[i]),g[i]==="undefined"&&(a.isUndefined=!0);return a}();break;case 33:this.$=new e.Assign(g[i-2],g[i]);break;case 34:this.$=new e.Assign(g[i-4],g[i-1]);break;case 35:this.$=new e.Value(g[i]);break;case 36:this.$=new e.Assign(new e.Value(g[i-2]),g[i],"object");break;case 37:this.$=new e.Assign(new e.Value(g[i-4]),g[i-1],"object");break;case 38:this.$=g[i];break;case 39:this.$=g[i];break;case 40:this.$=g[i];break;case 41:this.$=g[i];break;case 42:this.$=new e.Return(g[i]);break;case 43:this.$=new e.Return;break;case 44:this.$=new e.Comment(g[i]);break;case 45:this.$=new e.Code(g[i-3],g[i],g[i-1]);break;case 46:this.$=new e.Code([],g[i],g[i-1]);break;case 47:this.$="func";break;case 48:this.$="boundfunc";break;case 49:this.$=g[i];break;case 50:this.$=g[i];break;case 51:this.$=[];break;case 52:this.$=[g[i]];break;case 53:this.$=g[i-2].concat(g[i]);break;case 54:this.$=new e.Param(g[i]);break;case 55:this.$=new e.Param(g[i-1],null,!0);break;case 56:this.$=new e.Param(g[i-2],g[i]);break;case 57:this.$=g[i];break;case 58:this.$=g[i];break;case 59:this.$=g[i];break;case 60:this.$=g[i];break;case 61:this.$=new e.Splat(g[i-1]);break;case 62:this.$=new e.Value(g[i]);break;case 63:this.$=g[i-1].push(g[i]);break;case 64:this.$=new e.Value(g[i-1],[g[i]]);break;case 65:this.$=g[i];break;case 66:this.$=g[i];break;case 67:this.$=new e.Value(g[i]);break;case 68:this.$=new e.Value(g[i]);break;case 69:this.$=g[i];break;case 70:this.$=new e.Value(g[i]);break;case 71:this.$=new e.Value(g[i]);break;case 72:this.$=new e.Value(g[i]);break;case 73:this.$=g[i];break;case 74:this.$=new e.Access(g[i]);break;case 75:this.$=new e.Access(g[i],"soak");break;case 76:this.$=new e.Access(g[i],"proto");break;case 77:this.$=new e.Access(new e.Literal("prototype"));break;case 78:this.$=g[i];break;case 79:this.$=g[i-1];break;case 80:this.$=e.extend(g[i],{soak:!0});break;case 81:this.$=e.extend(g[i],{proto:!0});break;case 82:this.$=new e.Index(g[i]);break;case 83:this.$=new e.Slice(g[i]);break;case 84:this.$=new e.Obj(g[i-2],g[i-3].generated);break;case 85:this.$=[];break;case 86:this.$=[g[i]];break;case 87:this.$=g[i-2].concat(g[i]);break;case 88:this.$=g[i-3].concat(g[i]);break;case 89:this.$=g[i-5].concat(g[i-2]);break;case 90:this.$=new e.Class;break;case 91:this.$=new e.Class(null,null,g[i]);break;case 92:this.$=new e.Class(null,g[i]);break;case 93:this.$=new e.Class(null,g[i-1],g[i]);break;case 94:this.$=new e.Class(g[i]);break;case 95:this.$=new e.Class(g[i-1],null,g[i]);break;case 96:this.$=new e.Class(g[i-2],g[i]);break;case 97:this.$=new e.Class(g[i-3],g[i-1],g[i]);break;case 98:this.$=new e.Call(g[i-2],g[i],g[i-1]);break;case 99:this.$=new e.Call(g[i-2],g[i],g[i-1]);break;case 100:this.$=new e.Call("super",[new e.Splat(new e.Literal("arguments"))]);break;case 101:this.$=new e.Call("super",g[i]);break;case 102:this.$=!1;break;case 103:this.$=!0;break;case 104:this.$=[];break;case 105:this.$=g[i-2];break;case 106:this.$=new e.Value(new e.Literal("this"));break;case 107:this.$=new e.Value(new e.Literal("this"));break;case 108:this.$=new e.Value(new e.Literal("this"),[new e.Access(g[i])],"this");break;case 109:this.$=new e.Arr([]);break;case 110:this.$=new e.Arr(g[i-2]);break;case 111:this.$="inclusive";break;case 112:this.$="exclusive";break;case 113:this.$=new e.Range(g[i-3],g[i-1],g[i-2]);break;case 114:this.$=new e.Range(g[i-2],g[i],g[i-1]);break;case 115:this.$=new e.Range(g[i-1],null,g[i]);break;case 116:this.$=new e.Range(null,g[i],g[i-1]);break;case 117:this.$=[g[i]];break;case 118:this.$=g[i-2].concat(g[i]);break;case 119:this.$=g[i-3].concat(g[i]);break;case 120:this.$=g[i-2];break;case 121:this.$=g[i-5].concat(g[i-2]);break;case 122:this.$=g[i];break;case 123:this.$=g[i];break;case 124:this.$=g[i];break;case 125:this.$=[].concat(g[i-2],g[i]);break;case 126:this.$=new e.Try(g[i]);break;case 127:this.$=new e.Try(g[i-1],g[i][0],g[i][1]);break;case 128:this.$=new e.Try(g[i-2],null,null,g[i]);break;case 129:this.$=new e.Try(g[i-3],g[i-2][0],g[i-2][1],g[i]);break;case 130:this.$=[g[i-1],g[i]];break;case 131:this.$=new e.Throw(g[i]);break;case 132:this.$=new e.Parens(g[i-1]);break;case 133:this.$=new e.Parens(g[i-2]);break;case 134:this.$=new e.While(g[i]);break;case 135:this.$=new e.While(g[i-2],{guard:g[i]});break;case 136:this.$=new e.While(g[i],{invert:!0});break;case 137:this.$=new e.While(g[i-2],{invert:!0,guard:g[i]});break;case 138:this.$=g[i-1].addBody(g[i]);break;case 139:this.$=g[i].addBody(e.Block.wrap([g[i-1]]));break;case 140:this.$=g[i].addBody(e.Block.wrap([g[i-1]]));break;case 141:this.$=g[i];break;case 142:this.$=(new e.While(new e.Literal("true"))).addBody(g[i]);break;case 143:this.$=(new e.While(new e.Literal("true"))).addBody(e.Block.wrap([g[i]]));break;case 144:this.$=new e.For(g[i-1],g[i]);break;case 145:this.$=new e.For(g[i-1],g[i]);break;case 146:this.$=new e.For(g[i],g[i-1]);break;case 147:this.$={source:new e.Value(g[i])};break;case 148:this.$=function(){g[i].own=g[i-1].own,g[i].name=g[i-1][0],g[i].index=g[i-1][1];return g[i]}();break;case 149:this.$=g[i];break;case 150:this.$=function(){g[i].own=!0;return g[i]}();break;case 151:this.$=g[i];break;case 152:this.$=new e.Value(g[i]);break;case 153:this.$=new e.Value(g[i]);break;case 154:this.$=[g[i]];break;case 155:this.$=[g[i-2],g[i]];break;case 156:this.$={source:g[i]};break;case 157:this.$={source:g[i],object:!0};break;case 158:this.$={source:g[i-2],guard:g[i]};break;case 159:this.$={source:g[i-2],guard:g[i],object:!0};break;case 160:this.$={source:g[i-2],step:g[i]};break;case 161:this.$={source:g[i-4],guard:g[i-2],step:g[i]};break;case 162:this.$={source:g[i-4],step:g[i-2],guard:g[i]};break;case 163:this.$=new e.Switch(g[i-3],g[i-1]);break;case 164:this.$=new e.Switch(g[i-5],g[i-3],g[i-1]);break;case 165:this.$=new e.Switch(null,g[i-1]);break;case 166:this.$=new e.Switch(null,g[i-3],g[i-1]);break;case 167:this.$=g[i];break;case 168:this.$=g[i-1].concat(g[i]);break;case 169:this.$=[[g[i-1],g[i]]];break;case 170:this.$=[[g[i-2],g[i-1]]];break;case 171:this.$=new e.If(g[i-1],g[i],{type:g[i-2]});break;case 172:this.$=g[i-4].addElse(new e.If(g[i-1],g[i],{type:g[i-2]}));break;case 173:this.$=g[i];break;case 174:this.$=g[i-2].addElse(g[i]);break;case 175:this.$=new e.If(g[i],e.Block.wrap([g[i-2]]),{type:g[i-1],statement:!0});break;case 176:this.$=new e.If(g[i],e.Block.wrap([g[i-2]]),{type:g[i-1],statement:!0});break;case 177:this.$=new e.Op(g[i-1],g[i]);break;case 178:this.$=new e.Op("-",g[i]);break;case 179:this.$=new e.Op("+",g[i]);break;case 180:this.$=new e.Op("--",g[i]);break;case 181:this.$=new e.Op("++",g[i]);break;case 182:this.$=new e.Op("--",g[i-1],null,!0);break;case 183:this.$=new e.Op("++",g[i-1],null,!0);break;case 184:this.$=new e.Existence(g[i-1]);break;case 185:this.$=new e.Op("+",g[i-2],g[i]);break;case 186:this.$=new e.Op("-",g[i-2],g[i]);break;case 187:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 188:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 189:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 190:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 191:this.$=function(){return g[i-1].charAt(0)==="!"?(new e.Op(g[i-1].slice(1),g[i-2],g[i])).invert():new e.Op(g[i-1],g[i-2],g[i])}();break;case 192:this.$=new e.Assign(g[i-2],g[i],g[i-1]);break;case 193:this.$=new e.Assign(g[i-4],g[i-1],g[i-3]);break;case 194:this.$=new e.Extends(g[i-2],g[i])}},table:[{1:[2,1],3:1,4:2,5:3,7:4,8:6,9:7,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,5],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[3]},{1:[2,2],6:[1,71]},{6:[1,72]},{1:[2,4],6:[2,4],26:[2,4],99:[2,4]},{4:74,7:4,8:6,9:7,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,26:[1,73],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,7],6:[2,7],26:[2,7],99:[2,7],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,8],6:[2,8],26:[2,8],99:[2,8],100:87,101:[1,62],103:[1,63],106:88,107:[1,65],108:66,123:[1,86]},{1:[2,13],6:[2,13],25:[2,13],26:[2,13],46:[2,13],51:[2,13],54:[2,13],59:90,63:[1,92],64:[1,93],65:[1,94],66:95,67:[1,96],69:[2,13],70:[1,97],71:[1,98],75:[2,13],78:89,81:[1,91],82:[2,102],83:[2,13],88:[2,13],90:[2,13],99:[2,13],101:[2,13],102:[2,13],103:[2,13],107:[2,13],115:[2,13],123:[2,13],125:[2,13],126:[2,13],129:[2,13],130:[2,13],131:[2,13],132:[2,13],133:[2,13],134:[2,13]},{1:[2,14],6:[2,14],25:[2,14],26:[2,14],46:[2,14],51:[2,14],54:[2,14],59:100,63:[1,92],64:[1,93],65:[1,94],66:95,67:[1,96],69:[2,14],70:[1,97],71:[1,98],75:[2,14],78:99,81:[1,91],82:[2,102],83:[2,14],88:[2,14],90:[2,14],99:[2,14],101:[2,14],102:[2,14],103:[2,14],107:[2,14],115:[2,14],123:[2,14],125:[2,14],126:[2,14],129:[2,14],130:[2,14],131:[2,14],132:[2,14],133:[2,14],134:[2,14]},{1:[2,15],6:[2,15],25:[2,15],26:[2,15],46:[2,15],51:[2,15],54:[2,15],69:[2,15],75:[2,15],83:[2,15],88:[2,15],90:[2,15],99:[2,15],101:[2,15],102:[2,15],103:[2,15],107:[2,15],115:[2,15],123:[2,15],125:[2,15],126:[2,15],129:[2,15],130:[2,15],131:[2,15],132:[2,15],133:[2,15],134:[2,15]},{1:[2,16],6:[2,16],25:[2,16],26:[2,16],46:[2,16],51:[2,16],54:[2,16],69:[2,16],75:[2,16],83:[2,16],88:[2,16],90:[2,16],99:[2,16],101:[2,16],102:[2,16],103:[2,16],107:[2,16],115:[2,16],123:[2,16],125:[2,16],126:[2,16],129:[2,16],130:[2,16],131:[2,16],132:[2,16],133:[2,16],134:[2,16]},{1:[2,17],6:[2,17],25:[2,17],26:[2,17],46:[2,17],51:[2,17],54:[2,17],69:[2,17],75:[2,17],83:[2,17],88:[2,17],90:[2,17],99:[2,17],101:[2,17],102:[2,17],103:[2,17],107:[2,17],115:[2,17],123:[2,17],125:[2,17],126:[2,17],129:[2,17],130:[2,17],131:[2,17],132:[2,17],133:[2,17],134:[2,17]},{1:[2,18],6:[2,18],25:[2,18],26:[2,18],46:[2,18],51:[2,18],54:[2,18],69:[2,18],75:[2,18],83:[2,18],88:[2,18],90:[2,18],99:[2,18],101:[2,18],102:[2,18],103:[2,18],107:[2,18],115:[2,18],123:[2,18],125:[2,18],126:[2,18],129:[2,18],130:[2,18],131:[2,18],132:[2,18],133:[2,18],134:[2,18]},{1:[2,19],6:[2,19],25:[2,19],26:[2,19],46:[2,19],51:[2,19],54:[2,19],69:[2,19],75:[2,19],83:[2,19],88:[2,19],90:[2,19],99:[2,19],101:[2,19],102:[2,19],103:[2,19],107:[2,19],115:[2,19],123:[2,19],125:[2,19],126:[2,19],129:[2,19],130:[2,19],131:[2,19],132:[2,19],133:[2,19],134:[2,19]},{1:[2,20],6:[2,20],25:[2,20],26:[2,20],46:[2,20],51:[2,20],54:[2,20],69:[2,20],75:[2,20],83:[2,20],88:[2,20],90:[2,20],99:[2,20],101:[2,20],102:[2,20],103:[2,20],107:[2,20],115:[2,20],123:[2,20],125:[2,20],126:[2,20],129:[2,20],130:[2,20],131:[2,20],132:[2,20],133:[2,20],134:[2,20]},{1:[2,21],6:[2,21],25:[2,21],26:[2,21],46:[2,21],51:[2,21],54:[2,21],69:[2,21],75:[2,21],83:[2,21],88:[2,21],90:[2,21],99:[2,21],101:[2,21],102:[2,21],103:[2,21],107:[2,21],115:[2,21],123:[2,21],125:[2,21],126:[2,21],129:[2,21],130:[2,21],131:[2,21],132:[2,21],133:[2,21],134:[2,21]},{1:[2,22],6:[2,22],25:[2,22],26:[2,22],46:[2,22],51:[2,22],54:[2,22],69:[2,22],75:[2,22],83:[2,22],88:[2,22],90:[2,22],99:[2,22],101:[2,22],102:[2,22],103:[2,22],107:[2,22],115:[2,22],123:[2,22],125:[2,22],126:[2,22],129:[2,22],130:[2,22],131:[2,22],132:[2,22],133:[2,22],134:[2,22]},{1:[2,23],6:[2,23],25:[2,23],26:[2,23],46:[2,23],51:[2,23],54:[2,23],69:[2,23],75:[2,23],83:[2,23],88:[2,23],90:[2,23],99:[2,23],101:[2,23],102:[2,23],103:[2,23],107:[2,23],115:[2,23],123:[2,23],125:[2,23],126:[2,23],129:[2,23],130:[2,23],131:[2,23],132:[2,23],133:[2,23],134:[2,23]},{1:[2,9],6:[2,9],26:[2,9],99:[2,9],101:[2,9],103:[2,9],107:[2,9],123:[2,9]},{1:[2,10],6:[2,10],26:[2,10],99:[2,10],101:[2,10],103:[2,10],107:[2,10],123:[2,10]},{1:[2,11],6:[2,11],26:[2,11],99:[2,11],101:[2,11],103:[2,11],107:[2,11],123:[2,11]},{1:[2,12],6:[2,12],26:[2,12],99:[2,12],101:[2,12],103:[2,12],107:[2,12],123:[2,12]},{1:[2,69],6:[2,69],25:[2,69],26:[2,69],37:[1,101],46:[2,69],51:[2,69],54:[2,69],63:[2,69],64:[2,69],65:[2,69],67:[2,69],69:[2,69],70:[2,69],71:[2,69],75:[2,69],81:[2,69],82:[2,69],83:[2,69],88:[2,69],90:[2,69],99:[2,69],101:[2,69],102:[2,69],103:[2,69],107:[2,69],115:[2,69],123:[2,69],125:[2,69],126:[2,69],129:[2,69],130:[2,69],131:[2,69],132:[2,69],133:[2,69],134:[2,69]},{1:[2,70],6:[2,70],25:[2,70],26:[2,70],46:[2,70],51:[2,70],54:[2,70],63:[2,70],64:[2,70],65:[2,70],67:[2,70],69:[2,70],70:[2,70],71:[2,70],75:[2,70],81:[2,70],82:[2,70],83:[2,70],88:[2,70],90:[2,70],99:[2,70],101:[2,70],102:[2,70],103:[2,70],107:[2,70],115:[2,70],123:[2,70],125:[2,70],126:[2,70],129:[2,70],130:[2,70],131:[2,70],132:[2,70],133:[2,70],134:[2,70]},{1:[2,71],6:[2,71],25:[2,71],26:[2,71],46:[2,71],51:[2,71],54:[2,71],63:[2,71],64:[2,71],65:[2,71],67:[2,71],69:[2,71],70:[2,71],71:[2,71],75:[2,71],81:[2,71],82:[2,71],83:[2,71],88:[2,71],90:[2,71],99:[2,71],101:[2,71],102:[2,71],103:[2,71],107:[2,71],115:[2,71],123:[2,71],125:[2,71],126:[2,71],129:[2,71],130:[2,71],131:[2,71],132:[2,71],133:[2,71],134:[2,71]},{1:[2,72],6:[2,72],25:[2,72],26:[2,72],46:[2,72],51:[2,72],54:[2,72],63:[2,72],64:[2,72],65:[2,72],67:[2,72],69:[2,72],70:[2,72],71:[2,72],75:[2,72],81:[2,72],82:[2,72],83:[2,72],88:[2,72],90:[2,72],99:[2,72],101:[2,72],102:[2,72],103:[2,72],107:[2,72],115:[2,72],123:[2,72],125:[2,72],126:[2,72],129:[2,72],130:[2,72],131:[2,72],132:[2,72],133:[2,72],134:[2,72]},{1:[2,73],6:[2,73],25:[2,73],26:[2,73],46:[2,73],51:[2,73],54:[2,73],63:[2,73],64:[2,73],65:[2,73],67:[2,73],69:[2,73],70:[2,73],71:[2,73],75:[2,73],81:[2,73],82:[2,73],83:[2,73],88:[2,73],90:[2,73],99:[2,73],101:[2,73],102:[2,73],103:[2,73],107:[2,73],115:[2,73],123:[2,73],125:[2,73],126:[2,73],129:[2,73],130:[2,73],131:[2,73],132:[2,73],133:[2,73],134:[2,73]},{1:[2,100],6:[2,100],25:[2,100],26:[2,100],46:[2,100],51:[2,100],54:[2,100],63:[2,100],64:[2,100],65:[2,100],67:[2,100],69:[2,100],70:[2,100],71:[2,100],75:[2,100],79:102,81:[2,100],82:[1,103],83:[2,100],88:[2,100],90:[2,100],99:[2,100],101:[2,100],102:[2,100],103:[2,100],107:[2,100],115:[2,100],123:[2,100],125:[2,100],126:[2,100],129:[2,100],130:[2,100],131:[2,100],132:[2,100],133:[2,100],134:[2,100]},{27:107,28:[1,70],41:108,45:104,46:[2,51],51:[2,51],52:105,53:106,55:109,56:110,73:[1,67],86:[1,111],87:[1,112]},{5:113,25:[1,5]},{8:114,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:116,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:117,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{14:119,15:120,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:121,41:60,55:47,56:48,58:118,60:25,61:26,62:27,73:[1,67],80:[1,28],85:[1,55],86:[1,56],87:[1,54],98:[1,53]},{14:119,15:120,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:121,41:60,55:47,56:48,58:122,60:25,61:26,62:27,73:[1,67],80:[1,28],85:[1,55],86:[1,56],87:[1,54],98:[1,53]},{1:[2,66],6:[2,66],25:[2,66],26:[2,66],37:[2,66],46:[2,66],51:[2,66],54:[2,66],63:[2,66],64:[2,66],65:[2,66],67:[2,66],69:[2,66],70:[2,66],71:[2,66],75:[2,66],77:[1,126],81:[2,66],82:[2,66],83:[2,66],88:[2,66],90:[2,66],99:[2,66],101:[2,66],102:[2,66],103:[2,66],107:[2,66],115:[2,66],123:[2,66],125:[2,66],126:[2,66],127:[1,123],128:[1,124],129:[2,66],130:[2,66],131:[2,66],132:[2,66],133:[2,66],134:[2,66],135:[1,125]},{1:[2,173],6:[2,173],25:[2,173],26:[2,173],46:[2,173],51:[2,173],54:[2,173],69:[2,173],75:[2,173],83:[2,173],88:[2,173],90:[2,173],99:[2,173],101:[2,173],102:[2,173],103:[2,173],107:[2,173],115:[2,173],118:[1,127],123:[2,173],125:[2,173],126:[2,173],129:[2,173],130:[2,173],131:[2,173],132:[2,173],133:[2,173],134:[2,173]},{5:128,25:[1,5]},{5:129,25:[1,5]},{1:[2,141],6:[2,141],25:[2,141],26:[2,141],46:[2,141],51:[2,141],54:[2,141],69:[2,141],75:[2,141],83:[2,141],88:[2,141],90:[2,141],99:[2,141],101:[2,141],102:[2,141],103:[2,141],107:[2,141],115:[2,141],123:[2,141],125:[2,141],126:[2,141],129:[2,141],130:[2,141],131:[2,141],132:[2,141],133:[2,141],134:[2,141]},{5:130,25:[1,5]},{8:131,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,132],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,90],5:133,6:[2,90],14:119,15:120,25:[1,5],26:[2,90],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:121,41:60,46:[2,90],51:[2,90],54:[2,90],55:47,56:48,58:135,60:25,61:26,62:27,69:[2,90],73:[1,67],75:[2,90],77:[1,134],80:[1,28],83:[2,90],85:[1,55],86:[1,56],87:[1,54],88:[2,90],90:[2,90],98:[1,53],99:[2,90],101:[2,90],102:[2,90],103:[2,90],107:[2,90],115:[2,90],123:[2,90],125:[2,90],126:[2,90],129:[2,90],130:[2,90],131:[2,90],132:[2,90],133:[2,90],134:[2,90]},{1:[2,43],6:[2,43],8:136,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,26:[2,43],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],99:[2,43],100:39,101:[2,43],103:[2,43],104:40,105:[1,64],106:41,107:[2,43],108:66,116:[1,42],121:37,122:[1,61],123:[2,43],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:137,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,44],6:[2,44],25:[2,44],26:[2,44],51:[2,44],75:[2,44],99:[2,44],101:[2,44],103:[2,44],107:[2,44],123:[2,44]},{1:[2,67],6:[2,67],25:[2,67],26:[2,67],37:[2,67],46:[2,67],51:[2,67],54:[2,67],63:[2,67],64:[2,67],65:[2,67],67:[2,67],69:[2,67],70:[2,67],71:[2,67],75:[2,67],81:[2,67],82:[2,67],83:[2,67],88:[2,67],90:[2,67],99:[2,67],101:[2,67],102:[2,67],103:[2,67],107:[2,67],115:[2,67],123:[2,67],125:[2,67],126:[2,67],129:[2,67],130:[2,67],131:[2,67],132:[2,67],133:[2,67],134:[2,67]},{1:[2,68],6:[2,68],25:[2,68],26:[2,68],37:[2,68],46:[2,68],51:[2,68],54:[2,68],63:[2,68],64:[2,68],65:[2,68],67:[2,68],69:[2,68],70:[2,68],71:[2,68],75:[2,68],81:[2,68],82:[2,68],83:[2,68],88:[2,68],90:[2,68],99:[2,68],101:[2,68],102:[2,68],103:[2,68],107:[2,68],115:[2,68],123:[2,68],125:[2,68],126:[2,68],129:[2,68],130:[2,68],131:[2,68],132:[2,68],133:[2,68],134:[2,68]},{1:[2,29],6:[2,29],25:[2,29],26:[2,29],46:[2,29],51:[2,29],54:[2,29],63:[2,29],64:[2,29],65:[2,29],67:[2,29],69:[2,29],70:[2,29],71:[2,29],75:[2,29],81:[2,29],82:[2,29],83:[2,29],88:[2,29],90:[2,29],99:[2,29],101:[2,29],102:[2,29],103:[2,29],107:[2,29],115:[2,29],123:[2,29],125:[2,29],126:[2,29],129:[2,29],130:[2,29],131:[2,29],132:[2,29],133:[2,29],134:[2,29]},{1:[2,30],6:[2,30],25:[2,30],26:[2,30],46:[2,30],51:[2,30],54:[2,30],63:[2,30],64:[2,30],65:[2,30],67:[2,30],69:[2,30],70:[2,30],71:[2,30],75:[2,30],81:[2,30],82:[2,30],83:[2,30],88:[2,30],90:[2,30],99:[2,30],101:[2,30],102:[2,30],103:[2,30],107:[2,30],115:[2,30],123:[2,30],125:[2,30],126:[2,30],129:[2,30],130:[2,30],131:[2,30],132:[2,30],133:[2,30],134:[2,30]},{1:[2,31],6:[2,31],25:[2,31],26:[2,31],46:[2,31],51:[2,31],54:[2,31],63:[2,31],64:[2,31],65:[2,31],67:[2,31],69:[2,31],70:[2,31],71:[2,31],75:[2,31],81:[2,31],82:[2,31],83:[2,31],88:[2,31],90:[2,31],99:[2,31],101:[2,31],102:[2,31],103:[2,31],107:[2,31],115:[2,31],123:[2,31],125:[2,31],126:[2,31],129:[2,31],130:[2,31],131:[2,31],132:[2,31],133:[2,31],134:[2,31]},{1:[2,32],6:[2,32],25:[2,32],26:[2,32],46:[2,32],51:[2,32],54:[2,32],63:[2,32],64:[2,32],65:[2,32],67:[2,32],69:[2,32],70:[2,32],71:[2,32],75:[2,32],81:[2,32],82:[2,32],83:[2,32],88:[2,32],90:[2,32],99:[2,32],101:[2,32],102:[2,32],103:[2,32],107:[2,32],115:[2,32],123:[2,32],125:[2,32],126:[2,32],129:[2,32],130:[2,32],131:[2,32],132:[2,32],133:[2,32],134:[2,32]},{4:138,7:4,8:6,9:7,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,139],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:140,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,144],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,57:145,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],84:142,85:[1,55],86:[1,56],87:[1,54],88:[1,141],91:143,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,106],6:[2,106],25:[2,106],26:[2,106],46:[2,106],51:[2,106],54:[2,106],63:[2,106],64:[2,106],65:[2,106],67:[2,106],69:[2,106],70:[2,106],71:[2,106],75:[2,106],81:[2,106],82:[2,106],83:[2,106],88:[2,106],90:[2,106],99:[2,106],101:[2,106],102:[2,106],103:[2,106],107:[2,106],115:[2,106],123:[2,106],125:[2,106],126:[2,106],129:[2,106],130:[2,106],131:[2,106],132:[2,106],133:[2,106],134:[2,106]},{1:[2,107],6:[2,107],25:[2,107],26:[2,107],27:146,28:[1,70],46:[2,107],51:[2,107],54:[2,107],63:[2,107],64:[2,107],65:[2,107],67:[2,107],69:[2,107],70:[2,107],71:[2,107],75:[2,107],81:[2,107],82:[2,107],83:[2,107],88:[2,107],90:[2,107],99:[2,107],101:[2,107],102:[2,107],103:[2,107],107:[2,107],115:[2,107],123:[2,107],125:[2,107],126:[2,107],129:[2,107],130:[2,107],131:[2,107],132:[2,107],133:[2,107],134:[2,107]},{25:[2,47]},{25:[2,48]},{1:[2,62],6:[2,62],25:[2,62],26:[2,62],37:[2,62],46:[2,62],51:[2,62],54:[2,62],63:[2,62],64:[2,62],65:[2,62],67:[2,62],69:[2,62],70:[2,62],71:[2,62],75:[2,62],77:[2,62],81:[2,62],82:[2,62],83:[2,62],88:[2,62],90:[2,62],99:[2,62],101:[2,62],102:[2,62],103:[2,62],107:[2,62],115:[2,62],123:[2,62],125:[2,62],126:[2,62],127:[2,62],128:[2,62],129:[2,62],130:[2,62],131:[2,62],132:[2,62],133:[2,62],134:[2,62],135:[2,62]},{1:[2,65],6:[2,65],25:[2,65],26:[2,65],37:[2,65],46:[2,65],51:[2,65],54:[2,65],63:[2,65],64:[2,65],65:[2,65],67:[2,65],69:[2,65],70:[2,65],71:[2,65],75:[2,65],77:[2,65],81:[2,65],82:[2,65],83:[2,65],88:[2,65],90:[2,65],99:[2,65],101:[2,65],102:[2,65],103:[2,65],107:[2,65],115:[2,65],123:[2,65],125:[2,65],126:[2,65],127:[2,65],128:[2,65],129:[2,65],130:[2,65],131:[2,65],132:[2,65],133:[2,65],134:[2,65],135:[2,65]},{8:147,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:148,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:149,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{5:150,8:151,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,5],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{27:156,28:[1,70],55:157,56:158,61:152,73:[1,67],87:[1,54],110:153,111:[1,154],112:155},{109:159,113:[1,160],114:[1,161]},{6:[2,85],12:165,25:[2,85],27:166,28:[1,70],29:167,30:[1,68],31:[1,69],38:163,39:164,41:168,43:[1,46],51:[2,85],74:162,75:[2,85],86:[1,111]},{1:[2,27],6:[2,27],25:[2,27],26:[2,27],40:[2,27],46:[2,27],51:[2,27],54:[2,27],63:[2,27],64:[2,27],65:[2,27],67:[2,27],69:[2,27],70:[2,27],71:[2,27],75:[2,27],81:[2,27],82:[2,27],83:[2,27],88:[2,27],90:[2,27],99:[2,27],101:[2,27],102:[2,27],103:[2,27],107:[2,27],115:[2,27],123:[2,27],125:[2,27],126:[2,27],129:[2,27],130:[2,27],131:[2,27],132:[2,27],133:[2,27],134:[2,27]},{1:[2,28],6:[2,28],25:[2,28],26:[2,28],40:[2,28],46:[2,28],51:[2,28],54:[2,28],63:[2,28],64:[2,28],65:[2,28],67:[2,28],69:[2,28],70:[2,28],71:[2,28],75:[2,28],81:[2,28],82:[2,28],83:[2,28],88:[2,28],90:[2,28],99:[2,28],101:[2,28],102:[2,28],103:[2,28],107:[2,28],115:[2,28],123:[2,28],125:[2,28],126:[2,28],129:[2,28],130:[2,28],131:[2,28],132:[2,28],133:[2,28],134:[2,28]},{1:[2,26],6:[2,26],25:[2,26],26:[2,26],37:[2,26],40:[2,26],46:[2,26],51:[2,26],54:[2,26],63:[2,26],64:[2,26],65:[2,26],67:[2,26],69:[2,26],70:[2,26],71:[2,26],75:[2,26],77:[2,26],81:[2,26],82:[2,26],83:[2,26],88:[2,26],90:[2,26],99:[2,26],101:[2,26],102:[2,26],103:[2,26],107:[2,26],113:[2,26],114:[2,26],115:[2,26],123:[2,26],125:[2,26],126:[2,26],127:[2,26],128:[2,26],129:[2,26],130:[2,26],131:[2,26],132:[2,26],133:[2,26],134:[2,26],135:[2,26]},{1:[2,6],6:[2,6],7:169,8:6,9:7,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,26:[2,6],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],99:[2,6],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,3]},{1:[2,24],6:[2,24],25:[2,24],26:[2,24],46:[2,24],51:[2,24],54:[2,24],69:[2,24],75:[2,24],83:[2,24],88:[2,24],90:[2,24],95:[2,24],96:[2,24],99:[2,24],101:[2,24],102:[2,24],103:[2,24],107:[2,24],115:[2,24],118:[2,24],120:[2,24],123:[2,24],125:[2,24],126:[2,24],129:[2,24],130:[2,24],131:[2,24],132:[2,24],133:[2,24],134:[2,24]},{6:[1,71],26:[1,170]},{1:[2,184],6:[2,184],25:[2,184],26:[2,184],46:[2,184],51:[2,184],54:[2,184],69:[2,184],75:[2,184],83:[2,184],88:[2,184],90:[2,184],99:[2,184],101:[2,184],102:[2,184],103:[2,184],107:[2,184],115:[2,184],123:[2,184],125:[2,184],126:[2,184],129:[2,184],130:[2,184],131:[2,184],132:[2,184],133:[2,184],134:[2,184]},{8:171,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:172,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:173,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:174,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:175,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:176,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:177,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:178,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,140],6:[2,140],25:[2,140],26:[2,140],46:[2,140],51:[2,140],54:[2,140],69:[2,140],75:[2,140],83:[2,140],88:[2,140],90:[2,140],99:[2,140],101:[2,140],102:[2,140],103:[2,140],107:[2,140],115:[2,140],123:[2,140],125:[2,140],126:[2,140],129:[2,140],130:[2,140],131:[2,140],132:[2,140],133:[2,140],134:[2,140]},{1:[2,145],6:[2,145],25:[2,145],26:[2,145],46:[2,145],51:[2,145],54:[2,145],69:[2,145],75:[2,145],83:[2,145],88:[2,145],90:[2,145],99:[2,145],101:[2,145],102:[2,145],103:[2,145],107:[2,145],115:[2,145],123:[2,145],125:[2,145],126:[2,145],129:[2,145],130:[2,145],131:[2,145],132:[2,145],133:[2,145],134:[2,145]},{8:179,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,139],6:[2,139],25:[2,139],26:[2,139],46:[2,139],51:[2,139],54:[2,139],69:[2,139],75:[2,139],83:[2,139],88:[2,139],90:[2,139],99:[2,139],101:[2,139],102:[2,139],103:[2,139],107:[2,139],115:[2,139],123:[2,139],125:[2,139],126:[2,139],129:[2,139],130:[2,139],131:[2,139],132:[2,139],133:[2,139],134:[2,139]},{1:[2,144],6:[2,144],25:[2,144],26:[2,144],46:[2,144],51:[2,144],54:[2,144],69:[2,144],75:[2,144],83:[2,144],88:[2,144],90:[2,144],99:[2,144],101:[2,144],102:[2,144],103:[2,144],107:[2,144],115:[2,144],123:[2,144],125:[2,144],126:[2,144],129:[2,144],130:[2,144],131:[2,144],132:[2,144],133:[2,144],134:[2,144]},{79:180,82:[1,103]},{1:[2,63],6:[2,63],25:[2,63],26:[2,63],37:[2,63],46:[2,63],51:[2,63],54:[2,63],63:[2,63],64:[2,63],65:[2,63],67:[2,63],69:[2,63],70:[2,63],71:[2,63],75:[2,63],77:[2,63],81:[2,63],82:[2,63],83:[2,63],88:[2,63],90:[2,63],99:[2,63],101:[2,63],102:[2,63],103:[2,63],107:[2,63],115:[2,63],123:[2,63],125:[2,63],126:[2,63],127:[2,63],128:[2,63],129:[2,63],130:[2,63],131:[2,63],132:[2,63],133:[2,63],134:[2,63],135:[2,63]},{82:[2,103]},{27:181,28:[1,70]},{27:182,28:[1,70]},{1:[2,77],6:[2,77],25:[2,77],26:[2,77],27:183,28:[1,70],37:[2,77],46:[2,77],51:[2,77],54:[2,77],63:[2,77],64:[2,77],65:[2,77],67:[2,77],69:[2,77],70:[2,77],71:[2,77],75:[2,77],77:[2,77],81:[2,77],82:[2,77],83:[2,77],88:[2,77],90:[2,77],99:[2,77],101:[2,77],102:[2,77],103:[2,77],107:[2,77],115:[2,77],123:[2,77],125:[2,77],126:[2,77],127:[2,77],128:[2,77],129:[2,77],130:[2,77],131:[2,77],132:[2,77],133:[2,77],134:[2,77],135:[2,77]},{1:[2,78],6:[2,78],25:[2,78],26:[2,78],37:[2,78],46:[2,78],51:[2,78],54:[2,78],63:[2,78],64:[2,78],65:[2,78],67:[2,78],69:[2,78],70:[2,78],71:[2,78],75:[2,78],77:[2,78],81:[2,78],82:[2,78],83:[2,78],88:[2,78],90:[2,78],99:[2,78],101:[2,78],102:[2,78],103:[2,78],107:[2,78],115:[2,78],123:[2,78],125:[2,78],126:[2,78],127:[2,78],128:[2,78],129:[2,78],130:[2,78],131:[2,78],132:[2,78],133:[2,78],134:[2,78],135:[2,78]},{8:185,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],54:[1,189],55:47,56:48,58:36,60:25,61:26,62:27,68:184,72:186,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],89:187,90:[1,188],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{66:190,67:[1,96],70:[1,97],71:[1,98]},{66:191,67:[1,96],70:[1,97],71:[1,98]},{79:192,82:[1,103]},{1:[2,64],6:[2,64],25:[2,64],26:[2,64],37:[2,64],46:[2,64],51:[2,64],54:[2,64],63:[2,64],64:[2,64],65:[2,64],67:[2,64],69:[2,64],70:[2,64],71:[2,64],75:[2,64],77:[2,64],81:[2,64],82:[2,64],83:[2,64],88:[2,64],90:[2,64],99:[2,64],101:[2,64],102:[2,64],103:[2,64],107:[2,64],115:[2,64],123:[2,64],125:[2,64],126:[2,64],127:[2,64],128:[2,64],129:[2,64],130:[2,64],131:[2,64],132:[2,64],133:[2,64],134:[2,64],135:[2,64]},{8:193,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,194],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,101],6:[2,101],25:[2,101],26:[2,101],46:[2,101],51:[2,101],54:[2,101],63:[2,101],64:[2,101],65:[2,101],67:[2,101],69:[2,101],70:[2,101],71:[2,101],75:[2,101],81:[2,101],82:[2,101],83:[2,101],88:[2,101],90:[2,101],99:[2,101],101:[2,101],102:[2,101],103:[2,101],107:[2,101],115:[2,101],123:[2,101],125:[2,101],126:[2,101],129:[2,101],130:[2,101],131:[2,101],132:[2,101],133:[2,101],134:[2,101]},{8:197,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,144],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,57:145,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],83:[1,195],84:196,85:[1,55],86:[1,56],87:[1,54],91:143,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{46:[1,198],51:[1,199]},{46:[2,52],51:[2,52]},{37:[1,201],46:[2,54],51:[2,54],54:[1,200]},{37:[2,57],46:[2,57],51:[2,57],54:[2,57]},{37:[2,58],46:[2,58],51:[2,58],54:[2,58]},{37:[2,59],46:[2,59],51:[2,59],54:[2,59]},{37:[2,60],46:[2,60],51:[2,60],54:[2,60]},{27:146,28:[1,70]},{8:197,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,144],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,57:145,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],84:142,85:[1,55],86:[1,56],87:[1,54],88:[1,141],91:143,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,46],6:[2,46],25:[2,46],26:[2,46],46:[2,46],51:[2,46],54:[2,46],69:[2,46],75:[2,46],83:[2,46],88:[2,46],90:[2,46],99:[2,46],101:[2,46],102:[2,46],103:[2,46],107:[2,46],115:[2,46],123:[2,46],125:[2,46],126:[2,46],129:[2,46],130:[2,46],131:[2,46],132:[2,46],133:[2,46],134:[2,46]},{1:[2,177],6:[2,177],25:[2,177],26:[2,177],46:[2,177],51:[2,177],54:[2,177],69:[2,177],75:[2,177],83:[2,177],88:[2,177],90:[2,177],99:[2,177],100:84,101:[2,177],102:[2,177],103:[2,177],106:85,107:[2,177],108:66,115:[2,177],123:[2,177],125:[2,177],126:[2,177],129:[1,75],130:[2,177],131:[2,177],132:[2,177],133:[2,177],134:[2,177]},{100:87,101:[1,62],103:[1,63],106:88,107:[1,65],108:66,123:[1,86]},{1:[2,178],6:[2,178],25:[2,178],26:[2,178],46:[2,178],51:[2,178],54:[2,178],69:[2,178],75:[2,178],83:[2,178],88:[2,178],90:[2,178],99:[2,178],100:84,101:[2,178],102:[2,178],103:[2,178],106:85,107:[2,178],108:66,115:[2,178],123:[2,178],125:[2,178],126:[2,178],129:[1,75],130:[2,178],131:[2,178],132:[2,178],133:[2,178],134:[2,178]},{1:[2,179],6:[2,179],25:[2,179],26:[2,179],46:[2,179],51:[2,179],54:[2,179],69:[2,179],75:[2,179],83:[2,179],88:[2,179],90:[2,179],99:[2,179],100:84,101:[2,179],102:[2,179],103:[2,179],106:85,107:[2,179],108:66,115:[2,179],123:[2,179],125:[2,179],126:[2,179],129:[1,75],130:[2,179],131:[2,179],132:[2,179],133:[2,179],134:[2,179]},{1:[2,180],6:[2,180],25:[2,180],26:[2,180],46:[2,180],51:[2,180],54:[2,180],63:[2,66],64:[2,66],65:[2,66],67:[2,66],69:[2,180],70:[2,66],71:[2,66],75:[2,180],81:[2,66],82:[2,66],83:[2,180],88:[2,180],90:[2,180],99:[2,180],101:[2,180],102:[2,180],103:[2,180],107:[2,180],115:[2,180],123:[2,180],125:[2,180],126:[2,180],129:[2,180],130:[2,180],131:[2,180],132:[2,180],133:[2,180],134:[2,180]},{59:90,63:[1,92],64:[1,93],65:[1,94],66:95,67:[1,96],70:[1,97],71:[1,98],78:89,81:[1,91],82:[2,102]},{59:100,63:[1,92],64:[1,93],65:[1,94],66:95,67:[1,96],70:[1,97],71:[1,98],78:99,81:[1,91],82:[2,102]},{1:[2,69],6:[2,69],25:[2,69],26:[2,69],46:[2,69],51:[2,69],54:[2,69],63:[2,69],64:[2,69],65:[2,69],67:[2,69],69:[2,69],70:[2,69],71:[2,69],75:[2,69],81:[2,69],82:[2,69],83:[2,69],88:[2,69],90:[2,69],99:[2,69],101:[2,69],102:[2,69],103:[2,69],107:[2,69],115:[2,69],123:[2,69],125:[2,69],126:[2,69],129:[2,69],130:[2,69],131:[2,69],132:[2,69],133:[2,69],134:[2,69]},{1:[2,181],6:[2,181],25:[2,181],26:[2,181],46:[2,181],51:[2,181],54:[2,181],63:[2,66],64:[2,66],65:[2,66],67:[2,66],69:[2,181],70:[2,66],71:[2,66],75:[2,181],81:[2,66],82:[2,66],83:[2,181],88:[2,181],90:[2,181],99:[2,181],101:[2,181],102:[2,181],103:[2,181],107:[2,181],115:[2,181],123:[2,181],125:[2,181],126:[2,181],129:[2,181],130:[2,181],131:[2,181],132:[2,181],133:[2,181],134:[2,181]},{1:[2,182],6:[2,182],25:[2,182],26:[2,182],46:[2,182],51:[2,182],54:[2,182],69:[2,182],75:[2,182],83:[2,182],88:[2,182],90:[2,182],99:[2,182],101:[2,182],102:[2,182],103:[2,182],107:[2,182],115:[2,182],123:[2,182],125:[2,182],126:[2,182],129:[2,182],130:[2,182],131:[2,182],132:[2,182],133:[2,182],134:[2,182]},{1:[2,183],6:[2,183],25:[2,183],26:[2,183],46:[2,183],51:[2,183],54:[2,183],69:[2,183],75:[2,183],83:[2,183],88:[2,183],90:[2,183],99:[2,183],101:[2,183],102:[2,183],103:[2,183],107:[2,183],115:[2,183],123:[2,183],125:[2,183],126:[2,183],129:[2,183],130:[2,183],131:[2,183],132:[2,183],133:[2,183],134:[2,183]},{8:202,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,203],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:204,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{5:205,25:[1,5],122:[1,206]},{1:[2,126],6:[2,126],25:[2,126],26:[2,126],46:[2,126],51:[2,126],54:[2,126],69:[2,126],75:[2,126],83:[2,126],88:[2,126],90:[2,126],94:207,95:[1,208],96:[1,209],99:[2,126],101:[2,126],102:[2,126],103:[2,126],107:[2,126],115:[2,126],123:[2,126],125:[2,126],126:[2,126],129:[2,126],130:[2,126],131:[2,126],132:[2,126],133:[2,126],134:[2,126]},{1:[2,138],6:[2,138],25:[2,138],26:[2,138],46:[2,138],51:[2,138],54:[2,138],69:[2,138],75:[2,138],83:[2,138],88:[2,138],90:[2,138],99:[2,138],101:[2,138],102:[2,138],103:[2,138],107:[2,138],115:[2,138],123:[2,138],125:[2,138],126:[2,138],129:[2,138],130:[2,138],131:[2,138],132:[2,138],133:[2,138],134:[2,138]},{1:[2,146],6:[2,146],25:[2,146],26:[2,146],46:[2,146],51:[2,146],54:[2,146],69:[2,146],75:[2,146],83:[2,146],88:[2,146],90:[2,146],99:[2,146],101:[2,146],102:[2,146],103:[2,146],107:[2,146],115:[2,146],123:[2,146],125:[2,146],126:[2,146],129:[2,146],130:[2,146],131:[2,146],132:[2,146],133:[2,146],134:[2,146]},{25:[1,210],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{117:211,119:212,120:[1,213]},{1:[2,91],6:[2,91],25:[2,91],26:[2,91],46:[2,91],51:[2,91],54:[2,91],69:[2,91],75:[2,91],83:[2,91],88:[2,91],90:[2,91],99:[2,91],101:[2,91],102:[2,91],103:[2,91],107:[2,91],115:[2,91],123:[2,91],125:[2,91],126:[2,91],129:[2,91],130:[2,91],131:[2,91],132:[2,91],133:[2,91],134:[2,91]},{14:214,15:120,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:121,41:60,55:47,56:48,58:215,60:25,61:26,62:27,73:[1,67],80:[1,28],85:[1,55],86:[1,56],87:[1,54],98:[1,53]},{1:[2,94],5:216,6:[2,94],25:[1,5],26:[2,94],46:[2,94],51:[2,94],54:[2,94],63:[2,66],64:[2,66],65:[2,66],67:[2,66],69:[2,94],70:[2,66],71:[2,66],75:[2,94],77:[1,217],81:[2,66],82:[2,66],83:[2,94],88:[2,94],90:[2,94],99:[2,94],101:[2,94],102:[2,94],103:[2,94],107:[2,94],115:[2,94],123:[2,94],125:[2,94],126:[2,94],129:[2,94],130:[2,94],131:[2,94],132:[2,94],133:[2,94],134:[2,94]},{1:[2,42],6:[2,42],26:[2,42],99:[2,42],100:84,101:[2,42],103:[2,42],106:85,107:[2,42],108:66,123:[2,42],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,131],6:[2,131],26:[2,131],99:[2,131],100:84,101:[2,131],103:[2,131],106:85,107:[2,131],108:66,123:[2,131],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{6:[1,71],99:[1,218]},{4:219,7:4,8:6,9:7,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{6:[2,122],25:[2,122],51:[2,122],54:[1,221],88:[2,122],89:220,90:[1,188],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,109],6:[2,109],25:[2,109],26:[2,109],37:[2,109],46:[2,109],51:[2,109],54:[2,109],63:[2,109],64:[2,109],65:[2,109],67:[2,109],69:[2,109],70:[2,109],71:[2,109],75:[2,109],81:[2,109],82:[2,109],83:[2,109],88:[2,109],90:[2,109],99:[2,109],101:[2,109],102:[2,109],103:[2,109],107:[2,109],113:[2,109],114:[2,109],115:[2,109],123:[2,109],125:[2,109],126:[2,109],129:[2,109],130:[2,109],131:[2,109],132:[2,109],133:[2,109],134:[2,109]},{6:[2,49],25:[2,49],50:222,51:[1,223],88:[2,49]},{6:[2,117],25:[2,117],26:[2,117],51:[2,117],83:[2,117],88:[2,117]},{8:197,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,144],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,57:145,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],84:224,85:[1,55],86:[1,56],87:[1,54],91:143,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{6:[2,123],25:[2,123],26:[2,123],51:[2,123],83:[2,123],88:[2,123]},{1:[2,108],6:[2,108],25:[2,108],26:[2,108],37:[2,108],40:[2,108],46:[2,108],51:[2,108],54:[2,108],63:[2,108],64:[2,108],65:[2,108],67:[2,108],69:[2,108],70:[2,108],71:[2,108],75:[2,108],77:[2,108],81:[2,108],82:[2,108],83:[2,108],88:[2,108],90:[2,108],99:[2,108],101:[2,108],102:[2,108],103:[2,108],107:[2,108],115:[2,108],123:[2,108],125:[2,108],126:[2,108],127:[2,108],128:[2,108],129:[2,108],130:[2,108],131:[2,108],132:[2,108],133:[2,108],134:[2,108],135:[2,108]},{5:225,25:[1,5],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,134],6:[2,134],25:[2,134],26:[2,134],46:[2,134],51:[2,134],54:[2,134],69:[2,134],75:[2,134],83:[2,134],88:[2,134],90:[2,134],99:[2,134],100:84,101:[1,62],102:[1,226],103:[1,63],106:85,107:[1,65],108:66,115:[2,134],123:[2,134],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,136],6:[2,136],25:[2,136],26:[2,136],46:[2,136],51:[2,136],54:[2,136],69:[2,136],75:[2,136],83:[2,136],88:[2,136],90:[2,136],99:[2,136],100:84,101:[1,62],102:[1,227],103:[1,63],106:85,107:[1,65],108:66,115:[2,136],123:[2,136],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,142],6:[2,142],25:[2,142],26:[2,142],46:[2,142],51:[2,142],54:[2,142],69:[2,142],75:[2,142],83:[2,142],88:[2,142],90:[2,142],99:[2,142],101:[2,142],102:[2,142],103:[2,142],107:[2,142],115:[2,142],123:[2,142],125:[2,142],126:[2,142],129:[2,142],130:[2,142],131:[2,142],132:[2,142],133:[2,142],134:[2,142]},{1:[2,143],6:[2,143],25:[2,143],26:[2,143],46:[2,143],51:[2,143],54:[2,143],69:[2,143],75:[2,143],83:[2,143],88:[2,143],90:[2,143],99:[2,143],100:84,101:[1,62],102:[2,143],103:[1,63],106:85,107:[1,65],108:66,115:[2,143],123:[2,143],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,147],6:[2,147],25:[2,147],26:[2,147],46:[2,147],51:[2,147],54:[2,147],69:[2,147],75:[2,147],83:[2,147],88:[2,147],90:[2,147],99:[2,147],101:[2,147],102:[2,147],103:[2,147],107:[2,147],115:[2,147],123:[2,147],125:[2,147],126:[2,147],129:[2,147],130:[2,147],131:[2,147],132:[2,147],133:[2,147],134:[2,147]},{113:[2,149],114:[2,149]},{27:156,28:[1,70],55:157,56:158,73:[1,67],87:[1,112],110:228,112:155},{51:[1,229],113:[2,154],114:[2,154]},{51:[2,151],113:[2,151],114:[2,151]},{51:[2,152],113:[2,152],114:[2,152]},{51:[2,153],113:[2,153],114:[2,153]},{1:[2,148],6:[2,148],25:[2,148],26:[2,148],46:[2,148],51:[2,148],54:[2,148],69:[2,148],75:[2,148],83:[2,148],88:[2,148],90:[2,148],99:[2,148],101:[2,148],102:[2,148],103:[2,148],107:[2,148],115:[2,148],123:[2,148],125:[2,148],126:[2,148],129:[2,148],130:[2,148],131:[2,148],132:[2,148],133:[2,148],134:[2,148]},{8:230,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:231,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{6:[2,49],25:[2,49],50:232,51:[1,233],75:[2,49]},{6:[2,86],25:[2,86],26:[2,86],51:[2,86],75:[2,86]},{6:[2,35],25:[2,35],26:[2,35],40:[1,234],51:[2,35],75:[2,35]},{6:[2,38],25:[2,38],26:[2,38],51:[2,38],75:[2,38]},{6:[2,39],25:[2,39],26:[2,39],40:[2,39],51:[2,39],75:[2,39]},{6:[2,40],25:[2,40],26:[2,40],40:[2,40],51:[2,40],75:[2,40]},{6:[2,41],25:[2,41],26:[2,41],40:[2,41],51:[2,41],75:[2,41]},{1:[2,5],6:[2,5],26:[2,5],99:[2,5]},{1:[2,25],6:[2,25],25:[2,25],26:[2,25],46:[2,25],51:[2,25],54:[2,25],69:[2,25],75:[2,25],83:[2,25],88:[2,25],90:[2,25],95:[2,25],96:[2,25],99:[2,25],101:[2,25],102:[2,25],103:[2,25],107:[2,25],115:[2,25],118:[2,25],120:[2,25],123:[2,25],125:[2,25],126:[2,25],129:[2,25],130:[2,25],131:[2,25],132:[2,25],133:[2,25],134:[2,25]},{1:[2,185],6:[2,185],25:[2,185],26:[2,185],46:[2,185],51:[2,185],54:[2,185],69:[2,185],75:[2,185],83:[2,185],88:[2,185],90:[2,185],99:[2,185],100:84,101:[2,185],102:[2,185],103:[2,185],106:85,107:[2,185],108:66,115:[2,185],123:[2,185],125:[2,185],126:[2,185],129:[1,75],130:[1,78],131:[2,185],132:[2,185],133:[2,185],134:[2,185]},{1:[2,186],6:[2,186],25:[2,186],26:[2,186],46:[2,186],51:[2,186],54:[2,186],69:[2,186],75:[2,186],83:[2,186],88:[2,186],90:[2,186],99:[2,186],100:84,101:[2,186],102:[2,186],103:[2,186],106:85,107:[2,186],108:66,115:[2,186],123:[2,186],125:[2,186],126:[2,186],129:[1,75],130:[1,78],131:[2,186],132:[2,186],133:[2,186],134:[2,186]},{1:[2,187],6:[2,187],25:[2,187],26:[2,187],46:[2,187],51:[2,187],54:[2,187],69:[2,187],75:[2,187],83:[2,187],88:[2,187],90:[2,187],99:[2,187],100:84,101:[2,187],102:[2,187],103:[2,187],106:85,107:[2,187],108:66,115:[2,187],123:[2,187],125:[2,187],126:[2,187],129:[1,75],130:[2,187],131:[2,187],132:[2,187],133:[2,187],134:[2,187]},{1:[2,188],6:[2,188],25:[2,188],26:[2,188],46:[2,188],51:[2,188],54:[2,188],69:[2,188],75:[2,188],83:[2,188],88:[2,188],90:[2,188],99:[2,188],100:84,101:[2,188],102:[2,188],103:[2,188],106:85,107:[2,188],108:66,115:[2,188],123:[2,188],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[2,188],132:[2,188],133:[2,188],134:[2,188]},{1:[2,189],6:[2,189],25:[2,189],26:[2,189],46:[2,189],51:[2,189],54:[2,189],69:[2,189],75:[2,189],83:[2,189],88:[2,189],90:[2,189],99:[2,189],100:84,101:[2,189],102:[2,189],103:[2,189],106:85,107:[2,189],108:66,115:[2,189],123:[2,189],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[2,189],133:[2,189],134:[1,82]},{1:[2,190],6:[2,190],25:[2,190],26:[2,190],46:[2,190],51:[2,190],54:[2,190],69:[2,190],75:[2,190],83:[2,190],88:[2,190],90:[2,190],99:[2,190],100:84,101:[2,190],102:[2,190],103:[2,190],106:85,107:[2,190],108:66,115:[2,190],123:[2,190],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[2,190],134:[1,82]},{1:[2,191],6:[2,191],25:[2,191],26:[2,191],46:[2,191],51:[2,191],54:[2,191],69:[2,191],75:[2,191],83:[2,191],88:[2,191],90:[2,191],99:[2,191],100:84,101:[2,191],102:[2,191],103:[2,191],106:85,107:[2,191],108:66,115:[2,191],123:[2,191],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[2,191],133:[2,191],134:[2,191]},{1:[2,176],6:[2,176],25:[2,176],26:[2,176],46:[2,176],51:[2,176],54:[2,176],69:[2,176],75:[2,176],83:[2,176],88:[2,176],90:[2,176],99:[2,176],100:84,101:[1,62],102:[2,176],103:[1,63],106:85,107:[1,65],108:66,115:[2,176],123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,175],6:[2,175],25:[2,175],26:[2,175],46:[2,175],51:[2,175],54:[2,175],69:[2,175],75:[2,175],83:[2,175],88:[2,175],90:[2,175],99:[2,175],100:84,101:[1,62],102:[2,175],103:[1,63],106:85,107:[1,65],108:66,115:[2,175],123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,98],6:[2,98],25:[2,98],26:[2,98],46:[2,98],51:[2,98],54:[2,98],63:[2,98],64:[2,98],65:[2,98],67:[2,98],69:[2,98],70:[2,98],71:[2,98],75:[2,98],81:[2,98],82:[2,98],83:[2,98],88:[2,98],90:[2,98],99:[2,98],101:[2,98],102:[2,98],103:[2,98],107:[2,98],115:[2,98],123:[2,98],125:[2,98],126:[2,98],129:[2,98],130:[2,98],131:[2,98],132:[2,98],133:[2,98],134:[2,98]},{1:[2,74],6:[2,74],25:[2,74],26:[2,74],37:[2,74],46:[2,74],51:[2,74],54:[2,74],63:[2,74],64:[2,74],65:[2,74],67:[2,74],69:[2,74],70:[2,74],71:[2,74],75:[2,74],77:[2,74],81:[2,74],82:[2,74],83:[2,74],88:[2,74],90:[2,74],99:[2,74],101:[2,74],102:[2,74],103:[2,74],107:[2,74],115:[2,74],123:[2,74],125:[2,74],126:[2,74],127:[2,74],128:[2,74],129:[2,74],130:[2,74],131:[2,74],132:[2,74],133:[2,74],134:[2,74],135:[2,74]},{1:[2,75],6:[2,75],25:[2,75],26:[2,75],37:[2,75],46:[2,75],51:[2,75],54:[2,75],63:[2,75],64:[2,75],65:[2,75],67:[2,75],69:[2,75],70:[2,75],71:[2,75],75:[2,75],77:[2,75],81:[2,75],82:[2,75],83:[2,75],88:[2,75],90:[2,75],99:[2,75],101:[2,75],102:[2,75],103:[2,75],107:[2,75],115:[2,75],123:[2,75],125:[2,75],126:[2,75],127:[2,75],128:[2,75],129:[2,75],130:[2,75],131:[2,75],132:[2,75],133:[2,75],134:[2,75],135:[2,75]},{1:[2,76],6:[2,76],25:[2,76],26:[2,76],37:[2,76],46:[2,76],51:[2,76],54:[2,76],63:[2,76],64:[2,76],65:[2,76],67:[2,76],69:[2,76],70:[2,76],71:[2,76],75:[2,76],77:[2,76],81:[2,76],82:[2,76],83:[2,76],88:[2,76],90:[2,76],99:[2,76],101:[2,76],102:[2,76],103:[2,76],107:[2,76],115:[2,76],123:[2,76],125:[2,76],126:[2,76],127:[2,76],128:[2,76],129:[2,76],130:[2,76],131:[2,76],132:[2,76],133:[2,76],134:[2,76],135:[2,76]},{69:[1,235]},{54:[1,189],69:[2,82],89:236,90:[1,188],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{69:[2,83]},{8:237,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{13:[2,111],28:[2,111],30:[2,111],31:[2,111],33:[2,111],34:[2,111],35:[2,111],42:[2,111],43:[2,111],44:[2,111],48:[2,111],49:[2,111],69:[2,111],73:[2,111],76:[2,111],80:[2,111],85:[2,111],86:[2,111],87:[2,111],93:[2,111],97:[2,111],98:[2,111],101:[2,111],103:[2,111],105:[2,111],107:[2,111],116:[2,111],122:[2,111],124:[2,111],125:[2,111],126:[2,111],127:[2,111],128:[2,111]},{13:[2,112],28:[2,112],30:[2,112],31:[2,112],33:[2,112],34:[2,112],35:[2,112],42:[2,112],43:[2,112],44:[2,112],48:[2,112],49:[2,112],69:[2,112],73:[2,112],76:[2,112],80:[2,112],85:[2,112],86:[2,112],87:[2,112],93:[2,112],97:[2,112],98:[2,112],101:[2,112],103:[2,112],105:[2,112],107:[2,112],116:[2,112],122:[2,112],124:[2,112],125:[2,112],126:[2,112],127:[2,112],128:[2,112]},{1:[2,80],6:[2,80],25:[2,80],26:[2,80],37:[2,80],46:[2,80],51:[2,80],54:[2,80],63:[2,80],64:[2,80],65:[2,80],67:[2,80],69:[2,80],70:[2,80],71:[2,80],75:[2,80],77:[2,80],81:[2,80],82:[2,80],83:[2,80],88:[2,80],90:[2,80],99:[2,80],101:[2,80],102:[2,80],103:[2,80],107:[2,80],115:[2,80],123:[2,80],125:[2,80],126:[2,80],127:[2,80],128:[2,80],129:[2,80],130:[2,80],131:[2,80],132:[2,80],133:[2,80],134:[2,80],135:[2,80]},{1:[2,81],6:[2,81],25:[2,81],26:[2,81],37:[2,81],46:[2,81],51:[2,81],54:[2,81],63:[2,81],64:[2,81],65:[2,81],67:[2,81],69:[2,81],70:[2,81],71:[2,81],75:[2,81],77:[2,81],81:[2,81],82:[2,81],83:[2,81],88:[2,81],90:[2,81],99:[2,81],101:[2,81],102:[2,81],103:[2,81],107:[2,81],115:[2,81],123:[2,81],125:[2,81],126:[2,81],127:[2,81],128:[2,81],129:[2,81],130:[2,81],131:[2,81],132:[2,81],133:[2,81],134:[2,81],135:[2,81]},{1:[2,99],6:[2,99],25:[2,99],26:[2,99],46:[2,99],51:[2,99],54:[2,99],63:[2,99],64:[2,99],65:[2,99],67:[2,99],69:[2,99],70:[2,99],71:[2,99],75:[2,99],81:[2,99],82:[2,99],83:[2,99],88:[2,99],90:[2,99],99:[2,99],101:[2,99],102:[2,99],103:[2,99],107:[2,99],115:[2,99],123:[2,99],125:[2,99],126:[2,99],129:[2,99],130:[2,99],131:[2,99],132:[2,99],133:[2,99],134:[2,99]},{1:[2,33],6:[2,33],25:[2,33],26:[2,33],46:[2,33],51:[2,33],54:[2,33],69:[2,33],75:[2,33],83:[2,33],88:[2,33],90:[2,33],99:[2,33],100:84,101:[2,33],102:[2,33],103:[2,33],106:85,107:[2,33],108:66,115:[2,33],123:[2,33],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{8:238,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,104],6:[2,104],25:[2,104],26:[2,104],46:[2,104],51:[2,104],54:[2,104],63:[2,104],64:[2,104],65:[2,104],67:[2,104],69:[2,104],70:[2,104],71:[2,104],75:[2,104],81:[2,104],82:[2,104],83:[2,104],88:[2,104],90:[2,104],99:[2,104],101:[2,104],102:[2,104],103:[2,104],107:[2,104],115:[2,104],123:[2,104],125:[2,104],126:[2,104],129:[2,104],130:[2,104],131:[2,104],132:[2,104],133:[2,104],134:[2,104]},{6:[2,49],25:[2,49],50:239,51:[1,223],83:[2,49]},{6:[2,122],25:[2,122],26:[2,122],51:[2,122],54:[1,240],83:[2,122],88:[2,122],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{47:241,48:[1,57],49:[1,58]},{27:107,28:[1,70],41:108,52:242,53:106,55:109,56:110,73:[1,67],86:[1,111],87:[1,112]},{46:[2,55],51:[2,55]},{8:243,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,192],6:[2,192],25:[2,192],26:[2,192],46:[2,192],51:[2,192],54:[2,192],69:[2,192],75:[2,192],83:[2,192],88:[2,192],90:[2,192],99:[2,192],100:84,101:[2,192],102:[2,192],103:[2,192],106:85,107:[2,192],108:66,115:[2,192],123:[2,192],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{8:244,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,194],6:[2,194],25:[2,194],26:[2,194],46:[2,194],51:[2,194],54:[2,194],69:[2,194],75:[2,194],83:[2,194],88:[2,194],90:[2,194],99:[2,194],100:84,101:[2,194],102:[2,194],103:[2,194],106:85,107:[2,194],108:66,115:[2,194],123:[2,194],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,174],6:[2,174],25:[2,174],26:[2,174],46:[2,174],51:[2,174],54:[2,174],69:[2,174],75:[2,174],83:[2,174],88:[2,174],90:[2,174],99:[2,174],101:[2,174],102:[2,174],103:[2,174],107:[2,174],115:[2,174],123:[2,174],125:[2,174],126:[2,174],129:[2,174],130:[2,174],131:[2,174],132:[2,174],133:[2,174],134:[2,174]},{8:245,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,127],6:[2,127],25:[2,127],26:[2,127],46:[2,127],51:[2,127],54:[2,127],69:[2,127],75:[2,127],83:[2,127],88:[2,127],90:[2,127],95:[1,246],99:[2,127],101:[2,127],102:[2,127],103:[2,127],107:[2,127],115:[2,127],123:[2,127],125:[2,127],126:[2,127],129:[2,127],130:[2,127],131:[2,127],132:[2,127],133:[2,127],134:[2,127]},{5:247,25:[1,5]},{27:248,28:[1,70]},{117:249,119:212,120:[1,213]},{26:[1,250],118:[1,251],119:252,120:[1,213]},{26:[2,167],118:[2,167],120:[2,167]},{8:254,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],92:253,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,92],5:255,6:[2,92],25:[1,5],26:[2,92],46:[2,92],51:[2,92],54:[2,92],59:90,63:[1,92],64:[1,93],65:[1,94],66:95,67:[1,96],69:[2,92],70:[1,97],71:[1,98],75:[2,92],78:89,81:[1,91],82:[2,102],83:[2,92],88:[2,92],90:[2,92],99:[2,92],101:[2,92],102:[2,92],103:[2,92],107:[2,92],115:[2,92],123:[2,92],125:[2,92],126:[2,92],129:[2,92],130:[2,92],131:[2,92],132:[2,92],133:[2,92],134:[2,92]},{1:[2,66],6:[2,66],25:[2,66],26:[2,66],46:[2,66],51:[2,66],54:[2,66],63:[2,66],64:[2,66],65:[2,66],67:[2,66],69:[2,66],70:[2,66],71:[2,66],75:[2,66],81:[2,66],82:[2,66],83:[2,66],88:[2,66],90:[2,66],99:[2,66],101:[2,66],102:[2,66],103:[2,66],107:[2,66],115:[2,66],123:[2,66],125:[2,66],126:[2,66],129:[2,66],130:[2,66],131:[2,66],132:[2,66],133:[2,66],134:[2,66]},{1:[2,95],6:[2,95],25:[2,95],26:[2,95],46:[2,95],51:[2,95],54:[2,95],69:[2,95],75:[2,95],83:[2,95],88:[2,95],90:[2,95],99:[2,95],101:[2,95],102:[2,95],103:[2,95],107:[2,95],115:[2,95],123:[2,95],125:[2,95],126:[2,95],129:[2,95],130:[2,95],131:[2,95],132:[2,95],133:[2,95],134:[2,95]},{14:256,15:120,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:121,41:60,55:47,56:48,58:215,60:25,61:26,62:27,73:[1,67],80:[1,28],85:[1,55],86:[1,56],87:[1,54],98:[1,53]},{1:[2,132],6:[2,132],25:[2,132],26:[2,132],46:[2,132],51:[2,132],54:[2,132],63:[2,132],64:[2,132],65:[2,132],67:[2,132],69:[2,132],70:[2,132],71:[2,132],75:[2,132],81:[2,132],82:[2,132],83:[2,132],88:[2,132],90:[2,132],99:[2,132],101:[2,132],102:[2,132],103:[2,132],107:[2,132],115:[2,132],123:[2,132],125:[2,132],126:[2,132],129:[2,132],130:[2,132],131:[2,132],132:[2,132],133:[2,132],134:[2,132]},{6:[1,71],26:[1,257]},{8:258,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{6:[2,61],13:[2,112],25:[2,61],28:[2,112],30:[2,112],31:[2,112],33:[2,112],34:[2,112],35:[2,112],42:[2,112],43:[2,112],44:[2,112],48:[2,112],49:[2,112],51:[2,61],73:[2,112],76:[2,112],80:[2,112],85:[2,112],86:[2,112],87:[2,112],88:[2,61],93:[2,112],97:[2,112],98:[2,112],101:[2,112],103:[2,112],105:[2,112],107:[2,112],116:[2,112],122:[2,112],124:[2,112],125:[2,112],126:[2,112],127:[2,112],128:[2,112]},{6:[1,260],25:[1,261],88:[1,259]},{6:[2,50],8:197,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[2,50],26:[2,50],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,57:145,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],83:[2,50],85:[1,55],86:[1,56],87:[1,54],88:[2,50],91:262,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{6:[2,49],25:[2,49],26:[2,49],50:263,51:[1,223]},{1:[2,171],6:[2,171],25:[2,171],26:[2,171],46:[2,171],51:[2,171],54:[2,171],69:[2,171],75:[2,171],83:[2,171],88:[2,171],90:[2,171],99:[2,171],101:[2,171],102:[2,171],103:[2,171],107:[2,171],115:[2,171],118:[2,171],123:[2,171],125:[2,171],126:[2,171],129:[2,171],130:[2,171],131:[2,171],132:[2,171],133:[2,171],134:[2,171]},{8:264,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:265,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{113:[2,150],114:[2,150]},{27:156,28:[1,70],55:157,56:158,73:[1,67],87:[1,112],112:266},{1:[2,156],6:[2,156],25:[2,156],26:[2,156],46:[2,156],51:[2,156],54:[2,156],69:[2,156],75:[2,156],83:[2,156],88:[2,156],90:[2,156],99:[2,156],100:84,101:[2,156],102:[1,267],103:[2,156],106:85,107:[2,156],108:66,115:[1,268],123:[2,156],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,157],6:[2,157],25:[2,157],26:[2,157],46:[2,157],51:[2,157],54:[2,157],69:[2,157],75:[2,157],83:[2,157],88:[2,157],90:[2,157],99:[2,157],100:84,101:[2,157],102:[1,269],103:[2,157],106:85,107:[2,157],108:66,115:[2,157],123:[2,157],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{6:[1,271],25:[1,272],75:[1,270]},{6:[2,50],12:165,25:[2,50],26:[2,50],27:166,28:[1,70],29:167,30:[1,68],31:[1,69],38:273,39:164,41:168,43:[1,46],75:[2,50],86:[1,111]},{8:274,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,275],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,79],6:[2,79],25:[2,79],26:[2,79],37:[2,79],46:[2,79],51:[2,79],54:[2,79],63:[2,79],64:[2,79],65:[2,79],67:[2,79],69:[2,79],70:[2,79],71:[2,79],75:[2,79],77:[2,79],81:[2,79],82:[2,79],83:[2,79],88:[2,79],90:[2,79],99:[2,79],101:[2,79],102:[2,79],103:[2,79],107:[2,79],115:[2,79],123:[2,79],125:[2,79],126:[2,79],127:[2,79],128:[2,79],129:[2,79],130:[2,79],131:[2,79],132:[2,79],133:[2,79],134:[2,79],135:[2,79]},{8:276,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,69:[2,115],73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{69:[2,116],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{26:[1,277],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{6:[1,260],25:[1,261],83:[1,278]},{6:[2,61],25:[2,61],26:[2,61],51:[2,61],83:[2,61],88:[2,61]},{5:279,25:[1,5]},{46:[2,53],51:[2,53]},{46:[2,56],51:[2,56],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{26:[1,280],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{5:281,25:[1,5],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{5:282,25:[1,5]},{1:[2,128],6:[2,128],25:[2,128],26:[2,128],46:[2,128],51:[2,128],54:[2,128],69:[2,128],75:[2,128],83:[2,128],88:[2,128],90:[2,128],99:[2,128],101:[2,128],102:[2,128],103:[2,128],107:[2,128],115:[2,128],123:[2,128],125:[2,128],126:[2,128],129:[2,128],130:[2,128],131:[2,128],132:[2,128],133:[2,128],134:[2,128]},{5:283,25:[1,5]},{26:[1,284],118:[1,285],119:252,120:[1,213]},{1:[2,165],6:[2,165],25:[2,165],26:[2,165],46:[2,165],51:[2,165],54:[2,165],69:[2,165],75:[2,165],83:[2,165],88:[2,165],90:[2,165],99:[2,165],101:[2,165],102:[2,165],103:[2,165],107:[2,165],115:[2,165],123:[2,165],125:[2,165],126:[2,165],129:[2,165],130:[2,165],131:[2,165],132:[2,165],133:[2,165],134:[2,165]},{5:286,25:[1,5]},{26:[2,168],118:[2,168],120:[2,168]},{5:287,25:[1,5],51:[1,288]},{25:[2,124],51:[2,124],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,93],6:[2,93],25:[2,93],26:[2,93],46:[2,93],51:[2,93],54:[2,93],69:[2,93],75:[2,93],83:[2,93],88:[2,93],90:[2,93],99:[2,93],101:[2,93],102:[2,93],103:[2,93],107:[2,93],115:[2,93],123:[2,93],125:[2,93],126:[2,93],129:[2,93],130:[2,93],131:[2,93],132:[2,93],133:[2,93],134:[2,93]},{1:[2,96],5:289,6:[2,96],25:[1,5],26:[2,96],46:[2,96],51:[2,96],54:[2,96],59:90,63:[1,92],64:[1,93],65:[1,94],66:95,67:[1,96],69:[2,96],70:[1,97],71:[1,98],75:[2,96],78:89,81:[1,91],82:[2,102],83:[2,96],88:[2,96],90:[2,96],99:[2,96],101:[2,96],102:[2,96],103:[2,96],107:[2,96],115:[2,96],123:[2,96],125:[2,96],126:[2,96],129:[2,96],130:[2,96],131:[2,96],132:[2,96],133:[2,96],134:[2,96]},{99:[1,290]},{88:[1,291],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,110],6:[2,110],25:[2,110],26:[2,110],37:[2,110],46:[2,110],51:[2,110],54:[2,110],63:[2,110],64:[2,110],65:[2,110],67:[2,110],69:[2,110],70:[2,110],71:[2,110],75:[2,110],81:[2,110],82:[2,110],83:[2,110],88:[2,110],90:[2,110],99:[2,110],101:[2,110],102:[2,110],103:[2,110],107:[2,110],113:[2,110],114:[2,110],115:[2,110],123:[2,110],125:[2,110],126:[2,110],129:[2,110],130:[2,110],131:[2,110],132:[2,110],133:[2,110],134:[2,110]},{8:197,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,57:145,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],91:292,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:197,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,25:[1,144],27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,57:145,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],84:293,85:[1,55],86:[1,56],87:[1,54],91:143,93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{6:[2,118],25:[2,118],26:[2,118],51:[2,118],83:[2,118],88:[2,118]},{6:[1,260],25:[1,261],26:[1,294]},{1:[2,135],6:[2,135],25:[2,135],26:[2,135],46:[2,135],51:[2,135],54:[2,135],69:[2,135],75:[2,135],83:[2,135],88:[2,135],90:[2,135],99:[2,135],100:84,101:[1,62],102:[2,135],103:[1,63],106:85,107:[1,65],108:66,115:[2,135],123:[2,135],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,137],6:[2,137],25:[2,137],26:[2,137],46:[2,137],51:[2,137],54:[2,137],69:[2,137],75:[2,137],83:[2,137],88:[2,137],90:[2,137],99:[2,137],100:84,101:[1,62],102:[2,137],103:[1,63],106:85,107:[1,65],108:66,115:[2,137],123:[2,137],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{113:[2,155],114:[2,155]},{8:295,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:296,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:297,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,84],6:[2,84],25:[2,84],26:[2,84],37:[2,84],46:[2,84],51:[2,84],54:[2,84],63:[2,84],64:[2,84],65:[2,84],67:[2,84],69:[2,84],70:[2,84],71:[2,84],75:[2,84],81:[2,84],82:[2,84],83:[2,84],88:[2,84],90:[2,84],99:[2,84],101:[2,84],102:[2,84],103:[2,84],107:[2,84],113:[2,84],114:[2,84],115:[2,84],123:[2,84],125:[2,84],126:[2,84],129:[2,84],130:[2,84],131:[2,84],132:[2,84],133:[2,84],134:[2,84]},{12:165,27:166,28:[1,70],29:167,30:[1,68],31:[1,69],38:298,39:164,41:168,43:[1,46],86:[1,111]},{6:[2,85],12:165,25:[2,85],26:[2,85],27:166,28:[1,70],29:167,30:[1,68],31:[1,69],38:163,39:164,41:168,43:[1,46],51:[2,85],74:299,86:[1,111]},{6:[2,87],25:[2,87],26:[2,87],51:[2,87],75:[2,87]},{6:[2,36],25:[2,36],26:[2,36],51:[2,36],75:[2,36],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{8:300,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{69:[2,114],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,34],6:[2,34],25:[2,34],26:[2,34],46:[2,34],51:[2,34],54:[2,34],69:[2,34],75:[2,34],83:[2,34],88:[2,34],90:[2,34],99:[2,34],101:[2,34],102:[2,34],103:[2,34],107:[2,34],115:[2,34],123:[2,34],125:[2,34],126:[2,34],129:[2,34],130:[2,34],131:[2,34],132:[2,34],133:[2,34],134:[2,34]},{1:[2,105],6:[2,105],25:[2,105],26:[2,105],46:[2,105],51:[2,105],54:[2,105],63:[2,105],64:[2,105],65:[2,105],67:[2,105],69:[2,105],70:[2,105],71:[2,105],75:[2,105],81:[2,105],82:[2,105],83:[2,105],88:[2,105],90:[2,105],99:[2,105],101:[2,105],102:[2,105],103:[2,105],107:[2,105],115:[2,105],123:[2,105],125:[2,105],126:[2,105],129:[2,105],130:[2,105],131:[2,105],132:[2,105],133:[2,105],134:[2,105]},{1:[2,45],6:[2,45],25:[2,45],26:[2,45],46:[2,45],51:[2,45],54:[2,45],69:[2,45],75:[2,45],83:[2,45],88:[2,45],90:[2,45],99:[2,45],101:[2,45],102:[2,45],103:[2,45],107:[2,45],115:[2,45],123:[2,45],125:[2,45],126:[2,45],129:[2,45],130:[2,45],131:[2,45],132:[2,45],133:[2,45],134:[2,45]},{1:[2,193],6:[2,193],25:[2,193],26:[2,193],46:[2,193],51:[2,193],54:[2,193],69:[2,193],75:[2,193],83:[2,193],88:[2,193],90:[2,193],99:[2,193],101:[2,193],102:[2,193],103:[2,193],107:[2,193],115:[2,193],123:[2,193],125:[2,193],126:[2,193],129:[2,193],130:[2,193],131:[2,193],132:[2,193],133:[2,193],134:[2,193]},{1:[2,172],6:[2,172],25:[2,172],26:[2,172],46:[2,172],51:[2,172],54:[2,172],69:[2,172],75:[2,172],83:[2,172],88:[2,172],90:[2,172],99:[2,172],101:[2,172],102:[2,172],103:[2,172],107:[2,172],115:[2,172],118:[2,172],123:[2,172],125:[2,172],126:[2,172],129:[2,172],130:[2,172],131:[2,172],132:[2,172],133:[2,172],134:[2,172]},{1:[2,129],6:[2,129],25:[2,129],26:[2,129],46:[2,129],51:[2,129],54:[2,129],69:[2,129],75:[2,129],83:[2,129],88:[2,129],90:[2,129],99:[2,129],101:[2,129],102:[2,129],103:[2,129],107:[2,129],115:[2,129],123:[2,129],125:[2,129],126:[2,129],129:[2,129],130:[2,129],131:[2,129],132:[2,129],133:[2,129],134:[2,129]},{1:[2,130],6:[2,130],25:[2,130],26:[2,130],46:[2,130],51:[2,130],54:[2,130],69:[2,130],75:[2,130],83:[2,130],88:[2,130],90:[2,130],95:[2,130],99:[2,130],101:[2,130],102:[2,130],103:[2,130],107:[2,130],115:[2,130],123:[2,130],125:[2,130],126:[2,130],129:[2,130],130:[2,130],131:[2,130],132:[2,130],133:[2,130],134:[2,130]},{1:[2,163],6:[2,163],25:[2,163],26:[2,163],46:[2,163],51:[2,163],54:[2,163],69:[2,163],75:[2,163],83:[2,163],88:[2,163],90:[2,163],99:[2,163],101:[2,163],102:[2,163],103:[2,163],107:[2,163],115:[2,163],123:[2,163],125:[2,163],126:[2,163],129:[2,163],130:[2,163],131:[2,163],132:[2,163],133:[2,163],134:[2,163]},{5:301,25:[1,5]},{26:[1,302]},{6:[1,303],26:[2,169],118:[2,169],120:[2,169]},{8:304,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{1:[2,97],6:[2,97],25:[2,97],26:[2,97],46:[2,97],51:[2,97],54:[2,97],69:[2,97],75:[2,97],83:[2,97],88:[2,97],90:[2,97],99:[2,97],101:[2,97],102:[2,97],103:[2,97],107:[2,97],115:[2,97],123:[2,97],125:[2,97],126:[2,97],129:[2,97],130:[2,97],131:[2,97],132:[2,97],133:[2,97],134:[2,97]},{1:[2,133],6:[2,133],25:[2,133],26:[2,133],46:[2,133],51:[2,133],54:[2,133],63:[2,133],64:[2,133],65:[2,133],67:[2,133],69:[2,133],70:[2,133],71:[2,133],75:[2,133],81:[2,133],82:[2,133],83:[2,133],88:[2,133],90:[2,133],99:[2,133],101:[2,133],102:[2,133],103:[2,133],107:[2,133],115:[2,133],123:[2,133],125:[2,133],126:[2,133],129:[2,133],130:[2,133],131:[2,133],132:[2,133],133:[2,133],134:[2,133]},{1:[2,113],6:[2,113],25:[2,113],26:[2,113],46:[2,113],51:[2,113],54:[2,113],63:[2,113],64:[2,113],65:[2,113],67:[2,113],69:[2,113],70:[2,113],71:[2,113],75:[2,113],81:[2,113],82:[2,113],83:[2,113],88:[2,113],90:[2,113],99:[2,113],101:[2,113],102:[2,113],103:[2,113],107:[2,113],115:[2,113],123:[2,113],125:[2,113],126:[2,113],129:[2,113],130:[2,113],131:[2,113],132:[2,113],133:[2,113],134:[2,113]},{6:[2,119],25:[2,119],26:[2,119],51:[2,119],83:[2,119],88:[2,119]},{6:[2,49],25:[2,49],26:[2,49],50:305,51:[1,223]},{6:[2,120],25:[2,120],26:[2,120],51:[2,120],83:[2,120],88:[2,120]},{1:[2,158],6:[2,158],25:[2,158],26:[2,158],46:[2,158],51:[2,158],54:[2,158],69:[2,158],75:[2,158],83:[2,158],88:[2,158],90:[2,158],99:[2,158],100:84,101:[2,158],102:[2,158],103:[2,158],106:85,107:[2,158],108:66,115:[1,306],123:[2,158],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,160],6:[2,160],25:[2,160],26:[2,160],46:[2,160],51:[2,160],54:[2,160],69:[2,160],75:[2,160],83:[2,160],88:[2,160],90:[2,160],99:[2,160],100:84,101:[2,160],102:[1,307],103:[2,160],106:85,107:[2,160],108:66,115:[2,160],123:[2,160],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,159],6:[2,159],25:[2,159],26:[2,159],46:[2,159],51:[2,159],54:[2,159],69:[2,159],75:[2,159],83:[2,159],88:[2,159],90:[2,159],99:[2,159],100:84,101:[2,159],102:[2,159],103:[2,159],106:85,107:[2,159],108:66,115:[2,159],123:[2,159],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{6:[2,88],25:[2,88],26:[2,88],51:[2,88],75:[2,88]},{6:[2,49],25:[2,49],26:[2,49],50:308,51:[1,233]},{26:[1,309],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{26:[1,310]},{1:[2,166],6:[2,166],25:[2,166],26:[2,166],46:[2,166],51:[2,166],54:[2,166],69:[2,166],75:[2,166],83:[2,166],88:[2,166],90:[2,166],99:[2,166],101:[2,166],102:[2,166],103:[2,166],107:[2,166],115:[2,166],123:[2,166],125:[2,166],126:[2,166],129:[2,166],130:[2,166],131:[2,166],132:[2,166],133:[2,166],134:[2,166]},{26:[2,170],118:[2,170],120:[2,170]},{25:[2,125],51:[2,125],100:84,101:[1,62],103:[1,63],106:85,107:[1,65],108:66,123:[1,83],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{6:[1,260],25:[1,261],26:[1,311]},{8:312,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{8:313,9:115,10:19,11:20,12:21,13:[1,22],14:8,15:9,16:10,17:11,18:12,19:13,20:14,21:15,22:16,23:17,24:18,27:59,28:[1,70],29:49,30:[1,68],31:[1,69],32:24,33:[1,50],34:[1,51],35:[1,52],36:23,41:60,42:[1,44],43:[1,46],44:[1,29],47:30,48:[1,57],49:[1,58],55:47,56:48,58:36,60:25,61:26,62:27,73:[1,67],76:[1,43],80:[1,28],85:[1,55],86:[1,56],87:[1,54],93:[1,38],97:[1,45],98:[1,53],100:39,101:[1,62],103:[1,63],104:40,105:[1,64],106:41,107:[1,65],108:66,116:[1,42],121:37,122:[1,61],124:[1,31],125:[1,32],126:[1,33],127:[1,34],128:[1,35]},{6:[1,271],25:[1,272],26:[1,314]},{6:[2,37],25:[2,37],26:[2,37],51:[2,37],75:[2,37]},{1:[2,164],6:[2,164],25:[2,164],26:[2,164],46:[2,164],51:[2,164],54:[2,164],69:[2,164],75:[2,164],83:[2,164],88:[2,164],90:[2,164],99:[2,164],101:[2,164],102:[2,164],103:[2,164],107:[2,164],115:[2,164],123:[2,164],125:[2,164],126:[2,164],129:[2,164],130:[2,164],131:[2,164],132:[2,164],133:[2,164],134:[2,164]},{6:[2,121],25:[2,121],26:[2,121],51:[2,121],83:[2,121],88:[2,121]},{1:[2,161],6:[2,161],25:[2,161],26:[2,161],46:[2,161],51:[2,161],54:[2,161],69:[2,161],75:[2,161],83:[2,161],88:[2,161],90:[2,161],99:[2,161],100:84,101:[2,161],102:[2,161],103:[2,161],106:85,107:[2,161],108:66,115:[2,161],123:[2,161],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{1:[2,162],6:[2,162],25:[2,162],26:[2,162],46:[2,162],51:[2,162],54:[2,162],69:[2,162],75:[2,162],83:[2,162],88:[2,162],90:[2,162],99:[2,162],100:84,101:[2,162],102:[2,162],103:[2,162],106:85,107:[2,162],108:66,115:[2,162],123:[2,162],125:[1,77],126:[1,76],129:[1,75],130:[1,78],131:[1,79],132:[1,80],133:[1,81],134:[1,82]},{6:[2,89],25:[2,89],26:[2,89],51:[2,89],75:[2,89]}],defaultActions:{57:[2,47],58:[2,48],72:[2,3],91:[2,103],186:[2,83]},parseError:function(b,c){throw new Error(b)},parse:function(b){function p(){var a;a=c.lexer.lex()||1,typeof a!="number"&&(a=c.symbols_[a]||a);return a}function o(a){d.length=d.length-2*a,e.length=e.length-a,f.length=f.length-a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0,l=2,m=1;this.lexer.setInput(b),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,typeof this.lexer.yylloc=="undefined"&&(this.lexer.yylloc={});var n=this.lexer.yylloc;f.push(n),typeof this.yy.parseError=="function"&&(this.parseError=this.yy.parseError);var q,r,s,t,u,v,w={},x,y,z,A;for(;;){s=d[d.length-1],this.defaultActions[s]?t=this.defaultActions[s]:(q==null&&(q=p()),t=g[s]&&g[s][q]);if(typeof t=="undefined"||!t.length||!t[0]){if(!k){A=[];for(x in g[s])this.terminals_[x]&&x>2&&A.push("'"+this.terminals_[x]+"'");var B="";this.lexer.showPosition?B="Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+A.join(", "):B="Parse error on line "+(i+1)+": Unexpected "+(q==1?"end of input":"'"+(this.terminals_[q]||q)+"'"),this.parseError(B,{text:this.lexer.match,token:this.terminals_[q]||q,line:this.lexer.yylineno,loc:n,expected:A})}if(k==3){if(q==m)throw new Error(B||"Parsing halted.");j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,q=p()}for(;;){if(l.toString()in g[s])break;if(s==0)throw new Error(B||"Parsing halted.");o(1),s=d[d.length-1]}r=q,q=l,s=d[d.length-1],t=g[s]&&g[s][l],k=3}if(t[0]instanceof Array&&t.length>1)throw new Error("Parse Error: multiple actions possible at state: "+s+", token: "+q);switch(t[0]){case 1:d.push(q),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(t[1]),q=null,r?(q=r,r=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,k>0&&k--);break;case 2:y=this.productions_[t[1]][1],w.$=e[e.length-y],w._$={first_line:f[f.length-(y||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(y||1)].first_column,last_column:f[f.length-1].last_column},v=this.performAction.call(w,h,j,i,this.yy,t[1],e,f);if(typeof v!="undefined")return v;y&&(d=d.slice(0,-1*y*2),e=e.slice(0,-1*y),f=f.slice(0,-1*y)),d.push(this.productions_[t[1]][0]),e.push(w.$),f.push(w._$),z=g[d[d.length-2]][d[d.length-1]],d.push(z);break;case 3:return!0}}return!0}};c.exports=d}),define("ace/mode/coffee/nodes",["require","exports","module","ace/mode/coffee/scope","ace/mode/coffee/helpers"],function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,ba,bb,bc,bd,be,bf,bg,bh,bi,bj=Object.prototype.hasOwnProperty,bk=function(a,b){function d(){this.constructor=a}for(var c in b)bj.call(b,c)&&(a[c]=b[c]);d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype;return a},bl=function(a,b){return function(){return a.apply(b,arguments)}},bm=Array.prototype.indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]===a)return b;return-1};N=a("ace/mode/coffee/scope").Scope,bi=a("ace/mode/coffee/helpers"),Z=bi.compact,bb=bi.flatten,ba=bi.extend,bd=bi.merge,$=bi.del,bf=bi.starts,_=bi.ends,bc=bi.last,b.extend=ba,Y=function(){return!0},E=function(){return!1},S=function(){return this},D=function(){this.negated=!this.negated;return this},b.Base=g=function(){function a(){}a.prototype.compile=function(a,b){var c;a=ba({},a),b&&(a.level=b),c=this.unfoldSoak(a)||this,c.tab=a.indent;return a.level===A||!c.isStatement(a)?c.compileNode(a):c.compileClosure(a)},a.prototype.compileClosure=function(a){if(this.jumps()||this instanceof T)throw SyntaxError("cannot use a pure statement in an expression.");a.sharedScope=!0;return k.wrap(this).compileNode(a)},a.prototype.cache=function(a,b,c){var d,e;if(!this.isComplex()){d=b?this.compile(a,b):this;return[d,d]}d=new B(c||a.scope.freeVariable("ref")),e=new f(d,this);return b?[e.compile(a,b),d.value]:[e,d]},a.prototype.compileLoopReference=function(a,b){var c,d;c=d=this.compile(a,x),-Infinity<+c&&+c<Infinity||q.test(c)&&a.scope.check(c,!0)||(c=""+(d=a.scope.freeVariable(b))+" = "+c);return[c,d]},a.prototype.makeReturn=function(){return new L(this)},a.prototype.contains=function(a){var b;b=!1,this.traverseChildren(!1,function(c){if(a(c)){b=!0;return!1}});return b},a.prototype.containsType=function(a){return this instanceof a||this.contains(function(b){return b instanceof a})},a.prototype.lastNonComment=function(a){var b;b=a.length;while(b--)if(!(a[b]instanceof m))return a[b];return null},a.prototype.toString=function(a,b){var c;a==null&&(a=""),b==null&&(b=this.constructor.name),c="\n"+a+b,this.soak&&(c+="?"),this.eachChild(function(b){return c+=b.toString(a+R)});return c},a.prototype.eachChild=function(a){var b,c,d,e,f,g,h,i;if(!this.children)return this;h=this.children;for(d=0,f=h.length;d<f;d++){b=h[d];if(this[b]){i=bb([this[b]]);for(e=0,g=i.length;e<g;e++){c=i[e];if(a(c)===!1)return this}}}return this},a.prototype.traverseChildren=function(a,b){return this.eachChild(function(c){if(b(c)===!1)return!1;return c.traverseChildren(a,b)})},a.prototype.invert=function(){return new G("!",this)},a.prototype.unwrapAll=function(){var a;a=this;while(a!==(a=a.unwrap()))continue;return a},a.prototype.children=[],a.prototype.isStatement=E,a.prototype.jumps=E,a.prototype.isComplex=Y,a.prototype.isChainable=E,a.prototype.isAssignable=E,a.prototype.unwrap=S,a.prototype.unfoldSoak=E,a.prototype.assigns=E;return a}(),b.Block=h=function(){function a(a){this.expressions=Z(bb(a||[]))}bk(a,g),a.prototype.children=["expressions"],a.prototype.push=function(a){this.expressions.push(a);return this},a.prototype.pop=function(){return this.expressions.pop()},a.prototype.unshift=function(a){this.expressions.unshift(a);return this},a.prototype.unwrap=function(){return this.expressions.length===1?this.expressions[0]:this},a.prototype.isEmpty=function(){return!this.expressions.length},a.prototype.isStatement=function(a){var b,c,d,e;e=this.expressions;for(c=0,d=e.length;c<d;c++){b=e[c];if(b.isStatement(a))return!0}return!1},a.prototype.jumps=function(a){var b,c,d,e;e=this.expressions;for(c=0,d=e.length;c<d;c++){b=e[c];if(b.jumps(a))return b}},a.prototype.makeReturn=function(){var a,b;b=this.expressions.length;while(b--){a=this.expressions[b];if(!(a instanceof m)){this.expressions[b]=a.makeReturn(),a instanceof L&&!a.expression&&this.expressions.splice(b,1);break}}return this},a.prototype.compile=function(b,c){b==null&&(b={});return b.scope?a.__super__.compile.call(this,b,c):this.compileRoot(b)},a.prototype.compileNode=function(a){var b,c,d,e,f,g,h;this.tab=a.indent,e=a.level===A,c=[],h=this.expressions;for(f=0,g=h.length;f<g;f++)d=h[f],d=d.unwrapAll(),d=d.unfoldSoak(a)||d,e?(d.front=!0,b=d.compile(a),c.push(d.isStatement(a)?b:this.tab+b+";")):c.push(d.compile(a,x));if(e)return c.join("\n");b=c.join(", ")||"void 0";return c.length>1&&a.level>=x?"("+b+")":b},a.prototype.compileRoot=function(a){var b;a.indent=this.tab=a.bare?"":R,a.scope=new N(null,this,null),a.level=A,b=this.compileWithDeclarations(a);return a.bare?b:"(function() {\n"+b+"\n}).call(this);\n"},a.prototype.compileWithDeclarations=function(a){var b,c,d,e,f,g,h,i,j,k;c=g="",k=this.expressions;for(f=0,j=k.length;f<j;f++){e=k[f],e=e.unwrap();if(!(e instanceof m||e instanceof B))break}a=bd(a,{level:A}),f&&(h=this.expressions.splice(f,this.expressions.length),c=this.compileNode(a),this.expressions=h),g=this.compileNode(a),i=a.scope,i.expressions===this&&(d=a.scope.hasDeclarations(),b=i.hasAssignments,(d||b)&&f&&(c+="\n"),d&&(c+=""+this.tab+"var "+i.declaredVariables().join(", ")+";\n"),b&&(c+=""+this.tab+"var "+be(i.assignedVariables().join(", "),this.tab)+";\n"));return c+g},a.wrap=function(b){if(b.length===1&&b[0]instanceof a)return b[0];return new a(b)};return a}(),b.Literal=B=function(){function a(a){this.value=a}bk(a,g),a.prototype.makeReturn=function(){return this.isStatement()?this:new L(this)},a.prototype.isAssignable=function(){return q.test(this.value)},a.prototype.isStatement=function(){var a;return(a=this.value)==="break"||a==="continue"||a==="debugger"},a.prototype.isComplex=E,a.prototype.assigns=function(a){return a===this.value},a.prototype.jumps=function(a){if(!this.isStatement())return!1;return!a||!(a.loop||a.block&&this.value!=="continue")?this:!1},a.prototype.compileNode=function(a){var b;b=this.isUndefined?a.level>=v?"(void 0)":"void 0":this.value.reserved?'"'+this.value+'"':this.value;return this.isStatement()?""+this.tab+b+";":b},a.prototype.toString=function(){return' "'+this.value+'"'};return a}(),b.Return=L=function(){function a(a){a&&!a.unwrap().isUndefined&&(this.expression=a)}bk(a,g),a.prototype.children=["expression"],a.prototype.isStatement=Y,a.prototype.makeReturn=S,a.prototype.jumps=S,a.prototype.compile=function(b,c){var d,e;d=(e=this.expression)!=null?e.makeReturn():void 0;return!d||d instanceof a?a.__super__.compile.call(this,b,c):d.compile(b,c)},a.prototype.compileNode=function(a){return this.tab+("return"+(this.expression?" "+this.expression.compile(a,z):"")+";")};return a}(),b.Value=W=function(){function a(b,c,d){if(!c&&b instanceof a)return b;this.base=b,this.properties=c||[],d&&(this[d]=!0);return this}bk(a,g),a.prototype.children=["base","properties"],a.prototype.push=function(a){this.properties.push(a);return this},a.prototype.hasProperties=function(){return!!this.properties.length},a.prototype.isArray=function(){return!this.properties.length&&this.base instanceof e},a.prototype.isComplex=function(){return this.hasProperties()||this.base.isComplex()},a.prototype.isAssignable=function(){return this.hasProperties()||this.base.isAssignable()},a.prototype.isSimpleNumber=function(){return this.base instanceof B&&M.test(this.base.value)},a.prototype.isAtomic=function(){var a,b,c,d;d=this.properties.concat(this.base);for(b=0,c=d.length;b<c;b++){a=d[b];if(a.soak||a instanceof i)return!1}return!0},a.prototype.isStatement=function(a){return!this.properties.length&&this.base.isStatement(a)},a.prototype.assigns=function(a){return!this.properties.length&&this.base.assigns(a)},a.prototype.jumps=function(a){return!this.properties.length&&this.base.jumps(a)},a.prototype.isObject=function(a){if(this.properties.length)return!1;return this.base instanceof F&&(!a||this.base.generated)},a.prototype.isSplice=function(){return bc(this.properties)instanceof O},a.prototype.makeReturn=function(){return this.properties.length?a.__super__.makeReturn.call(this):this.base.makeReturn()},a.prototype.unwrap=function(){return this.properties.length?this:this.base},a.prototype.cacheReference=function(b){var c,d,e,g;e=bc(this.properties);if(this.properties.length<2&&!this.base.isComplex()&&(e!=null?!e.isComplex():!void 0))return[this,this];c=new a(this.base,this.properties.slice(0,-1)),c.isComplex()&&(d=new B(b.scope.freeVariable("base")),c=new a(new I(new f(d,c))));if(!e)return[c,d];e.isComplex()&&(g=new B(b.scope.freeVariable("name")),e=new u(new f(g,e.index)),g=new u(g));return[c.push(e),new a(d||c.base,[g||e])]},a.prototype.compileNode=function(a){var b,c,e,f,g;this.base.front=this.front,e=this.properties,b=this.base.compile(a,e.length?v:null),e[0]instanceof d&&this.isSimpleNumber()&&(b="("+b+")");for(f=0,g=e.length;f<g;f++)c=e[f],b+=c.compile(a);return b},a.prototype.unfoldSoak=function(b){var c;if(this.unfoldedSoak!=null)return this.unfoldedSoak;c=bl(function(){var c,d,e,g,h,i,j,k;if(e=this.base.unfoldSoak(b)){Array.prototype.push.apply(e.body.properties,this.properties);return e}k=this.properties;for(d=0,j=k.length;d<j;d++){g=k[d];if(g.soak){g.soak=!1,c=new a(this.base,this.properties.slice(0,d)),i=new a(this.base,this.properties.slice(d)),c.isComplex()&&(h=new B(b.scope.freeVariable("ref")),c=new I(new f(h,c)),i.base=h);return new s(new n(c),i,{soak:!0})}}return null},this)();return this.unfoldedSoak=c||!1};return a}(),b.Comment=m=function(){function a(a){this.comment=a}bk(a,g),a.prototype.isStatement=Y,a.prototype.makeReturn=S,a.prototype.compileNode=function(a,b){var c;c="/*"+be(this.comment,this.tab)+"*/",(b||a.level)===A&&(c=a.indent+c);return c};return a}(),b.Call=i=function(){function a(a,b,c){this.args=b!=null?b:[],this.soak=c,this.isNew=!1,this.isSuper=a==="super",this.variable=this.isSuper?null:a}bk(a,g),a.prototype.children=["variable","args"],a.prototype.newInstance=function(){var b;b=this.variable.base||this.variable,b instanceof a?b.newInstance():this.isNew=!0;return this},a.prototype.superReference=function(a){var b,c;b=a.scope.method;if(!b)throw SyntaxError("cannot call super outside of a function.");c=b.name;if(!c)throw SyntaxError("cannot call super on an anonymous function.");return b.klass?""+b.klass+".__super__."+c:""+c+".__super__.constructor"},a.prototype.unfoldSoak=function(b){var c,d,e,f,g,h,i,j,k;if(this.soak){if(this.variable){if(d=bg(b,this,"variable"))return d;j=(new W(this.variable)).cacheReference(b),e=j[0],g=j[1]}else e=new B(this.superReference(b)),g=new W(e);g=new a(g,this.args),g.isNew=this.isNew,e=new B("typeof "+e.compile(b)+' === "function"');return new s(e,new W(g),{soak:!0})}c=this,f=[];for(;;){if(c.variable instanceof a){f.push(c),c=c.variable;continue}if(!(c.variable instanceof W))break;f.push(c);if(!((c=c.variable.base)instanceof a))break}k=f.reverse();for(h=0,i=k.length;h<i;h++)c=k[h],d&&(c.variable instanceof a?c.variable=d:c.variable.base=d),d=bg(b,c,"variable");return d},a.prototype.filterImplicitObjects=function(a){var b,c,d,e,g,h,i,j,k,l;c=[];for(h=0,j=a.length;h<j;h++){b=a[h];if(!((typeof b.isObject=="function"?b.isObject():void 0)&&b.base.generated)){c.push(b);continue}d=null,l=b.base.properties;for(i=0,k=l.length;i<k;i++)e=l[i],e instanceof f?(d||c.push(d=new F(g=[],!0)),g.push(e)):(c.push(e),d=null)}return c},a.prototype.compileNode=function(a){var b,c,d,e;(e=this.variable)!=null&&(e.front=this.front);if(d=P.compileSplattedArray(a,this.args,!0))return this.compileSplat(a,d);c=this.filterImplicitObjects(this.args),c=function(){var d,e,f;f=[];for(d=0,e=c.length;d<e;d++)b=c[d],f.push(b.compile(a,x));return f}().join(", ");return this.isSuper?this.superReference(a)+(".call(this"+(c&&", "+c)+")"):(this.isNew?"new ":"")+this.variable.compile(a,v)+("("+c+")")},a.prototype.compileSuper=function(a,b){return""+this.superReference(b)+".call(this"+(a.length?", ":"")+a+")"},a.prototype.compileSplat=function(a,b){var c,d,e,f,g;if(this.isSuper)return""+this.superReference(a)+".apply(this, "+b+")";if(this.isNew){e=this.tab+R;return"(function(func, args, ctor) {\n"+e+"ctor.prototype = func.prototype;\n"+e+"var child = new ctor, result = func.apply(child, args);\n"+e+'return typeof result === "object" ? result : child;\n'+this.tab+"})("+this.variable.compile(a,x)+", "+b+", function() {})"}c=new W(this.variable),(f=c.properties.pop())&&c.isComplex()?(g=a.scope.freeVariable("ref"),d="("+g+" = "+c.compile(a,x)+")"+f.compile(a)):(d=c.compile(a,v),M.test(d)&&(d="("+d+")"),f?(g=d,d+=f.compile(a)):g="null");return""+d+".apply("+g+", "+b+")"};return a}(),b.Extends=o=function(){function a(a,b){this.child=a,this.parent=b}bk(a,g),a.prototype.children=["child","parent"],a.prototype.compile=function(a){bh("hasProp");return(new i(new W(new B(bh("extends"))),[this.child,this.parent])).compile(a)};return a}(),b.Access=d=function(){function a(a,b){this.name=a,this.name.asKey=!0,this.proto=b==="proto"?".prototype":"",this.soak=b==="soak"}bk(a,g),a.prototype.children=["name"],a.prototype.compile=function(a){var b;b=this.name.compile(a);return this.proto+(r.test(b)?"["+b+"]":"."+b)},a.prototype.isComplex=E;return a}(),b.Index=u=function(){function a(a){this.index=a}bk(a,g),a.prototype.children=["index"],a.prototype.compile=function(a){return(this.proto?".prototype":"")+("["+this.index.compile(a,z)+"]")},a.prototype.isComplex=function(){return this.index.isComplex()};return a}(),b.Range=K=function(){function a(a,b,c){this.from=a,this.to=b,this.exclusive=c==="exclusive",this.equals=this.exclusive?"":"="}bk(a,g),a.prototype.children=["from","to"],a.prototype.compileVariables=function(a){var b,c,d,e,f;a=bd(a,{top:!0}),c=this.from.cache(a,x),this.from=c[0],this.fromVar=c[1],d=this.to.cache(a,x),this.to=d[0],this.toVar=d[1];if(b=$(a,"step"))e=b.cache(a,x),this.step=e[0],this.stepVar=e[1];f=[this.fromVar.match(M),this.toVar.match(M)],this.fromNum=f[0],this.toNum=f[1];if(this.stepVar)return this.stepNum=this.stepVar.match(M)},a.prototype.compileNode=function(a){var b,c,d,e,f,g,h,i,j,k,l,m;this.fromVar||this.compileVariables(a);if(!a.index)return this.compileArray(a);g=this.fromNum&&this.toNum,f=$(a,"index"),k=""+f+" = "+this.from,this.to!==this.toVar&&(k+=", "+this.to),this.step!==this.stepVar&&(k+=", "+this.step),l=[""+f+" <"+this.equals,""+f+" >"+this.equals],h=l[0],e=l[1],c=this.stepNum?c=+this.stepNum>0?""+h+" "+this.toVar:""+e+" "+this.toVar:g?(m=[+this.fromNum,+this.toNum],d=m[0],j=m[1],m,c=d<=j?""+h+" "+j:""+e+" "+j):(b=""+this.fromVar+" <= "+this.toVar,c=""+b+" ? "+h+" "+this.toVar+" : "+e+" "+this.toVar),i=this.stepVar?""+f+" += "+this.stepVar:g?d<=j?""+f+"++":""+f+"--":""+b+" ? "+f+"++ : "+f+"--";return""+k+"; "+c+"; "+i},a.prototype.compileArray=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n;if(this.fromNum&&this.toNum&&Math.abs(this.fromNum-this.toNum)<=20){h=function(){n=[];for(var a=l=+this.fromNum,b=+this.toNum;l<=b?a<=b:a>=b;l<=b?a++:a--)n.push(a);return n}.apply(this,arguments),this.exclusive&&h.pop();return"["+h.join(", ")+"]"}e=this.tab+R,d=a.scope.freeVariable("i"),i=a.scope.freeVariable("results"),g="\n"+e+i+" = [];",this.fromNum&&this.toNum?(a.index=d,b=this.compileNode(a)):(j=""+d+" = "+this.from+(this.to!==this.toVar?", "+this.to:""),c=""+this.fromVar+" <= "+this.toVar,b="var "+j+"; "+c+" ? "+d+" <"+this.equals+" "+this.toVar+" : "+d+" >"+this.equals+" "+this.toVar+"; "+c+" ? "+d+"++ : "+d+"--"),f="{ "+i+".push("+d+"); }\n"+e+"return "+i+";\n"+a.indent;return"(function() {"+g+"\n"+e+"for ("+b+")"+f+"}).apply(this, arguments)"};return a}(),b.Slice=O=function(){function a(b){this.range=b,a.__super__.constructor.call(this)}bk(a,g),a.prototype.children=["range"],a.prototype.compileNode=function(a){var b,c,d,e,f,g;g=this.range,e=g.to,c=g.from,d=c&&c.compile(a,z)||"0",b=e&&e.compile(a,z),e&&(!!this.range.exclusive||+b!==-1)&&(f=", "+(this.range.exclusive?b:M.test(b)?(+b+1).toString():"("+b+" + 1) || 9e9"));return".slice("+d+(f||"")+")"};return a}(),b.Obj=F=function(){function a(a,b){this.generated=b!=null?b:!1,this.objects=this.properties=a||[]}bk(a,g),a.prototype.children=["properties"],a.prototype.compileNode=function(a){var b,c,d,e,g,h,i,j,k,l,n;k=this.properties;if(!k.length)return this.front?"({})":"{}";if(this.generated)for(l=0,n=k.length;l<n;l++){h=k[l];if(h instanceof W)throw new Error("cannot have an implicit value in an implicit object")}c=a.indent+=R,g=this.lastNonComment(this.properties),k=function(){var h,i;i=[];for(b=0,h=k.length;b<h;b++)j=k[b],e=b===k.length-1?"":j===g||j instanceof m?"\n":",\n",d=j instanceof m?"":c,j instanceof W&&j["this"]&&(j=new f(j.properties[0].name,j,"object")),j instanceof m||(j instanceof f||(j=new f(j,j,"object")),(j.variable.base||j.variable).asKey=!0),i.push(d+j.compile(a,A)+e);return i}(),k=k.join(""),i="{"+(k&&"\n"+k+"\n"+this.tab)+"}";return this.front?"("+i+")":i},a.prototype.assigns=function(a){var b,c,d,e;e=this.properties;for(c=0,d=e.length;c<d;c++){b=e[c];if(b.assigns(a))return!0}return!1};return a}(),b.Arr=e=function(){function a(a){this.objects=a||[]}bk(a,g),a.prototype.children=["objects"],a.prototype.filterImplicitObjects=i.prototype.filterImplicitObjects,a.prototype.compileNode=function(a){var b,c,d;if(!this.objects.length)return"[]";a.indent+=R,d=this.filterImplicitObjects(this.objects);if(b=P.compileSplattedArray(a,d))return b;b=function(){var b,e,f;f=[];for(b=0,e=d.length;b<e;b++)c=d[b],f.push(c.compile(a,x));return f}().join(", ");return b.indexOf("\n")>=0?"[\n"+a.indent+b+"\n"+this.tab+"]":"["+b+"]"},a.prototype.assigns=function(a){var b,c,d,e;e=this.objects;for(c=0,d=e.length;c<d;c++){b=e[c];if(b.assigns(a))return!0}return!1};return a}(),b.Class=j=function(){function a(a,b,c){this.variable=a,this.parent=b,this.body=c!=null?c:new h,this.boundFuncs=[],this.body.classBody=!0}bk(a,g),a.prototype.children=["variable","parent","body"],a.prototype.determineName=function(){var a,b;if(!this.variable)return null;a=(b=bc(this.variable.properties))?b instanceof d&&b.name.value:this.variable.base.value;return a&&(a=q.test(a)&&a)},a.prototype.setContext=function(a){return this.body.traverseChildren(!1,function(b){if(b.classBody)return!1;if(b instanceof B&&b.value==="this")return b.value=a;if(b instanceof l){b.klass=a;if(b.bound)return b.context=a}})},a.prototype.addBoundFunctions=function(a){var b,c,d,e,f,g;if(this.boundFuncs.length){f=this.boundFuncs,g=[];for(d=0,e=f.length;d<e;d++)c=f[d],b=c.compile(a),g.push(this.ctor.body.unshift(new B("this."+b+" = "+bh("bind")+"(this."+b+", this)")));return g}},a.prototype.addProperties=function(a,b,c){var e,g,h,i,j;j=a.base.properties.slice(0),h=function(){var a;a=[];while(e=j.shift()){if(e instanceof f){g=e.variable.base,delete e.context,i=e.value;if(g.value==="constructor"){if(this.ctor)throw new Error("cannot define more than one constructor in a class");if(i.bound)throw new Error("cannot define a constructor as a bound function");i instanceof l?e=this.ctor=i:(this.externalCtor=c.scope.freeVariable("class"),e=new f(new B(this.externalCtor),i))}else e.variable["this"]||(e.variable=new W(new B(b),[new d(g,"proto")])),i instanceof l&&i.bound&&(this.boundFuncs.push(g),i.bound=!1)}a.push(e)}return a}.call(this);return Z(h)},a.prototype.walkBody=function(b,c){return this.traverseChildren(!1,bl(function(d){var e,f,g,i,j;if(d instanceof a)return!1;if(d instanceof h){j=e=d.expressions;for(f=0,i=j.length;f<i;f++)g=j[f],g instanceof W&&g.isObject(!0)&&(e[f]=this.addProperties(g,b,c));return d.expressions=e=bb(e)}},this))},a.prototype.ensureConstructor=function(a){this.ctor||(this.ctor=new l,this.parent&&this.ctor.body.push(new B(""+a+".__super__.constructor.apply(this, arguments)")),this.externalCtor&&this.ctor.body.push(new B(""+this.externalCtor+".apply(this, arguments)")),this.body.expressions.unshift(this.ctor)),this.ctor.ctor=this.ctor.name=a,this.ctor.klass=null;return this.ctor.noReturn=!0},a.prototype.compileNode=function(a){var b,c,d,e;b=this.determineName(),e=b||this.name||"_Class",d=new B(e),this.setContext(e),this.walkBody(e,a),this.ensureConstructor(e),this.parent&&this.body.expressions.unshift(new o(d,this.parent)),this.ctor instanceof l||this.body.expressions.unshift(this.ctor),this.body.expressions.push(d),this.addBoundFunctions(a),c=new I(k.wrap(this.body),!0),this.variable&&(c=new f(this.variable,c));return c.compile(a)};return a}(),b.Assign=f=function(){function a(a,b,c,d){this.variable=a,this.value=b,this.context=c,this.param=d&&d.param}bk(a,g),a.prototype.children=["variable","value"],a.prototype.assigns=function(a){return this[this.context==="object"?"value":"variable"].assigns(a)},a.prototype.unfoldSoak=function(a){return bg(a,this,"variable")},a.prototype.compileNode=function(a){var b,c,d,e,f;if(b=this.variable instanceof W){if(this.variable.isArray()||this.variable.isObject())return this.compilePatternMatch(a);if(this.variable.isSplice())return this.compileSplice(a);if((f=this.context)==="||="||f==="&&="||f==="?=")return this.compileConditional(a)}d=this.variable.compile(a,x);if(!this.context&&!this.variable.isAssignable())throw SyntaxError('"'+this.variable.compile(a)+'" cannot be assigned.');this.context||b&&(this.variable.namespaced||this.variable.hasProperties())||(this.param?a.scope.add(d,"var"):a.scope.find(d)),this.value instanceof l&&(c=C.exec(d))&&(this.value.name=c[2],c[1]&&(this.value.klass=c[1])),e=this.value.compile(a,x);if(this.context==="object")return""+d+": "+e;e=d+(" "+(this.context||"=")+" ")+e;return a.level<=x?e:"("+e+")"},a.prototype.compilePatternMatch=function(b){var c,e,f,g,h,i,j,k,l,m,n,o,p,r,s,t,v,w,z,C,D,E;r=b.level===A,t=this.value,l=this.variable.base.objects;if(!(m=l.length)){f=t.compile(b);return b.level>=y?"("+f+")":f}i=this.variable.isObject();if(r&&m===1&&!((k=l[0])instanceof P)){k instanceof a?(z=k,h=z.variable.base,k=z.value):k.base instanceof I?(C=(new W(k.unwrapAll())).cacheReference(b),k=C[0],h=C[1]):h=i?k["this"]?k.properties[0].name:k:new B(0),c=q.test(h.unwrap().value||0),t=new W(t),t.properties.push(new(c?d:u)(h));return(new a(k,t,null,{param:this.param})).compile(b,A)}v=t.compile(b,x),e=[],p=!1;if(!q.test(v)||this.variable.assigns(v))e.push(""+(n=b.scope.freeVariable("ref"))+" = "+v),v=n;for(g=0,w=l.length;g<w;g++){k=l[g],h=g,i&&(k instanceof a?(D=k,h=D.variable.base,k=D.value):k.base instanceof I?(E=(new W(k.unwrapAll())).cacheReference(b),k=E[0],h=E[1]):h=k["this"]?k.properties[0].name:k);if(!p&&k instanceof P)s=""+m+" <= "+v+".length ? "+bh("slice")+".call("+v+", "+g,(o=m-g-1)?(j=b.scope.freeVariable("i"),s+=", "+j+" = "+v+".length - "+o+") : ("+j+" = "+g+", [])"):s+=") : []",s=new B(s),p=""+j+"++";else{if(k instanceof P){k=k.name.compile(b);throw SyntaxError("multiple splats are disallowed in an assignment: "+k+" ...")}typeof h=="number"?(h=new B(p||h),c=!1):c=i&&q.test(h.unwrap().value||0),s=new W(new B(v),[new(c?d:u)(h)])}e.push((new a(k,s,null,{param:this.param})).compile(b,A))}r||e.push(v),f=e.join(", ");return b.level<x?f:"("+f+")"},a.prototype.compileConditional=function(b){var c,d,e;e=this.variable.cacheReference(b),c=e[0],d=e[1],bm.call(this.context,"?")>=0&&(b.isExistentialEquals=!0);return(new G(this.context.slice(0,-1),c,new a(d,this.value,"="))).compile(b)},a.prototype.compileSplice=function(a){var b,c,d,e,f,g,h,i,j,k,l,m;k=this.variable.properties.pop().range,d=k.from,h=k.to,c=k.exclusive,g=this.variable.compile(a),l=(d!=null?d.cache(a,y):void 0)||["0","0"],e=l[0],f=l[1],h?(d!=null?d.isSimpleNumber():void 0)&&h.isSimpleNumber()?(h=+h.compile(a)- +f,c||(h+=1)):(h=h.compile(a)+" - "+f,c||(h+=" + 1")):h="9e9",m=this.value.cache(a,x),i=m[0],j=m[1],b="[].splice.apply("+g+", ["+e+", "+h+"].concat("+i+")), "+j;return a.level>A?"("+b+")":b};return a}(),b.Code=l=function(){function a(a,b,c){this.params=a||[],this.body=b||new h,this.bound=c==="boundfunc",this.bound&&(this.context="this")}bk(a,g),a.prototype.children=["params","body"],a.prototype.isStatement=function(){return!!this.ctor},a.prototype.jumps=E,a.prototype.compileNode=function(a){var b,c,d,g,h,i,j,k,l,m,n,o,p,q,r,t,u,w,x,y,z,A,C,D;a.scope=new N(a.scope,this.body,this),a.scope.shared=$(a,"sharedScope"),a.indent+=R,delete a.bare,o=[],c=[],z=this.params;for(q=0,u=z.length;q<u;q++){j=z[q];if(j.splat){A=this.params;for(r=0,w=A.length;r<w;r++)i=A[r],i.name.value&&a.scope.add(i.name.value,"var",!0);l=new f(new W(new e(function(){var b,c,d,e;d=this.params,e=[];for(b=0,c=d.length;b<c;b++)i=d[b],e.push(i.asReference(a));return e}.call(this))),new W(new B("arguments")));break}}C=this.params;for(t=0,x=C.length;t<x;t++)j=C[t],j.isComplex()?(n=k=j.asReference(a),j.value&&(n=new G("?",k,j.value)),c.push(new f(new W(j.name),n,"=",{param:!0}))):(k=j,j.value&&(h=new B(k.name.value+" == null"),n=new f(new W(j.name),j.value,"="),c.push(new s(h,n)))),l||o.push(k);p=this.body.isEmpty(),l&&c.unshift(l),c.length&&(D=this.body.expressions).unshift.apply(D,c);if(!l)for(d=0,y=o.length;d<y;d++)m=o[d],a.scope.parameter(o[d]=m.compile(a));!p&&!this.noReturn&&this.body.makeReturn(),g=a.indent,b="function",this.ctor&&(b+=" "+this.name),b+="("+o.join(", ")+") {",this.body.isEmpty()||(b+="\n"+this.body.compileWithDeclarations(a)+"\n"+this.tab),b+="}";if(this.ctor)return this.tab+b;if(this.bound)return bh("bind")+("("+b+", "+this.context+")");return this.front||a.level>=v?"("+b+")":b},a.prototype.traverseChildren=function(b,c){if(b)return a.__super__.traverseChildren.call(this,b,c)};return a}(),b.Param=H=function(){function a(a,b,c){this.name=a,this.value=b,this.splat=c}bk(a,g),a.prototype.children=["name","value"],a.prototype.compile=function(a){return this.name.compile(a,x)},a.prototype.asReference=function(a){var b;if(this.reference)return this.reference;b=this.name,b["this"]?(b=b.properties[0].name,b.value.reserved&&(b=new B("_"+b.value))):b.isComplex()&&(b=new B(a.scope.freeVariable("arg"))),b=new W(b),this.splat&&(b=new P(b));return this.reference=b},a.prototype.isComplex=function(){return this.name.isComplex()};return a}(),b.Splat=P=function(){function a(a){this.name=a.compile?a:new B(a)}bk(a,g),a.prototype.children=["name"],a.prototype.isAssignable=Y,a.prototype.assigns=function(a){return this.name.assigns(a)},a.prototype.compile=function(a){return this.index!=null?this.compileParam(a):this.name.compile(a)},a.compileSplattedArray=function(b,c,d){var e,f,g,h,i,j,k;i=-1;while((j=c[++i])&&!(j instanceof a))continue;if(i>=c.length)return"";if(c.length===1){g=c[0].compile(b,x);if(d)return g;return""+bh("slice")+".call("+g+")"}e=c.slice(i);for(h=0,k=e.length;h<k;h++)j=e[h],g=j.compile(b,x),e[h]=j instanceof a?""+bh("slice")+".call("+g+")":"["+g+"]";if(i===0)return e[0]+(".concat("+e.slice(1).join(", ")+")");f=function(){var a,d,e,f;e=c.slice(0,i),f=[];for(a=0,d=e.length;a<d;a++)j=e[a],f.push(j.compile(b,x));return f}();return"["+f.join(", ")+"].concat("+e.join(", ")+")"};return a}(),b.While=X=function(){function a(a,b){this.condition=(b!=null?b.invert:void 0)?a.invert():a,this.guard=b!=null?b.guard:void 0}bk(a,g),a.prototype.children=["condition","guard","body"],a.prototype.isStatement=Y,a.prototype.makeReturn=function(){this.returns=!0;return this},a.prototype.addBody=function(a){this.body=a;return this},a.prototype.jumps=function(){var a,b,c,d;a=this.body.expressions;if(!a.length)return!1;for(c=0,d=a.length;c<d;c++){b=a[c];if(b.jumps({loop:!0}))return b}return!1},a.prototype.compileNode=function(a){var b,c,d,e;a.indent+=R,e="",b=this.body;if(b.isEmpty())b="";else{if(a.level>A||this.returns)d=a.scope.freeVariable("results"),e=""+this.tab+d+" = [];\n",b&&(b=J.wrap(d,b));this.guard&&(b=h.wrap([new s(this.guard,b)])),b="\n"+b.compile(a,A)+"\n"+this.tab}c=e+this.tab+("while ("+this.condition.compile(a,z)+") {"+b+"}"),this.returns&&(c+="\n"+this.tab+"return "+d+";");return c};return a}(),b.Op=G=function(){function c(b,c,d,e){var f;if(b==="in")return new t(c,d);if(b==="do"){f=new i(c,c.params||[]),f["do"]=!0;return f}if(b==="new"){if(c instanceof i&&!c["do"])return c.newInstance();if(c instanceof l&&c.bound||c["do"])c=new I(c)}this.operator=a[b]||b,this.first=c,this.second=d,this.flip=!!e;return this}var a,b;bk(c,g),a={"==":"===","!=":"!==",of:"in"},b={"!==":"===","===":"!=="},c.prototype.children=["first","second"],c.prototype.isSimpleNumber=E,c.prototype.isUnary=function(){return!this.second},c.prototype.isComplex=function(){var a;return!this.isUnary()||(a=this.operator)!=="+"&&a!=="-"||this.first.isComplex()},c.prototype.isChainable=function(){var a;return(a=this.operator)==="<"||a===">"||a===">="||a==="<="||a==="==="||a==="!=="},c.prototype.invert=function(){var a,d,e,f,g;if(this.isChainable()&&this.first.isChainable()){a=!0,d=this;while(d&&d.operator)a&&(a=d.operator in b),d=d.first;if(!a)return(new I(this)).invert();d=this;while(d&&d.operator)d.invert=!d.invert,d.operator=b[d.operator],d=d.first;return this}if(f=b[this.operator]){this.operator=f,this.first.unwrap()instanceof c&&this.first.invert();return this}return this.second?(new I(this)).invert():this.operator==="!"&&(e=this.first.unwrap())instanceof c&&((g=e.operator)==="!"||g==="in"||g==="instanceof")?e:new c("!",this)},c.prototype.unfoldSoak=function(a){var b;return((b=this.operator)==="++"||b==="--"||b==="delete")&&bg(a,this,"first")},c.prototype.compileNode=function(a){var b;if(this.isUnary())return this.compileUnary(a);if(this.isChainable()&&this.first.isChainable())return this.compileChain(a);if(this.operator==="?")return this.compileExistence(a);this.first.front=this.front,b=this.first.compile(a,y)+" "+this.operator+" "+this.second.compile(a,y);return a.level<=y?b:"("+b+")"},c.prototype.compileChain=function(a){var b,c,d,e;e=this.first.second.cache(a),this.first.second=e[0],d=e[1],c=this.first.compile(a,y),b=""+c+" "+(this.invert?"&&":"||")+" "+d.compile(a)+" "+this.operator+" "+this.second.compile(a,y);return"("+b+")"},c.prototype.compileExistence=function(a){var b,c;this.first.isComplex()?(c=new B(a.scope.freeVariable("ref")),b=new I(new f(c,this.first))):(b=this.first,c=b);return(new s(new n(b),c,{type:"if"})).addElse(this.second).compile(a)},c.prototype.compileUnary=function(a){var b,d;d=[b=this.operator],(b==="new"||b==="typeof"||b==="delete"||(b==="+"||b==="-")&&this.first instanceof c&&this.first.operator===b)&&d.push(" "),b==="new"&&this.first.isStatement(a)&&(this.first=new I(this.first)),d.push(this.first.compile(a,y)),this.flip&&d.reverse();return d.join("")},c.prototype.toString=function(a){return c.__super__.toString.call(this,a,this.constructor.name+" "+this.operator)};return c}(),b.In=t=function(){function a(a,b){this.object=a,this.array=b}bk(a,g),a.prototype.children=["object","array"],a.prototype.invert=D,a.prototype.compileNode=function(a){var b,c,d,e,f;if(this.array instanceof W&&this.array.isArray()){f=this.array.base.objects;for(d=0,e=f.length;d<e;d++){c=f[d];if(c instanceof P){b=!0;break}}if(!b)return this.compileOrTest(a)}return this.compileLoopTest(a)},a.prototype.compileOrTest=function(a){var b,c,d,e,f,g,h,i,j;i=this.object.cache(a,y),g=i[0],f=i[1],j=this.negated?[" !== "," && "]:[" === "," || "],b=j[0],c=j[1],h=function(){var c,h,i;h=this.array.base.objects,i=[];for(d=0,c=h.length;d<c;d++)e=h[d],i.push((d?f:g)+b+e.compile(a,y));return i}.call(this);if(h.length===0)return"false";h=h.join(c);return a.level<y?h:"("+h+")"},a.prototype.compileLoopTest=function(a){var b,c,d,e;e=this.object.cache(a,x),d=e[0],c=e[1],b=bh("indexOf")+(".call("+this.array.compile(a,x)+", "+c+") ")+(this.negated?"< 0":">= 0");if(d===c)return b;b=d+", "+b;return a.level<x?b:"("+b+")"},a.prototype.toString=function(b){return a.__super__.toString.call(this,b,this.constructor.name+(this.negated?"!":""))};return a}(),b.Try=U=function(){function a(a,b,c,d){this.attempt=a,this.error=b,this.recovery=c,this.ensure=d}bk(a,g),a.prototype.children=["attempt","recovery","ensure"],a.prototype.isStatement=Y,a.prototype.jumps=function(a){var b;return this.attempt.jumps(a)||((b=this.recovery)!=null?b.jumps(a):void 0)},a.prototype.makeReturn=function(){this.attempt&&(this.attempt=this.attempt.makeReturn()),this.recovery&&(this.recovery=this.recovery.makeReturn());return this},a.prototype.compileNode=function(a){var b,c;a.indent+=R,c=this.error?" ("+this.error.compile(a)+") ":" ",b=this.recovery?" catch"+c+"{\n"+this.recovery.compile(a,A)+"\n"+this.tab+"}":!this.ensure&&!this.recovery?" catch (_e) {}":void 0;return""+this.tab+"try {\n"+this.attempt.compile(a,A)+"\n"+this.tab+"}"+(b||"")+(this.ensure?" finally {\n"+this.ensure.compile(a,A)+"\n"+this.tab+"}":"")};return a}(),b.Throw=T=function(){function a(a){this.expression=a}bk(a,g),a.prototype.children=["expression"],a.prototype.isStatement=Y,a.prototype.jumps=E,a.prototype.makeReturn=S,a.prototype.compileNode=function(a){return this.tab+("throw "+this.expression.compile(a)+";")};return a}(),b.Existence=n=function(){function a(a){this.expression=a}bk(a,g),a.prototype.children=["expression"],a.prototype.invert=D,a.prototype.compileNode=function(a){var b,c,d,e;d=this.expression.compile(a,y),d=q.test(d)&&!a.scope.check(d)?(e=this.negated?["===","||"]:["!==","&&"],b=e[0],c=e[1],e,"typeof "+d+" "+b+' "undefined" '+c+" "+d+" "+b+" null"):""+d+" "+(this.negated?"==":"!=")+" null";return a.level<=w?d:"("+d+")"};return a}(),b.Parens=I=function(){function a(a){this.body=a}bk(a,g),a.prototype.children=["body"],a.prototype.unwrap=function(){return this.body},a.prototype.isComplex=function(){return this.body.isComplex()},a.prototype.makeReturn=function(){return this.body.makeReturn()},a.prototype.compileNode=function(a){var b,c,d;d=this.body.unwrap();if(d instanceof W&&d.isAtomic()){d.front=this.front;return d.compile(a)}c=d.compile(a,z),b=a.level<y&&(d instanceof G||d instanceof i||d instanceof p&&d.returns);return b?c:"("+c+")"};return a}(),b.For=p=function(){function a(a,b){var c;this.source=b.source,this.guard=b.guard,this.step=b.step,this.name=b.name,this.index=b.index,this.body=h.wrap([a]),this.own=!!b.own,this.object=!!b.object,this.object&&(c=[this.index,this.name],this.name=c[0],this.index=c[1]);if(this.index instanceof W)throw SyntaxError("index cannot be a pattern matching expression");this.range=this.source instanceof W&&this.source.base instanceof K&&!this.source.properties.length,this.pattern=this.name instanceof W;if(this.range&&this.index)throw SyntaxError("indexes do not apply to range loops");if(this.range&&this.pattern)throw SyntaxError("cannot pattern match over range loops");this.returns=!1}bk(a,g),a.prototype.children=["body","source","guard","step"],a.prototype.isStatement=Y,a.prototype.jumps=X.prototype.jumps,a.prototype.makeReturn=function(){this.returns=!0;return this},a.prototype.compileNode=function(a){var b,c,d,e,g,i,j,k,l,m,n,o,p,r,t,u,v,w,z,C,D,E,F;b=h.wrap([this.body]),l=(F=bc(b.expressions))!=null?F.jumps():void 0,l&&l instanceof L&&(this.returns=!1),w=this.range?this.source.base:this.source,v=a.scope,n=this.name&&this.name.compile(a,x),j=this.index&&this.index.compile(a,x),n&&!this.pattern&&v.find(n,{immediate:!0}),j&&v.find(j,{immediate:!0}),this.returns&&(u=v.freeVariable("results")),k=(this.range?n:j)||v.freeVariable("i"),this.step&&!this.range&&(C=v.freeVariable("step")),this.pattern&&(n=k),E="",g="",c="",i=this.tab+R,this.range?d=w.compile(bd(a,{index:k,step:this.step})):(D=this.source.compile(a,x),(n||this.own)&&!q.test(D)&&(c=""+this.tab+(p=v.freeVariable("ref"))+" = "+D+";\n",D=p),n&&!this.pattern&&(o=""+n+" = "+D+"["+k+"]"),this.object||(m=v.freeVariable("len"),e=""+k+" = 0, "+m+" = "+D+".length"+(this.step?", "+C+" = "+this.step.compile(a,y):""),z=this.step?""+k+" += "+C:""+k+"++",d=""+e+"; "+k+" < "+m+"; "+z)),this.returns&&(r=""+this.tab+u+" = [];\n",t="\n"+this.tab+"return "+u+";",b=J.wrap(u,b)),this.guard&&(b=h.wrap([new s(this.guard,b)])),this.pattern&&b.expressions.unshift(new f(this.name,new B(""+D+"["+k+"]"))),c+=this.pluckDirectCall(a,b),o&&(E="\n"+i+o+";"),this.object&&(d=""+k+" in "+D,this.own&&(g="\n"+i+"if (!"+bh("hasProp")+".call("+D+", "+k+")) continue;")),b=b.compile(bd(a,{indent:i}),A),b&&(b="\n"+b+"\n");return""+c+(r||"")+this.tab+"for ("+d+") {"+g+E+b+this.tab+"}"+(t||"")},a.prototype.pluckDirectCall=function(a,b){var c,d,e,g,h,j,k,m,n,o,p,q,r,s;d="",n=b.expressions;for(h=0,m=n.length;h<m;h++){e=n[h],e=e.unwrapAll();if(!(e instanceof i))continue;k=e.variable.unwrapAll();if(!(k instanceof l||k instanceof W&&((o=k.base)!=null?o.unwrapAll():void 0)instanceof l&&k.properties.length===1&&((p=(q=k.properties[0].name)!=null?q.value:void 0)==="call"||p==="apply")))continue;g=((r=k.base)!=null?r.unwrapAll():void 0)||k,j=new B(a.scope.freeVariable("fn")),c=new W(j),k.base&&(s=[c,k],k.base=s[0],c=s[1],args.unshift(new B("this"))),b.expressions[h]=new i(c,e.args),d+=this.tab+(new f(j,g)).compile(a,A)+";\n"}return d};return a}(),b.Switch=Q=function(){function a(a,b,c){this.subject=a,this.cases=b,this.otherwise=c}bk(a,g),a.prototype.children=["subject","cases","otherwise"],a.prototype.isStatement=Y,a.prototype.jumps=function(a){var b,c,d,e,f,g,h;a==null&&(a={block:!0}),f=this.cases;for(d=0,e=f.length;d<e;d++){g=f[d],c=g[0],b=g[1];if(b.jumps(a))return b}return(h=this.otherwise)!=null?h.jumps(a):void 0},a.prototype.makeReturn=function(){var a,b,c,d,e;d=this.cases;for(b=0,c=d.length;b<c;b++)a=d[b],a[1].makeReturn();(e=this.otherwise)!=null&&e.makeReturn();return this},a.prototype.compileNode=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q;i=a.indent+R,j=a.indent=i+R,d=this.tab+("switch ("+(((n=this.subject)!=null?n.compile(a,z):void 0)||!1)+") {\n"),o=this.cases;for(h=0,l=o.length;h<l;h++){p=o[h],f=p[0],b=p[1],q=bb([f]);for(k=0,m=q.length;k<m;k++)e=q[k],this.subject||(e=e.invert()),d+=i+("case "+e.compile(a,z)+":\n");if(c=b.compile(a,A))d+=c+"\n";if(h===this.cases.length-1&&!this.otherwise)break;g=this.lastNonComment(b.expressions);if(g instanceof L||g instanceof B&&g.jumps()&&g.value!=="debugger")continue;d+=j+"break;\n"}this.otherwise&&this.otherwise.expressions.length&&(d+=i+("default:\n"+this.otherwise.compile(a,A)+"\n"));return d+this.tab+"}"};return a}(),b.If=s=function(){function a(a,b,c){this.body=b,c==null&&(c={}),this.condition=c.type==="unless"?a.invert():a,this.elseBody=null,this.isChain=!1,this.soak=c.soak}bk(a,g),a.prototype.children=["condition","body","elseBody"],a.prototype.bodyNode=function(){var a;return(a=this.body)!=null?a.unwrap():void 0},a.prototype.elseBodyNode=function(){var a;return(a=this.elseBody)!=null?a.unwrap():void 0},a.prototype.addElse=function(b){this.isChain?this.elseBodyNode().addElse(b):(this.isChain=b instanceof a,this.elseBody=this.ensureBlock(b));return this},a.prototype.isStatement=function(a){var b;return(a!=null?a.level:void 0)===A||this.bodyNode().isStatement(a)||((b=this.elseBodyNode())!=null?b.isStatement(a):void 0)},a.prototype.jumps=function(a){var b;return this.body.jumps(a)||((b=this.elseBody)!=null?b.jumps(a):void 0)},a.prototype.compileNode=function(a){return this.isStatement(a)?this.compileStatement(a):this.compileExpression(a)},a.prototype.makeReturn=function(){this.body&&(this.body=new h([this.body.makeReturn()])),this.elseBody&&(this.elseBody=new h([this.elseBody.makeReturn()]));return this},a.prototype.ensureBlock=function(a){return a instanceof h?a:new h([a])},a.prototype.compileStatement=function(b){var c,d,e,f,g;d=$(b,"chainChild"),f=$(b,"isExistentialEquals");if(f)return(new a(this.condition.invert(),this.elseBodyNode(),{type:"if"})).compile(b);e=this.condition.compile(b,z),b.indent+=R,c=this.ensureBlock(this.body).compile(b),c&&(c="\n"+c+"\n"+this.tab),g="if ("+e+") {"+c+"}",d||(g=this.tab+g);if(!this.elseBody)return g;return g+" else "+(this.isChain?(b.indent=this.tab,b.chainChild=!0,this.elseBody.unwrap().compile(b,A)):"{\n"+this.elseBody.compile(b,A)+"\n"+this.tab+"}")},a.prototype.compileExpression=function(a){var b,c,d,e;e=this.condition.compile(a,w),c=this.bodyNode().compile(a,x),b=this.elseBodyNode()?this.elseBodyNode().compile(a,x):"void 0",d=""+e+" ? "+c+" : "+b;return a.level>=w?"("+d+")":d},a.prototype.unfoldSoak=function(){return this.soak&&this};return a}(),J={wrap:function(a,b){if(b.isEmpty()||bc(b.expressions).jumps())return b;return b.push(new i(new W(new B(a),[new d(new B("push"))]),[b.pop()]))}},k={wrap:function(a,b,c){var e,f,g,j,k;if(a.jumps())return a;g=new l([],h.wrap([a])),e=[];if((j=a.contains(this.literalArgs))||a.contains(this.literalThis))k=new B(j?"apply":"call"),e=[new B("this")],j&&e.push(new B("arguments")),g=new W(g,[new d(k)]);g.noReturn=c,f=new i(g,e);return b?h.wrap([f]):f},literalArgs:function(a){return a instanceof B&&a.value==="arguments"&&!a.asKey},literalThis:function(a){return a instanceof B&&a.value==="this"&&!a.asKey||a instanceof l&&a.bound}},bg=function(a,b,c){var d;if(!!(d=b[c].unfoldSoak(a))){b[c]=d.body,d.body=new W(b);return d}},V={"extends":"function(child, parent) {\n for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }\n function ctor() { this.constructor = child; }\n ctor.prototype = parent.prototype;\n child.prototype = new ctor;\n child.__super__ = parent.prototype;\n return child;\n}",bind:"function(fn, me){ return function(){ return fn.apply(me, arguments); }; }",indexOf:"Array.prototype.indexOf || function(item) {\n for (var i = 0, l = this.length; i < l; i++) {\n if (this[i] === item) return i;\n }\n return -1;\n}",hasProp:"Object.prototype.hasOwnProperty",slice:"Array.prototype.slice"},A=1,z=2,x=3,w=4,y=5,v=6,R=" ",q=/^[$A-Za-z_\x7f-\uffff][$\w\x7f-\uffff]*$/,M=/^[+-]?\d+$/,C=/^(?:([$A-Za-z_][$\w\x7f-\uffff]*)\.prototype\.)?([$A-Za-z_][$\w\x7f-\uffff]*)$/,r=/^['"]/,bh=function(a){var b;b="__"+a,N.root.assign(b,V[a]);return b},be=function(a,b){return a.replace(/\n/g,"$&"+b)}}),define("ace/mode/coffee/scope",["require","exports","module","ace/mode/coffee/helpers"],function(a,b,c){var d,e,f,g;g=a("ace/mode/coffee/helpers"),e=g.extend,f=g.last,b.Scope=d=function(){function a(b,c,d){this.parent=b,this.expressions=c,this.method=d,this.variables=[{name:"arguments",type:"arguments"}],this.positions={},this.parent||(a.root=this)}a.root=null,a.prototype.add=function(a,b,c){var d;if(this.shared&&!c)return this.parent.add(a,b,c);return typeof (d=this.positions[a])=="number"?this.variables[d].type=b:this.positions[a]=this.variables.push({name:a,type:b})-1},a.prototype.find=function(a,b){if(this.check(a,b))return!0;this.add(a,"var");return!1},a.prototype.parameter=function(a){if(!this.shared||!this.parent.check(a,!0))return this.add(a,"param")},a.prototype.check=function(a,b){var c,d;c=!!this.type(a);if(c||b)return c;return(d=this.parent)!=null?!!d.check(a):!!void 0},a.prototype.temporary=function(a,b){return a.length>1?"_"+a+(b>1?b:""):"_"+(b+parseInt(a,36)).toString(36).replace(/\d/g,"a")},a.prototype.type=function(a){var b,c,d,e;e=this.variables;for(c=0,d=e.length;c<d;c++){b=e[c];if(b.name===a)return b.type}return null},a.prototype.freeVariable=function(a){var b,c;b=0;while(this.check(c=this.temporary(a,b)))b++;this.add(c,"var",!0);return c},a.prototype.assign=function(a,b){this.add(a,{value:b,assigned:!0});return this.hasAssignments=!0},a.prototype.hasDeclarations=function(){return!!this.declaredVariables().length},a.prototype.declaredVariables=function(){var a,b,c,d,e,f;a=[],b=[],f=this.variables;for(d=0,e=f.length;d<e;d++)c=f[d],c.type==="var"&&(c.name.charAt(0)==="_"?b:a).push(c.name);return a.sort().concat(b.sort())},a.prototype.assignedVariables=function(){var a,b,c,d,e;d=this.variables,e=[];for(b=0,c=d.length;b<c;b++)a=d[b],a.type.assigned&&e.push(""+a.name+" = "+a.type.value);return e};return a}()}) \ No newline at end of file
diff --git a/studio/static/js/ace/worker-css.js b/studio/static/js/ace/worker-css.js
new file mode 100644
index 0000000..f76ed43
--- /dev/null
+++ b/studio/static/js/ace/worker-css.js
@@ -0,0 +1 @@
+function initSender(){var a=require("pilot/event_emitter").EventEmitter,b=require("pilot/oop"),c=function(){};(function(){b.implement(this,a),this.callback=function(a,b){postMessage({type:"call",id:b,data:a})},this.emit=function(a,b){postMessage({type:"event",name:a,data:b})}}).call(c.prototype);return new c}function initBaseUrls(a){require.tlns=a}var console={log:function(a){postMessage({type:"log",data:a})}},window={console:console},require=function(a){var b=require.modules[a];if(b){b.initialized||(b.exports=b.factory().exports,b.initialized=!0);return b.exports}var c=a.split("/");c[0]=require.tlns[c[0]]||c[0],path=c.join("/")+".js",require.id=a,importScripts(path);return require(a)};require.modules={},require.tlns={};var define=function(a,b,c){arguments.length==2?c=b:arguments.length==1&&(c=a,a=require.id);a.indexOf("text/")!==0&&(require.modules[a]={factory:function(){var a={exports:{}},b=c(require,a.exports,a);b&&(a.exports=b);return a}})},main,sender;onmessage=function(a){var b=a.data;if(b.command)main[b.command].apply(main,b.args);else if(b.init){initBaseUrls(b.tlns),require("pilot/fixoldbrowsers"),sender=initSender();var c=require(b.module)[b.classname];main=new c(sender)}else b.event&&sender&&sender._dispatchEvent(b.event,b.data)},define("pilot/fixoldbrowsers",["require","exports","module"],function(a,b,c){if(!Function.prototype.bind){var d=Array.prototype.slice;Function.prototype.bind=function(b){var c=this;if(typeof c.apply!="function"||typeof c.call!="function")return new TypeError;var e=d.call(arguments),f=function a(){if(this instanceof a){var b=Object.create(c.prototype);c.apply(b,e.concat(d.call(arguments)));return b}return c.call.apply(c,e.concat(d.call(arguments)))};f.length=typeof c=="function"?Math.max(c.length-e.length,0):0;return f}}var e=Function.prototype.call,f=Array.prototype,g=Object.prototype,h=e.bind(g.hasOwnProperty),i,j,k,l,m;if(m=h(g,"__defineGetter__"))i=e.bind(g.__defineGetter__),j=e.bind(g.__defineSetter__),k=e.bind(g.__lookupGetter__),l=e.bind(g.__lookupSetter__);Array.isArray||(Array.isArray=function(b){return Object.prototype.toString.call(b)==="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(b,c){var d=+this.length;for(var e=0;e<d;e++)e in this&&b.call(c,this[e],e,this)}),Array.prototype.map||(Array.prototype.map=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;var d=Array(c),e=arguments[1];for(var f=0;f<c;f++)f in this&&(d[f]=b.call(e,this[f],f,this));return d}),Array.prototype.filter||(Array.prototype.filter=function(b){var c=[],d=arguments[1];for(var e=0;e<this.length;e++)b.call(d,this[e])&&c.push(this[e]);return c}),Array.prototype.every||(Array.prototype.every=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(!b.call(c,this[d]))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(b.call(c,this[d]))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=0;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d++];break}if(++d>=c)throw new TypeError}while(!0);for(;d<c;d++)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=c-1;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d--];break}if(--d<0)throw new TypeError}while(!0);for(;d>=0;d--)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.indexOf||(Array.prototype.indexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||0;if(d>=c)return-1;d<0&&(d+=c);for(;d<c;d++){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||c;d<0&&(d+=c),d=Math.min(d,c-1);for(;d>=0;d--){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(b){return b.__proto__||b.constructor.prototype});if(!Object.getOwnPropertyDescriptor){var n="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(b,c){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(n+b);if(!h(b,c))return undefined;var d,e,f;d={enumerable:!0,configurable:!0};if(m){var i=b.__proto__;b.__proto__=g;var e=k(b,c),f=l(b,c);b.__proto__=i;if(e||f){e&&(descriptor.get=e),f&&(descriptor.set=f);return descriptor}}descriptor.value=b[c];return descriptor}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(b){return Object.keys(b)}),Object.create||(Object.create=function(b,c){var d;if(b===null)d={"__proto__":null};else{if(typeof b!="object")throw new TypeError("typeof prototype["+typeof b+"] != 'object'");var e=function(){};e.prototype=b,d=new e,d.__proto__=b}typeof c!="undefined"&&Object.defineProperties(d,c);return d});if(!Object.defineProperty){var o="Property description must be an object: ",p="Object.defineProperty called on non-object: ",q="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(b,c,d){if(typeof b!="object"&&typeof b!="function")throw new TypeError(p+b);if(typeof b!="object"||b===null)throw new TypeError(o+d);if(h(d,"value"))if(m&&(k(b,c)||l(b,c))){var e=b.__proto__;b.__proto__=g,delete b[c],b[c]=d.value,b.prototype}else b[c]=d.value;else{if(!m)throw new TypeError(q);h(d,"get")&&i(b,c,d.get),h(d,"set")&&j(b,c,d.set)}return b}}Object.defineProperties||(Object.defineProperties=function(b,c){for(var d in c)h(c,d)&&Object.defineProperty(b,d,c[d]);return b}),Object.seal||(Object.seal=function(b){return b}),Object.freeze||(Object.freeze=function(b){return b});try{Object.freeze(function(){})}catch(r){Object.freeze=function(b){return function(c){return typeof c=="function"?c:b(c)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(b){return b}),Object.isSealed||(Object.isSealed=function(b){return!1}),Object.isFrozen||(Object.isFrozen=function(b){return!1}),Object.isExtensible||(Object.isExtensible=function(b){return!0});if(!Object.keys){var s=!0,t=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],u=t.length;for(var v in{toString:null})s=!1;Object.keys=function a(b){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError("Object.keys called on a non-object");var a=[];for(var c in b)h(b,c)&&a.push(c);if(s)for(var d=0,e=u;d<e;d++){var f=t[d];h(b,f)&&a.push(f)}return a}}Date.prototype.toISOString||(Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+(this.getUTCMonth()+1)+"-"+this.getUTCDate()+"T"+this.getUTCHours()+":"+this.getUTCMinutes()+":"+this.getUTCSeconds()+"Z"}),Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(b){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),isNaN(Date.parse("T00:00"))&&(Date=function(a){var b=function(c,d,e,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j===1&&String(c)===c?new a(b.parse(c)):j>=7?new a(c,d,e,f,g,h,i):j>=6?new a(c,d,e,f,g,h):j>=5?new a(c,d,e,f,g):j>=4?new a(c,d,e,f):j>=3?new a(c,d,e):j>=2?new a(c,d):j>=1?new a(c):new a;k.constructor=b;return k}return a.apply(this,arguments)},c=new RegExp("^(?:((?:[+-]\\d\\d)?\\d\\d\\d\\d)(?:-(\\d\\d)(?:-(\\d\\d))?)?)?(?:T(\\d\\d):(\\d\\d)(?::(\\d\\d)(?:\\.(\\d\\d\\d))?)?)?(?:Z|([+-])(\\d\\d):(\\d\\d))?$");for(var d in a)b[d]=a[d];b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(d){var e=c.exec(d);if(e){e.shift();var f=e[0]===undefined;for(var g=0;g<10;g++){if(g===7)continue;e[g]=+(e[g]||(g<3?1:0)),g===1&&e[g]--}if(f)return((e[3]*60+e[4])*60+e[5])*1e3+e[6];var h=(e[8]*60+e[9])*60*1e3;e[6]==="-"&&(h=-h);return a.UTC.apply(this,e.slice(0,7))+h}return a.parse.apply(this,arguments)};return b}(Date));if(!String.prototype.trim){var w=/^\s\s*/,x=/\s\s*$/;String.prototype.trim=function(){return String(this).replace(w,"").replace(x,"")}}}),define("pilot/event_emitter",["require","exports","module"],function(a,b,c){var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c&&!!c.length){var b=b||{};b.type=a;for(var d=0;d<c.length;d++)c[d](b)}},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c){var d=c.indexOf(b);d!==-1&&c.splice(d,1)}},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define("pilot/oop",["require","exports","module"],function(a,b,c){b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define("ace/mode/css_worker",["require","exports","module","pilot/oop","ace/worker/mirror","ace/mode/css/csslint"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/worker/mirror").Mirror,f=a("ace/mode/css/csslint").CSSLint,g=b.Worker=function(a){e.call(this,a),this.setTimeout(200)};d.inherits(g,e),function(){this.onUpdate=function(){var a=this.doc.getValue();result=f.verify(a),this.sender.emit("csslint",result.messages)}}.call(g.prototype)}),define("ace/worker/mirror",["require","exports","module","ace/document","pilot/lang"],function(a,b,c){var d=a("ace/document").Document,e=a("pilot/lang"),f=b.Mirror=function(a){this.sender=a;var b=this.doc=new d(""),c=this.deferredUpdate=e.deferredCall(this.onUpdate.bind(this)),f=this;a.on("change",function(a){b.applyDeltas([a.data]),c.schedule(f.$timeout)})};(function(){this.$timeout=500,this.setTimeout=function(a){this.$timeout=a},this.setValue=function(a){this.doc.setValue(a),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(a){this.sender.callback(this.doc.getValue(),a)},this.onUpdate=function(){}}).call(f.prototype)}),define("ace/document",["require","exports","module","pilot/oop","pilot/event_emitter","ace/range","ace/anchor"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=a("ace/range").Range,g=a("ace/anchor").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[""]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},"aaa".split(/a/).length==0?this.$split=function(a){return a.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(a){return a.split(/\r\n|\r|\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r?\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(a){this.$newLineMode!==a&&(this.$newLineMode=a)},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a=="\r\n"||a=="\r"||a=="\n"},this.getLine=function(a){return this.$lines[a]||""},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column));return b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length);return a},this.insert=function(a,b){if(b.length==0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];this._dispatchEvent("changeStart"),a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||"")),this._dispatchEvent("changeEnd");return a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c),this._dispatchEvent("changeStart");var d=new f(a,0,a+b.length,0),e={action:"insertLines",range:d,lines:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:"insertText",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:d}),this._dispatchEvent("changeEnd");return c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:"insertText",range:f.fromPoints(a,d),text:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;this._dispatchEvent("changeStart");if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);this._dispatchEvent("changeEnd");return a.start},this.removeInLine=function(a,b,c){if(b!=c){var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this._dispatchEvent("changeStart"),this.$lines.splice(a,1,h);var i={action:"removeText",range:d,text:g};this._dispatchEvent("change",{data:i}),this._dispatchEvent("changeEnd");return d.start}},this.removeLines=function(a,b){this._dispatchEvent("changeStart");var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:"removeLines",range:c,nl:this.getNewLineCharacter(),lines:d};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this._dispatchEvent("changeStart"),this.$lines.splice(a,2,e);var g={action:"removeText",range:d,text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:g}),this._dispatchEvent("changeEnd")},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this._dispatchEvent("changeStart"),this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;this._dispatchEvent("changeEnd");return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.insertLines(d.start.row,c.lines):c.action=="insertText"?this.insert(d.start,c.text):c.action=="removeLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="removeText"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="insertText"?this.remove(d):c.action=="removeLines"?this.insertLines(d.start.row,c.lines):c.action=="removeText"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define("ace/range",["require","exports","module"],function(a,b,c){var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(a,b){return this.compare(a,b)==0},this.compareRange=function(a){var b,c=a.end,d=a.start;b=this.compare(c.row,c.column);if(b==1){b=this.compare(d.row,d.column);return b==1?2:b==0?1:0}if(b==-1)return-2;b=this.compare(d.row,d.column);return b==-1?-1:b==1?42:0},this.containsRange=function(a){var b=this.compareRange(a);return b==-1||b==0||b==1},this.isEnd=function(a,b){return this.end.row==a&&this.end.column==b},this.isStart=function(a,b){return this.start.row==a&&this.start.column==b},this.setStart=function(a,b){typeof a=="object"?(this.start.column=a.column,this.start.row=a.row):(this.start.row=a,this.start.column=b)},this.setEnd=function(a,b){typeof a=="object"?(this.end.column=a.column,this.end.row=a.row):(this.end.row=a,this.end.column=b)},this.inside=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)||this.isStart(a,b)?!1:!0;return!1},this.insideStart=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)?!1:!0;return!1},this.insideEnd=function(a,b){if(this.compare(a,b)==0)return this.isStart(a,b)?!1:!0;return!1},this.compare=function(a,b){if(!this.isMultiLine()&&a===this.start.row)return b<this.start.column?-1:b>this.end.column?1:0;if(a<this.start.row)return-1;if(a>this.end.row)return 1;if(this.start.row===a)return b>=this.start.column?0:-1;if(this.end.row===a)return b<=this.end.column?0:1;return 0},this.compareStart=function(a,b){return this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.compareEnd=function(a,b){return this.end.row==a&&this.end.column==b?1:this.compare(a,b)},this.compareInside=function(a,b){return this.end.row==a&&this.end.column==b?1:this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define("ace/anchor",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c=="undefined"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on("change",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row!=c.end.row||c.start.row==this.row){if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action==="insertText"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action==="insertLines"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action=="removeText"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action=="removeLines"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)}},this.setPosition=function(a,b,c){c?pos={row:a,column:b}:pos=this.$clipPositionToDocument(a,b);if(this.row!=pos.row||this.column!=pos.column){var d={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._dispatchEvent("change",{old:d,value:pos})}},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0);return c}}).call(f.prototype)}),define("pilot/lang",["require","exports","module"],function(a,b,c){b.stringReverse=function(a){return a.split("").reverse().join("")},b.stringRepeat=function(a,b){return Array(b+1).join(a)};var d=/^\s\s*/,e=/\s\s*$/;b.stringTrimLeft=function(a){return a.replace(d,"")},b.stringTrimRight=function(a){return a.replace(e,"")},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.copyArray=function(a){var b=[];for(i=0,l=a.length;i<l;i++)a[i]&&typeof a[i]=="object"?b[i]=this.copyObject(a[i]):b[i]=a[i];return b},b.deepCopy=function(a){if(typeof a!="object")return a;var b=a.constructor();for(var c in a)typeof a[c]=="object"?b[c]=this.deepCopy(a[c]):b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){b||(b=setTimeout(c,a||0));return d};d.schedule=d,d.call=function(){this.cancel(),a();return d},d.cancel=function(){clearTimeout(b),b=null;return d};return d}}),define("ace/mode/css/csslint",["require","exports","module"],function(require,exports,module){function indexOf(a,b){if(a.indexOf)return a.indexOf(b);for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1}function mix(a,b){var c;for(c in b)b.hasOwnProperty(c)&&(receiver[c]=b[c]);return c}function Reporter(a){this.messages=[],this.stats=[],this.lines=a}var parserlib={};(function(){function e(a,c){this._reader=a?new b(a.toString()):null,this._token=null,this._tokenData=c,this._lt=[],this._ltIndex=0,this._ltIndexCache=[]}function d(a,b,c){this.col=c,this.line=b,this.text=a}function c(a,b,c){this.col=c,this.line=b,this.message=a}function b(a){this._input=a.replace(/\n\r?/g,"\n"),this._line=1,this._col=1,this._cursor=0}function a(){this._listeners={}}a.prototype={constructor:a,addListener:function(a,b){this._listeners[a]||(this._listeners[a]=[]),this._listeners[a].push(b)},fire:function(a){typeof a=="string"&&(a={type:a}),a.target||(a.target=this);if(!a.type)throw new Error("Event object missing 'type' property.");if(this._listeners[a.type]){var b=this._listeners[a.type].concat();for(var c=0,d=b.length;c<d;c++)b[c].call(this,a)}},removeListener:function(a,b){if(this._listeners[a]){var c=this._listeners[a];for(var d=0,e=c.length;d<e;d++)if(c[d]===b){c.splice(d,1);break}}}},b.prototype={constructor:b,getCol:function(){return this._col},getLine:function(){return this._line},eof:function(){return this._cursor==this._input.length},peek:function(a){var b=null;a=typeof a=="undefined"?1:a,this._cursor<this._input.length&&(b=this._input.charAt(this._cursor+a-1));return b},read:function(){var a=null;this._cursor<this._input.length&&(this._input.charAt(this._cursor)=="\n"?(this._line++,this._col=1):this._col++,a=this._input.charAt(this._cursor++));return a},mark:function(){this._bookmark={cursor:this._cursor,line:this._line,col:this._col}},reset:function(){this._bookmark&&(this._cursor=this._bookmark.cursor,this._line=this._bookmark.line,this._col=this._bookmark.col,delete this._bookmark)},readTo:function(a){var b="",c;while(b.length<a.length||b.lastIndexOf(a)!=b.length-a.length){c=this.read();if(c)b+=c;else throw new Error('Expected "'+a+'" at line '+this._line+", col "+this._col+".")}return b},readWhile:function(a){var b="",c=this.read();while(c!==null&&a(c))b+=c,c=this.read();return b},readMatch:function(a){var b=this._input.substring(this._cursor),c=null;typeof a=="string"?b.indexOf(a)===0&&(c=this.readCount(a.length)):a instanceof RegExp&&a.test(b)&&(c=this.readCount(RegExp.lastMatch.length));return c},readCount:function(a){var b="";while(a--)b+=this.read();return b}},c.prototype=new Error,d.fromToken=function(a){return new d(a.value,a.startLine,a.startCol)},d.prototype={constructor:d,valueOf:function(){return this.toString()},toString:function(){return this.text}},e.createTokenData=function(a){var b=[],c={},d=a.concat([]),e=0,f=d.length+1;d.UNKNOWN=-1,d.unshift({name:"EOF"});for(;e<f;e++)b.push(d[e].name),d[d[e].name]=e,d[e].text&&(c[d[e].text]=e);d.name=function(a){return b[a]},d.type=function(a){return c[a]};return d},e.prototype={constructor:e,match:function(a,b){a instanceof Array||(a=[a]);var c=this.get(b),d=0,e=a.length;while(d<e)if(c==a[d++])return!0;this.unget();return!1},mustMatch:function(a,b){a instanceof Array||(a=[a]);if(!this.match.apply(this,arguments)){token=this.LT(1);throw new c("Expected "+this._tokenData[a[0]].name+" at line "+token.startLine+", character "+token.startCol+".",token.startLine,token.startCol)}},advance:function(a,b){while(this.LA(0)!=0&&!this.match(a,b))this.get();return this.LA(0)},get:function(a){var b=this._tokenData,c=this._reader,d,e=0,f=b.length,g=!1,h,i;if(this._lt.length&&this._ltIndex>=0&&this._ltIndex<this._lt.length){e++,this._token=this._lt[this._ltIndex++],i=b[this._token.type];while(i.channel!==undefined&&a!==i.channel&&this._ltIndex<this._lt.length)this._token=this._lt[this._ltIndex++],i=b[this._token.type],e++;if((i.channel===undefined||a===i.channel)&&this._ltIndex<=this._lt.length){this._ltIndexCache.push(e);return this._token.type}}h=this._getToken(),h.type>-1&&!b[h.type].hide&&(h.channel=b[h.type].channel,this._token=h,this._lt.push(h),this._ltIndexCache.push(this._lt.length-this._ltIndex+e),this._lt.length>5&&this._lt.shift(),this._ltIndexCache.length>5&&this._ltIndexCache.shift(),this._ltIndex=this._lt.length),i=b[h.type];return i&&(i.hide||i.channel!==undefined&&a!==i.channel)?this.get(a):h.type},LA:function(a){var b=a,c;if(a>0){if(a>5)throw new Error("Too much lookahead.");while(b)c=this.get(),b--;while(b<a)this.unget(),b++}else if(a<0)if(this._lt[this._ltIndex+a])c=this._lt[this._ltIndex+a].type;else throw new Error("Too much lookbehind.");else c=this._token.type;return c},LT:function(a){this.LA(a);return this._lt[this._ltIndex+a-1]},peek:function(){return this.LA(1)},token:function(){return this._token},tokenName:function(a){return a<0||a>this._tokenData.length?"UNKNOWN_TOKEN":this._tokenData[a].name},tokenType:function(a){return this._tokenData[a]||-1},unget:function(){if(this._ltIndexCache.length)this._ltIndex-=this._ltIndexCache.pop(),this._token=this._lt[this._ltIndex-1];else throw new Error("Too much lookahead.")}},parserlib.util={StringReader:b,SyntaxError:c,SyntaxUnit:d,EventTarget:a,TokenStreamBase:e}})(),function(){function TokenStream(a){TokenStreamBase.call(this,a,Tokens)}function mix(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function isIdentStart(a){return a!=null&&(isNameStart(a)||a=="-")}function isNameChar(a){return a!=null&&(isNameStart(a)||/[0-9\-]/.test(a))}function isNameStart(a){return a!=null&&/[a-z_\u0080-\uFFFF\\]/i.test(a)}function isNewLine(a){return a!=null&&nl.test(a)}function isWhitespace(a){return a!=null&&/\s/.test(a)}function isDigit(a){return a!=null&&/\d/.test(a)}function isHexDigit(a){return a!=null&&h.test(a)}function SelectorSubPart(a,b,c,d){SyntaxUnit.call(this,a,c,d),this.type=b,this.args=[]}function SelectorPart(a,b,c,d,e){SyntaxUnit.call(this,c,d,e),this.elementName=a,this.modifiers=b}function Selector(a,b,c){SyntaxUnit.call(this,a.join(" "),b,c),this.parts=a}function PropertyValuePart(text,line,col){SyntaxUnit.apply(this,arguments),this.type="unknown";var temp;if(/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){this.type="dimension",this.value=+RegExp.$1,this.units=RegExp.$2;switch(this.units.toLowerCase()){case"em":case"rem":case"ex":case"px":case"cm":case"mm":case"in":case"pt":case"pc":this.type="length";break;case"deg":case"rad":case"grad":this.type="angle";break;case"ms":case"s":this.type="time";break;case"hz":case"khz":this.type="frequency";break;case"dpi":case"dpcm":this.type="resolution"}}else/^([+\-]?[\d\.]+)%$/i.test(text)?(this.type="percentage",this.value=+RegExp.$1):/^([+\-]?[\d\.]+)%$/i.test(text)?(this.type="percentage",this.value=+RegExp.$1):/^([+\-]?\d+)$/i.test(text)?(this.type="integer",this.value=+RegExp.$1):/^([+\-]?[\d\.]+)$/i.test(text)?(this.type="number",this.value=+RegExp.$1):/^#([a-f0-9]{3,6})/i.test(text)?(this.type="color",temp=RegExp.$1,temp.length==3?(this.red=parseInt(temp.charAt(0)+temp.charAt(0),16),this.green=parseInt(temp.charAt(1)+temp.charAt(1),16),this.blue=parseInt(temp.charAt(2)+temp.charAt(2),16)):(this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16))):/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1,this.green=+RegExp.$2,this.blue=+RegExp.$3):/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)?(this.type="color",this.red=+RegExp.$1*255/100,this.green=+RegExp.$2*255/100,this.blue=+RegExp.$3*255/100):/^url\(["']?([^\)"']+)["']?\)/i.test(text)?(this.type="uri",this.uri=RegExp.$1):/^["'][^"']*["']/.test(text)?(this.type="string",this.value=eval(text)):Colors[text.toLowerCase()]?(this.type="color",temp=Colors[text.toLowerCase()].substring(1),this.red=parseInt(temp.substring(0,2),16),this.green=parseInt(temp.substring(2,4),16),this.blue=parseInt(temp.substring(4,6),16)):/^[\,\/]$/.test(text)?(this.type="operator",this.value=text):/^[a-z\-\u0080-\uFFFF][a-z0-9\-\u0080-\uFFFF]*$/i.test(text)&&(this.type="identifier",this.value=text)}function PropertyValue(a,b,c){SyntaxUnit.call(this,a.join(" "),b,c),this.parts=a}function PropertyName(a,b,c,d){SyntaxUnit.call(this,(b||"")+a,c,d),this.hack=b}function Parser(a){EventTarget.call(this),this.options=a||{},this._tokenStream=null}function MediaQuery(a,b,c,d,e){SyntaxUnit.call(this,(a?a+" ":"")+(b?b+" ":"")+c.join(" and "),d,e),this.modifier=a,this.mediaType=b,this.features=c}function MediaFeature(a,b){SyntaxUnit.call(this,"("+a+(b!==null?":"+b:"")+")",a.startLine,a.startCol),this.name=a,this.value=b}function Combinator(a,b,c){SyntaxUnit.call(this,a,b,c),this.type="unknown",/^\s+$/.test(a)?this.type="descendant":a==">"?this.type="child":a=="+"?this.type="adjacent-sibling":a=="~"&&(this.type="sibling")}var EventTarget=parserlib.util.EventTarget,TokenStreamBase=parserlib.util.TokenStreamBase,StringReader=parserlib.util.StringReader,SyntaxError=parserlib.util.SyntaxError,SyntaxUnit=parserlib.util.SyntaxUnit,Colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"};Combinator.prototype=new SyntaxUnit,Combinator.prototype.constructor=Combinator;var Level1Properties={background:1,"background-attachment":1,"background-color":1,"background-image":1,"background-position":1,"background-repeat":1,border:1,"border-bottom":1,"border-bottom-width":1,"border-color":1,"border-left":1,"border-left-width":1,"border-right":1,"border-right-width":1,"border-style":1,"border-top":1,"border-top-width":1,"border-width":1,clear:1,color:1,display:1,"float":1,font:1,"font-family":1,"font-size":1,"font-style":1,"font-variant":1,"font-weight":1,height:1,"letter-spacing":1,"line-height":1,"list-style":1,"list-style-image":1,"list-style-position":1,"list-style-type":1,margin:1,"margin-bottom":1,"margin-left":1,"margin-right":1,"margin-top":1,padding:1,"padding-bottom":1,"padding-left":1,"padding-right":1,"padding-top":1,"text-align":1,"text-decoration":1,"text-indent":1,"text-transform":1,"vertical-align":1,"white-space":1,width:1,"word-spacing":1},Level2Properties={azimuth:1,"cue-after":1,"cue-before":1,cue:1,elevation:1,"pause-after":1,"pause-before":1,pause:1,"pitch-range":1,pitch:1,"play-during":1,richness:1,"speak-header":1,"speak-numeral":1,"speak-punctuation":1,speak:1,"speech-rate":1,stress:1,"voice-family":1,volume:1,orphans:1,"page-break-after":1,"page-break-before":1,"page-break-inside":1,widows:1,cursor:1,"outline-color":1,"outline-style":1,"outline-width":1,outline:1,"background-attachment":1,"background-color":1,"background-image":1,"background-position":1,"background-repeat":1,background:1,"border-collapse":1,"border-color":1,"border-spacing":1,"border-style":1,"border-top":1,"border-top-color":1,"border-top-style":1,"border-top-width":1,"border-width":1,border:1,bottom:1,"caption-side":1,clear:1,clip:1,color:1,content:1,"counter-increment":1,"counter-reset":1,direction:1,display:1,"empty-cells":1,"float":1,"font-family":1,"font-size":1,"font-style":1,"font-variant":1,"font-weight":1,font:1,height:1,left:1,"letter-spacing":1,"line-height":1,"list-style-image":1,"list-style-position":1,"list-style-type":1,"list-style":1,"margin-right":1,"margin-top":1,margin:1,"max-height":1,"max-width":1,"min-height":1,"min-width":1,overflow:1,"padding-top":1,padding:1,position:1,quotes:1,right:1,"table-layout":1,"text-align":1,"text-decoration":1,"text-indent":1,"text-transform":1,top:1,"unicode-bidi":1,"vertical-align":1,visibility:1,"white-space":1,width:1,"word-spacing":1,"z-index":1};MediaFeature.prototype=new SyntaxUnit,MediaFeature.prototype.constructor=MediaFeature,MediaQuery.prototype=new SyntaxUnit,MediaQuery.prototype.constructor=MediaQuery,Parser.prototype=function(){var a=new EventTarget,b,c={constructor:Parser,_stylesheet:function(){var a=this._tokenStream,b=null,c,d;this.fire("startstylesheet"),this._charset(),this._skipCruft();while(a.peek()==Tokens.IMPORT_SYM)this._import(),this._skipCruft();while(a.peek()==Tokens.NAMESPACE_SYM)this._namespace(),this._skipCruft();d=a.peek();while(d>Tokens.EOF){try{switch(d){case Tokens.MEDIA_SYM:this._media(),this._skipCruft();break;case Tokens.PAGE_SYM:this._page(),this._skipCruft();break;case Tokens.FONT_FACE_SYM:this._font_face(),this._skipCruft();break;case Tokens.S:this._readWhitespace();break;default:if(!this._ruleset())switch(d){case Tokens.CHARSET_SYM:c=a.LT(1),this._charset(!1);throw new SyntaxError("@charset not allowed here.",c.startLine,c.startCol);case Tokens.IMPORT_SYM:c=a.LT(1),this._import(!1);throw new SyntaxError("@import not allowed here.",c.startLine,c.startCol);case Tokens.NAMESPACE_SYM:c=a.LT(1),this._namespace(!1);throw new SyntaxError("@namespace not allowed here.",c.startLine,c.startCol);default:a.get(),this._unexpectedToken(a.token())}}}catch(e){if(e instanceof SyntaxError&&!this.options.strict)this.fire({type:"error",error:e,message:e.message,line:e.line,col:e.col});else throw e}d=a.peek()}d!=Tokens.EOF&&this._unexpectedToken(a.token()),this.fire("endstylesheet")},_charset:function(a){var b=this._tokenStream;b.match(Tokens.CHARSET_SYM)&&(this._readWhitespace(),b.mustMatch(Tokens.STRING),token=b.token(),charset=token.value,this._readWhitespace(),b.mustMatch(Tokens.SEMICOLON),a!==!1&&this.fire({type:"charset",charset:charset}))},_import:function(a){var b=this._tokenStream,c,d,e=[];b.mustMatch(Tokens.IMPORT_SYM),this._readWhitespace(),b.mustMatch([Tokens.STRING,Tokens.URI]),d=b.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/,"$1"),this._readWhitespace(),e=this._media_query_list(),b.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),a!==!1&&this.fire({type:"import",uri:d,media:e})},_namespace:function(a){var b=this._tokenStream,c,d;b.mustMatch(Tokens.NAMESPACE_SYM),this._readWhitespace(),b.match(Tokens.IDENT)&&(c=b.token().value,this._readWhitespace()),b.mustMatch([Tokens.STRING,Tokens.URI]),d=b.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/,"$1"),this._readWhitespace(),b.mustMatch(Tokens.SEMICOLON),this._readWhitespace(),a!==!1&&this.fire({type:"namespace",prefix:c,uri:d})},_media:function(){var a=this._tokenStream,b;a.mustMatch(Tokens.MEDIA_SYM),this._readWhitespace(),b=this._media_query_list(),a.mustMatch(Tokens.LBRACE),this._readWhitespace(),this.fire({type:"startmedia",media:b});while(this._ruleset());a.mustMatch(Tokens.RBRACE),this._readWhitespace(),this.fire({type:"endmedia",media:b})},_media_query_list:function(){var a=this._tokenStream,b=[];this._readWhitespace(),a.peek()==Tokens.IDENT&&b.push(this._media_query());while(a.match(Tokens.COMMA))this._readWhitespace(),b.push(this._media_query());return b},_media_query:function(){var a=this._tokenStream,b=null,c=null,d=null,e=[];a.match(Tokens.IDENT)&&(c=a.token().value.toLowerCase(),c!="only"&&c!="not"?(a.unget(),c=null):d=a.token()),this._readWhitespace(),a.peek()==Tokens.IDENT?(b=this._media_type(),d===null&&(d=a.token())):a.peek()==Tokens.LPAREN&&(d===null&&(d=a.LT(1)),e.push(this._media_expression()));if(b===null&&e.length===0)return null;this._readWhitespace();while(a.match(Tokens.IDENT))a.token().value.toLowerCase()!="and"&&this._unexpectedToken(a.token()),this._readWhitespace(),e.push(this._media_expression());return new MediaQuery(c,b,e,d.startLine,d.startCol)},_media_type:function(){return this._media_feature()},_media_expression:function(){var a=this._tokenStream,b=null,c,d=null;a.mustMatch(Tokens.LPAREN),b=this._media_feature(),this._readWhitespace(),a.match(Tokens.COLON)&&(this._readWhitespace(),c=a.LT(1),d=this._expression()),a.mustMatch(Tokens.RPAREN),this._readWhitespace();return new MediaFeature(b,d?new SyntaxUnit(d,c.startLine,c.startCol):null)},_media_feature:function(){var a=this._tokenStream;a.mustMatch(Tokens.IDENT);return SyntaxUnit.fromToken(a.token())},_page:function(){var a=this._tokenStream,b=null,c=null;a.mustMatch(Tokens.PAGE_SYM),this._readWhitespace(),a.match(Tokens.IDENT)&&(b=a.token().value,b.toLowerCase()==="auto"&&this._unexpectedToken(a.token())),a.peek()==Tokens.COLON&&(c=this._pseudo_page()),this._readWhitespace(),this.fire({type:"startpage",id:b,pseudo:c}),this._readDeclarations(!0,!0),this.fire({type:"endpage",id:b,pseudo:c})},_margin:function(){var a=this._tokenStream,b=this._margin_sym();if(b){this.fire({type:"startpagemargin",margin:b}),this._readDeclarations(!0),this.fire({type:"endpagemargin",margin:b});return!0}return!1},_margin_sym:function(){var a=this._tokenStream;return a.match([Tokens.TOPLEFTCORNER_SYM,Tokens.TOPLEFT_SYM,Tokens.TOPCENTER_SYM,Tokens.TOPRIGHT_SYM,Tokens.TOPRIGHTCORNER_SYM,Tokens.BOTTOMLEFTCORNER_SYM,Tokens.BOTTOMLEFT_SYM,Tokens.BOTTOMCENTER_SYM,Tokens.BOTTOMRIGHT_SYM,Tokens.BOTTOMRIGHTCORNER_SYM,Tokens.LEFTTOP_SYM,Tokens.LEFTMIDDLE_SYM,Tokens.LEFTBOTTOM_SYM,Tokens.RIGHTTOP_SYM,Tokens.RIGHTMIDDLE_SYM,Tokens.RIGHTBOTTOM_SYM])?SyntaxUnit.fromToken(a.token()):null},_pseudo_page:function(){var a=this._tokenStream;a.mustMatch(Tokens.COLON),a.mustMatch(Tokens.IDENT);return a.token().value},_font_face:function(){var a=this._tokenStream;a.mustMatch(Tokens.FONT_FACE_SYM),this._readWhitespace(),this.fire({type:"startfontface"}),this._readDeclarations(!0),this.fire({type:"endfontface"})},_operator:function(){var a=this._tokenStream,b=null;a.match([Tokens.SLASH,Tokens.COMMA])&&(b=a.token(),this._readWhitespace());return b?PropertyValuePart.fromToken(b):null},_combinator:function(){var a=this._tokenStream,b=null,c;a.match([Tokens.PLUS,Tokens.GREATER,Tokens.TILDE])&&(c=a.token(),b=new Combinator(c.value,c.startLine,c.startCol),this._readWhitespace());return b},_unary_operator:function(){var a=this._tokenStream;return a.match([Tokens.MINUS,Tokens.PLUS])?a.token().value:null},_property:function(){var a=this._tokenStream,b=null,c=null,d,e,f,g;a.peek()==Tokens.STAR&&this.options.starHack&&(a.get(),e=a.token(),c=e.value,f=e.startLine,g=e.startCol),a.match(Tokens.IDENT)&&(e=a.token(),d=e.value,d.charAt(0)=="_"&&this.options.underscoreHack&&(c="_",d=d.substring(1)),b=new PropertyName(d,c,f||e.startLine,g||e.startCol),this._readWhitespace());return b},_ruleset:function(){var a=this._tokenStream,b;try{b=this._selectors_group()}catch(c){if(!(c instanceof SyntaxError&&!this.options.strict))throw c;this.fire({type:"error",error:c,message:c.message,line:c.line,col:c.col}),tt=a.advance([Tokens.RBRACE]);if(tt!=Tokens.RBRACE)throw c;return!0}b&&(this.fire({type:"startrule",selectors:b}),this._readDeclarations(!0),this.fire({type:"endrule",selectors:b}));return b},_selectors_group:function(){var a=this._tokenStream,b=[],c;c=this._selector();if(c!==null){b.push(c);while(a.match(Tokens.COMMA))this._readWhitespace(),c=this._selector(),c!==null?b.push(c):this._unexpectedToken(a.LT(1))}return b.length?b:null},_selector:function(){var a=this._tokenStream,b=[],c=null,d=null,e=null;c=this._simple_selector_sequence();if(c===null)return null;b.push(c);do{d=this._combinator();if(d!==null)b.push(d),c=this._simple_selector_sequence(),c===null?this._unexpectedToken(this.LT(1)):b.push(c);else if(this._readWhitespace())e=new Combinator(a.token().value,a.token().startLine,a.token().startCol),d=this._combinator(),c=this._simple_selector_sequence(),c===null?d!==null&&this._unexpectedToken(a.LT(1)):(d!==null?b.push(d):b.push(e),b.push(c));else break}while(!0);return new Selector(b,b[0].line,b[0].col)},_simple_selector_sequence:function(){var a=this._tokenStream,b=null,c=[],d="",e=[function(){return a.match(Tokens.HASH)?new SelectorSubPart(a.token().value,"id",a.token().startLine,a.token().startCol):null},this._class,this._attrib,this._pseudo,this._negation],f=0,g=e.length,h=null,i=!1,j,k;j=a.LT(1).startLine,k=a.LT(1).startCol,b=this._type_selector(),b||(b=this._universal()),b!==null&&(d+=b);for(;;){if(a.peek()===Tokens.S)break;while(f<g&&h===null)h=e[f++].call(this);if(h===null){if(d==="")return null;break}f=0,c.push(h),d+=h.toString(),h=null}return d!==""?new SelectorPart(b,c,d,j,k):null},_type_selector:function(){var a=this._tokenStream,b=this._namespace_prefix(),c=this._element_name();if(!c){b&&(a.unget(),b.length>1&&a.unget());return null}b&&(c.text=b+c.text,c.col-=b.length);return c},_class:function(){var a=this._tokenStream,b;if(a.match(Tokens.DOT)){a.mustMatch(Tokens.IDENT),b=a.token();return new SelectorSubPart("."+b.value,"class",b.startLine,b.startCol-1)}return null},_element_name:function(){var a=this._tokenStream,b;if(a.match(Tokens.IDENT)){b=a.token();return new SelectorSubPart(b.value,"elementName",b.startLine,b.startCol)}return null},_namespace_prefix:function(){var a=this._tokenStream,b="";if(a.LA(1)===Tokens.PIPE||a.LA(2)===Tokens.PIPE)a.match([Tokens.IDENT,Tokens.STAR])&&(b+=a.token().value),a.mustMatch(Tokens.PIPE),b+="|";return b.length?b:null},_universal:function(){var a=this._tokenStream,b="",c;c=this._namespace_prefix(),c&&(b+=c),a.match(Tokens.STAR)&&(b+="*");return b.length?b:null},_attrib:function(){var a=this._tokenStream,b=null,c,d;if(a.match(Tokens.LBRACKET)){d=a.token(),b=d.value,b+=this._readWhitespace(),c=this._namespace_prefix(),c&&(b+=c),a.mustMatch(Tokens.IDENT),b+=a.token().value,b+=this._readWhitespace(),a.match([Tokens.PREFIXMATCH,Tokens.SUFFIXMATCH,Tokens.SUBSTRINGMATCH,Tokens.EQUALS,Tokens.INCLUDES,Tokens.DASHMATCH])&&(b+=a.token().value,b+=this._readWhitespace(),a.mustMatch([Tokens.IDENT,Tokens.STRING]),b+=a.token().value,b+=this._readWhitespace()),a.mustMatch(Tokens.RBRACKET);return new SelectorSubPart(b+"]","attribute",d.startLine,d.startCol)}return null},_pseudo:function(){var a=this._tokenStream,b=null,c=":",d,e;a.match(Tokens.COLON)&&(a.match(Tokens.COLON)&&(c+=":"),a.match(Tokens.IDENT)?(b=a.token().value,d=a.token().startLine,e=a.token().startCol-c.length):a.peek()==Tokens.FUNCTION&&(d=a.LT(1).startLine,e=a.LT(1).startCol-c.length,b=this._functional_pseudo()),b&&(b=new SelectorSubPart(c+b,"pseudo",d,e)));return b},_functional_pseudo:function(){var a=this._tokenStream,b=null;a.match(Tokens.FUNCTION)&&(b=a.token().value,b+=this._readWhitespace(),b+=this._expression(),a.mustMatch(Tokens.RPAREN),b+=")");return b},_expression:function(){var a=this._tokenStream,b="";while(a.match([Tokens.PLUS,Tokens.MINUS,Tokens.DIMENSION,Tokens.NUMBER,Tokens.STRING,Tokens.IDENT,Tokens.LENGTH,Tokens.FREQ,Tokens.EMS,Tokens.EXS,Tokens.ANGLE,Tokens.TIME,Tokens.RESOLUTION]))b+=a.token().value,b+=this._readWhitespace();return b.length?b:null},_negation:function(){var a=this._tokenStream,b,c,d="",e,f=null;a.match(Tokens.NOT)&&(d=a.token().value,b=a.token().startLine,c=a.token().startCol,d+=this._readWhitespace(),e=this._negation_arg(),d+=e,d+=this._readWhitespace(),a.match(Tokens.RPAREN),d+=a.token().value,f=new SelectorSubPart(d,"not",b,c),f.args.push(e));return f},_negation_arg:function(){var a=this._tokenStream,b=[this._type_selector,this._universal,function(){return a.match(Tokens.HASH)?new SelectorSubPart(a.token().value,"id",a.token().startLine,a.token().startCol):null},this._class,this._attrib,this._pseudo],c=null,d=0,e=b.length,f,g,h,i;g=a.LT(1).startLine,h=a.LT(1).startCol;while(d<e&&c===null)c=b[d].call(this),d++;c===null&&this._unexpectedToken(a.LT(1)),c.type=="elementName"?i=new SelectorPart(c,[],c.toString(),g,h):i=new SelectorPart(null,[c],c.toString(),g,h);return i},_declaration:function(){var a=this._tokenStream,b=null,c=null,d=null;b=this._property();if(b!==null){a.mustMatch(Tokens.COLON),this._readWhitespace(),c=this._expr(),(!c||c.length===0)&&this._unexpectedToken(a.LT(1)),d=this._prio(),this.fire({type:"property",property:b,value:c,important:d});return!0}return!1},_prio:function(){var a=this._tokenStream,b=a.match(Tokens.IMPORTANT_SYM);this._readWhitespace();return b},_expr:function(){var a=this._tokenStream,b=[],c=null,d=null;c=this._term();if(c!==null){b.push(c);do{d=this._operator(),d&&b.push(d),c=this._term();if(c===null)break;b.push(c)}while(!0)}return b.length>0?new PropertyValue(b,b[0].startLine,b[0].startCol):null},_term:function(){var a=this._tokenStream,b=null,c=null,d,e;b=this._unary_operator(),b!==null&&(d=a.token().startLine,e=a.token().startCol),a.peek()==Tokens.IE_FUNCTION&&this.options.ieFilters?(c=this._ie_function(),b===null&&(d=a.token().startLine,e=a.token().startCol)):a.match([Tokens.NUMBER,Tokens.PERCENTAGE,Tokens.LENGTH,Tokens.EMS,Tokens.EXS,Tokens.ANGLE,Tokens.TIME,Tokens.FREQ,Tokens.STRING,Tokens.IDENT,Tokens.URI,Tokens.UNICODE_RANGE])?(c=a.token().value,b===null&&(d=a.token().startLine,e=a.token().startCol),this._readWhitespace()):(c=this._hexcolor(),c===null?(b===null&&(d=a.LT(1).startLine,e=a.LT(1).startCol),c===null&&(a.LA(3)==Tokens.EQUALS&&this.options.ieFilters?c=this._ie_function():c=this._function())):b===null&&(d=a.token().startLine,e=a.token().startCol));return c!==null?new PropertyValuePart(b!==null?b+c:c,d,e):null},_function:function(){var a=this._tokenStream,b=null,c=null;a.match(Tokens.FUNCTION)&&(b=a.token().value,this._readWhitespace(),c=this._expr(),a.match(Tokens.RPAREN),b+=c+")",this._readWhitespace());return b},_ie_function:function(){var a=this._tokenStream,b=null,c=null,d;if(a.match([Tokens.IE_FUNCTION,Tokens.FUNCTION])){b=a.token().value;do{this._readWhitespace()&&(b+=a.token().value),a.LA(0)==Tokens.COMMA&&(b+=a.token().value),a.match(Tokens.IDENT),b+=a.token().value,a.match(Tokens.EQUALS),b+=a.token().value,d=a.peek();while(d!=Tokens.COMMA&&d!=Tokens.S&&d!=Tokens.RPAREN)a.get(),b+=a.token().value,d=a.peek()}while(a.match([Tokens.COMMA,Tokens.S]));a.match(Tokens.RPAREN),b+=")",this._readWhitespace()}return b},_hexcolor:function(){var a=this._tokenStream,b,c=null;if(a.match(Tokens.HASH)){b=a.token(),c=b.value;if(!/#[a-f0-9]{3,6}/i.test(c))throw new SyntaxError("Expected a hex color but found '"+c+"' at line "+b.startLine+", character "+b.startCol+".",b.startLine,b.startCol);this._readWhitespace()}return c},_skipCruft:function(){while(this._tokenStream.match([Tokens.S,Tokens.CDO,Tokens.CDC]));},_readDeclarations:function(a,b){var c=this._tokenStream,d;this._readWhitespace(),a&&c.mustMatch(Tokens.LBRACE),this._readWhitespace();try{for(;;){if(!b||!this._margin()){if(!this._declaration())break;if(!c.match(Tokens.SEMICOLON))break}this._readWhitespace()}c.mustMatch(Tokens.RBRACE),this._readWhitespace()}catch(e){if(!(e instanceof SyntaxError&&!this.options.strict))throw e;this.fire({type:"error",error:e,message:e.message,line:e.line,col:e.col}),d=c.advance([Tokens.SEMICOLON,Tokens.RBRACE]);if(d==Tokens.SEMICOLON)this._readDeclarations(!1,b);else if(d!=Tokens.RBRACE)throw e}},_readWhitespace:function(){var a=this._tokenStream,b="";while(a.match(Tokens.S))b+=a.token().value;return b},_unexpectedToken:function(a){throw new SyntaxError("Unexpected token '"+a.value+"' at line "+a.startLine+", char "+a.startCol+".",a.startLine,a.startCol)},_verifyEnd:function(){this._tokenStream.LA(1)!=Tokens.EOF&&this._unexpectedToken(this._tokenStream.LT(1))},parse:function(a){this._tokenStream=new TokenStream(a,Tokens),this._stylesheet()},parseStyleSheet:function(a){return this.parse(a)},parseMediaQuery:function(a){this._tokenStream=new TokenStream(a,Tokens);var b=this._media_query();this._verifyEnd();return b},parsePropertyValue:function(a){this._tokenStream=new TokenStream(a,Tokens),this._readWhitespace();var b=this._expr();this._readWhitespace(),this._verifyEnd();return b},parseRule:function(a){this._tokenStream=new TokenStream(a,Tokens),this._readWhitespace();var b=this._ruleset();this._readWhitespace(),this._verifyEnd();return b},parseSelector:function(a){this._tokenStream=new TokenStream(a,Tokens),this._readWhitespace();var b=this._selector();this._readWhitespace(),this._verifyEnd();return b}};for(b in c)a[b]=c[b];return a}(),PropertyName.prototype=new SyntaxUnit,PropertyName.prototype.constructor=PropertyName,PropertyValue.prototype=new SyntaxUnit,PropertyValue.prototype.constructor=PropertyValue,PropertyValuePart.prototype=new SyntaxUnit,PropertyValuePart.prototype.constructor=PropertyValue,PropertyValuePart.fromToken=function(a){return new PropertyValuePart(a.value,a.startLine,a.startCol)},Selector.prototype=new SyntaxUnit,Selector.prototype.constructor=Selector,SelectorPart.prototype=new SyntaxUnit,SelectorPart.prototype.constructor=SelectorPart,SelectorSubPart.prototype=new SyntaxUnit,SelectorSubPart.prototype.constructor=SelectorSubPart;var h=/^[0-9a-fA-F]$/,nonascii=/^[\u0080-\uFFFF]$/,nl=/\n|\r\n|\r|\f/;TokenStream.prototype=mix(new TokenStreamBase,{_getToken:function(a){var b,c=this._reader,d=null,e=c.getLine(),f=c.getCol();b=c.read();while(b){switch(b){case"/":c.peek()=="*"?d=this.commentToken(b,e,f):d=this.charToken(b,e,f);break;case"|":case"~":case"^":case"$":case"*":c.peek()=="="?d=this.comparisonToken(b,e,f):d=this.charToken(b,e,f);break;case'"':case"'":d=this.stringToken(b,e,f);break;case"#":isNameChar(c.peek())?d=this.hashToken(b,e,f):d=this.charToken(b,e,f);break;case".":isDigit(c.peek())?d=this.numberToken(b,e,f):d=this.charToken(b,e,f);break;case"-":c.peek()=="-"?d=this.htmlCommentEndToken(b,e,f):isNameStart(c.peek())?d=this.identOrFunctionToken(b,e,f):d=this.charToken(b,e,f);break;case"!":d=this.importantToken(b,e,f);break;case"@":d=this.atRuleToken(b,e,f);break;case":":d=this.notToken(b,e,f);break;case"<":d=this.htmlCommentStartToken(b,e,f);break;case"U":case"u":if(c.peek()=="+"){d=this.unicodeRangeToken(b,e,f);break};default:isDigit(b)?d=this.numberToken(b,e,f):isWhitespace(b)?d=this.whitespaceToken(b,e,f):isIdentStart(b)?d=this.identOrFunctionToken(b,e,f):d=this.charToken(b,e,f)}break}!d&&b==null&&(d=this.createToken(Tokens.EOF,null,e,f));return d},createToken:function(a,b,c,d,e){var f=this._reader;e=e||{};return{value:b,type:a,channel:e.channel,hide:e.hide||!1,startLine:c,startCol:d,endLine:f.getLine(),endCol:f.getCol()}},atRuleToken:function(a,b,c){var d=a,e=this._reader,f=Tokens.CHAR,g=!1,h,i;e.mark(),h=this.readName(),d=a+h,f=Tokens.type(d.toLowerCase());if(f==Tokens.CHAR||f==Tokens.UNKNOWN)f=Tokens.CHAR,d=a,e.reset();return this.createToken(f,d,b,c)},charToken:function(a,b,c){var d=Tokens.type(a);d==-1&&(d=Tokens.CHAR);return this.createToken(d,a,b,c)},commentToken:function(a,b,c){var d=this._reader,e=this.readComment(a);return this.createToken(Tokens.COMMENT,e,b,c)},comparisonToken:function(a,b,c){var d=this._reader,e=a+d.read(),f=Tokens.type(e)||Tokens.CHAR;return this.createToken(f,e,b,c)},hashToken:function(a,b,c){var d=this._reader,e=this.readName(a);return this.createToken(Tokens.HASH,e,b,c)},htmlCommentStartToken:function(a,b,c){var d=this._reader,e=a;d.mark(),e+=d.readCount(3);if(e=="<!--")return this.createToken(Tokens.CDO,e,b,c);d.reset();return this.charToken(a,b,c)},htmlCommentEndToken:function(a,b,c){var d=this._reader,e=a;d.mark(),e+=d.readCount(2);if(e=="-->")return this.createToken(Tokens.CDC,e,b,c);d.reset();return this.charToken(a,b,c)},identOrFunctionToken:function(a,b,c){var d=this._reader,e=this.readName(a),f=Tokens.IDENT;d.peek()=="("?(e+=d.read(),e.toLowerCase()=="url("?(f=Tokens.URI,e=this.readURI(e),e.toLowerCase()=="url("&&(f=Tokens.FUNCTION)):f=Tokens.FUNCTION):d.peek()==":"&&e.toLowerCase()=="progid"&&(e+=d.readTo("("),f=Tokens.IE_FUNCTION);return this.createToken(f,e,b,c)},importantToken:function(a,b,c){var d=this._reader,e=a,f=Tokens.CHAR,g,h;d.mark(),h=d.read();while(h){if(h=="/"){if(d.peek()!="*")break;g=this.readComment(h);if(g=="")break}else if(isWhitespace(h))e+=h+this.readWhitespace();else{if(/i/i.test(h)){g=d.readCount(8),/mportant/i.test(g)&&(e+=h+g,f=Tokens.IMPORTANT_SYM);break}break}h=d.read()}if(f==Tokens.CHAR){d.reset();return this.charToken(a,b,c)}return this.createToken(f,e,b,c)},notToken:function(a,b,c){var d=this._reader,e=a;d.mark(),e+=d.readCount(4);if(e.toLowerCase()==":not(")return this.createToken(Tokens.NOT,e,b,c);d.reset();return this.charToken(a,b,c)},numberToken:function(a,b,c){var d=this._reader,e=this.readNumber(a),f,g=Tokens.NUMBER,h=d.peek();isIdentStart(h)?(f=this.readName(d.read()),e+=f,/em/i.test(f)?g=Tokens.EMS:/ex/i.test(f)?g=Tokens.EXS:/px|cm|mm|in|pt|pc/i.test(f)?g=Tokens.LENGTH:/deg|rad|grad/i.test(f)?g=Tokens.ANGLE:/ms|s/i.test(f)?g=Tokens.TIME:/hz|khz/i.test(f)?g=Tokens.FREQ:/dpi|dpcm/i.test(f)?g=Tokens.RESOLUTION:g=Tokens.DIMENSION):h=="%"&&(e+=d.read(),g=Tokens.PERCENTAGE);return this.createToken(g,e,b,c)},stringToken:function(a,b,c){var d=a,e=a,f=this._reader,g=a,h=Tokens.STRING,i=f.read();while(i){e+=i;if(i==d&&g!="\\")break;if(isNewLine(f.peek())&&i!="\\"){h=Tokens.INVALID;break}g=i,i=f.read()}i==null&&(h=Tokens.INVALID);return this.createToken(h,e,b,c)},unicodeRangeToken:function(a,b,c){var d=this._reader,e=a,f,g=Tokens.CHAR;d.peek()=="+"&&(d.mark(),e+=d.read(),e+=this.readUnicodeRangePart(!0),e.length==2?d.reset():(g=Tokens.UNICODE_RANGE,e.indexOf("?")==-1&&d.peek()=="-"&&(d.mark(),f=d.read(),f+=this.readUnicodeRangePart(!1),f.length==1?d.reset():e+=f)));return this.createToken(g,e,b,c)},whitespaceToken:function(a,b,c){var d=this._reader,e=a+this.readWhitespace();return this.createToken(Tokens.S,e,b,c)},readUnicodeRangePart:function(a){var b=this._reader,c="",d=b.peek();while(isHexDigit(d)&&c.length<6)b.read(),c+=d,d=b.peek();if(a)while(d=="?"&&c.length<6)b.read(),c+=d,d=b.peek();return c},readWhitespace:function(){var a=this._reader,b="",c=a.peek();while(isWhitespace(c))a.read(),b+=c,c=a.peek();return b},readNumber:function(a){var b=this._reader,c=a,d=a==".",e=b.peek();while(e){if(isDigit(e))c+=b.read();else{if(e!=".")break;if(d)break;d=!0,c+=b.read()}e=b.peek()}return c},readString:function(){var a=this._reader,b=a.read(),c=b,d=b,e=a.peek();while(e){e=a.read(),c+=e;if(e==b&&d!="\\")break;if(isNewLine(a.peek())&&e!="\\"){c="";break}d=e,e=a.peek()}e==null&&(c="");return c},readURI:function(a){var b=this._reader,c=a,d="",e=b.peek();b.mark(),e=="'"||e=='"'?d=this.readString():d=this.readURL(),e=b.peek(),d==""||e!=")"?(c=a,b.reset()):c+=d+b.read();return c},readURL:function(){var a=this._reader,b="",c=a.peek();while(/^[!#$%&\\*-~]$/.test(c))b+=a.read(),c=a.peek();return b},readName:function(a){var b=this._reader,c=a||"",d=b.peek();while(d&&isNameChar(d))c+=b.read(),d=b.peek();return c},readComment:function(a){var b=this._reader,c=a||"",d=b.read();if(d=="*"){while(d){c+=d;if(d=="*"&&b.peek()=="/"){c+=b.read();break}d=b.read()}return c}return""}});var Tokens=[{name:"CDO"},{name:"CDC"},{name:"S",whitespace:!0},{name:"COMMENT",comment:!0,hide:!0,channel:"comment"},{name:"INCLUDES",text:"~="},{name:"DASHMATCH",text:"|="},{name:"PREFIXMATCH",text:"^="},{name:"SUFFIXMATCH",text:"$="},{name:"SUBSTRINGMATCH",text:"*="},{name:"STRING"},{name:"IDENT"},{name:"HASH"},{name:"IMPORT_SYM",text:"@import"},{name:"PAGE_SYM",text:"@page"},{name:"MEDIA_SYM",text:"@media"},{name:"FONT_FACE_SYM",text:"@font-face"},{name:"CHARSET_SYM",text:"@charset"},{name:"NAMESPACE_SYM",text:"@namespace"},{name:"IMPORTANT_SYM"},{name:"EMS"},{name:"EXS"},{name:"LENGTH"},{name:"ANGLE"},{name:"TIME"},{name:"FREQ"},{name:"DIMENSION"},{name:"PERCENTAGE"},{name:"NUMBER"},{name:"URI"},{name:"FUNCTION"},{name:"UNICODE_RANGE"},{name:"INVALID"},{name:"PLUS",text:"+"},{name:"GREATER",text:">"},{name:"COMMA",text:","},{name:"TILDE",text:"~"},{name:"NOT"},{name:"TOPLEFTCORNER_SYM",text:"@top-left-corner"},{name:"TOPLEFT_SYM",text:"@top-left"},{name:"TOPCENTER_SYM",text:"@top-center"},{name:"TOPRIGHT_SYM",text:"@top-right"},{name:"TOPRIGHTCORNER_SYM",text:"@top-right-corner"},{name:"BOTTOMLEFTCORNER_SYM",text:"@bottom-left-corner"},{name:"BOTTOMLEFT_SYM",text:"@bottom-left"},{name:"BOTTOMCENTER_SYM",text:"@bottom-center"},{name:"BOTTOMRIGHT_SYM",text:"@bottom-right"},{name:"BOTTOMRIGHTCORNER_SYM",text:"@bottom-right-corner"},{name:"LEFTTOP_SYM",text:"@left-top"},{name:"LEFTMIDDLE_SYM",text:"@left-middle"},{name:"LEFTBOTTOM_SYM",text:"@left-bottom"},{name:"RIGHTTOP_SYM",text:"@right-top"},{name:"RIGHTMIDDLE_SYM",text:"@right-middle"},{name:"RIGHTBOTTOM_SYM",text:"@right-bottom"},{name:"RESOLUTION",state:"media"},{name:"IE_FUNCTION"},{name:"CHAR"},{name:"PIPE",text:"|"},{name:"SLASH",text:"/"},{name:"MINUS",text:"-"},{name:"STAR",text:"*"},{name:"LBRACE",text:"{"},{name:"RBRACE",text:"}"},{name:"LBRACKET",text:"["},{name:"RBRACKET",text:"]"},{name:"EQUALS",text:"="},{name:"COLON",text:":"},{name:"SEMICOLON",text:";"},{name:"LPAREN",text:"("},{name:"RPAREN",text:")"},{name:"DOT",text:"."}];(function(){var a=[],b={};Tokens.UNKNOWN=-1,Tokens.unshift({name:"EOF"});for(var c=0,d=Tokens.length;c<d;c++)a.push(Tokens[c].name),Tokens[Tokens[c].name]=c,Tokens[c].text&&(b[Tokens[c].text]=c);Tokens.name=function(b){return a[b]},Tokens.type=function(a){return b[a]||-1}})(),parserlib.css={Colors:Colors,Combinator:Combinator,Parser:Parser,PropertyName:PropertyName,PropertyValue:PropertyValue,PropertyValuePart:PropertyValuePart,MediaFeature:MediaFeature,MediaQuery:MediaQuery,Selector:Selector,SelectorPart:SelectorPart,SelectorSubPart:SelectorSubPart,TokenStream:TokenStream,Tokens:Tokens}}();var CSSLint=function(){var a=[],b=new parserlib.util.EventTarget;b.addRule=function(b){a.push(b),a[b.id]=b},b.clearRules=function(){a=[]},b.verify=function(b,c){var d=0,e=a.length,f,g,h=new parserlib.css.Parser({starHack:!0,ieFilters:!0,underscoreHack:!0,strict:!1});g=b.split(/\n\r?/g),f=new Reporter(g);if(!c)while(d<e)a[d++].init(h,f);else for(d in c)c.hasOwnProperty(d)&&a[d]&&a[d].init(h,f);try{h.parse(b)}catch(i){f.error("Fatal error, cannot continue: "+i.message,i.line,i.col)}return{messages:f.messages,stats:f.stats}};return b}();Reporter.prototype={constructor:Reporter,error:function(a,b,c,d){this.messages.push({type:"error",line:b,col:c,message:a,evidence:this.lines[b-1],rule:d})},warn:function(a,b,c,d){this.messages.push({type:"warning",line:b,col:c,message:a,evidence:this.lines[b-1],rule:d})},info:function(a,b,c,d){this.messages.push({type:"info",line:b,col:c,message:a,evidence:this.lines[b-1],rule:d})},rollupError:function(a,b){this.messages.push({type:"error",rollup:!0,message:a,rule:b})},rollupWarn:function(a,b){this.messages.push({type:"warning",rollup:!0,message:a,rule:b})},stat:function(a,b){this.stats[a]=b}},CSSLint.addRule({id:"adjoining-classes",name:"Adjoining Classes",desc:"Don't use adjoining classes.",browsers:"IE6, IE7",init:function(a,b){var c=this;a.addListener("startrule",function(a){var d=a.selectors,e,f,g,h,i,j,k;for(i=0;i<d.length;i++){e=d[i];for(j=0;j<e.parts.length;j++){f=e.parts[j];if(f instanceof parserlib.css.SelectorPart){h=0;for(k=0;k<f.modifiers.length;k++)g=f.modifiers[k],g.type=="class"&&h++,h>1&&b.warn("Don't use adjoining classes.",f.line,f.col,c)}}}})}}),CSSLint.addRule({id:"box-model",name:"Box Model",desc:"Don't use width or height when using padding or border.",browsers:"All",init:function(a,b){var c=this,d={border:1,"border-left":1,"border-right":1,"border-bottom":1,"border-top":1,padding:1,"padding-left":1,"padding-right":1,"padding-bottom":1,"padding-top":1},e;a.addListener("startrule",function(a){e={}}),a.addListener("property",function(a){var b=a.property;if(d[b])e[b]={line:b.line,col:b.col};else if(b=="width"||b=="height")e._flagProperty=b.text}),a.addListener("endrule",function(a){var f;if(e._flagProperty)for(f in d)d.hasOwnProperty(f)&&e[f]&&b.warn("Broken box model: using "+e._flagProperty+" with "+f+".",e[f].line,e[f].col,c)})}}),CSSLint.addRule({id:"display-property-grouping",name:"Display Property Grouping",desc:"Certain properties shouldn't be used with certain display property values.",browsers:"All",init:function(a,b){function f(a,d){e[a]&&b.warn(a+" can't be used with display: "+d+".",e[a].line,e[a].col,c)}var c=this,d={display:1,"float":1,height:1,width:1,margin:1,"margin-left":1,"margin-right":1,"margin-bottom":1,"margin-top":1,padding:1,"padding-left":1,"padding-right":1,"padding-bottom":1,"padding-top":1,"vertical-align":1},e;a.addListener("startrule",function(a){e={}}),a.addListener("property",function(a){var b=a.property;d[b]&&(e[b]={value:a.value.text,line:b.line,col:b.col})}),a.addListener("endrule",function(a){var b=e.display?e.display.value:null;if(b)switch(b){case"inline":f("height",b),f("width",b),f("margin",b),f("margin-left",b),f("margin-right",b),f("margin-top",b),f("margin-bottom",b),f("padding",b),f("padding-left",b),f("padding-right",b),f("padding-top",b),f("padding-bottom",b),f("float",b);break;case"block":f("vertical-align",b);break;case"inline-block":f("float",b);break;default:b.indexOf("table-")==0&&(f("margin",b),f("margin-left",b),f("margin-right",b),f("margin-top",b),f("margin-bottom",b),f("float",b))}})}}),CSSLint.addRule({id:"empty-rules",name:"Empty Rules",desc:"Rules without any properties specified should be removed.",browsers:"All",init:function(a,b){var c=this,d=0;a.addListener("startrule",function(a){d=0}),a.addListener("property",function(a){d++}),a.addListener("endrule",function(a){var e=a.selectors;d==0&&b.warn("Rule is empty.",e[0].line,e[0].col,c)})}}),CSSLint.addRule({id:"errors",name:"Parsing Errors",desc:"This rule looks for recoverable syntax errors.",browsers:"All",init:function(a,b){var c=this;a.addListener("error",function(a){b.error(a.message,a.line,a.col,c)})}}),CSSLint.addRule({id:"floats",name:"Floats",desc:"This rule tests if the float property is used too many times",browsers:"All",init:function(a,b){var c=this,d=0;a.addListener("property",function(a){a.property=="float"&&d++}),a.addListener("endstylesheet",function(a){b.stat("floats",d),d>=10&&b.rollupWarn("Too many floats ("+d+"), abstraction needed.",c)})}}),CSSLint.addRule({id:"font-faces",name:"Font Faces",desc:"Too many different web fonts in the same stylesheet.",browsers:"All",init:function(a,b){var c=this,d=0;a.addListener("startfontface",function(a){d++}),a.addListener("endstylesheet",function(a){d>5&&b.rollupWarn("Too many @font-face declarations ("+d+").",c)})}}),CSSLint.addRule({id:"font-sizes",name:"Font Sizes",desc:"Checks the number of font-size declarations.",browsers:"All",init:function(a,b){var c=this,d=0;a.addListener("property",function(a){var b=a.value.parts[0];a.property=="font-size"&&d++}),a.addListener("endstylesheet",function(a){b.stat("font-sizes",d),d>=10&&b.rollupWarn("Too many font-size declarations ("+d+"), abstraction needed.",c)})}}),CSSLint.addRule({id:"gradients",name:"Gradients",desc:"When using a vendor-prefixed gradient, make sure to use them all.",browsers:"All",init:function(a,b){var c=this,d;a.addListener("startrule",function(a){d={moz:0,webkit:0,ms:0,o:0}}),a.addListener("property",function(a){/\-(moz|ms|o|webkit)(?:\-(?:linear|radial))\-gradient/.test(a.value)&&(d[RegExp.$1]=1)}),a.addListener("endrule",function(a){var e=[];d.moz||e.push("Firefox 3.6+"),d.webkit||e.push("Webkit (Safari, Chrome)"),d.ms||e.push("Internet Explorer 10+"),d.o||e.push("Opera 11.1+"),e.length&&e.length<4&&b.warn("Missing vendor-prefixed CSS gradients for "+e.join(", ")+".",a.selectors[0].line,a.selectors[0].col,c)})}}),CSSLint.addRule({id:"ids",name:"IDs",desc:"Selectors should not contain IDs.",browsers:"All",init:function(a,b){var c=this;a.addListener("startrule",function(a){var d=a.selectors,e,f,g,h,i,j,k;for(i=0;i<d.length;i++){e=d[i],h=0;for(j=0;j<e.parts.length;j++){f=e.parts[j];if(f instanceof parserlib.css.SelectorPart)for(k=0;k<f.modifiers.length;k++)g=f.modifiers[k],g.type=="id"&&h++}h==1?b.warn("Don't use IDs in selectors.",e.line,e.col,c):h>1&&b.warn(h+" IDs in the selector, really?",e.line,e.col,c)}})}}),CSSLint.addRule({id:"overqualified-elements",name:"Overqualified Elements",desc:"Don't use classes or IDs with elements (a.foo or a#foo).",browsers:"All",init:function(a,b){var c=this;a.addListener("startrule",function(a){var d=a.selectors,e,f,g,h,i,j;for(h=0;h<d.length;h++){e=d[h];for(i=0;i<e.parts.length;i++){f=e.parts[i];if(f instanceof parserlib.css.SelectorPart&&f.elementName)for(j=0;j<f.modifiers.length;j++)g=f.modifiers[j],(g.type=="class"||g.type=="id")&&b.warn("Element ("+f+") is overqualified, just use "+g+" without element name.",f.line,f.col,c)}}})}}),CSSLint.addRule({id:"qualified-headings",name:"Qualified Headings",desc:"Headings should not be qualified (namespaced).",browsers:"All",init:function(a,b){var c=this;a.addListener("startrule",function(a){var d=a.selectors,e,f,g,h,i,j;for(h=0;h<d.length;h++){e=d[h];for(i=0;i<e.parts.length;i++)f=e.parts[i],f instanceof parserlib.css.SelectorPart&&f.elementName&&/h[1-6]/.test(f.elementName.toString())&&i>0&&b.warn("Heading ("+f.elementName+") should not be qualified.",f.line,f.col,c)}})}}),CSSLint.addRule({id:"regex-selectors",name:"Regex Selectors",desc:"Selectors that look like regular expressions are slow and should be avoided.",browsers:"All",init:function(a,b){var c=this;a.addListener("startrule",function(a){var d=a.selectors,e,f,g,h,i,j;for(h=0;h<d.length;h++){e=d[h];for(i=0;i<e.parts.length;i++){f=e.parts[i];if(f instanceof parserlib.css.SelectorPart)for(j=0;j<f.modifiers.length;j++)g=f.modifiers[j],g.type=="attribute"&&/([\~\|\^\$\*]=)/.test(g)&&b.warn("Attribute selectors with "+RegExp.$1+" are slow!",g.line,g.col,c)}}})}}),CSSLint.addRule({id:"rules-count",name:"Rules Count",desc:"Track how many rules there are.",browsers:"All",init:function(a,b){var c=this,d=0;a.addListener("startrule",function(a){d++}),a.addListener("endstylesheet",function(a){b.stat("rule-count",d)})}}),CSSLint.addRule({id:"unique-headings",name:"Unique Headings",desc:"Headings should be defined only once.",browsers:"All",init:function(a,b){var c=this,d={h1:0,h2:0,h3:0,h4:0,h5:0,h6:0};a.addListener("startrule",function(a){var e=a.selectors,f,g,h,i,j,k;for(i=0;i<e.length;i++){f=e[i];for(j=0;j<f.parts.length;j++)g=f.parts[j],g instanceof parserlib.css.SelectorPart&&g.elementName&&/(h[1-6])/.test(g.elementName.toString())&&(d[RegExp.$1]++,d[RegExp.$1]>1&&b.warn("Heading ("+g.elementName+") has already been defined.",g.line,g.col,c))}})}}),CSSLint.addRule({id:"vendor-prefix",name:"Vendor Prefix",desc:"When using a vendor-prefixed property, make sure to include the standard one.",browsers:"All",init:function(a,b){var c=this,d,e;a.addListener("startrule",function(a){d={},e=1}),a.addListener("property",function(a){var b=a.property,c=a.value.parts,f=0,g=c.length,h;d[b]||(d[b]=[]),d[b].push({name:a.property,value:a.value,pos:e++})}),a.addListener("endrule",function(a){var e,f,g,h,i,j,k=[];for(e in d)/(\-(?:ms|moz|webkit|o)\-)/.test(e)&&k.push({actual:e,needed:e.substring(RegExp.$1.length)});for(f=0,g=k.length;f<g;f++)i=k[f].needed,j=k[f].actual,/\-moz\-border\-radius\-(.+)/.test(j)?h="border-"+RegExp.$1.replace(/(left|right)/,"-$1")+"-radius":h=i,d[i]?d[i][0].pos<d[j][0].pos&&b.warn("Standard property '"+h+"' should come after vendor-prefixed property '"+j+"'.",a.selectors[0].line,a.selectors[0].col,c):b.warn("Missing standard property '"+h+"' to go along with '"+j+"'.",a.selectors[0].line,a.selectors[0].col,c)})}}),CSSLint.addRule({id:"width-100",name:"Width 100%",desc:"Be careful when using width: 100% on elements.",browsers:"All",init:function(a,b){var c=this;a.addListener("property",function(a){var d=a.property,e=a.value;d=="width"&&e=="100%"&&b.warn("Elements with a width of 100% may not appear as you expect inside of other elements.",d.line,d.col,c)})}}),CSSLint.addRule({id:"zero-units",name:"Zero Units",desc:"You don't need to specify units when a value is 0.",browsers:"All",init:function(a,b){var c=this;a.addListener("property",function(a){var d=a.value.parts,e=0,f=d.length,g;while(e<f)(d[e].units||d[e].type=="percentage")&&d[e].value===0&&b.warn("Values of 0 shouldn't have units specified.",d[e].line,d[e].col,c),e++})}}),exports.CSSLint=CSSLint}) \ No newline at end of file
diff --git a/studio/static/js/ace/worker-javascript.js b/studio/static/js/ace/worker-javascript.js
new file mode 100644
index 0000000..1156c45
--- /dev/null
+++ b/studio/static/js/ace/worker-javascript.js
@@ -0,0 +1 @@
+function initSender(){var a=require("pilot/event_emitter").EventEmitter,b=require("pilot/oop"),c=function(){};(function(){b.implement(this,a),this.callback=function(a,b){postMessage({type:"call",id:b,data:a})},this.emit=function(a,b){postMessage({type:"event",name:a,data:b})}}).call(c.prototype);return new c}function initBaseUrls(a){require.tlns=a}var console={log:function(a){postMessage({type:"log",data:a})}},window={console:console},require=function(a){var b=require.modules[a];if(b){b.initialized||(b.exports=b.factory().exports,b.initialized=!0);return b.exports}var c=a.split("/");c[0]=require.tlns[c[0]]||c[0],path=c.join("/")+".js",require.id=a,importScripts(path);return require(a)};require.modules={},require.tlns={};var define=function(a,b,c){arguments.length==2?c=b:arguments.length==1&&(c=a,a=require.id);a.indexOf("text/")!==0&&(require.modules[a]={factory:function(){var a={exports:{}},b=c(require,a.exports,a);b&&(a.exports=b);return a}})},main,sender;onmessage=function(a){var b=a.data;if(b.command)main[b.command].apply(main,b.args);else if(b.init){initBaseUrls(b.tlns),require("pilot/fixoldbrowsers"),sender=initSender();var c=require(b.module)[b.classname];main=new c(sender)}else b.event&&sender&&sender._dispatchEvent(b.event,b.data)},define("pilot/fixoldbrowsers",["require","exports","module"],function(a,b,c){if(!Function.prototype.bind){var d=Array.prototype.slice;Function.prototype.bind=function(b){var c=this;if(typeof c.apply!="function"||typeof c.call!="function")return new TypeError;var e=d.call(arguments),f=function a(){if(this instanceof a){var b=Object.create(c.prototype);c.apply(b,e.concat(d.call(arguments)));return b}return c.call.apply(c,e.concat(d.call(arguments)))};f.length=typeof c=="function"?Math.max(c.length-e.length,0):0;return f}}var e=Function.prototype.call,f=Array.prototype,g=Object.prototype,h=e.bind(g.hasOwnProperty),i,j,k,l,m;if(m=h(g,"__defineGetter__"))i=e.bind(g.__defineGetter__),j=e.bind(g.__defineSetter__),k=e.bind(g.__lookupGetter__),l=e.bind(g.__lookupSetter__);Array.isArray||(Array.isArray=function(b){return Object.prototype.toString.call(b)==="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(b,c){var d=+this.length;for(var e=0;e<d;e++)e in this&&b.call(c,this[e],e,this)}),Array.prototype.map||(Array.prototype.map=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;var d=Array(c),e=arguments[1];for(var f=0;f<c;f++)f in this&&(d[f]=b.call(e,this[f],f,this));return d}),Array.prototype.filter||(Array.prototype.filter=function(b){var c=[],d=arguments[1];for(var e=0;e<this.length;e++)b.call(d,this[e])&&c.push(this[e]);return c}),Array.prototype.every||(Array.prototype.every=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(!b.call(c,this[d]))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(b){var c=arguments[1];for(var d=0;d<this.length;d++)if(b.call(c,this[d]))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=0;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d++];break}if(++d>=c)throw new TypeError}while(!0);for(;d<c;d++)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(b){var c=+this.length;if(typeof b!="function")throw new TypeError;if(c===0&&arguments.length===1)throw new TypeError;var d=c-1;if(arguments.length>=2)var e=arguments[1];else do{if(d in this){e=this[d--];break}if(--d<0)throw new TypeError}while(!0);for(;d>=0;d--)d in this&&(e=b.call(null,e,this[d],d,this));return e}),Array.prototype.indexOf||(Array.prototype.indexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||0;if(d>=c)return-1;d<0&&(d+=c);for(;d<c;d++){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(b){var c=this.length;if(!c)return-1;var d=arguments[1]||c;d<0&&(d+=c),d=Math.min(d,c-1);for(;d>=0;d--){if(!h(this,d))continue;if(b===this[d])return d}return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(b){return b.__proto__||b.constructor.prototype});if(!Object.getOwnPropertyDescriptor){var n="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(b,c){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(n+b);if(!h(b,c))return undefined;var d,e,f;d={enumerable:!0,configurable:!0};if(m){var i=b.__proto__;b.__proto__=g;var e=k(b,c),f=l(b,c);b.__proto__=i;if(e||f){e&&(descriptor.get=e),f&&(descriptor.set=f);return descriptor}}descriptor.value=b[c];return descriptor}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(b){return Object.keys(b)}),Object.create||(Object.create=function(b,c){var d;if(b===null)d={"__proto__":null};else{if(typeof b!="object")throw new TypeError("typeof prototype["+typeof b+"] != 'object'");var e=function(){};e.prototype=b,d=new e,d.__proto__=b}typeof c!="undefined"&&Object.defineProperties(d,c);return d});if(!Object.defineProperty){var o="Property description must be an object: ",p="Object.defineProperty called on non-object: ",q="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(b,c,d){if(typeof b!="object"&&typeof b!="function")throw new TypeError(p+b);if(typeof b!="object"||b===null)throw new TypeError(o+d);if(h(d,"value"))if(m&&(k(b,c)||l(b,c))){var e=b.__proto__;b.__proto__=g,delete b[c],b[c]=d.value,b.prototype}else b[c]=d.value;else{if(!m)throw new TypeError(q);h(d,"get")&&i(b,c,d.get),h(d,"set")&&j(b,c,d.set)}return b}}Object.defineProperties||(Object.defineProperties=function(b,c){for(var d in c)h(c,d)&&Object.defineProperty(b,d,c[d]);return b}),Object.seal||(Object.seal=function(b){return b}),Object.freeze||(Object.freeze=function(b){return b});try{Object.freeze(function(){})}catch(r){Object.freeze=function(b){return function(c){return typeof c=="function"?c:b(c)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(b){return b}),Object.isSealed||(Object.isSealed=function(b){return!1}),Object.isFrozen||(Object.isFrozen=function(b){return!1}),Object.isExtensible||(Object.isExtensible=function(b){return!0});if(!Object.keys){var s=!0,t=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],u=t.length;for(var v in{toString:null})s=!1;Object.keys=function a(b){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError("Object.keys called on a non-object");var a=[];for(var c in b)h(b,c)&&a.push(c);if(s)for(var d=0,e=u;d<e;d++){var f=t[d];h(b,f)&&a.push(f)}return a}}Date.prototype.toISOString||(Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+(this.getUTCMonth()+1)+"-"+this.getUTCDate()+"T"+this.getUTCHours()+":"+this.getUTCMinutes()+":"+this.getUTCSeconds()+"Z"}),Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(b){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),isNaN(Date.parse("T00:00"))&&(Date=function(a){var b=function(c,d,e,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j===1&&String(c)===c?new a(b.parse(c)):j>=7?new a(c,d,e,f,g,h,i):j>=6?new a(c,d,e,f,g,h):j>=5?new a(c,d,e,f,g):j>=4?new a(c,d,e,f):j>=3?new a(c,d,e):j>=2?new a(c,d):j>=1?new a(c):new a;k.constructor=b;return k}return a.apply(this,arguments)},c=new RegExp("^(?:((?:[+-]\\d\\d)?\\d\\d\\d\\d)(?:-(\\d\\d)(?:-(\\d\\d))?)?)?(?:T(\\d\\d):(\\d\\d)(?::(\\d\\d)(?:\\.(\\d\\d\\d))?)?)?(?:Z|([+-])(\\d\\d):(\\d\\d))?$");for(var d in a)b[d]=a[d];b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(d){var e=c.exec(d);if(e){e.shift();var f=e[0]===undefined;for(var g=0;g<10;g++){if(g===7)continue;e[g]=+(e[g]||(g<3?1:0)),g===1&&e[g]--}if(f)return((e[3]*60+e[4])*60+e[5])*1e3+e[6];var h=(e[8]*60+e[9])*60*1e3;e[6]==="-"&&(h=-h);return a.UTC.apply(this,e.slice(0,7))+h}return a.parse.apply(this,arguments)};return b}(Date));if(!String.prototype.trim){var w=/^\s\s*/,x=/\s\s*$/;String.prototype.trim=function(){return String(this).replace(w,"").replace(x,"")}}}),define("pilot/event_emitter",["require","exports","module"],function(a,b,c){var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c&&!!c.length){var b=b||{};b.type=a;for(var d=0;d<c.length;d++)c[d](b)}},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c){var d=c.indexOf(b);d!==-1&&c.splice(d,1)}},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define("pilot/oop",["require","exports","module"],function(a,b,c){b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define("ace/mode/javascript_worker",["require","exports","module","pilot/oop","ace/worker/mirror","ace/worker/jshint","ace/narcissus/jsparse"],function(a,b,c){var d=a("pilot/oop"),e=a("ace/worker/mirror").Mirror,f=a("ace/worker/jshint").JSHINT,g=b.JavaScriptWorker=function(a){e.call(this,a),this.setTimeout(500)};d.inherits(g,e),function(){this.onUpdate=function(){var b=this.doc.getValue();b=b.replace(/^#!.*\n/,"\n");var c=a("ace/narcissus/jsparse");try{c.parse(b)}catch(d){this.sender.emit("narcissus",{row:d.lineno-1,column:null,text:d.message,type:"error"});return}finally{}f(b,{undef:!1,onevar:!1,passfail:!1}),this.sender.emit("jslint",f.errors)}}.call(g.prototype)}),define("ace/worker/mirror",["require","exports","module","ace/document","pilot/lang"],function(a,b,c){var d=a("ace/document").Document,e=a("pilot/lang"),f=b.Mirror=function(a){this.sender=a;var b=this.doc=new d(""),c=this.deferredUpdate=e.deferredCall(this.onUpdate.bind(this)),f=this;a.on("change",function(a){b.applyDeltas([a.data]),c.schedule(f.$timeout)})};(function(){this.$timeout=500,this.setTimeout=function(a){this.$timeout=a},this.setValue=function(a){this.doc.setValue(a),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(a){this.sender.callback(this.doc.getValue(),a)},this.onUpdate=function(){}}).call(f.prototype)}),define("ace/document",["require","exports","module","pilot/oop","pilot/event_emitter","ace/range","ace/anchor"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=a("ace/range").Range,g=a("ace/anchor").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[""]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},"aaa".split(/a/).length==0?this.$split=function(a){return a.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(a){return a.split(/\r\n|\r|\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\r?\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine="\n"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";case"auto":return this.$autoNewLine}},this.$autoNewLine="\n",this.$newLineMode="auto",this.setNewLineMode=function(a){this.$newLineMode!==a&&(this.$newLineMode=a)},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a=="\r\n"||a=="\r"||a=="\n"},this.getLine=function(a){return this.$lines[a]||""},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column));return b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length);return a},this.insert=function(a,b){if(b.length==0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];this._dispatchEvent("changeStart"),a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||"")),this._dispatchEvent("changeEnd");return a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c),this._dispatchEvent("changeStart");var d=new f(a,0,a+b.length,0),e={action:"insertLines",range:d,lines:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:"insertText",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:d}),this._dispatchEvent("changeEnd");return c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||"";this._dispatchEvent("changeStart"),this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:"insertText",range:f.fromPoints(a,d),text:b};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;this._dispatchEvent("changeStart");if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);this._dispatchEvent("changeEnd");return a.start},this.removeInLine=function(a,b,c){if(b!=c){var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this._dispatchEvent("changeStart"),this.$lines.splice(a,1,h);var i={action:"removeText",range:d,text:g};this._dispatchEvent("change",{data:i}),this._dispatchEvent("changeEnd");return d.start}},this.removeLines=function(a,b){this._dispatchEvent("changeStart");var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:"removeLines",range:c,nl:this.getNewLineCharacter(),lines:d};this._dispatchEvent("change",{data:e}),this._dispatchEvent("changeEnd");return d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this._dispatchEvent("changeStart"),this.$lines.splice(a,2,e);var g={action:"removeText",range:d,text:this.getNewLineCharacter()};this._dispatchEvent("change",{data:g}),this._dispatchEvent("changeEnd")},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this._dispatchEvent("changeStart"),this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;this._dispatchEvent("changeEnd");return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.insertLines(d.start.row,c.lines):c.action=="insertText"?this.insert(d.start,c.text):c.action=="removeLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="removeText"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="insertText"?this.remove(d):c.action=="removeLines"?this.insertLines(d.start.row,c.lines):c.action=="removeText"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define("ace/range",["require","exports","module"],function(a,b,c){var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(a,b){return this.compare(a,b)==0},this.compareRange=function(a){var b,c=a.end,d=a.start;b=this.compare(c.row,c.column);if(b==1){b=this.compare(d.row,d.column);return b==1?2:b==0?1:0}if(b==-1)return-2;b=this.compare(d.row,d.column);return b==-1?-1:b==1?42:0},this.containsRange=function(a){var b=this.compareRange(a);return b==-1||b==0||b==1},this.isEnd=function(a,b){return this.end.row==a&&this.end.column==b},this.isStart=function(a,b){return this.start.row==a&&this.start.column==b},this.setStart=function(a,b){typeof a=="object"?(this.start.column=a.column,this.start.row=a.row):(this.start.row=a,this.start.column=b)},this.setEnd=function(a,b){typeof a=="object"?(this.end.column=a.column,this.end.row=a.row):(this.end.row=a,this.end.column=b)},this.inside=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)||this.isStart(a,b)?!1:!0;return!1},this.insideStart=function(a,b){if(this.compare(a,b)==0)return this.isEnd(a,b)?!1:!0;return!1},this.insideEnd=function(a,b){if(this.compare(a,b)==0)return this.isStart(a,b)?!1:!0;return!1},this.compare=function(a,b){if(!this.isMultiLine()&&a===this.start.row)return b<this.start.column?-1:b>this.end.column?1:0;if(a<this.start.row)return-1;if(a>this.end.row)return 1;if(this.start.row===a)return b>=this.start.column?0:-1;if(this.end.row===a)return b<=this.end.column?0:1;return 0},this.compareStart=function(a,b){return this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.compareEnd=function(a,b){return this.end.row==a&&this.end.column==b?1:this.compare(a,b)},this.compareInside=function(a,b){return this.end.row==a&&this.end.column==b?1:this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define("ace/anchor",["require","exports","module","pilot/oop","pilot/event_emitter"],function(a,b,c){var d=a("pilot/oop"),e=a("pilot/event_emitter").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c=="undefined"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on("change",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row!=c.end.row||c.start.row==this.row){if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action==="insertText"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action==="insertLines"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action=="removeText"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action=="removeLines"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)}},this.setPosition=function(a,b,c){c?pos={row:a,column:b}:pos=this.$clipPositionToDocument(a,b);if(this.row!=pos.row||this.column!=pos.column){var d={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._dispatchEvent("change",{old:d,value:pos})}},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0);return c}}).call(f.prototype)}),define("pilot/lang",["require","exports","module"],function(a,b,c){b.stringReverse=function(a){return a.split("").reverse().join("")},b.stringRepeat=function(a,b){return Array(b+1).join(a)};var d=/^\s\s*/,e=/\s\s*$/;b.stringTrimLeft=function(a){return a.replace(d,"")},b.stringTrimRight=function(a){return a.replace(e,"")},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.copyArray=function(a){var b=[];for(i=0,l=a.length;i<l;i++)a[i]&&typeof a[i]=="object"?b[i]=this.copyObject(a[i]):b[i]=a[i];return b},b.deepCopy=function(a){if(typeof a!="object")return a;var b=a.constructor();for(var c in a)typeof a[c]=="object"?b[c]=this.deepCopy(a[c]):b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){b||(b=setTimeout(c,a||0));return d};d.schedule=d,d.call=function(){this.cancel(),a();return d},d.cancel=function(){clearTimeout(b),b=null;return d};return d}}),define("ace/worker/jshint",["require","exports","module"],function(a,b,c){var d=function(){function bY(){function b(){var a=w;bl("[");if(w.id!=="]")for(;;){if(w.id==="(end)")bf("Missing ']' to match '[' from line {a}.",w,a.line);else{if(w.id==="]"){bd("Unexpected comma.",N);break}w.id===","&&bf("Unexpected comma.",w)}bY();if(w.id!==",")break;bl(",")}bl("]")}function a(){var a={},b=w;bl("{");if(w.id!=="}")for(;;){if(w.id==="(end)")bf("Missing '}' to match '{' from line {a}.",w,b.line);else{if(w.id==="}"){bd("Unexpected comma.",N);break}w.id===","?bf("Unexpected comma.",w):w.id!=="(string)"&&bd("Expected a string and instead saw {a}.",w,w.value)}a[w.value]===!0?bd("Duplicate key '{a}'.",w,w.value):w.value==="__proto__"?bd("Stupid key '{a}'.",w,w.value):a[w.value]=!0,bl(),bl(":"),bY();if(w.id!==",")break;bl(",")}bl("}")}switch(w.id){case"{":a();break;case"[":b();break;case"true":case"false":case"null":case"(number)":case"(string)":bl();break;case"-":bl("-"),N.character!==w.from&&bd("Unexpected space after '-'.",N),bn(N,w),bl("(number)");break;default:bf("Expected a JSON value.",w)}}function bW(b,c){var d,e=z,f=F;z=Object.create(z),F=Object.create(F),i={"(name)":b||'"'+a+'"',"(line)":w.line,"(context)":i,"(breakage)":0,"(loopage)":0,"(scope)":F,"(statement)":c},d=i,N.funct=i,k.push(i),b&&bi(b,"function"),i["(params)"]=bV(),bR(!1),F=f,z=e,i["(last)"]=N.line,i=i["(context)"];return d}function bV(){var a,b=w,c=[];bl("("),bp();if(w.id===")")bl(")"),bp(C,N);else for(;;){a=bM(!0),c.push(a),bi(a,"parameter");if(w.id===",")bu();else{bl(")",b),bp(C,N);return c}}}function bU(){var a=bL(!0);a||(w.id==="(string)"?(a=w.value,bl()):w.id==="(number)"&&(a=w.value.toString(),bl()));return a}function bT(a){var b=a.value,c=a.line,d=m[b];typeof d=="function"&&(d=!1),d?d[d.length-1]!==c&&d.push(c):(d=[c],m[b]=d)}function bS(a){u&&typeof u[a]!="boolean"&&bd("Unexpected /*member '{a}'.",N,a),typeof t[a]=="number"?t[a]+=1:t[a]=1}function bR(a,b){var c,d=n,e=o,f=K,g=F,h;n=a,F=Object.create(F),bq(N,w),h=w;if(w.id==="{"){bl("{");if(w.id!=="}"||N.line!==w.line){o+=z.indent;while(!a&&w.from>o)o+=z.indent;!a&&!bP()&&!f&&z.strict&&i["(context)"]["(global)"]&&bd('Missing "use strict" statement.'),c=bQ(),K=f,o-=z.indent,bs()}bl("}",h),o=e}else a?((!b||z.curly)&&bd("Expected '{a}' and instead saw '{b}'.",w,"{",w.value),y=!0,c=[bO()],y=!1):bf("Expected '{a}' and instead saw '{b}'.",w,"{",w.value);i["(verb)"]=null,F=g,n=d,a&&z.noempty&&(!c||c.length===0)&&bd("Empty block.");return c}function bQ(a){var b=[],c,d;while(!w.reach&&w.id!=="(end)")w.id===";"?(bd("Unnecessary semicolon."),bl(";")):b.push(bO());return b}function bP(){if(w.value==="use strict"){K&&bd('Unnecessary "use strict".'),bl(),bl(";"),K=!0,z.newcap=!0,z.undef=!0;return!0}return!1}function bO(a){var b=o,c,d=F,e=w;if(e.id===";")bd("Unnecessary semicolon.",e),bl(";");else{e.identifier&&!e.reserved&&bk().id===":"&&(bl(),bl(":"),F=Object.create(d),bi(e.value,"label"),w.labelled||bd("Label '{a}' on {b} statement.",w,e.value,w.value),Y.test(e.value+":")&&bd("Label '{a}' looks like a javascript url.",e,e.value),w.label=e.value,e=w),a||bs(),c=bm(0,!0),e.block||(!z.expr&&(!c||!c.exps)?bd("Expected an assignment or function call and instead saw an expression.",N):z.nonew&&c.id==="("&&c.left.id==="new"&&bd("Do not use 'new' for side effects."),w.id!==";"?!z.asi&&(!z.lastsemic||w.id!="}"||w.line!=N.line)&&be("Missing semicolon.",N.line,N.from+N.value.length):(bn(N,w),bl(";"),bq(N,w))),o=b,F=d;return c}}function bN(a){var b=0,c;if(w.id===";"&&!y)for(;;){c=bk(b);if(c.reach)return;if(c.id!=="(endline)"){if(c.id==="function"){bd("Inner functions should be listed at the top of the outer function.",c);break}bd("Unreachable '{a}' after '{b}'.",c,c.value,a);break}b+=1}}function bM(a){var b=bL(a);if(b)return b;N.id==="function"&&w.id==="("?bd("Missing name in function declaration."):bf("Expected an identifier and instead saw '{a}'.",w,w.value)}function bL(a){if(w.identifier){bl(),N.reserved&&!z.es5&&(!a||N.value!="undefined")&&bd("Expected an identifier and instead saw '{a}' (a reserved word).",N,N.id);return N.value}}function bK(a,b){var c=bv(a,150);c.led=function(a){z.plusplus?bd("Unexpected use of '{a}'.",this,this.id):(!a.identifier||a.reserved)&&a.id!=="."&&a.id!=="["&&bd("Bad operand.",this),this.left=a;return this};return c}function bJ(a){bv(a,20).exps=!0;return bE(a,function(a,b){z.bitwise&&bd("Unexpected use of '{a}'.",b,b.id),bq(C,N),bq(N,w);if(a){if(a.id==="."||a.id==="["||a.identifier&&!a.reserved){bm(19);return b}a===L["function"]&&bd("Expected an identifier in an assignment, and instead saw a function invocation.",N);return b}bf("Bad assignment.",b)},20)}function bI(a,b,c){var d=bv(a,c);bz(d),d.led=typeof b=="function"?b:function(a){z.bitwise&&bd("Unexpected use of '{a}'.",this,this.id),this.left=a,this.right=bm(c);return this};return d}function bH(a,b){bv(a,20).exps=!0;return bE(a,function(a,b){var c;b.left=a,A[a.value]===!1&&F[a.value]["(global)"]===!0?bd("Read only.",a):a["function"]&&bd("'{a}' is a function.",a,a.value);if(a){if(a.id==="."||a.id==="["){(!a.left||a.left.value==="arguments")&&bd("Bad assignment.",b),b.right=bm(19);return b}if(a.identifier&&!a.reserved){i[a.value]==="exception"&&bd("Do not assign to the exception parameter.",a),b.right=bm(19);return b}a===L["function"]&&bd("Expected an identifier in an assignment and instead saw a function invocation.",N)}bf("Bad assignment.",b)},20)}function bG(a){return a&&(a.type==="(number)"&&+a.value===0||a.type==="(string)"&&a.value===""||a.type==="null"&&!z.eqnull||a.type==="true"||a.type==="false"||a.type==="undefined")}function bF(a,b){var c=bv(a,100);c.led=function(a){br(C,N),bq(N,w);var c=bm(100);a&&a.id==="NaN"||c&&c.id==="NaN"?bd("Use the isNaN function to compare with NaN.",this):b&&b.apply(this,[a,c]),a.id==="!"&&bd("Confusing use of '{a}'.",a,"!"),c.id==="!"&&bd("Confusing use of '{a}'.",a,"!"),this.left=a,this.right=c;return this};return c}function bE(a,b,c,d){var e=bv(a,c);bz(e),e.led=function(a){d||(br(C,N),bq(N,w));if(typeof b=="function")return b(a,this);this.left=a,this.right=bm(c);return this};return e}function bD(a,b){return bC(a,function(){typeof b=="function"&&b(this);return this})}function bC(a,b){var c=bB(a,b);c.identifier=c.reserved=!0;return c}function bB(a,b){var c=bw(a);c.type=a,c.nud=b;return c}function bA(a,b){var c=bv(a,150);bz(c),c.nud=typeof b=="function"?b:function(){this.right=bm(150),this.arity="unary";if(this.id==="++"||this.id==="--")z.plusplus?bd("Unexpected use of '{a}'.",this,this.id):(!this.right.identifier||this.right.reserved)&&this.right.id!=="."&&this.right.id!=="["&&bd("Bad operand.",this);return this};return c}function bz(a){var b=a.id.charAt(0);if(b>="a"&&b<="z"||b>="A"&&b<="Z")a.identifier=a.reserved=!0;return a}function by(a,b){var c=bx(a,b);c.block=!0;return c}function bx(a,b){var c=bw(a);c.identifier=c.reserved=!0,c.fud=b;return c}function bw(a){return bv(a,0)}function bv(a,b){var c=L[a];if(!c||typeof c!="object")L[a]=c={id:a,lbp:b,value:a};return c}function bu(){N.line!==w.line?z.laxbreak||bd("Bad line breaking before '{a}'.",N,w.id):N.character!==w.from&&z.white&&bd("Unexpected space after '{a}'.",w,N.value),bl(","),bq(N,w)}function bt(a){a=a||N,a.line!==w.line&&bd("Line breaking error '{a}'.",a,a.value)}function bs(a){var b;z.white&&w.id!=="(end)"&&(b=o+(a||0),w.from!==b&&bd("Expected '{a}' to have an indentation at {b} instead at {c}.",w,w.value,b,w.from))}function br(a,b){a=a||N,b=b||w,!z.laxbreak&&a.line!==b.line?bd("Bad line breaking before '{a}'.",b,b.id):z.white&&(a=a||N,b=b||w,a.character===b.from&&bd("Missing space after '{a}'.",w,a.value))}function bq(a,b){z.white&&(a=a||N,b=b||w,a.line===b.line&&a.character===b.from&&bd("Missing space after '{a}'.",w,a.value))}function bp(a,b){a=a||N,b=b||w,z.white&&!a.comment&&a.line===b.line&&bn(a,b)}function bo(a,b){a=a||N,b=b||w,z.white&&(a.character!==b.from||a.line!==b.line)&&bd("Unexpected space before '{a}'.",b,b.value)}function bn(a,b){a=a||N,b=b||w,z.white&&a.character!==b.from&&a.line===b.line&&bd("Unexpected space after '{a}'.",b,a.value)}function bm(b,c){var d,e=!1;w.id==="(end)"&&bf("Unexpected early end of program.",N),bl(),c&&(a="anonymous",i["(verb)"]=N.value);if(c===!0&&N.fud)d=N.fud();else{if(N.nud)d=N.nud();else{if(w.type==="(number)"&&N.id==="."){bd("A leading decimal point can be confused with a dot: '.{a}'.",N,w.value),bl();return N}bf("Expected an identifier and instead saw '{a}'.",N,N.id)}while(b<w.lbp)e=N.value=="Array",bl(),e&&N.id=="("&&w.id==")"&&bd("Use the array literal notation [].",N),N.led?d=N.led(d):bf("Expected an operator and instead saw '{a}'.",N,N.id)}return d}function bl(b,c){switch(N.id){case"(number)":w.id==="."&&bd("A dot following a number can be confused with a decimal point.",N);break;case"-":(w.id==="-"||w.id==="--")&&bd("Confusing minusses.");break;case"+":(w.id==="+"||w.id==="++")&&bd("Confusing plusses.")}if(N.type==="(string)"||N.identifier)a=N.value;b&&w.id!==b&&(c?w.id==="(end)"?bd("Unmatched '{a}'.",c,c.id):bd("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",w,b,c.id,c.line,w.value):(w.type!=="(identifier)"||w.value!==b)&&bd("Expected '{a}' and instead saw '{b}'.",w,b,w.value)),C=N,N=w;for(;;){w=s.shift()||bh.token();if(w.id==="(end)"||w.id==="(error)")return;if(w.type==="special")bj();else if(w.id!=="(endline)")break}}function bk(a){var b=a||0,c=0,d;while(c<=b)d=s[c],d||(d=s[c]=bh.token()),c+=1;return d}function bj(){var a,b,d,e=w.value,f,g;switch(e){case"*/":bf("Unbegun comment.");break;case"/*members":case"/*member":e="/*members",u||(u={}),b=u;break;case"/*jshint":case"/*jslint":b=z,d=c;break;case"/*global":b=A;break;default:bf("What?")}f=bh.token();loop:for(;;){for(;;){if(f.type==="special"&&f.value==="*/")break loop;if(f.id!=="(endline)"&&f.id!==",")break;f=bh.token()}f.type!=="(string)"&&f.type!=="(identifier)"&&e!=="/*members"&&bf("Bad option.",f),g=bh.token(),g.id===":"?(g=bh.token(),b===u&&bf("Expected '{a}' and instead saw '{b}'.",f,"*/",":"),f.value!=="indent"||e!=="/*jshint"&&e!=="/*jslint"?f.value!=="maxerr"||e!=="/*jshint"&&e!=="/*jslint"?f.value!=="maxlen"||e!=="/*jshint"&&e!=="/*jslint"?g.value==="true"?b[f.value]=!0:g.value==="false"?b[f.value]=!1:bf("Bad option value.",g):(a=+g.value,(typeof a!="number"||!isFinite(a)||a<=0||Math.floor(a)!==a)&&bf("Expected a small integer and instead saw '{a}'.",g,g.value),b.maxlen=a):(a=+g.value,(typeof a!="number"||!isFinite(a)||a<=0||Math.floor(a)!==a)&&bf("Expected a small integer and instead saw '{a}'.",g,g.value),b.maxerr=a):(a=+g.value,(typeof a!="number"||!isFinite(a)||a<=0||Math.floor(a)!==a)&&bf("Expected a small integer and instead saw '{a}'.",g,g.value),b.white=!0,b.indent=a),f=bh.token()):((e==="/*jshint"||e==="/*jslint")&&bf("Missing option value.",f),b[f.value]=!1,f=g)}d&&bb()}function bi(a,b){a==="hasOwnProperty"&&bd("'hasOwnProperty' is a really bad name."),_(i,a)&&!i["(global)"]&&(i[a]===!0?z.latedef&&bd("'{a}' was used before it was defined.",w,a):z.shadow||bd("'{a}' is already defined.",w,a)),i[a]=b,i["(global)"]?(l[a]=i,_(m,a)&&(z.latedef&&bd("'{a}' was used before it was defined.",w,a),delete m[a])):F[a]=i}function bg(a,b,c,d,e,f,g){return bf(a,{line:b,from:c},d,e,f,g)}function bf(a,b,c,d,e,f){var g=bd(a,b,c,d,e,f);bc("Stopping, unable to continue.",g.line,g.character)}function be(a,b,c,d,e,f,g){return bd(a,{line:b,from:c},d,e,f,g)}function bd(a,b,c,e,f,g){var h,i,j;b=b||w,b.id==="(end)"&&(b=N),i=b.line||0,h=b.from||0,j={id:"(error)",raw:a,evidence:r[i-1]||"",line:i,character:h,a:c,b:e,c:f,d:g},j.reason=a.supplant(j),d.errors.push(j),z.passfail&&bc("Stopping. ",i,h),P+=1,P>=z.maxerr&&bc("Too many errors.",i,h);return j}function bc(a,b,c){var d=Math.floor(b/r.length*100);throw{name:"JSHintError",line:b,character:c,message:a+" ("+d+"% scanned)."}}function bb(){z.couch&&ba(A,f),z.rhino&&ba(A,E),z.prototypejs&&ba(A,D),z.node&&ba(A,x),z.devel&&ba(A,g),z.browser&&ba(A,e),z.jquery&&ba(A,q),z.mootools&&ba(A,v),z.wsh&&ba(A,Q),z.globalstrict&&z.strict!==!1&&(z.strict=!0)}function ba(a,b){var c;for(c in b)_(b,c)&&(a[c]=b[c])}function _(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function $(){}"use strict";var a,b={"<":!0,"<=":!0,"==":!0,"===":!0,"!==":!0,"!=":!0,">":!0,">=":!0,"+":!0,"-":!0,"*":!0,"/":!0,"%":!0},c={asi:!0,bitwise:!0,boss:!0,browser:!0,couch:!0,curly:!0,debug:!0,devel:!0,eqeqeq:!0,eqnull:!0,es5:!0,evil:!0,expr:!0,forin:!0,globalstrict:!0,immed:!0,jquery:!0,latedef:!0,laxbreak:!0,loopfunc:!0,mootools:!0,newcap:!0,noarg:!0,node:!0,noempty:!0,nonew:!0,nomen:!0,onevar:!0,passfail:!0,plusplus:!0,prototypejs:!0,regexdash:!0,regexp:!0,rhino:!0,undef:!0,shadow:!0,strict:!0,sub:!0,supernew:!0,trailing:!0,white:!0,wsh:!0},e={ArrayBuffer:!1,ArrayBufferView:!1,addEventListener:!1,applicationCache:!1,blur:!1,clearInterval:!1,clearTimeout:!1,close:!1,closed:!1,DataView:!1,defaultStatus:!1,document:!1,event:!1,FileReader:!1,Float32Array:!1,Float64Array:!1,focus:!1,frames:!1,getComputedStyle:!1,HTMLElement:!1,history:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,Image:!1,length:!1,localStorage:!1,location:!1,moveBy:!1,moveTo:!1,name:!1,navigator:!1,onbeforeunload:!0,onblur:!0,onerror:!0,onfocus:!0,onload:!0,onresize:!0,onunload:!0,open:!1,openDatabase:!1,opener:!1,Option:!1,parent:!1,print:!1,removeEventListener:!1,resizeBy:!1,resizeTo:!1,screen:!1,scroll:!1,scrollBy:!1,scrollTo:!1,setInterval:!1,setTimeout:!1,status:!1,top:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,WebSocket:!1,window:!1,Worker:!1,XMLHttpRequest:!1,XPathEvaluator:!1,XPathException:!1,XPathExpression:!1,XPathNamespace:!1,XPathNSResolver:!1,XPathResult:!1},f={require:!1,respond:!1,getRow:!1,emit:!1,send:!1,start:!1,sum:!1,log:!1,exports:!1,module:!1},g={alert:!1,confirm:!1,console:!1,Debug:!1,opera:!1,prompt:!1},h={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"/":"\\/","\\":"\\\\"},i,j=["closure","exception","global","label","outer","unused","var"],k,l,m,n,o,p,q={$:!1,jQuery:!1},r,s,t,u,v={$:!1,$$:!1,Assets:!1,Browser:!1,Chain:!1,Class:!1,Color:!1,Cookie:!1,Core:!1,Document:!1,DomReady:!1,DOMReady:!1,Drag:!1,Element:!1,Elements:!1,Event:!1,Events:!1,Fx:!1,Group:!1,Hash:!1,HtmlTable:!1,Iframe:!1,IframeShim:!1,InputValidator:!1,instanceOf:!1,Keyboard:!1,Locale:!1,Mask:!1,MooTools:!1,Native:!1,Options:!1,OverText:!1,Request:!1,Scroller:!1,Slick:!1,Slider:!1,Sortables:!1,Spinner:!1,Swiff:!1,Tips:!1,Type:!1,typeOf:!1,URI:!1,Window:!1},w,x={__filename:!1,__dirname:!1,exports:!1,Buffer:!1,GLOBAL:!1,global:!1,module:!1,process:!1,require:!1},y,z,A,B,C,D={$:!1,$$:!1,$A:!1,$F:!1,$H:!1,$R:!1,$break:!1,$continue:!1,$w:!1,Abstract:!1,Ajax:!1,Class:!1,Enumerable:!1,Element:!1,Event:!1,Field:!1,Form:!1,Hash:!1,Insertion:!1,ObjectRange:!1,PeriodicalExecuter:!1,Position:!1,Prototype:!1,Selector:!1,Template:!1,Toggle:!1,Try:!1,Autocompleter:!1,Builder:!1,Control:!1,Draggable:!1,Draggables:!1,Droppables:!1,Effect:!1,Sortable:!1,SortableObserver:!1,Sound:!1,Scriptaculous:!1},E={defineClass:!1,deserialize:!1,gc:!1,help:!1,load:!1,loadClass:!1,print:!1,quit:!1,readFile:!1,readUrl:!1,runCommand:!1,seal:!1,serialize:!1,spawn:!1,sync:!1,toint32:!1,version:!1},F,G,H,I={Array:!1,Boolean:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,eval:!1,EvalError:!1,Function:!1,hasOwnProperty:!1,isFinite:!1,isNaN:!1,JSON:!1,Math:!1,Number:!1,Object:!1,parseInt:!1,parseFloat:!1,RangeError:!1,ReferenceError:!1,RegExp:!1,String:!1,SyntaxError:!1,TypeError:!1,URIError:!1},J={E:!0,LN2:!0,LN10:!0,LOG2E:!0,LOG10E:!0,MAX_VALUE:!0,MIN_VALUE:!0,NEGATIVE_INFINITY:!0,PI:!0,POSITIVE_INFINITY:!0,SQRT1_2:!0,SQRT2:!0},K,L={},M,N,O,P,Q={ActiveXObject:!0,Enumerator:!0,GetObject:!0,ScriptEngine:!0,ScriptEngineBuildVersion:!0,ScriptEngineMajorVersion:!0,ScriptEngineMinorVersion:!0,VBArray:!0,WSH:!0,WScript:!0},R=/@cc|<\/?|script|\]\s*\]|<\s*!|&lt/i,S=/[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,T=/^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jshint|jslint|members?|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/,U=/[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,V=/[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,W=/\*\/|\/\*/,X=/^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,Y=/^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i,Z=/^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/;typeof Array.isArray!="function"&&(Array.isArray=function(a){return Object.prototype.toString.apply(a)==="[object Array]"}),typeof Object.create!="function"&&(Object.create=function(a){$.prototype=a;return new $}),typeof Object.keys!="function"&&(Object.keys=function(a){var b=[],c;for(c in a)_(a,c)&&b.push(c);return b}),typeof String.prototype.entityify!="function"&&(String.prototype.entityify=function(){return this.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}),typeof String.prototype.isAlpha!="function"&&(String.prototype.isAlpha=function(){return this>="a"&&this<="z￿"||this>="A"&&this<="Z￿"}),typeof String.prototype.isDigit!="function"&&(String.prototype.isDigit=function(){return this>="0"&&this<="9"}),typeof String.prototype.supplant!="function"&&(String.prototype.supplant=function(a){return this.replace(/\{([^{}]*)\}/g,function(b,c){var d=a[c];return typeof d=="string"||typeof d=="number"?d:b})}),typeof String.prototype.name!="function"&&(String.prototype.name=function(){if(X.test(this))return this;if(U.test(this))return'"'+this.replace(V,function(a){var b=h[a];if(b)return b;return"\\u"+("0000"+a.charCodeAt().toString(16)).slice(-4)})+'"';return'"'+this+'"'});var bh=function(){function g(a,e){var f,g;a==="(color)"||a==="(range)"?g={type:a}:a==="(punctuator)"||a==="(identifier)"&&_(L,e)?g=L[e]||L["(error)"]:g=L[a],g=Object.create(g),(a==="(string)"||a==="(range)")&&Y.test(e)&&be("Script URL.",d,c),a==="(identifier)"&&(g.identifier=!0,e==="__iterator__"||e==="__proto__"?bg("Reserved name '{a}'.",d,c,e):z.nomen&&(e.charAt(0)==="_"||e.charAt(e.length-1)==="_")&&be("Unexpected {a} in '{b}'.",d,c,"dangling '_'",e)),g.value=e,g.line=d,g.character=b,g.from=c,f=g.id,f!=="(endline)"&&(B=f&&("(,=:[!&|?{};".indexOf(f.charAt(f.length-1))>=0||f==="return"));return g}function f(){var a,c;if(d>=r.length)return!1;b=1,e=r[d],d+=1,a=e.search(/ \t/),a>=0&&be("Mixed spaces and tabs.",d,a+1),e=e.replace(/\t/g,M),a=e.search(S),a>=0&&be("Unsafe character.",d,a),z.maxlen&&z.maxlen<e.length&&be("Line too long.",d,e.length),c=e.search(/\s+$/),z.trailing&&~c&&be("Trailing whitespace.",d,c);return!0}var b,c,d,e;return{init:function(a){typeof a=="string"?r=a.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split("\n"):r=a,r[0]&&r[0].substr(0,2)=="#!"&&(r[0]=""),d=0,f(),c=1},range:function(a,f){var h,i="";c=b,e.charAt(0)!==a&&bg("Expected '{a}' and instead saw '{b}'.",d,b,a,e.charAt(0));for(;;){e=e.slice(1),b+=1,h=e.charAt(0);switch(h){case"":bg("Missing '{a}'.",d,b,h);break;case f:e=e.slice(1),b+=1;return g("(range)",i);case"\\":be("Unexpected '{a}'.",d,b,h)}i+=h}},token:function(){function t(a){function k(a){var c=parseInt(e.substr(i+1,a),16);i+=a,c>=32&&c<=126&&c!==34&&c!==92&&c!==39&&be("Unnecessary escapement.",d,b),b+=a,h=String.fromCharCode(c)}var h,i,j="";p&&a!=='"'&&be("Strings must use doublequote.",d,b),i=0;for(;;){while(i>=e.length)i=0,f()||bg("Unclosed string.",d,c);h=e.charAt(i);if(h===a){b+=1,e=e.substr(i+1);return g("(string)",j,a)}if(h<" "){if(h==="\n"||h==="\r")break;be("Control character in string: {a}.",d,b+i,e.slice(0,i))}else if(h==="\\"){i+=1,b+=1,h=e.charAt(i);switch(h){case"\\":case'"':case"/":break;case"'":p&&be("Avoid \\'.",d,b);break;case"b":h="\b";break;case"f":h="\f";break;case"n":h="\n";break;case"r":h="\r";break;case"t":h="\t";break;case"u":k(4);break;case"v":p&&be("Avoid \\v.",d,b),h=" ";break;case"x":p&&be("Avoid \\x-.",d,b),k(2);break;default:be("Bad escapement.",d,b)}}j+=h,b+=1,i+=1}}function s(a){var d=a.exec(e),f;if(d){n=d[0].length,f=d[1],h=f.charAt(0),e=e.substr(n),c=b+n-f.length,b+=n;return f}}var a,h,i,j,k,l,m,n,o,q,r;for(;;){if(!e)return g(f()?"(endline)":"(end)","");r=s(T);if(!r){r="",h="";while(e&&e<"!")e=e.substr(1);e&&bg("Unexpected '{a}'.",d,b,e.substr(0,1))}else{if(h.isAlpha()||h==="_"||h==="$")return g("(identifier)",r);if(h.isDigit()){isFinite(Number(r))||be("Bad number '{a}'.",d,b,r),e.substr(0,1).isAlpha()&&be("Missing space after '{a}'.",d,b,r),h==="0"&&(j=r.substr(1,1),j.isDigit()?N.id!=="."&&be("Don't use extra leading zeros '{a}'.",d,b,r):p&&(j==="x"||j==="X")&&be("Avoid 0x-. '{a}'.",d,b,r)),r.substr(r.length-1)==="."&&be("A trailing decimal point can be confused with a dot '{a}'.",d,b,r);return g("(number)",r)}switch(r){case'"':case"'":return t(r);case"//":G&&be("Unexpected comment.",d,b),e="",N.comment=!0;break;case"/*":G&&be("Unexpected comment.",d,b);for(;;){m=e.search(W);if(m>=0)break;f()||bg("Unclosed comment.",d,b)}b+=m+2,e.substr(m,1)==="/"&&bg("Nested comment.",d,b),e=e.substr(m+2),N.comment=!0;break;case"/*members":case"/*member":case"/*jshint":case"/*jslint":case"/*global":case"*/":return{value:r,type:"special",line:d,character:b,from:c};case"":break;case"/":N.id==="/="&&bg("A regular expression literal can be confused with '/='.",d,c);if(B){k=0,i=0,n=0;for(;;){a=!0,h=e.charAt(n),n+=1;switch(h){case"":bg("Unclosed regular expression.",d,c);return;case"/":k>0&&be("Unescaped '{a}'.",d,c+n,"/"),h=e.substr(0,n-1),q={g:!0,i:!0,m:!0};while(q[e.charAt(n)]===!0)q[e.charAt(n)]=!1,n+=1;b+=n,e=e.substr(n),q=e.charAt(0),(q==="/"||q==="*")&&bg("Confusing regular expression.",d,c);return g("(regexp)",h);case"\\":h=e.charAt(n),h<" "?be("Unexpected control character in regular expression.",d,c+n):h==="<"&&be("Unexpected escaped character '{a}' in regular expression.",d,c+n,h),n+=1;break;case"(":k+=1,a=!1;if(e.charAt(n)==="?"){n+=1;switch(e.charAt(n)){case":":case"=":case"!":n+=1;break;default:be("Expected '{a}' and instead saw '{b}'.",d,c+n,":",e.charAt(n))}}else i+=1;break;case"|":a=!1;break;case")":k===0?be("Unescaped '{a}'.",d,c+n,")"):k-=1;break;case" ":q=1;while(e.charAt(n)===" ")n+=1,q+=1;q>1&&be("Spaces are hard to count. Use {{a}}.",d,c+n,q);break;case"[":h=e.charAt(n),h==="^"&&(n+=1,z.regexp?be("Insecure '{a}'.",d,c+n,h):e.charAt(n)==="]"&&bg("Unescaped '{a}'.",d,c+n,"^")),q=!1,h==="]"&&(be("Empty class.",d,c+n-1),q=!0);klass:do{h=e.charAt(n),n+=1;switch(h){case"[":case"^":be("Unescaped '{a}'.",d,c+n,h),q=!0;break;case"-":q?q=!1:(be("Unescaped '{a}'.",d,c+n,"-"),q=!0);break;case"]":!q&&!z.regexdash&&be("Unescaped '{a}'.",d,c+n-1,"-");break klass;case"\\":h=e.charAt(n),h<" "?be("Unexpected control character in regular expression.",d,c+n):h==="<"&&be("Unexpected escaped character '{a}' in regular expression.",d,c+n,h),n+=1,q=!0;break;case"/":be("Unescaped '{a}'.",d,c+n-1,"/"),q=!0;break;case"<":q=!0;break;default:q=!0}}while(h);break;case".":z.regexp&&be("Insecure '{a}'.",d,c+n,h);break;case"]":case"?":case"{":case"}":case"+":case"*":be("Unescaped '{a}'.",d,c+n,h)}if(a)switch(e.charAt(n)){case"?":case"+":case"*":n+=1,e.charAt(n)==="?"&&(n+=1);break;case"{":n+=1,h=e.charAt(n),(h<"0"||h>"9")&&be("Expected a number and instead saw '{a}'.",d,c+n,h),n+=1,o=+h;for(;;){h=e.charAt(n);if(h<"0"||h>"9")break;n+=1,o=+h+o*10}l=o;if(h===","){n+=1,l=Infinity,h=e.charAt(n);if(h>="0"&&h<="9"){n+=1,l=+h;for(;;){h=e.charAt(n);if(h<"0"||h>"9")break;n+=1,l=+h+l*10}}}e.charAt(n)!=="}"?be("Expected '{a}' and instead saw '{b}'.",d,c+n,"}",h):n+=1,e.charAt(n)==="?"&&(n+=1),o>l&&be("'{a}' should not be greater than '{b}'.",d,c+n,o,l)}}h=e.substr(0,n-1),b+=n,e=e.substr(n);return g("(regexp)",h)}return g("(punctuator)",r);case"#":return g("(punctuator)",r);default:return g("(punctuator)",r)}}}}}}();bB("(number)",function(){return this}),bB("(string)",function(){return this}),L["(identifier)"]={type:"(identifier)",lbp:0,identifier:!0,nud:function(){var b=this.value,c=F[b],d;typeof c=="function"?c=undefined:typeof c=="boolean"&&(d=i,i=k[0],bi(b,"var"),c=i,i=d);if(i===c)switch(i[b]){case"unused":i[b]="var";break;case"unction":i[b]="function",this["function"]=!0;break;case"function":this["function"]=!0;break;case"label":bd("'{a}' is a statement label.",N,b)}else if(i["(global)"])a!="typeof"&&a!="delete"&&z.undef&&typeof A[b]!="boolean"&&bd("'{a}' is not defined.",N,b),bT(N);else switch(i[b]){case"closure":case"function":case"var":case"unused":bd("'{a}' used out of scope.",N,b);break;case"label":bd("'{a}' is a statement label.",N,b);break;case"outer":case"global":break;default:if(c===!0)i[b]=!0;else if(c===null)bd("'{a}' is not allowed.",N,b),bT(N);else if(typeof c!="object")a!="typeof"&&a!="delete"&&z.undef?bd("'{a}' is not defined.",N,b):i[b]=!0,bT(N);else switch(c[b]){case"function":case"unction":this["function"]=!0,c[b]="closure",i[b]=c["(global)"]?"global":"outer";break;case"var":case"unused":c[b]="closure",i[b]=c["(global)"]?"global":"outer";break;case"closure":case"parameter":i[b]=c["(global)"]?"global":"outer";break;case"label":bd("'{a}' is a statement label.",N,b)}}return this},led:function(){bf("Expected an operator and instead saw '{a}'.",w,w.value)}},bB("(regexp)",function(){return this}),bw("(endline)"),bw("(begin)"),bw("(end)").reach=!0,bw("</").reach=!0,bw("<!"),bw("<!--"),bw("-->"),bw("(error)").reach=!0,bw("}").reach=!0,bw(")"),bw("]"),bw('"').reach=!0,bw("'").reach=!0,bw(";"),bw(":").reach=!0,bw(","),bw("#"),bw("@"),bC("else"),bC("case").reach=!0,bC("catch"),bC("default").reach=!0,bC("finally"),bD("arguments",function(a){K&&i["(global)"]&&bd("Strict violation.",a)}),bD("eval"),bD("false"),bD("Infinity"),bD("NaN"),bD("null"),bD("this",function(a){K&&(i["(statement)"]&&i["(name)"].charAt(0)>"Z"||i["(global)"])&&bd("Strict violation.",a)}),bD("true"),bD("undefined"),bH("=","assign",20),bH("+=","assignadd",20),bH("-=","assignsub",20),bH("*=","assignmult",20),bH("/=","assigndiv",20).nud=function(){bf("A regular expression literal can be confused with '/='.")},bH("%=","assignmod",20),bJ("&=","assignbitand",20),bJ("|=","assignbitor",20),bJ("^=","assignbitxor",20),bJ("<<=","assignshiftleft",20),bJ(">>=","assignshiftright",20),bJ(">>>=","assignshiftrightunsigned",20),bE("?",function(a,b){b.left=a,b.right=bm(10),bl(":"),b["else"]=bm(10);return b},30),bE("||","or",40),bE("&&","and",50),bI("|","bitor",70),bI("^","bitxor",80),bI("&","bitand",90),bF("==",function(a,b){var c=z.eqnull&&(a.value=="null"||b.value=="null");!c&&z.eqeqeq?bd("Expected '{a}' and instead saw '{b}'.",this,"===","=="):bG(a)?bd("Use '{a}' to compare with '{b}'.",this,"===",a.value):bG(b)&&bd("Use '{a}' to compare with '{b}'.",this,"===",b.value);return this}),bF("==="),bF("!=",function(a,b){z.eqeqeq?bd("Expected '{a}' and instead saw '{b}'.",this,"!==","!="):bG(a)?bd("Use '{a}' to compare with '{b}'.",this,"!==",a.value):bG(b)&&bd("Use '{a}' to compare with '{b}'.",this,"!==",b.value);return this}),bF("!=="),bF("<"),bF(">"),bF("<="),bF(">="),bI("<<","shiftleft",120),bI(">>","shiftright",120),bI(">>>","shiftrightunsigned",120),bE("in","in",120),bE("instanceof","instanceof",120),bE("+",function(a,b){var c=bm(130);if(a&&c&&a.id==="(string)"&&c.id==="(string)"){a.value+=c.value,a.character=c.character,Y.test(a.value)&&bd("JavaScript URL.",a);return a}b.left=a,b.right=c;return b},130),bA("+","num"),bA("+++",function(){bd("Confusing pluses."),this.right=bm(150),this.arity="unary";return this}),bE("+++",function(a){bd("Confusing pluses."),this.left=a,this.right=bm(130);return this},130),bE("-","sub",130),bA("-","neg"),bA("---",function(){bd("Confusing minuses."),this.right=bm(150),this.arity="unary";return this}),bE("---",function(a){bd("Confusing minuses."),this.left=a,this.right=bm(130);return this},130),bE("*","mult",140),bE("/","div",140),bE("%","mod",140),bK("++","postinc"),bA("++","preinc"),L["++"].exps=!0,bK("--","postdec"),bA("--","predec"),L["--"].exps=!0,bA("delete",function(){var a=bm(0);(!a||a.id!=="."&&a.id!=="[")&&bd("Variables should not be deleted."),this.first=a;return this}).exps=!0,bA("~",function(){z.bitwise&&bd("Unexpected '{a}'.",this,"~"),bm(150);return this}),bA("!",function(){this.right=bm(150),this.arity="unary",b[this.right.id]===!0&&bd("Confusing use of '{a}'.",this,"!");return this}),bA("typeof","typeof"),bA("new",function(){var a=bm(155),b;if(a&&a.id!=="function")if(a.identifier){a["new"]=!0;switch(a.value){case"Object":bd("Use the object literal notation {}.",N);break;case"Number":case"String":case"Boolean":case"Math":case"JSON":bd("Do not use {a} as a constructor.",N,a.value);break;case"Function":z.evil||bd("The Function constructor is eval.");break;case"Date":case"RegExp":break;default:a.id!=="function"&&(b=a.value.substr(0,1),z.newcap&&(b<"A"||b>"Z")&&bd("A constructor name should start with an uppercase letter.",N))}}else a.id!=="."&&a.id!=="["&&a.id!=="("&&bd("Bad constructor.",N);else z.supernew||bd("Weird construction. Delete 'new'.",this);bn(N,w),w.id!=="("&&!z.supernew&&bd("Missing '()' invoking a constructor."),this.first=a;return this}),L["new"].exps=!0,bA("void").exps=!0,bE(".",function(a,b){bn(C,N),bo();var c=bM();typeof c=="string"&&bS(c),b.left=a,b.right=c,z.noarg&&a&&a.value==="arguments"&&(c==="callee"||c==="caller")?bd("Avoid arguments.{a}.",a,c):!z.evil&&a&&a.value==="document"&&(c==="write"||c==="writeln")&&bd("document.write can be a form of eval.",a),!z.evil&&(c==="eval"||c==="execScript")&&bd("eval is evil.");return b},160,!0),bE("(",function(a,b){C.id!=="}"&&C.id!==")"&&bo(C,N),bp(),z.immed&&!a.immed&&a.id==="function"&&bd("Wrap an immediate function invocation in parentheses to assist the reader in understanding that the expression is the result of a function, and not the function itself.");var c=0,d=[];a&&a.type==="(identifier)"&&a.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)&&a.value!=="Number"&&a.value!=="String"&&a.value!=="Boolean"&&a.value!=="Date"&&(a.value==="Math"?bd("Math is not a function.",a):z.newcap&&bd("Missing 'new' prefix when invoking a constructor.",a));if(w.id!==")")for(;;){d[d.length]=bm(10),c+=1;if(w.id!==",")break;bu()}bl(")"),bp(C,N),typeof a=="object"&&(a.value==="parseInt"&&c===1&&bd("Missing radix parameter.",a),z.evil||(a.value==="eval"||a.value==="Function"||a.value==="execScript"?bd("eval is evil.",a):d[0]&&d[0].id==="(string)"&&(a.value==="setTimeout"||a.value==="setInterval")&&bd("Implied eval is evil. Pass a function instead of a string.",a)),!a.identifier&&a.id!=="."&&a.id!=="["&&a.id!=="("&&a.id!=="&&"&&a.id!=="||"&&a.id!=="?"&&bd("Bad invocation.",a)),b.left=a;return b},155,!0).exps=!0,bA("(",function(){bp(),w.id==="function"&&(w.immed=!0);var a=bm(0);bl(")",this),bp(C,N),z.immed&&a.id==="function"&&(w.id==="("?bd("Move the invocation into the parens that contain the function.",w):bd("Do not wrap function literals in parens unless they are to be immediately invoked.",this));return a}),bE("[",function(a,b){bo(C,N),bp();var c=bm(0),d;c&&c.type==="(string)"&&(!z.evil&&(c.value==="eval"||c.value==="execScript")&&bd("eval is evil.",b),bS(c.value),!z.sub&&X.test(c.value)&&(d=L[c.value],(!d||!d.reserved)&&bd("['{a}'] is better written in dot notation.",c,c.value))),bl("]",b),bp(C,N),b.left=a,b.right=c;return b},160,!0),bA("[",function(){var a=N.line!==w.line;this.first=[],a&&(o+=z.indent,w.from===o+z.indent&&(o+=z.indent));while(w.id!=="(end)"){while(w.id===",")bd("Extra comma."),bl(",");if(w.id==="]")break;a&&N.line!==w.line&&bs(),this.first.push(bm(10));if(w.id!==",")break;bu();if(w.id==="]"&&!z.es5){bd("Extra comma.",N);break}}a&&(o-=z.indent,bs()),bl("]",this);return this},160),function(a){a.nud=function(){var a,b,c,d,e,f={},g;a=N.line!==w.line,a&&(o+=z.indent,w.from===o+z.indent&&(o+=z.indent));for(;;){if(w.id==="}")break;a&&bs();if(w.value==="get"&&bk().id!==":")bl("get"),z.es5||bf("get/set are ES5 features."),c=bU(),c||bf("Missing property name."),g=w,bn(N,w),b=bW(),!z.loopfunc&&i["(loopage)"]&&bd("Don't make functions within a loop.",g),e=b["(params)"],e&&bd("Unexpected parameter '{a}' in get {b} function.",g,e[0],c),bn(N,w),bl(","),bs(),bl("set"),d=bU(),c!==d&&bf("Expected {a} and instead saw {b}.",N,c,d),g=w,bn(N,w),b=bW(),e=b["(params)"],(!e||e.length!==1||e[0]!=="value")&&bd("Expected (value) in set {a} function.",g,c);else{c=bU();if(typeof c!="string")break;bl(":"),bq(N,w),bm(10)}f[c]===!0&&bd("Duplicate member '{a}'.",w,c),f[c]=!0,bS(c);if(w.id===",")bu(),w.id===","?bd("Extra comma.",N):w.id==="}"&&!z.es5&&bd("Extra comma.",N);else break}a&&(o-=z.indent,bs()),bl("}",this);return this},a.fud=function(){bf("Expected to see a statement and instead saw a block.",N)}}(bw("{"));var bX=bx("var",function(a){var b,c,d;i["(onevar)"]&&z.onevar?bd("Too many var statements."):i["(global)"]||(i["(onevar)"]=!0),this.first=[];for(;;){bq(N,w),b=bM(),i["(global)"]&&A[b]===!1&&bd("Redefinition of '{a}'.",N,b),bi(b,"unused");if(a)break;c=N,this.first.push(N),w.id==="="&&(bq(N,w),bl("="),bq(N,w),w.id==="undefined"&&bd("It is not necessary to initialize '{a}' to 'undefined'.",N,b),bk(0).id==="="&&w.identifier&&bf("Variable {a} was not declared correctly.",w,w.value),d=bm(0),c.first=d);if(w.id!==",")break;bu()}return this});bX.exps=!0,by("function",function(){n&&bd("Function declarations should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.",N);var a=bM();bn(N,w),bi(a,"unction"),bW(a,!0),w.id==="("&&w.line===N.line&&bf("Function declarations are not invocable. Wrap the whole function invocation in parens.");return this}),bA("function",function(){var a=bL();a?bn(N,w):bq(N,w),bW(a),!z.loopfunc&&i["(loopage)"]&&bd("Don't make functions within a loop.");return this}),by("if",function(){var a=w;bl("("),bq(this,a),bp(),bm(20),w.id==="="&&(z.boss||bd("Expected a conditional expression and instead saw an assignment."),bl("="),bm(20)),bl(")",a),bp(C,N),bR(!0,!0),w.id==="else"&&(bq(N,w),bl("else"),w.id==="if"||w.id==="switch"?bO(!0):bR(!0,!0));return this}),by("try",function(){var a,b,c;bR(!1),w.id==="catch"&&(bl("catch"),bq(N,w),bl("("),c=F,F=Object.create(c),b=w.value,w.type!=="(identifier)"?bd("Expected an identifier and instead saw '{a}'.",w,b):bi(b,"exception"),bl(),bl(")"),bR(!1),a=!0,F=c);if(w.id==="finally")bl("finally"),bR(!1);else{a||bf("Expected '{a}' and instead saw '{b}'.",w,"catch",w.value);return this}}),by("while",function(){var a=w;i["(breakage)"]+=1,i["(loopage)"]+=1,bl("("),bq(this,a),bp(),bm(20),w.id==="="&&(z.boss||bd("Expected a conditional expression and instead saw an assignment."),bl("="),bm(20)),bl(")",a),bp(C,N),bR(!0,!0),i["(breakage)"]-=1,i["(loopage)"]-=1;return this}).labelled=!0,bC("with"),by("switch",function(){var a=w,b=!1;i["(breakage)"]+=1,bl("("),bq(this,a),bp(),this.condition=bm(20),bl(")",a),bp(C,N),bq(N,w),a=w,bl("{"),bq(N,w),o+=z.indent,this.cases=[];for(;;)switch(w.id){case"case":switch(i["(verb)"]){case"break":case"case":case"continue":case"return":case"switch":case"throw":break;default:Z.test(r[w.line-2])||bd("Expected a 'break' statement before 'case'.",N)}bs(-z.indent),bl("case"),this.cases.push(bm(20)),b=!0,bl(":"),i["(verb)"]="case";break;case"default":switch(i["(verb)"]){case"break":case"continue":case"return":case"throw":break;default:Z.test(r[w.line-2])||bd("Expected a 'break' statement before 'default'.",N)}bs(-z.indent),bl("default"),b=!0,bl(":");break;case"}":o-=z.indent,bs(),bl("}",a),(this.cases.length===1||this.condition.id==="true"||this.condition.id==="false")&&bd("This 'switch' should be an 'if'.",this),i["(breakage)"]-=1,i["(verb)"]=undefined;return;case"(end)":bf("Missing '{a}'.",w,"}");return;default:if(b)switch(N.id){case",":bf("Each value should have its own case label.");return;case":":bQ();break;default:bf("Missing ':' on a case clause.",N)}else bf("Expected '{a}' and instead saw '{b}'.",w,"case",w.value)}}).labelled=!0,bx("debugger",function(){z.debug||bd("All 'debugger' statements should be removed.");return this}).exps=!0,function(){var a=bx("do",function(){i["(breakage)"]+=1,i["(loopage)"]+=1,this.first=bR(!0),bl("while");var a=w;bq(N,a),bl("("),bp(),bm(20),w.id==="="&&(z.boss||bd("Expected a conditional expression and instead saw an assignment."),bl("="),bm(20)),bl(")",a),bp(C,N),i["(breakage)"]-=1,i["(loopage)"]-=1;return this});a.labelled=!0,a.exps=!0}(),by("for",function(){var a,b=w;i["(breakage)"]+=1,i["(loopage)"]+=1,bl("("),bq(this,b),bp();if(bk(w.id==="var"?1:0).id==="in"){if(w.id==="var")bl("var"),bX.fud.call(bX,!0);else{switch(i[w.value]){case"unused":i[w.value]="var";break;case"var":break;default:bd("Bad for in variable '{a}'.",w,w.value)}bl()}bl("in"),bm(20),bl(")",b),a=bR(!0,!0),z.forin&&(a.length>1||typeof a[0]!="object"||a[0].value!=="if")&&bd("The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.",this),i["(breakage)"]-=1,i["(loopage)"]-=1;return this}if(w.id!==";")if(w.id==="var")bl("var"),bX.fud.call(bX);else for(;;){bm(0,"for");if(w.id!==",")break;bu()}bt(N),bl(";"),w.id!==";"&&(bm(20),w.id==="="&&(z.boss||bd("Expected a conditional expression and instead saw an assignment."),bl("="),bm(20))),bt(N),bl(";"),w.id===";"&&bf("Expected '{a}' and instead saw '{b}'.",w,")",";");if(w.id!==")")for(;;){bm(0,"for");if(w.id!==",")break;bu()}bl(")",b),bp(C,N),bR(!0,!0),i["(breakage)"]-=1,i["(loopage)"]-=1;return this}).labelled=!0,bx("break",function(){var a=w.value;i["(breakage)"]===0&&bd("Unexpected '{a}'.",w,this.value),bt(this),w.id!==";"&&N.line===w.line&&(i[a]!=="label"?bd("'{a}' is not a statement label.",w,a):F[a]!==i&&bd("'{a}' is out of scope.",w,a),this.first=w,bl()),bN("break");return this}).exps=!0,bx("continue",function(){var a=w.value;i["(breakage)"]===0&&bd("Unexpected '{a}'.",w,this.value),bt(this),w.id!==";"?N.line===w.line&&(i[a]!=="label"?bd("'{a}' is not a statement label.",w,a):F[a]!==i&&bd("'{a}' is out of scope.",w,a),this.first=w,bl()):i["(loopage)"]||bd("Unexpected '{a}'.",w,this.value),bN("continue");return this}).exps=!0,bx("return",function(){bt(this),w.id==="(regexp)"&&bd("Wrap the /regexp/ literal in parens to disambiguate the slash operator."),w.id!==";"&&!w.reach&&(bq(N,w),this.first=bm(20)),bN("return");return this}).exps=!0,bx("throw",function(){bt(this),bq(N,w),this.first=bm(20),bN("throw");return this}).exps=!0,bC("class"),bC("const"),bC("enum"),bC("export"),bC("extends"),bC("import"),bC("super"),bC("let"),bC("yield"),bC("implements"),bC("interface"),bC("package"),bC("private"),bC("protected"),bC("public"),bC("static");var bZ=function(a,b,c){var e,f,g;d.errors=[],A=Object.create(I),ba(A,c||{});if(b){e=b.predef;if(e)if(Array.isArray(e))for(f=0;f<e.length;f+=1)A[e[f]]=!0;else if(typeof e=="object"){g=Object.keys(e);for(f=0;f<g.length;f+=1)A[g[f]]=!!e[g]}z=b}else z={};z.indent=z.indent||4,z.maxerr=z.maxerr||50,M="";for(f=0;f<z.indent;f+=1)M+=" ";o=1,l=Object.create(A),F=l,i={"(global)":!0,"(name)":"(global)","(scope)":F,"(breakage)":0,"(loopage)":0},k=[i],O=[],G=!1,H=null,t={},u=null,m={},n=!1,s=[],p=!1,P=0,bh.init(a),B=!0,K=!1,C=N=w=L["(begin)"],bb();try{bl();switch(w.id){case"{":case"[":z.laxbreak=!0,p=!0,bY();break;default:w.value==="use strict"&&(z.globalstrict||bd('Use the function form of "use strict".'),bP()),bQ("lib")}bl("(end)")}catch(h){h&&d.errors.push({reason:h.message,line:h.line||w.line,character:h.character||w.from},null)}return d.errors.length===0};bZ.data=function(){var a={functions:[]},b,c,d=[],e,f,g,h=[],i,l=[],n;bZ.errors.length&&(a.errors=bZ.errors),p&&(a.json=!0);for(i in m)_(m,i)&&d.push({name:i,line:m[i]});d.length>0&&(a.implieds=d),O.length>0&&(a.urls=O),c=Object.keys(F),c.length>0&&(a.globals=c);for(f=1;f<k.length;f+=1){e=k[f],b={};for(g=0;g<j.length;g+=1)b[j[g]]=[];for(i in e)_(e,i)&&i.charAt(0)!=="("&&(n=e[i],n==="unction"&&(n="unused"),Array.isArray(b[n])&&(b[n].push(i),n==="unused"&&l.push({name:i,line:e["(line)"],"function":e["(name)"]})));for(g=0;g<j.length;g+=1)b[j[g]].length===0&&delete b[j[g]];b.name=e["(name)"],b.param=e["(params)"],b.line=e["(line)"],b.last=e["(last)"],a.functions.push(b)}l.length>0&&(a.unused=l),h=[];for(i in t)if(typeof t[i]=="number"){a.member=t;break}return a},bZ.report=function(a){function o(a,b){var c,d,e;if(b){m.push("<div><i>"+a+"</i> "),b=b.sort();for(d=0;d<b.length;d+=1)b[d]!==e&&(e=b[d],m.push((c?", ":"")+e),c=!0);m.push("</div>")}}var b=bZ.data(),c=[],d,e,f,g,h,i,j,k="",l,m=[],n;if(b.errors||b.implieds||b.unused){f=!0,m.push("<div id=errors><i>Error:</i>");if(b.errors)for(h=0;h<b.errors.length;h+=1)d=b.errors[h],d&&(e=d.evidence||"",m.push("<p>Problem"+(isFinite(d.line)?" at line "+d.line+" character "+d.character:"")+": "+d.reason.entityify()+"</p><p class=evidence>"+(e&&(e.length>80?e.slice(0,77)+"...":e).entityify())+"</p>"));if(b.implieds){n=[];for(h=0;h<b.implieds.length;h+=1)n[h]="<code>"+b.implieds[h].name+"</code>&nbsp;<i>"+b.implieds[h].line+"</i>";m.push("<p><i>Implied global:</i> "+n.join(", ")+"</p>")}if(b.unused){n=[];for(h=0;h<b.unused.length;h+=1)n[h]="<code><u>"+b.unused[h].name+"</u></code>&nbsp;<i>"+b.unused[h].line+"</i> <code>"+b.unused[h]["function"]+"</code>";m.push("<p><i>Unused variable:</i> "+n.join(", ")+"</p>")}b.json&&m.push("<p>JSON: bad.</p>"),m.push("</div>")}if(!a){m.push("<br><div id=functions>"),b.urls&&o("URLs<br>",b.urls,"<br>"),b.json&&!f?m.push("<p>JSON: good.</p>"):b.globals?m.push("<div><i>Global</i> "+b.globals.sort().join(", ")+"</div>"):m.push("<div><i>No new global variables introduced.</i></div>");for(h=0;h<b.functions.length;h+=1)g=b.functions[h],m.push("<br><div class=function><i>"+g.line+"-"+g.last+"</i> "+(g.name||"")+"("+(g.param?g.param.join(", "):"")+")</div>"),o("<big><b>Unused</b></big>",g.unused),o("Closure",g.closure),o("Variable",g["var"]),o("Exception",g.exception),o("Outer",g.outer),o("Global",g.global),o("Label",g.label);if(b.member){c=Object.keys(b.member);if(c.length){c=c.sort(),k="<br><pre id=members>/*members ",j=10;for(h=0;h<c.length;h+=1)i=c[h],l=i.name(),j+l.length>72&&(m.push(k+"<br>"),k=" ",j=1),j+=l.length+2,b.member[i]===1&&(l="<i>"+l+"</i>"),h<c.length-1&&(l+=", "),k+=l;m.push(k+"<br>*/</pre>")}m.push("</div>")}}return m.join("")},bZ.jshint=bZ,bZ.edition="2011-04-16";return bZ}();typeof b=="object"&&b&&(b.JSHINT=d)}),define("ace/narcissus/jsparse",["require","exports","module","ace/narcissus/jslex","ace/narcissus/jsdefs"],function(require,exports,module){function parseStdin(a,b){for(;;)try{var c=new lexer.Tokenizer(a,"stdin",b.value),d=Script(c,!1);b.value=c.lineno;return d}catch(e){if(!c.unexpectedEOF)throw e;var f=readline();if(!f)throw e;a+="\n"+f}}function parse(a,b,c){var d=new lexer.Tokenizer(a,b,c),e=Script(d,!1);if(!d.done)throw d.newSyntaxError("Syntax error");return e}function PrimaryExpression(a,b){var c,d,e=a.get(!0);switch(e){case FUNCTION:c=FunctionDefinition(a,b,!1,EXPRESSED_FORM);break;case LEFT_BRACKET:c=new Node(a,{type:ARRAY_INIT});while((e=a.peek(!0))!==RIGHT_BRACKET){if(e===COMMA){a.get(),c.push(null);continue}c.push(AssignExpression(a,b));if(e!==COMMA&&!a.match(COMMA))break}c.children.length===1&&a.match(FOR)&&(d=new Node(a,{type:ARRAY_COMP,expression:c.children[0],tail:ComprehensionTail(a,b)}),c=d),a.mustMatch(RIGHT_BRACKET);break;case LEFT_CURLY:var f,g;c=new Node(a,{type:OBJECT_INIT});object_init:if(!a.match(RIGHT_CURLY)){do{e=a.get();if(a.token.value!=="get"&&a.token.value!=="set"||a.peek()!==IDENTIFIER){switch(e){case IDENTIFIER:case NUMBER:case STRING:f=new Node(a,{type:IDENTIFIER});break;case RIGHT_CURLY:if(b.ecma3OnlyMode)throw a.newSyntaxError("Illegal trailing ,");break object_init;default:if(a.token.value in definitions.keywords){f=new Node(a,{type:IDENTIFIER});break}throw a.newSyntaxError("Invalid property name")}if(a.match(COLON))d=new Node(a,{type:PROPERTY_INIT}),d.push(f),d.push(AssignExpression(a,b)),c.push(d);else{if(a.peek()!==COMMA&&a.peek()!==RIGHT_CURLY)throw a.newSyntaxError("missing : after property");c.push(f)}}else{if(b.ecma3OnlyMode)throw a.newSyntaxError("Illegal property accessor");c.push(FunctionDefinition(a,b,!0,EXPRESSED_FORM))}}while(a.match(COMMA));a.mustMatch(RIGHT_CURLY)}break;case LEFT_PAREN:c=ParenExpression(a,b),a.mustMatch(RIGHT_PAREN),c.parenthesized=!0;break;case LET:c=LetBlock(a,b,!1);break;case NULL:case THIS:case TRUE:case FALSE:case IDENTIFIER:case NUMBER:case STRING:case REGEXP:c=new Node(a);break;default:throw a.newSyntaxError("missing operand")}return c}function ArgumentList(a,b){var c,d;c=new Node(a,{type:LIST});if(a.match(RIGHT_PAREN,!0))return c;do{d=AssignExpression(a,b);if(d.type===YIELD&&!d.parenthesized&&a.peek()===COMMA)throw a.newSyntaxError("Yield expression must be parenthesized");if(a.match(FOR)){d=GeneratorExpression(a,b,d);if(c.children.length>1||a.peek(!0)===COMMA)throw a.newSyntaxError("Generator expression must be parenthesized")}c.push(d)}while(a.match(COMMA));a.mustMatch(RIGHT_PAREN);return c}function MemberExpression(a,b,c){var d,e,f,g;a.match(NEW)?(d=new Node(a),d.push(MemberExpression(a,b,!1)),a.match(LEFT_PAREN)&&(d.type=NEW_WITH_ARGS,d.push(ArgumentList(a,b)))):d=PrimaryExpression(a,b);while((g=a.get())!==END){switch(g){case DOT:e=new Node(a),e.push(d),a.mustMatch(IDENTIFIER),e.push(new Node(a));break;case LEFT_BRACKET:e=new Node(a,{type:INDEX}),e.push(d),e.push(Expression(a,b)),a.mustMatch(RIGHT_BRACKET);break;case LEFT_PAREN:if(c){e=new Node(a,{type:CALL}),e.push(d),e.push(ArgumentList(a,b));break};default:a.unget();return d}d=e}return d}function UnaryExpression(a,b){var c,d,e;switch(e=a.get(!0)){case DELETE:case VOID:case TYPEOF:case NOT:case BITWISE_NOT:case PLUS:case MINUS:e===PLUS?c=new Node(a,{type:UNARY_PLUS}):e===MINUS?c=new Node(a,{type:UNARY_MINUS}):c=new Node(a),c.push(UnaryExpression(a,b));break;case INCREMENT:case DECREMENT:c=new Node(a),c.push(MemberExpression(a,b,!0));break;default:a.unget(),c=MemberExpression(a,b,!0),a.tokens[a.tokenIndex+a.lookahead-1&3].lineno===a.lineno&&(a.match(INCREMENT)||a.match(DECREMENT))&&(d=new Node(a,{postfix:!0}),d.push(c),c=d)}return c}function MultiplyExpression(a,b){var c,d;c=UnaryExpression(a,b);while(a.match(MUL)||a.match(DIV)||a.match(MOD))d=new Node(a),d.push(c),d.push(UnaryExpression(a,b)),c=d;return c}function AddExpression(a,b){var c,d;c=MultiplyExpression(a,b);while(a.match(PLUS)||a.match(MINUS))d=new Node(a),d.push(c),d.push(MultiplyExpression(a,b)),c=d;return c}function ShiftExpression(a,b){var c,d;c=AddExpression(a,b);while(a.match(LSH)||a.match(RSH)||a.match(URSH))d=new Node(a),d.push(c),d.push(AddExpression(a,b)),c=d;return c}function RelationalExpression(a,b){var c,d,e=b.update({inForLoopInit:!1});c=ShiftExpression(a,e);while(a.match(LT)||a.match(LE)||a.match(GE)||a.match(GT)||!b.inForLoopInit&&a.match(IN)||a.match(INSTANCEOF))d=new Node(a),d.push(c),d.push(ShiftExpression(a,e)),c=d;return c}function EqualityExpression(a,b){var c,d;c=RelationalExpression(a,b);while(a.match(EQ)||a.match(NE)||a.match(STRICT_EQ)||a.match(STRICT_NE))d=new Node(a),d.push(c),d.push(RelationalExpression(a,b)),c=d;return c}function BitwiseAndExpression(a,b){var c,d;c=EqualityExpression(a,b);while(a.match(BITWISE_AND))d=new Node(a),d.push(c),d.push(EqualityExpression(a,b)),c=d;return c}function BitwiseXorExpression(a,b){var c,d;c=BitwiseAndExpression(a,b);while(a.match(BITWISE_XOR))d=new Node(a),d.push(c),d.push(BitwiseAndExpression(a,b)),c=d;return c}function BitwiseOrExpression(a,b){var c,d;c=BitwiseXorExpression(a,b);while(a.match(BITWISE_OR))d=new Node(a),d.push(c),d.push(BitwiseXorExpression(a,b)),c=d;return c}function AndExpression(a,b){var c,d;c=BitwiseOrExpression(a,b);while(a.match(AND))d=new Node(a),d.push(c),d.push(BitwiseOrExpression(a,b)),c=d;return c}function OrExpression(a,b){var c,d;c=AndExpression(a,b);while(a.match(OR))d=new Node(a),d.push(c),d.push(AndExpression(a,b)),c=d;return c}function ConditionalExpression(a,b){var c,d;c=OrExpression(a,b);if(a.match(HOOK)){d=c,c=new Node(a,{type:HOOK}),c.push(d),c.push(AssignExpression(a,b.update({inForLoopInit:!1})));if(!a.match(COLON))throw a.newSyntaxError("missing : after ?");c.push(AssignExpression(a,b))}return c}function AssignExpression(a,b){var c,d;if(a.match(YIELD,!0))return ReturnOrYield(a,b);c=new Node(a,{type:ASSIGN}),d=ConditionalExpression(a,b);if(!a.match(ASSIGN))return d;switch(d.type){case OBJECT_INIT:case ARRAY_INIT:d.destructuredNames=checkDestructuring(a,b,d);case IDENTIFIER:case DOT:case INDEX:case CALL:break;default:throw a.newSyntaxError("Bad left-hand side of assignment")}c.assignOp=a.token.assignOp,c.push(d),c.push(AssignExpression(a,b));return c}function Expression(a,b){var c,d;c=AssignExpression(a,b);if(a.match(COMMA)){d=new Node(a,{type:COMMA}),d.push(c),c=d;do{d=c.children[c.children.length-1];if(d.type===YIELD&&!d.parenthesized)throw a.newSyntaxError("Yield expression must be parenthesized");c.push(AssignExpression(a,b))}while(a.match(COMMA))}return c}function ParenExpression(a,b){var c=Expression(a,b.update({inForLoopInit:b.inForLoopInit&&a.token.type===LEFT_PAREN}));if(a.match(FOR)){if(c.type===YIELD&&!c.parenthesized)throw a.newSyntaxError("Yield expression must be parenthesized");if(c.type===COMMA&&!c.parenthesized)throw a.newSyntaxError("Generator expression must be parenthesized");c=GeneratorExpression(a,b,c)}return c}function HeadExpression(a,b){var c=MaybeLeftParen(a,b),d=ParenExpression(a,b);MaybeRightParen(a,c);if(c===END&&!d.parenthesized){var e=a.peek();if(e!==LEFT_CURLY&&!definitions.isStatementStartCode[e])throw a.newSyntaxError("Unparenthesized head followed by unbraced body")}return d}function ComprehensionTail(a,b){var c,d,e,f,g;c=new Node(a,{type:COMP_TAIL});do{d=new Node(a,{type:FOR_IN,isLoop:!0}),a.match(IDENTIFIER)&&(a.token.value==="each"?d.isEach=!0:a.unget()),g=MaybeLeftParen(a,b);switch(a.get()){case LEFT_BRACKET:case LEFT_CURLY:a.unget(),d.iterator=DestructuringExpression(a,b);break;case IDENTIFIER:d.iterator=f=new Node(a,{type:IDENTIFIER}),f.name=f.value,d.varDecl=e=new Node(a,{type:VAR}),e.push(f),b.parentScript.varDecls.push(f);break;default:throw a.newSyntaxError("missing identifier")}a.mustMatch(IN),d.object=Expression(a,b),MaybeRightParen(a,g),c.push(d)}while(a.match(FOR));a.match(IF)&&(c.guard=HeadExpression(a,b));return c}function GeneratorExpression(a,b,c){return new Node(a,{type:GENERATOR,expression:c,tail:ComprehensionTail(a,b)})}function DestructuringExpression(a,b,c){var d=PrimaryExpression(a,b);d.destructuredNames=checkDestructuring(a,b,d,c);return d}function checkDestructuring(a,b,c,d){if(c.type===ARRAY_COMP)throw a.newSyntaxError("Invalid array comprehension left-hand side");if(c.type===ARRAY_INIT||c.type===OBJECT_INIT){var e={},f,g,h,i,j,k=c.children;for(var l=0,m=k.length;l<m;l++){if(!(f=k[l]))continue;f.type===PROPERTY_INIT?(j=f.children,i=j[1],h=j[0].value):c.type===OBJECT_INIT?(i=f,h=f.value):(i=f,h=l);if(i.type===ARRAY_INIT||i.type===OBJECT_INIT)e[h]=checkDestructuring(a,b,i,d);else{if(d&&i.type!==IDENTIFIER)throw a.newSyntaxError("missing name in pattern");e[h]=i}}return e}}function LetBlock(a,b,c){var d,e;d=new Node(a,{type:LET_BLOCK,varDecls:[]}),a.mustMatch(LEFT_PAREN),d.variables=Variables(a,b,d),a.mustMatch(RIGHT_PAREN),c&&a.peek()!==LEFT_CURLY&&(e=new Node(a,{type:SEMICOLON,expression:d}),c=!1),c?d.block=Block(a,b):d.expression=AssignExpression(a,b);return d}function Variables(a,b,c){var d,e,f,g,h,i;i=a.token.type;switch(i){case VAR:case CONST:h=b.parentScript;break;case LET:h=b.parentBlock;break;case LEFT_PAREN:i=LET,h=c}d=new Node(a,{type:i,destructurings:[]});do{i=a.get();if(i===LEFT_BRACKET||i===LEFT_CURLY){a.unget();var j=DestructuringExpression(a,b,!0);e=new Node(a,{type:IDENTIFIER,name:j,readOnly:d.type===CONST}),d.push(e),pushDestructuringVarDecls(e.name.destructuredNames,h),d.destructurings.push({exp:j,decl:e});if(b.inForLoopInit&&a.peek()===IN)continue;a.mustMatch(ASSIGN);if(a.token.assignOp)throw a.newSyntaxError("Invalid variable initialization");e.initializer=AssignExpression(a,b);continue}if(i!==IDENTIFIER)throw a.newSyntaxError("missing variable name");e=new Node(a,{type:IDENTIFIER,name:a.token.value,readOnly:d.type===CONST}),d.push(e),h.varDecls.push(e);if(a.match(ASSIGN)){if(a.token.assignOp)throw a.newSyntaxError("Invalid variable initialization");e.initializer=AssignExpression(a,b)}}while(a.match(COMMA));return d}function FunctionDefinition(a,b,c,d){var e,f=new Node(a,{params:[]});f.type!==FUNCTION&&(f.type=f.value==="get"?GETTER:SETTER);if(a.match(IDENTIFIER))f.name=a.token.value;else if(c)throw a.newSyntaxError("missing function identifier");var g=new StaticContext(null,null,!0,!1,NESTING_TOP);a.mustMatch(LEFT_PAREN);if(!a.match(RIGHT_PAREN)){do switch(a.get()){case LEFT_BRACKET:case LEFT_CURLY:a.unget(),f.params.push(DestructuringExpression(a,g));break;case IDENTIFIER:f.params.push(a.token.value);break;default:throw a.newSyntaxError("missing formal parameter")}while(a.match(COMMA));a.mustMatch(RIGHT_PAREN)}e=a.get(),e!==LEFT_CURLY&&a.unget();if(e!==LEFT_CURLY){f.body=AssignExpression(a,g);if(f.body.isGenerator)throw a.newSyntaxError("Generator returns a value")}else f.body=Script(a,!0);e===LEFT_CURLY&&a.mustMatch(RIGHT_CURLY),f.end=a.token.end,f.functionForm=d,d===DECLARED_FORM&&b.parentScript.funDecls.push(f);return f}function ReturnOrYield(a,b){var c,d,e=a.token.type,f,g=b.parentScript;if(e===RETURN){if(!b.inFunction)throw a.newSyntaxError("Return not in function")}else{if(!b.inFunction)throw a.newSyntaxError("Yield not in function");g.isGenerator=!0}c=new Node(a,{value:undefined}),f=a.peek(!0),f!==END&&f!==NEWLINE&&f!==SEMICOLON&&f!==RIGHT_CURLY&&(e!==YIELD||f!==e&&f!==RIGHT_BRACKET&&f!==RIGHT_PAREN&&f!==COLON&&f!==COMMA)?e===RETURN?(c.value=Expression(a,b),g.hasReturnWithValue=!0):c.value=AssignExpression(a,b):e===RETURN&&(g.hasEmptyReturn=!0);if(g.hasReturnWithValue&&g.isGenerator)throw a.newSyntaxError("Generator returns a value");return c}function MagicalSemicolon(a){var b;if(a.lineno===a.token.lineno){b=a.peekOnSameLine();if(b!==END&&b!==NEWLINE&&b!==SEMICOLON&&b!==RIGHT_CURLY)throw a.newSyntaxError("missing ; before statement")}a.match(SEMICOLON)}function Statement(a,b){var c,d,e,f,g,h,i,j=a.get(!0),k,l,m;switch(j){case FUNCTION:return FunctionDefinition(a,b,!0,b.nesting!==NESTING_TOP?STATEMENT_FORM:DECLARED_FORM);case LEFT_CURLY:e=new Node(a,blockInit()),Statements(a,b.update({parentBlock:e}).pushTarget(e).nest(NESTING_SHALLOW),e),a.mustMatch(RIGHT_CURLY);return e;case IF:e=new Node(a),e.condition=HeadExpression(a,b),l=b.pushTarget(e).nest(NESTING_DEEP),e.thenPart=Statement(a,l),e.elsePart=a.match(ELSE)?Statement(a,l):null;return e;case SWITCH:e=new Node(a,{cases:[],defaultIndex:-1}),e.discriminant=HeadExpression(a,b),l=b.pushTarget(e).nest(NESTING_DEEP),a.mustMatch(LEFT_CURLY);while((j=a.get())!==RIGHT_CURLY){switch(j){case DEFAULT:if(e.defaultIndex>=0)throw a.newSyntaxError("More than one switch default");case CASE:f=new Node(a),j===DEFAULT?e.defaultIndex=e.cases.length:f.caseLabel=Expression(a,l,COLON);break;default:throw a.newSyntaxError("Invalid switch case")}a.mustMatch(COLON),f.statements=new Node(a,blockInit());while((j=a.peek(!0))!==CASE&&j!==DEFAULT&&j!==RIGHT_CURLY)f.statements.push(Statement(a,l));e.cases.push(f)}return e;case FOR:e=new Node(a,LOOP_INIT),a.match(IDENTIFIER)&&(a.token.value==="each"?e.isEach=!0:a.unget()),b.parenFreeMode||a.mustMatch(LEFT_PAREN),l=b.pushTarget(e).nest(NESTING_DEEP),m=b.update({inForLoopInit:!0}),(j=a.peek())!==SEMICOLON&&(j===VAR||j===CONST?(a.get(),f=Variables(a,m)):j===LET?(a.get(),a.peek()===LEFT_PAREN?f=LetBlock(a,m,!1):(m.parentBlock=e,e.varDecls=[],f=Variables(a,m))):f=Expression(a,m));if(f&&a.match(IN)){e.type=FOR_IN,e.object=Expression(a,m);if(f.type===VAR||f.type===LET){h=f.children;if(h.length!==1&&f.destructurings.length!==1)throw new SyntaxError("Invalid for..in left-hand side",a.filename,f.lineno);f.destructurings.length>0?e.iterator=f.destructurings[0]:e.iterator=h[0],e.varDecl=f}else{if(f.type===ARRAY_INIT||f.type===OBJECT_INIT)f.destructuredNames=checkDestructuring(a,m,f);e.iterator=f}}else{e.setup=f,a.mustMatch(SEMICOLON);if(e.isEach)throw a.newSyntaxError("Invalid for each..in loop");e.condition=a.peek()===SEMICOLON?null:Expression(a,m),a.mustMatch(SEMICOLON),k=a.peek(),e.update=(b.parenFreeMode?k===LEFT_CURLY||definitions.isStatementStartCode[k]:k===RIGHT_PAREN)?null:Expression(a,m)}b.parenFreeMode||a.mustMatch(RIGHT_PAREN),e.body=Statement(a,l);return e;case WHILE:e=new Node(a,{isLoop:!0}),e.condition=HeadExpression(a,b),e.body=Statement(a,b.pushTarget(e).nest(NESTING_DEEP));return e;case DO:e=new Node(a,{isLoop:!0}),e.body=Statement(a,b.pushTarget(e).nest(NESTING_DEEP)),a.mustMatch(WHILE),e.condition=HeadExpression(a,b);if(!b.ecmaStrictMode){a.match(SEMICOLON);return e}break;case BREAK:case CONTINUE:e=new Node(a),l=b.pushTarget(e),a.peekOnSameLine()===IDENTIFIER&&(a.get(),e.label=a.token.value),e.target=e.label?l.labeledTargets.find(function(a){return a.labels.has(e.label)}):l.defaultTarget;if(!e.target)throw a.newSyntaxError("Invalid "+(j===BREAK?"break":"continue"));if(!e.target.isLoop&&j===CONTINUE)throw a.newSyntaxError("Invalid continue");break;case TRY:e=new Node(a,{catchClauses:[]}),e.tryBlock=Block(a,b);while(a.match(CATCH)){f=new Node(a),g=MaybeLeftParen(a,b);switch(a.get()){case LEFT_BRACKET:case LEFT_CURLY:a.unget(),f.varName=DestructuringExpression(a,b,!0);break;case IDENTIFIER:f.varName=a.token.value;break;default:throw a.newSyntaxError("missing identifier in catch")}if(a.match(IF)){if(b.ecma3OnlyMode)throw a.newSyntaxError("Illegal catch guard");if(e.catchClauses.length&&!e.catchClauses.top().guard)throw a.newSyntaxError("Guarded catch after unguarded");f.guard=Expression(a,b)}MaybeRightParen(a,g),f.block=Block(a,b),e.catchClauses.push(f)}a.match(FINALLY)&&(e.finallyBlock=Block(a,b));if(!e.catchClauses.length&&!e.finallyBlock)throw a.newSyntaxError("Invalid try statement");return e;case CATCH:case FINALLY:throw a.newSyntaxError(definitions.tokens[j]+" without preceding try");case THROW:e=new Node(a),e.exception=Expression(a,b);break;case RETURN:e=ReturnOrYield(a,b);break;case WITH:e=new Node(a),e.object=HeadExpression(a,b),e.body=Statement(a,b.pushTarget(e).nest(NESTING_DEEP));return e;case VAR:case CONST:e=Variables(a,b);break;case LET:a.peek()===LEFT_PAREN?e=LetBlock(a,b,!0):e=Variables(a,b);break;case DEBUGGER:e=new Node(a);break;case NEWLINE:case SEMICOLON:e=new Node(a,{type:SEMICOLON}),e.expression=null;return e;default:if(j===IDENTIFIER){j=a.peek();if(j===COLON){d=a.token.value;if(b.allLabels.has(d))throw a.newSyntaxError("Duplicate label");a.get(),e=new Node(a,{type:LABEL,label:d}),e.statement=Statement(a,b.pushLabel(d).nest(NESTING_SHALLOW)),e.target=e.statement.type===LABEL?e.statement.target:e.statement;return e}}e=new Node(a,{type:SEMICOLON}),a.unget(),e.expression=Expression(a,b),e.end=e.expression.end}MagicalSemicolon(a);return e}function Block(a,b){a.mustMatch(LEFT_CURLY);var c=new Node(a,blockInit());Statements(a,b.update({parentBlock:c}).pushTarget(c),c),a.mustMatch(RIGHT_CURLY);return c}function Statements(a,b,c){try{while(!a.done&&a.peek(!0)!==RIGHT_CURLY)c.push(Statement(a,b))}catch(d){a.done&&(a.unexpectedEOF=!0);throw d}}function MaybeRightParen(a,b){b===LEFT_PAREN&&a.mustMatch(RIGHT_PAREN)}function MaybeLeftParen(a,b){if(b.parenFreeMode)return a.match(LEFT_PAREN)?LEFT_PAREN:END;return a.mustMatch(LEFT_PAREN).type}function scriptInit(){return{type:SCRIPT,funDecls:[],varDecls:[],modDecls:[],impDecls:[],expDecls:[],loadDeps:[],hasEmptyReturn:!1,hasReturnWithValue:!1,isGenerator:!1}}function blockInit(){return{type:BLOCK,varDecls:[]}}function tokenString(a){var b=definitions.tokens[a];return/^\W/.test(b)?definitions.opTypeNames[b]:b.toUpperCase()}function Node(a,b){var c=a.token;c?(this.type=c.type,this.value=c.value,this.lineno=c.lineno,this.start=c.start,this.end=c.end):this.lineno=a.lineno,this.tokenizer=a,this.children=[];for(var d in b)this[d]=b[d]}function Script(a,b){var c=new Node(a,scriptInit()),d=new StaticContext(c,c,b,!1,NESTING_TOP);Statements(a,d,c);return c}function StaticContext(a,b,c,d,e){this.parentScript=a,this.parentBlock=b,this.inFunction=c,this.inForLoopInit=d,this.nesting=e,this.allLabels=new Stack,this.currentLabels=new Stack,this.labeledTargets=new Stack,this.defaultTarget=null,definitions.options.ecma3OnlyMode&&(this.ecma3OnlyMode=!0),definitions.options.parenFreeMode&&(this.parenFreeMode=!0)}function pushDestructuringVarDecls(a,b){for(var c in a){var d=a[c];d.type===IDENTIFIER?b.varDecls.push(d):pushDestructuringVarDecls(d,b)}}var lexer=require("ace/narcissus/jslex"),definitions=require("ace/narcissus/jsdefs");const StringMap=definitions.StringMap,Stack=definitions.Stack;eval(definitions.consts);const NESTING_TOP=0,NESTING_SHALLOW=1,NESTING_DEEP=2;StaticContext.prototype={ecma3OnlyMode:!1,parenFreeMode:!1,update:function(a){var b={};for(var c in a)b[c]={value:a[c],writable:!0,enumerable:!0,configurable:!0};return Object.create(this,b)},pushLabel:function(a){return this.update({currentLabels:this.currentLabels.push(a),allLabels:this.allLabels.push(a)})},pushTarget:function(a){var b=a.isLoop||a.type===SWITCH;if(this.currentLabels.isEmpty())return b?this.update({defaultTarget:a}):this;a.labels=new StringMap,this.currentLabels.forEach(function(b){a.labels.set(b,!0)});return this.update({currentLabels:new Stack,labeledTargets:this.labeledTargets.push(a),defaultTarget:b?a:this.defaultTarget})},nest:function(a){var b=Math.max(this.nesting,a);return b!==this.nesting?this.update({nesting:b}):this}},definitions.defineProperty(Array.prototype,"top",function(){return this.length&&this[this.length-1]},!1,!1,!0);var Np=Node.prototype={};Np.constructor=Node,Np.toSource=Object.prototype.toSource,Np.push=function(a){a!==null&&(a.start<this.start&&(this.start=a.start),this.end<a.end&&(this.end=a.end));return this.children.push(a)},Node.indentLevel=0,Np.toString=function(){var a=[];for(var b in this)this.hasOwnProperty(b)&&b!=="type"&&b!=="target"&&a.push({id:b,value:this[b]});a.sort(function(a,b){return a.id<b.id?-1:1});const c=" ";var d=++Node.indentLevel,e="{\n"+c.repeat(d)+"type: "+tokenString(this.type);for(b=0;b<a.length;b++)e+=",\n"+c.repeat(d)+a[b].id+": "+a[b].value;d=--Node.indentLevel,e+="\n"+c.repeat(d)+"}";return e},Np.getSource=function(){return this.tokenizer.source.slice(this.start,this.end)};const LOOP_INIT={isLoop:!0};definitions.defineGetter(Np,"filename",function(){return this.tokenizer.filename}),definitions.defineProperty(String.prototype,"repeat",function(a){var b="",c=this+b;while(--a>=0)b+=c;return b},!1,!1,!0);const DECLARED_FORM=0,EXPRESSED_FORM=1,STATEMENT_FORM=2;exports.parse=parse,exports.parseStdin=parseStdin,exports.Node=Node,exports.DECLARED_FORM=DECLARED_FORM,exports.EXPRESSED_FORM=EXPRESSED_FORM,exports.STATEMENT_FORM=STATEMENT_FORM,exports.Tokenizer=lexer.Tokenizer,exports.FunctionDefinition=FunctionDefinition}),define("ace/narcissus/jslex",["require","exports","module","ace/narcissus/jsdefs"],function(require,exports,module){function Tokenizer(a,b,c){this.cursor=0,this.source=String(a),this.tokens=[],this.tokenIndex=0,this.lookahead=0,this.scanNewlines=!1,this.unexpectedEOF=!1,this.filename=b||"",this.lineno=c||1}var definitions=require("ace/narcissus/jsdefs");eval(definitions.consts);var opTokens={};for(var op in definitions.opTypeNames){if(op==="\n"||op===".")continue;var node=opTokens;for(var i=0;i<op.length;i++){var ch=op[i];ch in node||(node[ch]={}),node=node[ch],node.op=op}}Tokenizer.prototype={get done(){return this.peek(!0)===END},get token(){return this.tokens[this.tokenIndex]},match:function(a,b){return this.get(b)===a||this.unget()},mustMatch:function(a){if(!this.match(a))throw this.newSyntaxError("Missing "+definitions.tokens[a].toLowerCase());return this.token},peek:function(a){var b,c;this.lookahead?(c=this.tokens[this.tokenIndex+this.lookahead&3],b=this.scanNewlines&&c.lineno!==this.lineno?NEWLINE:c.type):(b=this.get(a),this.unget());return b},peekOnSameLine:function(a){this.scanNewlines=!0;var b=this.peek(a);this.scanNewlines=!1;return b},skip:function(){var a=this.source;for(;;){var b=a[this.cursor++],c=a[this.cursor];if(b==="\n"&&!this.scanNewlines)this.lineno++;else if(b==="/"&&c==="*"){this.cursor++;for(;;){b=a[this.cursor++];if(b===undefined)throw this.newSyntaxError("Unterminated comment");if(b==="*"){c=a[this.cursor];if(c==="/"){this.cursor++;break}}else b==="\n"&&this.lineno++}}else if(b==="/"&&c==="/"){this.cursor++;for(;;){b=a[this.cursor++];if(b===undefined)return;if(b==="\n"){this.lineno++;break}}}else if(b!==" "&&b!=="\t"){this.cursor--;return}}},lexExponent:function(){var a=this.source,b=a[this.cursor];if(b==="e"||b==="E"){this.cursor++,ch=a[this.cursor++];if(ch==="+"||ch==="-")ch=a[this.cursor++];if(ch<"0"||ch>"9")throw this.newSyntaxError("Missing exponent");do ch=a[this.cursor++];while(ch>="0"&&ch<="9");this.cursor--;return!0}return!1},lexZeroNumber:function(a){var b=this.token,c=this.source;b.type=NUMBER,a=c[this.cursor++];if(a==="."){do a=c[this.cursor++];while(a>="0"&&a<="9");this.cursor--,this.lexExponent(),b.value=parseFloat(b.start,this.cursor)}else if(a==="x"||a==="X"){do a=c[this.cursor++];while(a>="0"&&a<="9"||a>="a"&&a<="f"||a>="A"&&a<="F");this.cursor--,b.value=parseInt(c.substring(b.start,this.cursor))}else if(a>="0"&&a<="7"){do a=c[this.cursor++];while(a>="0"&&a<="7");this.cursor--,b.value=parseInt(c.substring(b.start,this.cursor))}else this.cursor--,this.lexExponent(),b.value=0},lexNumber:function(a){var b=this.token,c=this.source;b.type=NUMBER;var d=!1;do a=c[this.cursor++],a==="."&&!d&&(d=!0,a=c[this.cursor++]);while(a>="0"&&a<="9");this.cursor--;var e=this.lexExponent();d=d||e;var f=c.substring(b.start,this.cursor);b.value=d?parseFloat(f):parseInt(f)},lexDot:function(a){var b=this.token,c=this.source,d=c[this.cursor];if(d>="0"&&d<="9"){do a=c[this.cursor++];while(a>="0"&&a<="9");this.cursor--,this.lexExponent(),b.type=NUMBER,b.value=parseFloat(b.start,this.cursor)}else b.type=DOT,b.assignOp=null,b.value="."},lexString:function(ch){var token=this.token,input=this.source;token.type=STRING;var hasEscapes=!1,delim=ch;if(input.length<=this.cursor)throw this.newSyntaxError("Unterminated string literal");while((ch=input[this.cursor++])!==delim){if(this.cursor==input.length)throw this.newSyntaxError("Unterminated string literal");if(ch==="\\"){hasEscapes=!0;if(++this.cursor==input.length)throw this.newSyntaxError("Unterminated string literal")}}token.value=hasEscapes?eval(input.substring(token.start,this.cursor)):input.substring(token.start+1,this.cursor-1)},lexRegExp:function(ch){var token=this.token,input=this.source;token.type=REGEXP;do{ch=input[this.cursor++];if(ch==="\\")this.cursor++;else if(ch==="["){do{if(ch===undefined)throw this.newSyntaxError("Unterminated character class");ch==="\\"&&this.cursor++,ch=input[this.cursor++]}while(ch!=="]")}else if(ch===undefined)throw this.newSyntaxError("Unterminated regex")}while(ch!=="/");do ch=input[this.cursor++];while(ch>="a"&&ch<="z");this.cursor--,token.value=eval(input.substring(token.start,this.cursor))},lexOp:function(a){var b=this.token,c=this.source,d=opTokens[a],e=c[this.cursor];e in d&&(d=d[e],this.cursor++,e=c[this.cursor],e in d&&(d=d[e],this.cursor++,e=c[this.cursor]));var f=d.op;definitions.assignOps[f]&&c[this.cursor]==="="?(this.cursor++,b.type=ASSIGN,b.assignOp=definitions.tokenIds[definitions.opTypeNames[f]],f+="="):(b.type=definitions.tokenIds[definitions.opTypeNames[f]],b.assignOp=null),b.value=f},lexIdent:function(a){var b=this.token,c=this.source;do a=c[this.cursor++];while(a>="a"&&a<="z"||a>="A"&&a<="Z"||a>="0"&&a<="9"||a==="$"||a==="_");this.cursor--;var d=c.substring(b.start,this.cursor);b.type=definitions.keywords[d]||IDENTIFIER,b.value=d},get:function(a){var b;while(this.lookahead){--this.lookahead,this.tokenIndex=this.tokenIndex+1&3,b=this.tokens[this.tokenIndex];if(b.type!==NEWLINE||this.scanNewlines)return b.type}this.skip(),this.tokenIndex=this.tokenIndex+1&3,b=this.tokens[this.tokenIndex],b||(this.tokens[this.tokenIndex]=b={});var c=this.source;if(this.cursor===c.length)return b.type=END;b.start=this.cursor,b.lineno=this.lineno;var d=c[this.cursor++];if(d>="a"&&d<="z"||d>="A"&&d<="Z"||d==="$"||d==="_")this.lexIdent(d);else if(a&&d==="/")this.lexRegExp(d);else if(d in opTokens)this.lexOp(d);else if(d===".")this.lexDot(d);else if(d>="1"&&d<="9")this.lexNumber(d);else if(d==="0")this.lexZeroNumber(d);else if(d==='"'||d==="'")this.lexString(d);else if(this.scanNewlines&&d==="\n")b.type=NEWLINE,b.value="\n",this.lineno++;else throw this.newSyntaxError("Illegal token");b.end=this.cursor;return b.type},unget:function(){if(++this.lookahead===4)throw"PANIC: too much lookahead!";this.tokenIndex=this.tokenIndex-1&3},newSyntaxError:function(a){var b=new SyntaxError(a,this.filename,this.lineno);b.source=this.source,b.lineno=this.lineno,b.cursor=this.lookahead?this.tokens[this.tokenIndex+this.lookahead&3].start:this.cursor;return b}},exports.Tokenizer=Tokenizer}),define("ace/narcissus/jsdefs",["require","exports","module"],function(a,b,c){function y(a){this.elts=a||null}function x(){this.table=Object.create(null,{}),this.size=0}function v(){return undefined}function u(a){return{getOwnPropertyDescriptor:function(b){var c=Object.getOwnPropertyDescriptor(a,b);c.configurable=!0;return c},getPropertyDescriptor:function(b){var c=s(a,b);c.configurable=!0;return c},getOwnPropertyNames:function(){return Object.getOwnPropertyNames(a)},defineProperty:function(b,c){Object.defineProperty(a,b,c)},"delete":function(b){return delete a[b]},fix:function(){if(Object.isFrozen(a))return t(a);return undefined},has:function(b){return b in a},hasOwn:function(b){return{}.hasOwnProperty.call(a,b)},get:function(b,c){return a[c]},set:function(b,c,d){a[c]=d;return!0},enumerate:function(){var b=[];for(m in a)b.push(m);return b},keys:function(){return Object.keys(a)}}}function t(a){var b={};for(var c in Object.getOwnPropertyNames(a))b[c]=Object.getOwnPropertyDescriptor(a,c);return b}function s(a,b){while(a){if({}.hasOwnProperty.call(a,b))return Object.getOwnPropertyDescriptor(a,b);a=Object.getPrototypeOf(a)}}function r(a){return typeof a=="function"&&a.toString().match(/\[native code\]/)}function q(a,b,c,d,e,f){Object.defineProperty(a,b,{value:c,writable:!e,configurable:!d,enumerable:!f})}function p(a,b,c,d,e){Object.defineProperty(a,b,{get:c,configurable:!d,enumerable:!e})}b.options={version:185},function(){b.hostGlobal=this}();var d=["END","\n",";",",","=","?",":","CONDITIONAL","||","&&","|","^","&","==","!=","===","!==","<","<=",">=",">","<<",">>",">>>","+","-","*","/","%","!","~","UNARY_PLUS","UNARY_MINUS","++","--",".","[","]","{","}","(",")","SCRIPT","BLOCK","LABEL","FOR_IN","CALL","NEW_WITH_ARGS","INDEX","ARRAY_INIT","OBJECT_INIT","PROPERTY_INIT","GETTER","SETTER","GROUP","LIST","LET_BLOCK","ARRAY_COMP","GENERATOR","COMP_TAIL","IDENTIFIER","NUMBER","STRING","REGEXP","break","case","catch","const","continue","debugger","default","delete","do","else","false","finally","for","function","if","in","instanceof","let","new","null","return","switch","this","throw","true","try","typeof","var","void","yield","while","with"],e=["break","const","continue","debugger","do","for","if","return","switch","throw","try","var","yield","while","with"],f={"\n":"NEWLINE",";":"SEMICOLON",",":"COMMA","?":"HOOK",":":"COLON","||":"OR","&&":"AND","|":"BITWISE_OR","^":"BITWISE_XOR","&":"BITWISE_AND","===":"STRICT_EQ","==":"EQ","=":"ASSIGN","!==":"STRICT_NE","!=":"NE","<<":"LSH","<=":"LE","<":"LT",">>>":"URSH",">>":"RSH",">=":"GE",">":"GT","++":"INCREMENT","--":"DECREMENT","+":"PLUS","-":"MINUS","*":"MUL","/":"DIV","%":"MOD","!":"NOT","~":"BITWISE_NOT",".":"DOT","[":"LEFT_BRACKET","]":"RIGHT_BRACKET","{":"LEFT_CURLY","}":"RIGHT_CURLY","(":"LEFT_PAREN",")":"RIGHT_PAREN"},g={"__proto__":null},h={},i="const ";for(var j=0,k=d.length;j<k;j++){j>0&&(i+=", ");var l=d[j],m;/^[a-z]/.test(l)?(m=l.toUpperCase(),g[l]=j):m=/^\W/.test(l)?f[l]:l,i+=m+" = "+j,h[m]=j,d[l]=j}i+=";";var n={"__proto__":null};for(j=0,k=e.length;j<k;j++)n[g[e[j]]]=!0;var o=["|","^","&","<<",">>",">>>","+","-","*","/","%"];for(j=0,k=o.length;j<k;j++)l=o[j],o[l]=d[l];var w={}.hasOwnProperty;x.prototype={has:function(a){return w.call(this.table,a)},set:function(a,b){w.call(this.table,a)||this.size++,this.table[a]=b},get:function(a){return this.table[a]},getDef:function(a,b){w.call(this.table,a)||(this.size++,this.table[a]=b());return this.table[a]},forEach:function(a){var b=this.table;for(var c in b)a.call(this,c,b[c])},toString:function(){return"[object StringMap]"}},y.prototype={push:function(a){return new y({top:a,rest:this.elts})},top:function(){if(!this.elts)throw new Error("empty stack");return this.elts.top},isEmpty:function(){return this.top===null},find:function(a){for(var b=this.elts;b;b=b.rest)if(a(b.top))return b.top;return null},has:function(a){return Boolean(this.find(function(b){return b===a}))},forEach:function(a){for(var b=this.elts;b;b=b.rest)a(b.top)}},b.tokens=d,b.opTypeNames=f,b.keywords=g,b.isStatementStartCode=n,b.tokenIds=h,b.consts=i,b.assignOps=o,b.defineGetter=p,b.defineProperty=q,b.isNativeCode=r,b.makePassthruHandler=u,b.noPropFound=v,b.StringMap=x,b.Stack=y}) \ No newline at end of file
diff --git a/studio/static/js/jquery-1.6.2.js b/studio/static/js/jquery-1.6.2.js
new file mode 100644
index 0000000..f3201aa
--- /dev/null
+++ b/studio/static/js/jquery-1.6.2.js
@@ -0,0 +1,8981 @@
+/*!
+ * jQuery JavaScript Library v1.6.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Jun 30 14:16:56 2011 -0400
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+ navigator = window.navigator,
+ location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Check for digits
+ rdigit = /\d/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Matches dashed string for camelizing
+ rdashAlpha = /-([a-z])/ig,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ },
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = quickExpr.exec( selector );
+ }
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = (context ? context.ownerDocument || context : document);
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return (context || rootjQuery).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if (selector.selector !== undefined) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.6.2",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.done( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, +i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // Either a released hold or an DOMready/load event and not yet ready
+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).unbind( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyList ) {
+ return;
+ }
+
+ readyList = jQuery._Deferred();
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ // A crude way of determining if an object is a window
+ isWindow: function( obj ) {
+ return obj && typeof obj === "object" && "setInterval" in obj;
+ },
+
+ isNaN: function( obj ) {
+ return obj == null || !rdigit.test( obj ) || isNaN( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw msg;
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return (new Function( "return " + data ))();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ // (xml & tmp used internally)
+ parseXML: function( data , xml , tmp ) {
+
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+
+ tmp = xml.documentElement;
+
+ if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Converts a dashed string to camelCased string;
+ // Used by both the css and data modules
+ camelCase: function( string ) {
+ return string.replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction( object );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // The extra typeof function check is to prevent crashes
+ // in Safari 2 (See: #3039)
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type( array );
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+
+ if ( indexOf ) {
+ return indexOf.call( array, elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key, ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ if ( typeof context === "string" ) {
+ var tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ var args = slice.call( arguments, 2 ),
+ proxy = function() {
+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ jQuery.access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+ },
+
+ now: function() {
+ return (new Date()).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+return jQuery;
+
+})();
+
+
+var // Promise methods
+ promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
+ // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+ // Create a simple deferred (one callbacks list)
+ _Deferred: function() {
+ var // callbacks list
+ callbacks = [],
+ // stored [ context , args ]
+ fired,
+ // to avoid firing when already doing so
+ firing,
+ // flag to know if the deferred has been cancelled
+ cancelled,
+ // the deferred itself
+ deferred = {
+
+ // done( f1, f2, ...)
+ done: function() {
+ if ( !cancelled ) {
+ var args = arguments,
+ i,
+ length,
+ elem,
+ type,
+ _fired;
+ if ( fired ) {
+ _fired = fired;
+ fired = 0;
+ }
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ deferred.done.apply( deferred, elem );
+ } else if ( type === "function" ) {
+ callbacks.push( elem );
+ }
+ }
+ if ( _fired ) {
+ deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
+ }
+ }
+ return this;
+ },
+
+ // resolve with given context and args
+ resolveWith: function( context, args ) {
+ if ( !cancelled && !fired && !firing ) {
+ // make sure args are available (#8421)
+ args = args || [];
+ firing = 1;
+ try {
+ while( callbacks[ 0 ] ) {
+ callbacks.shift().apply( context, args );
+ }
+ }
+ finally {
+ fired = [ context, args ];
+ firing = 0;
+ }
+ }
+ return this;
+ },
+
+ // resolve with this as context and given arguments
+ resolve: function() {
+ deferred.resolveWith( this, arguments );
+ return this;
+ },
+
+ // Has this deferred been resolved?
+ isResolved: function() {
+ return !!( firing || fired );
+ },
+
+ // Cancel
+ cancel: function() {
+ cancelled = 1;
+ callbacks = [];
+ return this;
+ }
+ };
+
+ return deferred;
+ },
+
+ // Full fledged deferred (two callbacks list)
+ Deferred: function( func ) {
+ var deferred = jQuery._Deferred(),
+ failDeferred = jQuery._Deferred(),
+ promise;
+ // Add errorDeferred methods, then and promise
+ jQuery.extend( deferred, {
+ then: function( doneCallbacks, failCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks );
+ return this;
+ },
+ always: function() {
+ return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
+ },
+ fail: failDeferred.done,
+ rejectWith: failDeferred.resolveWith,
+ reject: failDeferred.resolve,
+ isRejected: failDeferred.isResolved,
+ pipe: function( fnDone, fnFail ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject );
+ } else {
+ newDefer[ action ]( returned );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ if ( promise ) {
+ return promise;
+ }
+ promise = obj = {};
+ }
+ var i = promiseMethods.length;
+ while( i-- ) {
+ obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
+ }
+ return obj;
+ }
+ });
+ // Make sure only one callback list will be used
+ deferred.done( failDeferred.cancel ).fail( deferred.cancel );
+ // Unexpose cancel
+ delete deferred.cancel;
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( firstParam ) {
+ var args = arguments,
+ i = 0,
+ length = args.length,
+ count = length,
+ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+ firstParam :
+ jQuery.Deferred();
+ function resolveFunc( i ) {
+ return function( value ) {
+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ if ( !( --count ) ) {
+ // Strange bug in FF4:
+ // Values changed onto the arguments object sometimes end up as undefined values
+ // outside the $.when method. Cloning the object into a fresh array solves the issue
+ deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
+ }
+ };
+ }
+ if ( length > 1 ) {
+ for( ; i < length; i++ ) {
+ if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject );
+ } else {
+ --count;
+ }
+ }
+ if ( !count ) {
+ deferred.resolveWith( deferred, args );
+ }
+ } else if ( deferred !== firstParam ) {
+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+ }
+ return deferred.promise();
+ }
+});
+
+
+
+jQuery.support = (function() {
+
+ var div = document.createElement( "div" ),
+ documentElement = document.documentElement,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ marginDiv,
+ support,
+ fragment,
+ body,
+ testElementParent,
+ testElement,
+ testElementStyle,
+ tds,
+ events,
+ eventName,
+ i,
+ isSupported;
+
+ // Preliminary tests
+ div.setAttribute("className", "t");
+ div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ all = div.getElementsByTagName( "*" );
+ a = div.getElementsByTagName( "a" )[ 0 ];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return {};
+ }
+
+ // First batch of supports tests
+ select = document.createElement( "select" );
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName( "input" )[ 0 ];
+
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName( "tbody" ).length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName( "link" ).length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55$/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true
+ };
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", function() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ });
+ div.cloneNode( true ).fireEvent( "onclick" );
+ }
+
+ // Check if a radio maintains it's value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute("type", "radio");
+ support.radioValue = input.value === "t";
+
+ input.setAttribute("checked", "checked");
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.firstChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ div.innerHTML = "";
+
+ // Figure out if the W3C box model works as expected
+ div.style.width = div.style.paddingLeft = "1px";
+
+ body = document.getElementsByTagName( "body" )[ 0 ];
+ // We use our own, invisible, body unless the body is already present
+ // in which case we use a div (#9239)
+ testElement = document.createElement( body ? "div" : "body" );
+ testElementStyle = {
+ visibility: "hidden",
+ width: 0,
+ height: 0,
+ border: 0,
+ margin: 0
+ };
+ if ( body ) {
+ jQuery.extend( testElementStyle, {
+ position: "absolute",
+ left: -1000,
+ top: -1000
+ });
+ }
+ for ( i in testElementStyle ) {
+ testElement.style[ i ] = testElementStyle[ i ];
+ }
+ testElement.appendChild( div );
+ testElementParent = body || documentElement;
+ testElementParent.insertBefore( testElement, testElementParent.firstChild );
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ support.boxModel = div.offsetWidth === 2;
+
+ if ( "zoom" in div.style ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "";
+ div.innerHTML = "<div style='width:4px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
+ }
+
+ div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE < 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+ div.innerHTML = "";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ marginDiv = document.createElement( "div" );
+ marginDiv.style.width = "0";
+ marginDiv.style.marginRight = "0";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ }
+
+ // Remove the body element we added
+ testElement.innerHTML = "";
+ testElementParent.removeChild( testElement );
+
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for( i in {
+ submit: 1,
+ change: 1,
+ focusin: 1
+ } ) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ // Null connected elements to avoid leaks in IE
+ testElement = fragment = select = opt = body = marginDiv = div = input = null;
+
+ return support;
+})();
+
+// Keep track of boxModel
+jQuery.boxModel = jQuery.support.boxModel;
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+ rmultiDash = /([a-z])([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ jQuery.expando ] = id = ++jQuery.uuid;
+ } else {
+ id = jQuery.expando;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
+ // metadata on plain JS objects when the object is serialized using
+ // JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
+ } else {
+ cache[ id ] = jQuery.extend(cache[ id ], name);
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // Internal jQuery data is stored in a separate object inside the object's data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data
+ if ( pvt ) {
+ if ( !thisCache[ internalKey ] ) {
+ thisCache[ internalKey ] = {};
+ }
+
+ thisCache = thisCache[ internalKey ];
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
+ // not attempt to inspect the internal events object using jQuery.data, as this
+ // internal data object is undocumented and subject to change.
+ if ( name === "events" && !thisCache[name] ) {
+ return thisCache[ internalKey ] && thisCache[ internalKey ].events;
+ }
+
+ return getByName ?
+ // Check for both converted-to-camel and non-converted data property names
+ thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] :
+ thisCache;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando, isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+
+ // See jQuery.data for more information
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+ var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
+
+ if ( thisCache ) {
+ delete thisCache[ name ];
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !isEmptyDataObject(thisCache) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( pvt ) {
+ delete cache[ id ][ internalKey ];
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject(cache[ id ]) ) {
+ return;
+ }
+ }
+
+ var internalCache = cache[ id ][ internalKey ];
+
+ // Browsers that fail expando deletion also refuse to delete expandos on
+ // the window, but it will allow it on all other JS objects; other browsers
+ // don't care
+ if ( jQuery.support.deleteExpando || cache != window ) {
+ delete cache[ id ];
+ } else {
+ cache[ id ] = null;
+ }
+
+ // We destroyed the entire user cache at once because it's faster than
+ // iterating through each key, but we need to continue to persist internal
+ // data if it existed
+ if ( internalCache ) {
+ cache[ id ] = {};
+ // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
+ // metadata on plain JS objects when the object is serialized using
+ // JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+
+ cache[ id ][ internalKey ] = internalCache;
+
+ // Otherwise, we need to eliminate the expando on the node to avoid
+ // false lookups in the cache for entries that no longer exist
+ } else if ( isNode ) {
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ jQuery.expando ];
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ } else {
+ elem[ jQuery.expando ] = null;
+ }
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var data = null;
+
+ if ( typeof key === "undefined" ) {
+ if ( this.length ) {
+ data = jQuery.data( this[0] );
+
+ if ( this[0].nodeType === 1 ) {
+ var attr = this[0].attributes, name;
+ for ( var i = 0, l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( this[0], name, data[ name ] );
+ }
+ }
+ }
+ }
+
+ return data;
+
+ } else if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && this.length ) {
+ data = jQuery.data( this[0], key );
+ data = dataAttr( this[0], key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+
+ } else {
+ return this.each(function() {
+ var $this = jQuery( this ),
+ args = [ parts[0], value ];
+
+ $this.triggerHandler( "setData" + parts[1] + "!", args );
+ jQuery.data( this, key, value );
+ $this.triggerHandler( "changeData" + parts[1] + "!", args );
+ });
+ }
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+ var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ !jQuery.isNaN( data ) ? parseFloat( data ) :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
+// property to be considered empty objects; this property always exists in
+// order to make sure JSON.stringify does not expose internal metadata
+function isEmptyDataObject( obj ) {
+ for ( var name in obj ) {
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+ var deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ defer = jQuery.data( elem, deferDataKey, undefined, true );
+ if ( defer &&
+ ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) &&
+ ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) {
+ // Give room for hard-coded callbacks to fire first
+ // and eventually mark/queue something else on the element
+ setTimeout( function() {
+ if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
+ !jQuery.data( elem, markDataKey, undefined, true ) ) {
+ jQuery.removeData( elem, deferDataKey, true );
+ defer.resolve();
+ }
+ }, 0 );
+ }
+}
+
+jQuery.extend({
+
+ _mark: function( elem, type ) {
+ if ( elem ) {
+ type = (type || "fx") + "mark";
+ jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true );
+ }
+ },
+
+ _unmark: function( force, elem, type ) {
+ if ( force !== true ) {
+ type = elem;
+ elem = force;
+ force = false;
+ }
+ if ( elem ) {
+ type = type || "fx";
+ var key = type + "mark",
+ count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 );
+ if ( count ) {
+ jQuery.data( elem, key, count, true );
+ } else {
+ jQuery.removeData( elem, key, true );
+ handleQueueMarkDefer( elem, type, "mark" );
+ }
+ }
+ },
+
+ queue: function( elem, type, data ) {
+ if ( elem ) {
+ type = (type || "fx") + "queue";
+ var q = jQuery.data( elem, type, undefined, true );
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery.data( elem, type, jQuery.makeArray(data), true );
+ } else {
+ q.push( data );
+ }
+ }
+ return q || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift(),
+ defer;
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift("inprogress");
+ }
+
+ fn.call(elem, function() {
+ jQuery.dequeue(elem, type);
+ });
+ }
+
+ if ( !queue.length ) {
+ jQuery.removeData( elem, type + "queue", true );
+ handleQueueMarkDefer( elem, type, "queue" );
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined ) {
+ return jQuery.queue( this[0], type );
+ }
+ return this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function() {
+ var elem = this;
+ setTimeout(function() {
+ jQuery.dequeue( elem, type );
+ }, time );
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, object ) {
+ if ( typeof type !== "string" ) {
+ object = type;
+ type = undefined;
+ }
+ type = type || "fx";
+ var defer = jQuery.Deferred(),
+ elements = this,
+ i = elements.length,
+ count = 1,
+ deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ tmp;
+ function resolve() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ }
+ while( i-- ) {
+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+ jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
+ count++;
+ tmp.done( resolve );
+ }
+ }
+ resolve();
+ return defer.promise();
+ }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ rinvalidChar = /\:|^on/,
+ formHook, boolHook;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.attr );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.prop );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classNames, i, l, elem,
+ setClass, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ classNames = value.split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className && classNames.length === 1 ) {
+ elem.className = value;
+
+ } else {
+ setClass = " " + elem.className + " ";
+
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
+ setClass += classNames[ c ] + " ";
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var classNames, i, l, elem, className, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ classNames = (value || "").split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ className = (" " + elem.className + " ").replace( rclass, " " );
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[ c ] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ";
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return undefined;
+ }
+
+ var isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var self = jQuery(this), val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value,
+ index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+ if ( one && !values.length && options.length ) {
+ return jQuery( options[ index ] ).val();
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attrFix: {
+ // Always normalize to ensure hook usage
+ tabindex: "tabIndex"
+ },
+
+ attr: function( elem, name, value, pass ) {
+ var nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return undefined;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( !("getAttribute" in elem) ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ var ret, hooks,
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // Normalize the name if needed
+ if ( notxml ) {
+ name = jQuery.attrFix[ name ] || name;
+
+ hooks = jQuery.attrHooks[ name ];
+
+ if ( !hooks ) {
+ // Use boolHook for boolean attributes
+ if ( rboolean.test( name ) ) {
+
+ hooks = boolHook;
+
+ // Use formHook for forms and if the name contains certain characters
+ } else if ( formHook && name !== "className" &&
+ (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) {
+
+ hooks = formHook;
+ }
+ }
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return undefined;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, "" + value );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, name ) {
+ var propName;
+ if ( elem.nodeType === 1 ) {
+ name = jQuery.attrFix[ name ] || name;
+
+ if ( jQuery.support.getSetAttribute ) {
+ // Use removeAttribute in browsers that support it
+ elem.removeAttribute( name );
+ } else {
+ jQuery.attr( elem, name, "" );
+ elem.removeAttributeNode( elem.getAttributeNode( name ) );
+ }
+
+ // Set corresponding property to false for boolean attributes
+ if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabIndex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ },
+ // Use the value property for back compat
+ // Use the formHook for button elements in IE6/7 (#1954)
+ value: {
+ get: function( elem, name ) {
+ if ( formHook && jQuery.nodeName( elem, "button" ) ) {
+ return formHook.get( elem, name );
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value, name ) {
+ if ( formHook && jQuery.nodeName( elem, "button" ) ) {
+ return formHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return undefined;
+ }
+
+ var ret, hooks,
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return (elem[ name ] = value);
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {}
+});
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ return jQuery.prop( elem, name ) ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !jQuery.support.getSetAttribute ) {
+
+ // propFix is more comprehensive and contains all fixes
+ jQuery.attrFix = jQuery.propFix;
+
+ // Use this for any attribute on a form in IE6/7
+ formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ // Return undefined if nodeValue is empty string
+ return ret && ret.nodeValue !== "" ?
+ ret.nodeValue :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Check form objects in IE (multiple bugs related)
+ // Only use nodeValue if the attribute node exists on the form
+ var ret = elem.getAttributeNode( name );
+ if ( ret ) {
+ ret.nodeValue = value;
+ return value;
+ }
+ }
+ };
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return (elem.style.cssText = "" + value);
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ }
+ });
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0);
+ }
+ }
+ });
+});
+
+
+
+
+var rnamespaces = /\.(.*)$/,
+ rformElems = /^(?:textarea|input|select)$/i,
+ rperiod = /\./g,
+ rspaces = / /g,
+ rescape = /[^\w\s.|`]/g,
+ fcleanup = function( nm ) {
+ return nm.replace(rescape, "\\$&");
+ };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function( elem, types, handler, data ) {
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ } else if ( !handler ) {
+ // Fixes bug #7229. Fix recommended by jdalton
+ return;
+ }
+
+ var handleObjIn, handleObj;
+
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure
+ var elemData = jQuery._data( elem );
+
+ // If no elemData is found then we must be trying to bind to one of the
+ // banned noData elements
+ if ( !elemData ) {
+ return;
+ }
+
+ var events = elemData.events,
+ eventHandle = elemData.handle;
+
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ }
+
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native events in IE.
+ eventHandle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ var type, i = 0, namespaces;
+
+ while ( (type = types[ i++ ]) ) {
+ handleObj = handleObjIn ?
+ jQuery.extend({}, handleObjIn) :
+ { handler: handler, data: data };
+
+ // Namespaced event handlers
+ if ( type.indexOf(".") > -1 ) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+ } else {
+ namespaces = [];
+ handleObj.namespace = "";
+ }
+
+ handleObj.type = type;
+ if ( !handleObj.guid ) {
+ handleObj.guid = handler.guid;
+ }
+
+ // Get the current list of functions bound to this event
+ var handlers = events[ type ],
+ special = jQuery.event.special[ type ] || {};
+
+ // Init the event handler queue
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers.push( handleObj );
+
+ // Keep track of which events have been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, pos ) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ }
+
+ var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ events = elemData && elemData.events;
+
+ if ( !elemData || !events ) {
+ return;
+ }
+
+ // types is actually an event object here
+ if ( types && types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Unbind all events for the element
+ if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+ types = types || "";
+
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types );
+ }
+
+ return;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ while ( (type = types[ i++ ]) ) {
+ origType = type;
+ handleObj = null;
+ all = type.indexOf(".") < 0;
+ namespaces = [];
+
+ if ( !all ) {
+ // Namespaced event handlers
+ namespaces = type.split(".");
+ type = namespaces.shift();
+
+ namespace = new RegExp("(^|\\.)" +
+ jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ eventType = events[ type ];
+
+ if ( !eventType ) {
+ continue;
+ }
+
+ if ( !handler ) {
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ jQuery.event.remove( elem, origType, handleObj.handler, j );
+ eventType.splice( j--, 1 );
+ }
+ }
+
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+
+ for ( j = pos || 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( handler.guid === handleObj.guid ) {
+ // remove the given handler for the given type
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ if ( pos == null ) {
+ eventType.splice( j--, 1 );
+ }
+
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+
+ if ( pos != null ) {
+ break;
+ }
+ }
+ }
+
+ // remove generic event handler if no more handlers exist
+ if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ ret = null;
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ var handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ delete elemData.events;
+ delete elemData.handle;
+
+ if ( jQuery.isEmptyObject( elemData ) ) {
+ jQuery.removeData( elem, undefined, true );
+ }
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Event object or event type
+ var type = event.type || event,
+ namespaces = [],
+ exclusive;
+
+ if ( type.indexOf("!") >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf(".") >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join(".");
+ event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
+
+ // triggerHandler() and global events don't bubble or run the default action
+ if ( onlyHandlers || !elem ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ jQuery.each( jQuery.cache, function() {
+ // internalKey variable is just used to make it easier to find
+ // and potentially change this stuff later; currently it just
+ // points to jQuery.expando
+ var internalKey = jQuery.expando,
+ internalCache = this[ internalKey ];
+ if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
+ jQuery.event.trigger( event, data, internalCache.handle.elem );
+ }
+ });
+ return;
+ }
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ var cur = elem,
+ // IE doesn't like method names with a colon (#3533, #8272)
+ ontype = type.indexOf(":") < 0 ? "on" + type : "";
+
+ // Fire event on the current element, then bubble up the DOM tree
+ do {
+ var handle = jQuery._data( cur, "handle" );
+
+ event.currentTarget = cur;
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+
+ // Trigger an inline bound script
+ if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
+ event.result = false;
+ event.preventDefault();
+ }
+
+ // Bubble up to document, then to window
+ cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
+ } while ( cur && !event.isPropagationStopped() );
+
+ // If nobody prevented the default action, do it now
+ if ( !event.isDefaultPrevented() ) {
+ var old,
+ special = jQuery.event.special[ type ] || {};
+
+ if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction)() check here because IE6/7 fails that test.
+ // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
+ try {
+ if ( ontype && elem[ type ] ) {
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ }
+ } catch ( ieError ) {}
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+
+ jQuery.event.triggered = undefined;
+ }
+ }
+
+ return event.result;
+ },
+
+ handle: function( event ) {
+ event = jQuery.event.fix( event || window.event );
+ // Snapshot the handlers list since a called handler may add/remove events.
+ var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0),
+ run_all = !event.exclusive && !event.namespace,
+ args = Array.prototype.slice.call( arguments, 0 );
+
+ // Use the fix-ed Event rather than the (read-only) native event
+ args[0] = event;
+ event.currentTarget = this;
+
+ for ( var j = 0, l = handlers.length; j < l; j++ ) {
+ var handleObj = handlers[ j ];
+
+ // Triggered event must 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event.
+ if ( run_all || event.namespace_re.test( handleObj.namespace ) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handleObj.handler;
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ var ret = handleObj.handler.apply( this, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+ return event.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 pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ) {
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target ) {
+ // Fixes #1925 where srcElement might not be defined either
+ event.target = event.srcElement || document;
+ }
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement ) {
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var eventDocument = event.target.ownerDocument || document,
+ doc = eventDocument.documentElement,
+ body = eventDocument.body;
+
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
+ event.which = event.charCode != null ? event.charCode : event.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button !== undefined ) {
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+ }
+
+ return event;
+ },
+
+ // Deprecated, use jQuery.guid instead
+ guid: 1E8,
+
+ // Deprecated, use jQuery.proxy instead
+ proxy: jQuery.proxy,
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady,
+ teardown: jQuery.noop
+ },
+
+ live: {
+ add: function( handleObj ) {
+ jQuery.event.add( this,
+ liveConvert( handleObj.origType, handleObj.selector ),
+ jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
+ },
+
+ remove: function( handleObj ) {
+ jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
+ }
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ }
+};
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !this.preventDefault ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+
+ // Check if mouse(over|out) are still within the same parent element
+ var related = event.relatedTarget,
+ inside = false,
+ eventType = event.type;
+
+ event.type = event.data;
+
+ if ( related !== this ) {
+
+ if ( related ) {
+ inside = jQuery.contains( this, related );
+ }
+
+ if ( !inside ) {
+
+ jQuery.event.handle.apply( this, arguments );
+
+ event.type = eventType;
+ }
+ }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+ event.type = event.data;
+ jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ setup: function( data ) {
+ jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+ },
+ teardown: function( data ) {
+ jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+ }
+ };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function( data, namespaces ) {
+ if ( !jQuery.nodeName( this, "form" ) ) {
+ jQuery.event.add(this, "click.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+ trigger( "submit", this, arguments );
+ }
+ });
+
+ jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+ trigger( "submit", this, arguments );
+ }
+ });
+
+ } else {
+ return false;
+ }
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialSubmit" );
+ }
+ };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+ var changeFilters,
+
+ getVal = function( elem ) {
+ var type = elem.type, val = elem.value;
+
+ if ( type === "radio" || type === "checkbox" ) {
+ val = elem.checked;
+
+ } else if ( type === "select-multiple" ) {
+ val = elem.selectedIndex > -1 ?
+ jQuery.map( elem.options, function( elem ) {
+ return elem.selected;
+ }).join("-") :
+ "";
+
+ } else if ( jQuery.nodeName( elem, "select" ) ) {
+ val = elem.selectedIndex;
+ }
+
+ return val;
+ },
+
+ testChange = function testChange( e ) {
+ var elem = e.target, data, val;
+
+ if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
+ return;
+ }
+
+ data = jQuery._data( elem, "_change_data" );
+ val = getVal(elem);
+
+ // the current data will be also retrieved by beforeactivate
+ if ( e.type !== "focusout" || elem.type !== "radio" ) {
+ jQuery._data( elem, "_change_data", val );
+ }
+
+ if ( data === undefined || val === data ) {
+ return;
+ }
+
+ if ( data != null || val ) {
+ e.type = "change";
+ e.liveFired = undefined;
+ jQuery.event.trigger( e, arguments[1], elem );
+ }
+ };
+
+ jQuery.event.special.change = {
+ filters: {
+ focusout: testChange,
+
+ beforedeactivate: testChange,
+
+ click: function( e ) {
+ var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
+
+ if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) {
+ testChange.call( this, e );
+ }
+ },
+
+ // Change has to be called before submit
+ // Keydown will be called before keypress, which is used in submit-event delegation
+ keydown: function( e ) {
+ var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
+
+ if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) ||
+ (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+ type === "select-multiple" ) {
+ testChange.call( this, e );
+ }
+ },
+
+ // Beforeactivate happens also before the previous element is blurred
+ // with this event you can't trigger a change event, but you can store
+ // information
+ beforeactivate: function( e ) {
+ var elem = e.target;
+ jQuery._data( elem, "_change_data", getVal(elem) );
+ }
+ },
+
+ setup: function( data, namespaces ) {
+ if ( this.type === "file" ) {
+ return false;
+ }
+
+ for ( var type in changeFilters ) {
+ jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+ }
+
+ return rformElems.test( this.nodeName );
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialChange" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+
+ changeFilters = jQuery.event.special.change.filters;
+
+ // Handle when the input is .focus()'d
+ changeFilters.focus = changeFilters.beforeactivate;
+}
+
+function trigger( type, elem, args ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ // Don't pass args or remember liveFired; they apply to the donor event.
+ var event = jQuery.extend( {}, args[ 0 ] );
+ event.type = type;
+ event.originalEvent = {};
+ event.liveFired = undefined;
+ jQuery.event.handle.call( elem, event );
+ if ( event.isDefaultPrevented() ) {
+ args[ 0 ].preventDefault();
+ }
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0;
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+
+ function handler( donor ) {
+ // Donor event is always a native one; fix it and switch its type.
+ // Let focusin/out handler cancel the donor focus/blur event.
+ var e = jQuery.event.fix( donor );
+ e.type = fix;
+ e.originalEvent = {};
+ jQuery.event.trigger( e, null, e.target );
+ if ( e.isDefaultPrevented() ) {
+ donor.preventDefault();
+ }
+ }
+ });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+ jQuery.fn[ name ] = function( type, data, fn ) {
+ var handler;
+
+ // Handle object literals
+ if ( typeof type === "object" ) {
+ for ( var key in type ) {
+ this[ name ](key, data, type[key], fn);
+ }
+ return this;
+ }
+
+ if ( arguments.length === 2 || data === false ) {
+ fn = data;
+ data = undefined;
+ }
+
+ if ( name === "one" ) {
+ handler = function( event ) {
+ jQuery( this ).unbind( event, handler );
+ return fn.apply( this, arguments );
+ };
+ handler.guid = fn.guid || jQuery.guid++;
+ } else {
+ handler = fn;
+ }
+
+ if ( type === "unload" && name !== "one" ) {
+ this.one( type, data, fn );
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.add( this[i], type, handler, data );
+ }
+ }
+
+ return this;
+ };
+});
+
+jQuery.fn.extend({
+ unbind: function( type, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" && !type.preventDefault ) {
+ for ( var key in type ) {
+ this.unbind(key, type[key]);
+ }
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.remove( this[i], type, fn );
+ }
+ }
+
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.live( types, data, fn, selector );
+ },
+
+ undelegate: function( selector, types, fn ) {
+ if ( arguments.length === 0 ) {
+ return this.unbind( "live" );
+
+ } else {
+ return this.die( types, null, fn, selector );
+ }
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+var liveMap = {
+ focus: "focusin",
+ blur: "focusout",
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+ jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+ var type, i = 0, match, namespaces, preType,
+ selector = origSelector || this.selector,
+ context = origSelector ? this : jQuery( this.context );
+
+ if ( typeof types === "object" && !types.preventDefault ) {
+ for ( var key in types ) {
+ context[ name ]( key, data, types[key], selector );
+ }
+
+ return this;
+ }
+
+ if ( name === "die" && !types &&
+ origSelector && origSelector.charAt(0) === "." ) {
+
+ context.unbind( origSelector );
+
+ return this;
+ }
+
+ if ( data === false || jQuery.isFunction( data ) ) {
+ fn = data || returnFalse;
+ data = undefined;
+ }
+
+ types = (types || "").split(" ");
+
+ while ( (type = types[ i++ ]) != null ) {
+ match = rnamespaces.exec( type );
+ namespaces = "";
+
+ if ( match ) {
+ namespaces = match[0];
+ type = type.replace( rnamespaces, "" );
+ }
+
+ if ( type === "hover" ) {
+ types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+ continue;
+ }
+
+ preType = type;
+
+ if ( liveMap[ type ] ) {
+ types.push( liveMap[ type ] + namespaces );
+ type = type + namespaces;
+
+ } else {
+ type = (liveMap[ type ] || type) + namespaces;
+ }
+
+ if ( name === "live" ) {
+ // bind live handler
+ for ( var j = 0, l = context.length; j < l; j++ ) {
+ jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
+ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+ }
+
+ } else {
+ // unbind live handler
+ context.unbind( "live." + liveConvert( type, selector ), fn );
+ }
+ }
+
+ return this;
+ };
+});
+
+function liveHandler( event ) {
+ var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
+ elems = [],
+ selectors = [],
+ events = jQuery._data( this, "events" );
+
+ // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
+ if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
+ return;
+ }
+
+ if ( event.namespace ) {
+ namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ event.liveFired = this;
+
+ var live = events.live.slice(0);
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+ selectors.push( handleObj.selector );
+
+ } else {
+ live.splice( j--, 1 );
+ }
+ }
+
+ match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+ for ( i = 0, l = match.length; i < l; i++ ) {
+ close = match[i];
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {
+ elem = close.elem;
+ related = null;
+
+ // Those two events require additional checking
+ if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+ event.type = handleObj.preType;
+ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+
+ // Make sure not to accidentally match a child element with the same selector
+ if ( related && jQuery.contains( elem, related ) ) {
+ related = elem;
+ }
+ }
+
+ if ( !related || related !== elem ) {
+ elems.push({ elem: elem, handleObj: handleObj, level: close.level });
+ }
+ }
+ }
+ }
+
+ for ( i = 0, l = elems.length; i < l; i++ ) {
+ match = elems[i];
+
+ if ( maxLevel && match.level > maxLevel ) {
+ break;
+ }
+
+ event.currentTarget = match.elem;
+ event.data = match.handleObj.data;
+ event.handleObj = match.handleObj;
+
+ ret = match.handleObj.origHandler.apply( match.elem, arguments );
+
+ if ( ret === false || event.isPropagationStopped() ) {
+ maxLevel = match.level;
+
+ if ( ret === false ) {
+ stop = false;
+ }
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+
+ return stop;
+}
+
+function liveConvert( type, selector ) {
+ return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&");
+}
+
+jQuery.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( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.bind( name, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray( set );
+
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ cur = parts.pop();
+ pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var match,
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ var left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace( rBackslash, "" );
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ var found, item,
+ filter = Expr.filter[ type ],
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+
+ } else {
+ curLoop[i] = false;
+ }
+
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.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|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !rNonWord.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+
+ "": function(checkSet, part, isXML){
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+ }
+ },
+
+ find: {
+ ID: function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [],
+ results = context.getElementsByName( match[1] );
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+
+ TAG: function( match, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+
+ return false;
+ }
+
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+
+ POS: function( match ) {
+ match.unshift( true );
+
+ return match;
+ }
+ },
+
+ filters: {
+ enabled: function( elem ) {
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+
+ disabled: function( elem ) {
+ return elem.disabled === true;
+ },
+
+ checked: function( elem ) {
+ return elem.checked === true;
+ },
+
+ selected: function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ parent: function( elem ) {
+ return !!elem.firstChild;
+ },
+
+ empty: function( elem ) {
+ return !elem.firstChild;
+ },
+
+ has: function( elem, i, match ) {
+ return !!Sizzle( match[3], elem ).length;
+ },
+
+ header: function( elem ) {
+ return (/h\d/i).test( elem.nodeName );
+ },
+
+ text: function( elem ) {
+ var attr = elem.getAttribute( "type" ), type = elem.type;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
+ },
+
+ radio: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
+ },
+
+ password: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
+ },
+
+ reset: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && "button" === elem.type || name === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ },
+
+ focus: function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ }
+ },
+ setFilters: {
+ first: function( elem, i ) {
+ return i === 0;
+ },
+
+ last: function( elem, i, match, array ) {
+ return i === array.length - 1;
+ },
+
+ even: function( elem, i ) {
+ return i % 2 === 0;
+ },
+
+ odd: function( elem, i ) {
+ return i % 2 === 1;
+ },
+
+ lt: function( elem, i, match ) {
+ return i < match[3] - 0;
+ },
+
+ gt: function( elem, i, match ) {
+ return i > match[3] - 0;
+ },
+
+ nth: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ },
+
+ eq: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ var first = match[2],
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+
+ if ( first === 0 ) {
+ return diff === 0;
+
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+
+ ID: function( elem, match ) {
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+Sizzle.getText = function( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += Sizzle.getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+};
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+
+ Expr.filter.ID = function( elem, match ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id === match[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+ if ( matches ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9 fails this)
+ var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ var ret = matches.call( node, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || !disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9, so check for that
+ node.document && node.document.nodeType !== 11 ) {
+ return ret;
+ }
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function( match, context, isXML ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.POS,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var self = this,
+ i, l;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ var ret = this.pushStack( "", "find", selector ),
+ length, n, r;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && ( typeof selector === "string" ?
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ // Array
+ if ( jQuery.isArray( selectors ) ) {
+ var match, selector,
+ matches = {},
+ level = 1;
+
+ if ( cur && selectors.length ) {
+ for ( i = 0, l = selectors.length; i < l; i++ ) {
+ selector = selectors[i];
+
+ if ( !matches[ selector ] ) {
+ matches[ selector ] = POS.test( selector ) ?
+ jQuery( selector, context || this.context ) :
+ selector;
+ }
+ }
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( selector in matches ) {
+ match = matches[ selector ];
+
+ if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
+ ret.push({ selector: selector, elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+ }
+
+ return ret;
+ }
+
+ // String
+ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ if ( !elem || typeof elem === "string" ) {
+ return jQuery.inArray( this[0],
+ // If it receives a string, the selector is used
+ // If it receives nothing, the siblings are used
+ elem ? jQuery( elem ) : this.parent().children() );
+ }
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( elem.parentNode.firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until ),
+ // The variable 'args' was introduced in
+ // https://github.com/jquery/jquery/commit/52a0238
+ // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
+ // http://code.google.com/p/v8/issues/detail?id=1050
+ args = slice.call(arguments);
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, args.join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return (elem === qualifier) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+ });
+}
+
+
+
+
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+ wrapMap = {
+ 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, "", "" ]
+ };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( text ) {
+ if ( jQuery.isFunction(text) ) {
+ return this.each(function(i) {
+ var self = jQuery( this );
+
+ self.text( text.call(this, i, self.text()) );
+ });
+ }
+
+ if ( typeof text !== "object" && text !== undefined ) {
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+ }
+
+ return jQuery.text( this );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function() {
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery(arguments[0]);
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery(arguments[0]).toArray() );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ if ( value === undefined ) {
+ return this[0] && this[0].nodeType === 1 ?
+ this[0].innerHTML.replace(rinlinejQuery, "") :
+ null;
+
+ // See if we can take a shortcut and just use innerHTML
+ } else if ( typeof value === "string" && !rnocache.test( value ) &&
+ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+ value = value.replace(rxhtmlTag, "<$1></$2>");
+
+ try {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( this[i].nodeType === 1 ) {
+ jQuery.cleanData( this[i].getElementsByTagName("*") );
+ this[i].innerHTML = value;
+ }
+ }
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {
+ this.empty().append( value );
+ }
+
+ } else if ( jQuery.isFunction( value ) ) {
+ this.each(function(i){
+ var self = jQuery( this );
+
+ self.html( value.call(this, i, self.html()) );
+ });
+
+ } else {
+ this.empty().append( value );
+ }
+
+ return this;
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, fragment, parent,
+ value = args[0],
+ scripts = [];
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = jQuery.buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ // Make sure that we do not leak memory by inadvertently discarding
+ // the original fragment (which might have attached data) instead of
+ // using it; in addition, use the original fragment object for the last
+ // item instead of first because it can end up being emptied incorrectly
+ // in certain situations (Bug #8070).
+ // Fragments from the fragment cache must always be cloned and never used
+ // in place.
+ results.cacheable || (l > 1 && i < lastIndex) ?
+ jQuery.clone( fragment, true, true ) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, evalScript );
+ }
+ }
+
+ return this;
+ }
+});
+
+function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando,
+ oldData = jQuery.data( src ),
+ curData = jQuery.data( dest, oldData );
+
+ // Switch to use the internal data object, if it exists, for the next
+ // stage of data copying
+ if ( (oldData = oldData[ internalKey ]) ) {
+ var events = oldData.events;
+ curData = curData[ internalKey ] = jQuery.extend({}, oldData);
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( var type in events ) {
+ for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
+ }
+ }
+ }
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ // IE6-8 fail to clone children inside object elements that use
+ // the proprietary classid attribute value (rather than the type
+ // attribute) to identify the type of content to display
+ if ( nodeName === "object" ) {
+ dest.outerHTML = src.outerHTML;
+
+ } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+ if ( src.checked ) {
+ dest.defaultChecked = dest.checked = src.checked;
+ }
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults, doc;
+
+ // nodes may contain either an explicit document object,
+ // a jQuery collection or context object.
+ // If nodes[0] contains a valid object to assign to doc
+ if ( nodes && nodes[0] ) {
+ doc = nodes[0].ownerDocument || nodes[0];
+ }
+
+ // Ensure that an attr object doesn't incorrectly stand in as a document object
+ // Chrome and Firefox seem to allow this to occur and will throw exception
+ // Fixes #8950
+ if ( !doc.createDocumentFragment ) {
+ doc = document;
+ }
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+ args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+ cacheable = true;
+
+ cacheresults = jQuery.fragments[ args[0] ];
+ if ( cacheresults && cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [],
+ insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( "getElementsByTagName" in elem ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( "querySelectorAll" in elem ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( elem.type === "checkbox" || elem.type === "radio" ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+// Finds all inputs and passes them to fixDefaultChecked
+function findInputs( elem ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ fixDefaultChecked( elem );
+ } else if ( "getElementsByTagName" in elem ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var clone = elem.cloneNode(true),
+ srcElements,
+ destElements,
+ i;
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName
+ // instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ srcElements = destElements = null;
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var checkScriptType;
+
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ var ret = [], j;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div");
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+ }
+ }
+
+ // Resets defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ var len;
+ if ( !jQuery.support.appendChecked ) {
+ if ( elem[0] && typeof (len = elem.length) === "number" ) {
+ for ( j = 0; j < len; j++ ) {
+ findInputs( elem[j] );
+ }
+ } else {
+ findInputs( elem );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ checkScriptType = function( elem ) {
+ return !elem.type || rscriptType.test( elem.type );
+ };
+ for ( i = 0; ret[i]; i++ ) {
+ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+
+ } else {
+ if ( ret[i].nodeType === 1 ) {
+ var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType );
+
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ }
+ fragment.appendChild( ret[i] );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ continue;
+ }
+
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ] && cache[ id ][ internalKey ];
+
+ if ( data && data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+
+ // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+ if ( data.handle ) {
+ data.handle.elem = null;
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+
+function evalScript( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+}
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ // fixed for IE9, see #8346
+ rupper = /([A-Z]|^ms)/g,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/,
+ rrelNum = /^[+\-]=/,
+ rrelNumFilter = /[^+\-\.\de]+/g,
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssWidth = [ "Left", "Right" ],
+ cssHeight = [ "Top", "Bottom" ],
+ curCSS,
+
+ getComputedStyle,
+ currentStyle;
+
+jQuery.fn.css = function( name, value ) {
+ // Setting 'undefined' is a no-op
+ if ( arguments.length === 2 && value === undefined ) {
+ return this;
+ }
+
+ return jQuery.access( this, name, value, true, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ });
+};
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity", "opacity" );
+ return ret === "" ? "1" : ret;
+
+ } else {
+ return elem.style.opacity;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "fillOpacity": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, origName = jQuery.camelCase( name ),
+ style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( type === "number" && isNaN( value ) || value == null ) {
+ return;
+ }
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && rrelNum.test( value ) ) {
+ value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra ) {
+ var ret, hooks;
+
+ // Make sure that we're working with the right name
+ name = jQuery.camelCase( name );
+ hooks = jQuery.cssHooks[ name ];
+ name = jQuery.cssProps[ name ] || name;
+
+ // cssFloat needs a special treatment
+ if ( name === "cssFloat" ) {
+ name = "float";
+ }
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
+ return ret;
+
+ // Otherwise, if a way to get the computed value exists, use that
+ } else if ( curCSS ) {
+ return curCSS( elem, name );
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+ }
+});
+
+// DEPRECATED, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+jQuery.each(["height", "width"], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ var val;
+
+ if ( computed ) {
+ if ( elem.offsetWidth !== 0 ) {
+ return getWH( elem, name, extra );
+ } else {
+ jQuery.swap( elem, cssShow, function() {
+ val = getWH( elem, name, extra );
+ });
+ }
+
+ return val;
+ }
+ },
+
+ set: function( elem, value ) {
+ if ( rnumpx.test( value ) ) {
+ // ignore negative width and height values #1599
+ value = parseFloat( value );
+
+ if ( value >= 0 ) {
+ return value + "px";
+ }
+
+ } else {
+ return value;
+ }
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle;
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ var opacity = jQuery.isNaN( value ) ?
+ "" :
+ "alpha(opacity=" + value * 100 + ")",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+jQuery(function() {
+ // This hook cannot be added until DOM ready because the support test
+ // for it is not run until after DOM ready
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ var ret;
+ jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ ret = curCSS( elem, "margin-right", "marginRight" );
+ } else {
+ ret = elem.style.marginRight;
+ }
+ });
+ return ret;
+ }
+ };
+ }
+});
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ getComputedStyle = function( elem, name ) {
+ var ret, defaultView, computedStyle;
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ if ( !(defaultView = elem.ownerDocument.defaultView) ) {
+ return undefined;
+ }
+
+ if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+ ret = computedStyle.getPropertyValue( name );
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+ }
+
+ return ret;
+ };
+}
+
+if ( document.documentElement.currentStyle ) {
+ currentStyle = function( elem, name ) {
+ var left,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
+ style = elem.style;
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+ // Remember the original values
+ left = style.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : (ret || 0);
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWH( elem, name, extra ) {
+
+ // Start with offset property
+ var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ which = name === "width" ? cssWidth : cssHeight;
+
+ if ( val > 0 ) {
+ if ( extra !== "border" ) {
+ jQuery.each( which, function() {
+ if ( !extra ) {
+ val -= parseFloat( jQuery.css( elem, "padding" + this ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + this ) ) || 0;
+ } else {
+ val -= parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0;
+ }
+ });
+ }
+
+ return val + "px";
+ }
+
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name, name );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ] || 0;
+ }
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+
+ // Add padding, border, margin
+ if ( extra ) {
+ jQuery.each( which, function() {
+ val += parseFloat( jQuery.css( elem, "padding" + this ) ) || 0;
+ if ( extra !== "padding" ) {
+ val += parseFloat( jQuery.css( elem, "border" + this + "Width" ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + this ) ) || 0;
+ }
+ });
+ }
+
+ return val + "px";
+}
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth,
+ height = elem.offsetHeight;
+
+ return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+
+
+
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rselectTextarea = /^(?:select|textarea)/i,
+ rspacesAjax = /\s+/,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Document location
+ ajaxLocation,
+
+ // Document location segments
+ ajaxLocParts;
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ if ( jQuery.isFunction( func ) ) {
+ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
+ i = 0,
+ length = dataTypes.length,
+ dataType,
+ list,
+ placeBefore;
+
+ // For each dataType in the dataTypeExpression
+ for(; i < length; i++ ) {
+ dataType = dataTypes[ i ];
+ // We control if we're asked to add before
+ // any existing element
+ placeBefore = /^\+/.test( dataType );
+ if ( placeBefore ) {
+ dataType = dataType.substr( 1 ) || "*";
+ }
+ list = structure[ dataType ] = structure[ dataType ] || [];
+ // then we add to the structure accordingly
+ list[ placeBefore ? "unshift" : "push" ]( func );
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+ dataType /* internal */, inspected /* internal */ ) {
+
+ dataType = dataType || options.dataTypes[ 0 ];
+ inspected = inspected || {};
+
+ inspected[ dataType ] = true;
+
+ var list = structure[ dataType ],
+ i = 0,
+ length = list ? list.length : 0,
+ executeOnly = ( structure === prefilters ),
+ selection;
+
+ for(; i < length && ( executeOnly || !selection ); i++ ) {
+ selection = list[ i ]( options, originalOptions, jqXHR );
+ // If we got redirected to another dataType
+ // we try there if executing only and not done already
+ if ( typeof selection === "string" ) {
+ if ( !executeOnly || inspected[ selection ] ) {
+ selection = undefined;
+ } else {
+ options.dataTypes.unshift( selection );
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, selection, inspected );
+ }
+ }
+ }
+ // If we're only executing or nothing was selected
+ // we try the catchall dataType if not done already
+ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, "*", inspected );
+ }
+ // unnecessary when only executing (prefilters)
+ // but it'll be ignored by the caller in that case
+ return selection;
+}
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf( " " );
+ if ( off >= 0 ) {
+ var selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ // Complete callback (responseText is used internally)
+ complete: function( jqXHR, status, responseText ) {
+ // Store the response as specified by the jqXHR object
+ responseText = jqXHR.responseText;
+ // If successful, inject the HTML into all the matched elements
+ if ( jqXHR.isResolved() ) {
+ // #4825: Get the actual response in case
+ // a dataFilter is present in ajaxSettings
+ jqXHR.done(function( r ) {
+ responseText = r;
+ });
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [ responseText, status, jqXHR ] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+ jQuery.fn[ o ] = function( f ){
+ return this.bind( o, f );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ };
+});
+
+jQuery.extend({
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function ( target, settings ) {
+ if ( !settings ) {
+ // Only one parameter, we extend ajaxSettings
+ settings = target;
+ target = jQuery.extend( true, jQuery.ajaxSettings, settings );
+ } else {
+ // target was provided, we extend into it
+ jQuery.extend( true, target, jQuery.ajaxSettings, settings );
+ }
+ // Flatten fields we don't want deep extended
+ for( var field in { context: 1, url: 1 } ) {
+ if ( field in settings ) {
+ target[ field ] = settings[ field ];
+ } else if( field in jQuery.ajaxSettings ) {
+ target[ field ] = jQuery.ajaxSettings[ field ];
+ }
+ }
+ return target;
+ },
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ text: "text/plain",
+ json: "application/json, text/javascript",
+ "*": "*/*"
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // List of data converters
+ // 1) key format is "source_type destination_type" (a single space in-between)
+ // 2) the catchall symbol "*" can be used for source_type
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ }
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node or a jQuery collection
+ globalEventContext = callbackContext !== s &&
+ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+ jQuery( callbackContext ) : jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery._Deferred(),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // ifModified key
+ ifModifiedKey,
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // The jqXHR state
+ state = 0,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Fake xhr
+ jqXHR = {
+
+ readyState: 0,
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ if ( !state ) {
+ var lname = name.toLowerCase();
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match === undefined ? null : match;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ statusText = statusText || "abort";
+ if ( transport ) {
+ transport.abort( statusText );
+ }
+ done( 0, statusText );
+ return this;
+ }
+ };
+
+ // Callback for when everything is done
+ // It is defined here because jslint complains if it is declared
+ // at the end of the function (which would be more logical and readable)
+ function done( status, statusText, responses, headers ) {
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status ? 4 : 0;
+
+ var isSuccess,
+ success,
+ error,
+ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
+ lastModified,
+ etag;
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+
+ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+ jQuery.lastModified[ ifModifiedKey ] = lastModified;
+ }
+ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+ jQuery.etag[ ifModifiedKey ] = etag;
+ }
+ }
+
+ // If not modified
+ if ( status === 304 ) {
+
+ statusText = "notmodified";
+ isSuccess = true;
+
+ // If we have data
+ } else {
+
+ try {
+ success = ajaxConvert( s, response );
+ statusText = "success";
+ isSuccess = true;
+ } catch(e) {
+ // We have a parsererror
+ statusText = "parsererror";
+ error = e;
+ }
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if( !statusText || status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = statusText;
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+ }
+
+ // Attach deferreds
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.done;
+
+ // Status-dependent callbacks
+ jqXHR.statusCode = function( map ) {
+ if ( map ) {
+ var tmp;
+ if ( state < 2 ) {
+ for( tmp in map ) {
+ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+ }
+ } else {
+ tmp = map[ jqXHR.status ];
+ jqXHR.then( tmp, tmp );
+ }
+ }
+ return this;
+ };
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
+
+ // Determine if a cross-domain request is in order
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefiler, stop there
+ if ( state === 2 ) {
+ return false;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+ }
+
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+
+ var ts = jQuery.now(),
+ // try replacing _= if it is there
+ ret = s.url.replace( rts, "$1_=" + ts );
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+ }
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+ }
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already
+ jqXHR.abort();
+ return false;
+
+ }
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( status < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ jQuery.error( e );
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : value;
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+ }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && obj != null && typeof obj === "object" ) {
+ // Serialize object item.
+ for ( var name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields,
+ ct,
+ type,
+ finalDataType,
+ firstDataType;
+
+ // Fill responseXXX fields
+ for( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ var dataTypes = s.dataTypes,
+ converters = {},
+ i,
+ key,
+ length = dataTypes.length,
+ tmp,
+ // Current and previous dataTypes
+ current = dataTypes[ 0 ],
+ prev,
+ // Conversion expression
+ conversion,
+ // Conversion function
+ conv,
+ // Conversion functions (transitive conversion)
+ conv1,
+ conv2;
+
+ // For each dataType in the chain
+ for( i = 1; i < length; i++ ) {
+
+ // Create converters map
+ // with lowercased keys
+ if ( i === 1 ) {
+ for( key in s.converters ) {
+ if( typeof key === "string" ) {
+ converters[ key.toLowerCase() ] = s.converters[ key ];
+ }
+ }
+ }
+
+ // Get the dataTypes
+ prev = current;
+ current = dataTypes[ i ];
+
+ // If current is auto dataType, update it to prev
+ if( current === "*" ) {
+ current = prev;
+ // If no auto and dataTypes are actually different
+ } else if ( prev !== "*" && prev !== current ) {
+
+ // Get the converter
+ conversion = prev + " " + current;
+ conv = converters[ conversion ] || converters[ "* " + current ];
+
+ // If there is no direct converter, search transitively
+ if ( !conv ) {
+ conv2 = undefined;
+ for( conv1 in converters ) {
+ tmp = conv1.split( " " );
+ if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
+ conv2 = converters[ tmp[1] + " " + current ];
+ if ( conv2 ) {
+ conv1 = converters[ conv1 ];
+ if ( conv1 === true ) {
+ conv = conv2;
+ } else if ( conv2 === true ) {
+ conv = conv1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ // If we found no converter, dispatch an error
+ if ( !( conv || conv2 ) ) {
+ jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
+ }
+ // If found converter is not an equivalence
+ if ( conv !== true ) {
+ // Convert with 1 or 2 converters accordingly
+ response = conv ? conv( response ) : conv2( conv1(response) );
+ }
+ }
+ }
+ return response;
+}
+
+
+
+
+var jsc = jQuery.now(),
+ jsre = /(\=)\?(&|$)|\?\?/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ return jQuery.expando + "_" + ( jsc++ );
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var inspectData = s.contentType === "application/x-www-form-urlencoded" &&
+ ( typeof s.data === "string" );
+
+ if ( s.dataTypes[ 0 ] === "jsonp" ||
+ s.jsonp !== false && ( jsre.test( s.url ) ||
+ inspectData && jsre.test( s.data ) ) ) {
+
+ var responseContainer,
+ jsonpCallback = s.jsonpCallback =
+ jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+ previous = window[ jsonpCallback ],
+ url = s.url,
+ data = s.data,
+ replace = "$1" + jsonpCallback + "$2";
+
+ if ( s.jsonp !== false ) {
+ url = url.replace( jsre, replace );
+ if ( s.url === url ) {
+ if ( inspectData ) {
+ data = data.replace( jsre, replace );
+ }
+ if ( s.data === data ) {
+ // Add callback manually
+ url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
+ }
+ }
+ }
+
+ s.url = url;
+ s.data = data;
+
+ // Install callback
+ window[ jsonpCallback ] = function( response ) {
+ responseContainer = [ response ];
+ };
+
+ // Clean-up function
+ jqXHR.always(function() {
+ // Set callback back to previous value
+ window[ jsonpCallback ] = previous;
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( previous ) ) {
+ window[ jsonpCallback ]( responseContainer[ 0 ] );
+ }
+ });
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( jsonpCallback + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Delegate to script
+ return "script";
+ }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /javascript|ecmascript/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement( "script" );
+
+ script.async = "async";
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( 0, 1 );
+ }
+ }
+ };
+ }
+});
+
+
+
+
+var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject ? function() {
+ // Abort all pending requests
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( 0, 1 );
+ }
+ } : false,
+ xhrId = 0,
+ xhrCallbacks;
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+ jQuery.extend( jQuery.support, {
+ ajax: !!xhr,
+ cors: !!xhr && ( "withCredentials" in xhr )
+ });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var xhr = s.xhr(),
+ handle,
+ i;
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers[ "X-Requested-With" ] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( _ ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+
+ var status,
+ statusText,
+ responseHeaders,
+ responses,
+ xml;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occured
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+ responses = {};
+ xml = xhr.responseXML;
+
+ // Construct response list
+ if ( xml && xml.documentElement /* #4958 */ ) {
+ responses.xml = xml;
+ }
+ responses.text = xhr.responseText;
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ // if we're in sync mode or it's in cache
+ // and has been retrieved directly (IE6 & IE7)
+ // we need to manually fire the callback
+ if ( !s.async || xhr.readyState === 4 ) {
+ callback();
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback(0,1);
+ }
+ }
+ };
+ }
+ });
+}
+
+
+
+
+var elemdisplay = {},
+ iframe, iframeDoc,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ],
+ fxNow,
+ requestAnimationFrame = window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame;
+
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ var elem, display;
+
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("show", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ elem = this[i];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
+ display = elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
+ jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ elem = this[i];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ if ( display === "" || display === "none" ) {
+ elem.style.display = jQuery._data(elem, "olddisplay") || "";
+ }
+ }
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, easing, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ if ( this[i].style ) {
+ var display = jQuery.css( this[i], "display" );
+
+ if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
+ jQuery._data( this[i], "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ if ( this[i].style ) {
+ this[i].style.display = "none";
+ }
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2, callback);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete, [ false ] );
+ }
+
+ // Do not change referenced properties as per-property easing will be lost
+ prop = jQuery.extend( {}, prop );
+
+ return this[ optall.queue === false ? "each" : "queue" ](function() {
+ // XXX 'this' does not always have a nodeName when running the
+ // test suite
+
+ if ( optall.queue === false ) {
+ jQuery._mark( this );
+ }
+
+ var opt = jQuery.extend( {}, optall ),
+ isElement = this.nodeType === 1,
+ hidden = isElement && jQuery(this).is(":hidden"),
+ name, val, p,
+ display, e,
+ parts, start, end, unit;
+
+ // will store per property easing and be used to determine when an animation is complete
+ opt.animatedProperties = {};
+
+ for ( p in prop ) {
+
+ // property name normalization
+ name = jQuery.camelCase( p );
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ }
+
+ val = prop[ name ];
+
+ // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
+ if ( jQuery.isArray( val ) ) {
+ opt.animatedProperties[ name ] = val[ 1 ];
+ val = prop[ name ] = val[ 0 ];
+ } else {
+ opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
+ }
+
+ if ( val === "hide" && hidden || val === "show" && !hidden ) {
+ return opt.complete.call( this );
+ }
+
+ if ( isElement && ( name === "height" || name === "width" ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height
+ // animated
+ if ( jQuery.css( this, "display" ) === "inline" &&
+ jQuery.css( this, "float" ) === "none" ) {
+ if ( !jQuery.support.inlineBlockNeedsLayout ) {
+ this.style.display = "inline-block";
+
+ } else {
+ display = defaultDisplay( this.nodeName );
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( display === "inline" ) {
+ this.style.display = "inline-block";
+
+ } else {
+ this.style.display = "inline";
+ this.style.zoom = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ for ( p in prop ) {
+ e = new jQuery.fx( this, opt, p );
+ val = prop[ p ];
+
+ if ( rfxtypes.test(val) ) {
+ e[ val === "toggle" ? hidden ? "show" : "hide" : val ]();
+
+ } else {
+ parts = rfxnum.exec( val );
+ start = e.cur();
+
+ if ( parts ) {
+ end = parseFloat( parts[2] );
+ unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( this, p, (end || 1) + unit);
+ start = ((end || 1) / e.cur()) * start;
+ jQuery.style( this, p, start + unit);
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ }
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function( clearQueue, gotoEnd ) {
+ if ( clearQueue ) {
+ this.queue([]);
+ }
+
+ this.each(function() {
+ var timers = jQuery.timers,
+ i = timers.length;
+ // clear marker counters if we know they won't be
+ if ( !gotoEnd ) {
+ jQuery._unmark( true, this );
+ }
+ while ( i-- ) {
+ if ( timers[i].elem === this ) {
+ if (gotoEnd) {
+ // force the next step to be the last
+ timers[i](true);
+ }
+
+ timers.splice(i, 1);
+ }
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if ( !gotoEnd ) {
+ this.dequeue();
+ }
+
+ return this;
+ }
+
+});
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout( clearFxNow, 0 );
+ return ( fxNow = jQuery.now() );
+}
+
+function clearFxNow() {
+ fxNow = undefined;
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function( noUnmark ) {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue !== false ) {
+ jQuery.dequeue( this );
+ } else if ( noUnmark !== false ) {
+ jQuery._unmark( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ options.orig = options.orig || {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+ },
+
+ // Get the current size
+ cur: function() {
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var parsed,
+ r = jQuery.css( this.elem, this.prop );
+ // Empty strings, null, undefined and "auto" are converted to 0,
+ // complex values such as "rotate(1rad)" are returned as is,
+ // simple values such as "10px" are parsed to Float.
+ return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ var self = this,
+ fx = jQuery.fx,
+ raf;
+
+ this.startTime = fxNow || createFxNow();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ function t( gotoEnd ) {
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ // Use requestAnimationFrame instead of setInterval if available
+ if ( requestAnimationFrame ) {
+ timerId = true;
+ raf = function() {
+ // When timerId gets set to null at any point, this stops
+ if ( timerId ) {
+ requestAnimationFrame( raf );
+ fx.tick();
+ }
+ };
+ requestAnimationFrame( raf );
+ } else {
+ timerId = setInterval( fx.tick, fx.interval );
+ }
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var t = fxNow || createFxNow(),
+ done = true,
+ elem = this.elem,
+ options = this.options,
+ i, n;
+
+ if ( gotoEnd || t >= options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ options.animatedProperties[ this.prop ] = true;
+
+ for ( i in options.animatedProperties ) {
+ if ( options.animatedProperties[i] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ // Reset the overflow
+ if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+
+ jQuery.each( [ "", "X", "Y" ], function (index, value) {
+ elem.style[ "overflow" + value ] = options.overflow[index];
+ });
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( options.hide ) {
+ jQuery(elem).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( options.hide || options.show ) {
+ for ( var p in options.animatedProperties ) {
+ jQuery.style( elem, p, options.orig[p] );
+ }
+ }
+
+ // Execute the complete function
+ options.complete.call( elem );
+ }
+
+ return false;
+
+ } else {
+ // classical easing cannot be used with an Infinity duration
+ if ( options.duration == Infinity ) {
+ this.now = t;
+ } else {
+ n = t - this.startTime;
+ this.state = n / options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration );
+ this.now = this.start + ((this.end - this.start) * this.pos);
+ }
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) {
+ if ( !timers[i]() ) {
+ timers.splice(i--, 1);
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ interval: 13,
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style( fx.elem, "opacity", fx.now );
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+// Try to restore the default display value of an element
+function defaultDisplay( nodeName ) {
+
+ if ( !elemdisplay[ nodeName ] ) {
+
+ var body = document.body,
+ elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
+ display = elem.css( "display" );
+
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // No iframe to use yet, so create it
+ if ( !iframe ) {
+ iframe = document.createElement( "iframe" );
+ iframe.frameBorder = iframe.width = iframe.height = 0;
+ }
+
+ body.appendChild( iframe );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "<!doctype html>" : "" ) + "<html><body>" );
+ iframeDoc.close();
+ }
+
+ elem = iframeDoc.createElement( nodeName );
+
+ iframeDoc.body.appendChild( elem );
+
+ display = jQuery.css( elem, "display" );
+
+ body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return elemdisplay[ nodeName ];
+}
+
+
+
+
+var rtable = /^t(?:able|d|h)$/i,
+ rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0], box;
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ try {
+ box = elem.getBoundingClientRect();
+ } catch(e) {}
+
+ var doc = elem.ownerDocument,
+ docElem = doc.documentElement;
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !box || !jQuery.contains( docElem, elem ) ) {
+ return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+ }
+
+ var body = doc.body,
+ win = getWindow(doc),
+ clientTop = docElem.clientTop || body.clientTop || 0,
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
+ scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
+ top = box.top + scrollTop - clientTop,
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ jQuery.offset.initialize();
+
+ var computedStyle,
+ offsetParent = elem.offsetParent,
+ prevOffsetParent = elem,
+ doc = elem.ownerDocument,
+ docElem = doc.documentElement,
+ body = doc.body,
+ defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop,
+ left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent;
+ offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.offset = {
+ initialize: function() {
+ var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
+ html = "<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>";
+
+ jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+ container.innerHTML = html;
+ body.insertBefore( container, body.firstChild );
+ innerDiv = container.firstChild;
+ checkDiv = innerDiv.firstChild;
+ td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ checkDiv.style.position = "fixed";
+ checkDiv.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+ checkDiv.style.position = checkDiv.style.top = "";
+
+ innerDiv.style.overflow = "hidden";
+ innerDiv.style.position = "relative";
+
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+ body.removeChild( container );
+ jQuery.offset.initialize = jQuery.noop;
+ },
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ jQuery.offset.initialize();
+
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if (options.top != null) {
+ props.top = (options.top - curOffset.top) + curTop;
+ }
+ if (options.left != null) {
+ props.left = (options.left - curOffset.left) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+ var method = "scroll" + name;
+
+ jQuery.fn[ method ] = function( val ) {
+ var elem, win;
+
+ if ( val === undefined ) {
+ elem = this[ 0 ];
+
+ if ( !elem ) {
+ return null;
+ }
+
+ win = getWindow( elem );
+
+ // Return the scroll offset
+ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+
+ // Set the scroll offset
+ return this.each(function() {
+ win = getWindow( this );
+
+ if ( win ) {
+ win.scrollTo(
+ !i ? val : jQuery( win ).scrollLeft(),
+ i ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ this[ method ] = val;
+ }
+ });
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+
+
+
+// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+ var type = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn[ "inner" + name ] = function() {
+ var elem = this[0];
+ return elem && elem.style ?
+ parseFloat( jQuery.css( elem, type, "padding" ) ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn[ "outer" + name ] = function( margin ) {
+ var elem = this[0];
+ return elem && elem.style ?
+ parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ var elem = this[0];
+ if ( !elem ) {
+ return size == null ? null : this;
+ }
+
+ if ( jQuery.isFunction( size ) ) {
+ return this.each(function( i ) {
+ var self = jQuery( this );
+ self[ type ]( size.call( this, i, self[ type ]() ) );
+ });
+ }
+
+ if ( jQuery.isWindow( elem ) ) {
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
+ var docElemProp = elem.document.documentElement[ "client" + name ];
+ return elem.document.compatMode === "CSS1Compat" && docElemProp ||
+ elem.document.body[ "client" + name ] || docElemProp;
+
+ // Get document width or height
+ } else if ( elem.nodeType === 9 ) {
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ return Math.max(
+ elem.documentElement["client" + name],
+ elem.body["scroll" + name], elem.documentElement["scroll" + name],
+ elem.body["offset" + name], elem.documentElement["offset" + name]
+ );
+
+ // Get or set width or height on the element
+ } else if ( size === undefined ) {
+ var orig = jQuery.css( elem, type ),
+ ret = parseFloat( orig );
+
+ return jQuery.isNaN( ret ) ? orig : ret;
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ } else {
+ return this.css( type, typeof size === "string" ? size : size + "px" );
+ }
+ };
+
+});
+
+
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+})(window);
diff --git a/studio/static/js/jquery-ui-1.8.16.sugar.min.js b/studio/static/js/jquery-ui-1.8.16.sugar.min.js
new file mode 100644
index 0000000..14c9064
--- /dev/null
+++ b/studio/static/js/jquery-ui-1.8.16.sugar.min.js
@@ -0,0 +1,791 @@
+/*!
+ * jQuery UI 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.16",
+keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({propAttr:c.fn.prop||c.fn.attr,_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=
+this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,
+"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":
+"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,
+outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,
+"tabindex"),d=isNaN(b);return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&
+a.element[0].parentNode)for(var e=0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&
+c.ui.isOverAxis(b,e,i)}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)try{b(d).triggerHandler("remove")}catch(e){}k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(d){}});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=
+function(h){return!!b.data(h,a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):
+d;if(e&&d.charAt(0)==="_")return h;e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=
+b.extend(true,{},this.options,this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+
+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",
+c);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*!
+ * jQuery UI Mouse 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */
+(function(b){var d=false;b(document).mouseup(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
+this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"&&a.target.nodeName?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=
+this._mouseStart(a)!==false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
+!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
+false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Position 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
+left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
+k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
+m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
+d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
+a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
+g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
+;/*
+ * jQuery UI Draggable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
+"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
+this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;if(b.iframeFix)d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;
+this.helper=this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
+this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
+_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
+false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
+10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
+!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
+a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
+this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
+10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
+10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
+(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
+"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
+10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
+!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
+if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
+b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
+526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
+c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.16"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
+h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
+false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
+this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
+c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
+this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
+a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
+"x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
+c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
+c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
+width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
+o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
+p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
+(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
+10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
+;/*
+ * jQuery UI Droppable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */
+(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
+a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
+this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
+this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
+d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
+a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.16"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
+switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
+i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
+"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
+a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parents(":not(body,html)").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);
+if(c=!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parents(":not(body,html)").unbind("scroll.droppable");
+a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
+;/*
+ * jQuery UI Resizable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.resizable",e.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1E3},_create:function(){var b=this,a=this.options;this.element.addClass("ui-resizable");e.extend(this,{_aspectRatio:!!a.aspectRatio,aspectRatio:a.aspectRatio,originalElement:this.element,
+_proportionallyResizeElements:[],_helper:a.helper||a.ghost||a.animate?a.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){/relative/.test(this.element.css("position"))&&e.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"});this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
+top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
+this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
+nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d<c.length;d++){var f=e.trim(c[d]),g=e('<div class="ui-resizable-handle '+("ui-resizable-"+f)+'"></div>');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor==
+String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection();
+this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();
+var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=
+false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});
+this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff=
+{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];
+if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},
+_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,
+{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight:
+Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(c<a.maxWidth)a.maxWidth=c;if(f<a.maxHeight)a.maxHeight=f}this._vBoundaries=a},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=
+b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(k(b.height))b.width=b.height*this.aspectRatio;else if(k(b.width))b.height=b.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this._vBoundaries,c=this.axis,d=k(b.width)&&a.maxWidth&&a.maxWidth<b.width,f=k(b.height)&&a.maxHeight&&a.maxHeight<b.height,g=k(b.width)&&a.minWidth&&
+a.minWidth>b.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=
+null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a<this._proportionallyResizeElements.length;a++){var c=this._proportionallyResizeElements[a];if(!this.borderDif){var d=[c.css("borderTopWidth"),c.css("borderRightWidth"),c.css("borderBottomWidth"),c.css("borderLeftWidth")],f=[c.css("paddingTop"),c.css("paddingRight"),c.css("paddingBottom"),c.css("paddingLeft")];this.borderDif=e.map(d,function(g,h){g=parseInt(g,10)||
+0;h=parseInt(f[h],10)||0;return g+h})}e.browser.msie&&(e(b).is(":hidden")||e(b).parents(":hidden").length)||c.css({height:b.height()-this.borderDif[0]-this.borderDif[2]||0,width:b.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var b=this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+
+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+
+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);
+b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.16"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),
+10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-
+f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?
+e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=
+e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,
+step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=
+e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;
+var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:
+a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-
+d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,
+f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,
+display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=
+e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=
+d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery);
+;/*
+ * jQuery UI Selectable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"),
+selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX,
+c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting",
+c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d=
+this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.right<b||a.top>i||a.bottom<g);else if(d.tolerance=="fit")k=a.left>b&&a.right<h&&a.top>g&&a.bottom<i;if(k){if(a.selected){a.$element.removeClass("ui-selected");a.selected=false}if(a.unselecting){a.$element.removeClass("ui-unselecting");
+a.unselecting=false}if(!a.selecting){a.$element.addClass("ui-selecting");a.selecting=true;f._trigger("selecting",c,{selecting:a.element})}}else{if(a.selecting)if(c.metaKey&&a.startselected){a.$element.removeClass("ui-selecting");a.selecting=false;a.$element.addClass("ui-selected");a.selected=true}else{a.$element.removeClass("ui-selecting");a.selecting=false;if(a.startselected){a.$element.addClass("ui-unselecting");a.unselecting=true}f._trigger("unselecting",c,{unselecting:a.element})}if(a.selected)if(!c.metaKey&&
+!a.startselected){a.$element.removeClass("ui-selected");a.selected=false;a.$element.addClass("ui-unselecting");a.unselecting=true;f._trigger("unselecting",c,{unselecting:a.element})}}}});return false}},_mouseStop:function(c){var f=this;this.dragged=false;e(".ui-unselecting",this.element[0]).each(function(){var d=e.data(this,"selectable-item");d.$element.removeClass("ui-unselecting");d.unselecting=false;d.startselected=false;f._trigger("unselected",c,{unselected:d.element})});e(".ui-selecting",this.element[0]).each(function(){var d=
+e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Sortable 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable");
+this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a===
+"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&
+!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,
+left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};
+this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=
+document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);
+return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<
+b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-
+b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,
+a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],
+e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();
+c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):
+this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,
+dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},
+toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||
+this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();
+var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},
+_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();
+if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),
+this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),
+this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&
+this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=
+this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=
+d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||
+0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",
+a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-
+f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=
+this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==
+""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=
+this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a=
+{top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),
+10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?
+document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),
+10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=
+this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&
+this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();
+var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])f=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-
+this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-
+this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],
+this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]=
+"";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",
+f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,
+this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",
+a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},
+_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Accordion 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");
+a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
+if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",
+function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=
+this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex");
+this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();
+b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);
+a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+
+c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;
+if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);
+if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(),
+e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight||
+e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",
+"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.16",
+animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);
+f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",
+paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);
+;/*
+ * jQuery UI Autocomplete 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */
+(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.propAttr("readOnly"))){g=
+false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=
+a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};
+this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&
+a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");
+d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&
+b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=
+this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();
+this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||
+b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position));this.options.autoFocus&&this.menu.next(new d.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;
+d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,
+"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
+(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
+-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");
+this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,
+this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
+this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
+this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[d.fn.prop?"prop":"attr"]("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
+;/*
+ * jQuery UI Button 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(b){var h,i,j,g,l=function(){var a=b(this).find(":ui-button");setTimeout(function(){a.button("refresh")},1)},k=function(a){var c=a.name,e=a.form,f=b([]);if(c)f=e?b(e).find("[name='"+c+"']"):b("[name='"+c+"']",a.ownerDocument).filter(function(){return!this.form});return f};b.widget("ui.button",{options:{disabled:null,text:true,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",l);if(typeof this.options.disabled!==
+"boolean")this.options.disabled=this.element.propAttr("disabled");this._determineButtonType();this.hasTitle=!!this.buttonElement.attr("title");var a=this,c=this.options,e=this.type==="checkbox"||this.type==="radio",f="ui-state-hover"+(!e?" ui-state-active":"");if(c.label===null)c.label=this.buttonElement.html();if(this.element.is(":disabled"))c.disabled=true;this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role","button").bind("mouseenter.button",function(){if(!c.disabled){b(this).addClass("ui-state-hover");
+this===h&&b(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||b(this).removeClass(f)}).bind("click.button",function(d){if(c.disabled){d.preventDefault();d.stopImmediatePropagation()}});this.element.bind("focus.button",function(){a.buttonElement.addClass("ui-state-focus")}).bind("blur.button",function(){a.buttonElement.removeClass("ui-state-focus")});if(e){this.element.bind("change.button",function(){g||a.refresh()});this.buttonElement.bind("mousedown.button",function(d){if(!c.disabled){g=
+false;i=d.pageX;j=d.pageY}}).bind("mouseup.button",function(d){if(!c.disabled)if(i!==d.pageX||j!==d.pageY)g=true})}if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).toggleClass("ui-state-active");a.buttonElement.attr("aria-pressed",a.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).addClass("ui-state-active");a.buttonElement.attr("aria-pressed","true");
+var d=a.element[0];k(d).not(d).map(function(){return b(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;b(this).addClass("ui-state-active");h=this;b(document).one("mouseup",function(){h=null})}).bind("mouseup.button",function(){if(c.disabled)return false;b(this).removeClass("ui-state-active")}).bind("keydown.button",function(d){if(c.disabled)return false;if(d.keyCode==b.ui.keyCode.SPACE||
+d.keyCode==b.ui.keyCode.ENTER)b(this).addClass("ui-state-active")}).bind("keyup.button",function(){b(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(d){d.keyCode===b.ui.keyCode.SPACE&&b(this).click()})}this._setOption("disabled",c.disabled);this._resetButton()},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type===
+"radio"){var a=this.element.parents().filter(":last"),c="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(c);if(!this.buttonElement.length){a=a.length?a.siblings():this.element.siblings();this.buttonElement=a.filter(c);if(!this.buttonElement.length)this.buttonElement=a.find(c)}this.element.addClass("ui-helper-hidden-accessible");(a=this.element.is(":checked"))&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",a)}else this.buttonElement=this.element},
+widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle||this.buttonElement.removeAttr("title");
+b.Widget.prototype.destroy.call(this)},_setOption:function(a,c){b.Widget.prototype._setOption.apply(this,arguments);if(a==="disabled")c?this.element.propAttr("disabled",true):this.element.propAttr("disabled",false);else this._resetButton()},refresh:function(){var a=this.element.is(":disabled");a!==this.options.disabled&&this._setOption("disabled",a);if(this.type==="radio")k(this.element[0]).each(function(){b(this).is(":checked")?b(this).button("widget").addClass("ui-state-active").attr("aria-pressed",
+"true"):b(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false")},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var a=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
+c=b("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("<span class='ui-button-icon-primary ui-icon "+e.primary+"'></span>");e.secondary&&a.append("<span class='ui-button-icon-secondary ui-icon "+e.secondary+"'></span>");if(!this.options.text){d.push(f?"ui-button-icons-only":
+"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")===
+"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");
+b.Widget.prototype.destroy.call(this)}})})(jQuery);
+;/*
+ * jQuery UI Dialog 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.button.js
+ * jquery.ui.draggable.js
+ * jquery.ui.mouse.js
+ * jquery.ui.position.js
+ * jquery.ui.resizable.js
+ */
+(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,
+position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||"&#160;",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("<div></div>")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+
+b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&!i.isDefaultPrevented()&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),
+h=c('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("<span></span>")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("<span></span>").addClass("ui-dialog-title").attr("id",
+e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");
+a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==
+b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()};c.ui.dialog.maxZ+=1;
+d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target===
+f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,
+function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('<button type="button"></button>').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",
+handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,
+originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",
+f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):
+[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f);
+if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):
+e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||"&#160;"));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a=
+this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height-
+b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.16",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),
+create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()<c.ui.dialog.overlay.maxZ)return false})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&!d.isDefaultPrevented()&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||
+c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&
+c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a<b?c(window).height()+"px":a+"px"}else return c(document).height()+"px"},width:function(){var a,b;if(c.browser.msie){a=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);b=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return a<b?c(window).width()+"px":a+"px"}else return c(document).width()+
+"px"},resize:function(){var a=c([]);c.each(c.ui.dialog.overlay.instances,function(){a=a.add(this)});a.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);
+;/*
+ * jQuery UI Slider 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var a=this,b=this.options,c=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f=b.values&&b.values.length||1,e=[];this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+
+this.orientation+" ui-widget ui-widget-content ui-corner-all"+(b.disabled?" ui-slider-disabled ui-disabled":""));this.range=d([]);if(b.range){if(b.range===true){if(!b.values)b.values=[this._valueMin(),this._valueMin()];if(b.values.length&&b.values.length!==2)b.values=[b.values[0],b.values[0]]}this.range=d("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(b.range==="min"||b.range==="max"?" ui-slider-range-"+b.range:""))}for(var j=c.length;j<f;j+=1)e.push("<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>");
+this.handles=c.add(d(e.join("")).appendTo(a.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){b.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(b.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle",
+g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!a.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!a._keySliding){a._keySliding=true;d(this).addClass("ui-state-active");i=a._start(g,l);if(i===false)return}break}m=a.options.step;i=a.options.values&&a.options.values.length?
+(h=a.values(l)):(h=a.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=a._valueMin();break;case d.ui.keyCode.END:h=a._valueMax();break;case d.ui.keyCode.PAGE_UP:h=a._trimAlignValue(i+(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=a._trimAlignValue(i-(a._valueMax()-a._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===a._valueMax())return;h=a._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===a._valueMin())return;h=a._trimAlignValue(i-
+m);break}a._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(a._keySliding){a._keySliding=false;a._stop(g,k);a._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy();
+return this},_mouseCapture:function(a){var b=this.options,c,f,e,j,g;if(b.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:a.pageX,y:a.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(b.range===true&&this.values(1)===b.min){g+=1;e=d(this.handles[g])}if(this._start(a,g)===false)return false;
+this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();b=e.offset();this._clickOffset=!d(a.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:a.pageX-b.left-e.width()/2,top:a.pageY-b.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(a,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(a){var b=
+this._normValueFromMouse({x:a.pageX,y:a.pageY});this._slide(a,this._handleIndex,b);return false},_mouseStop:function(a){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(a,this._handleIndex);this._change(a,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b;if(this.orientation==="horizontal"){b=
+this.elementSize.width;a=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{b=this.elementSize.height;a=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}b=a/b;if(b>1)b=1;if(b<0)b=0;if(this.orientation==="vertical")b=1-b;a=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+b*a)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);
+c.values=this.values()}return this._trigger("start",a,c)},_slide:function(a,b,c){var f;if(this.options.values&&this.options.values.length){f=this.values(b?0:1);if(this.options.values.length===2&&this.options.range===true&&(b===0&&c>f||b===1&&c<f))c=f;if(c!==this.values(b)){f=this.values();f[b]=c;a=this._trigger("slide",a,{handle:this.handles[b],value:c,values:f});this.values(b?0:1);a!==false&&this.values(b,c,true)}}else if(c!==this.value()){a=this._trigger("slide",a,{handle:this.handles[b],value:c});
+a!==false&&this.value(c)}},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(b);c.values=this.values()}this._trigger("change",a,c)}},value:function(a){if(arguments.length){this.options.value=
+this._trimAlignValue(a);this._refreshValue();this._change(null,0)}else return this._value()},values:function(a,b){var c,f,e;if(arguments.length>1){this.options.values[a]=this._trimAlignValue(b);this._refreshValue();this._change(null,a)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e<c.length;e+=1){c[e]=this._trimAlignValue(f[e]);this._change(null,e)}this._refreshValue()}else return this.options.values&&this.options.values.length?this._values(a):
+this.value();else return this._values()},_setOption:function(a,b){var c,f=0;if(d.isArray(this.options.values))f=this.options.values.length;d.Widget.prototype._setOption.apply(this,arguments);switch(a){case "disabled":if(b){this.handles.filter(".ui-state-focus").blur();this.handles.removeClass("ui-state-hover");this.handles.propAttr("disabled",true);this.element.addClass("ui-disabled")}else{this.handles.propAttr("disabled",false);this.element.removeClass("ui-disabled")}break;case "orientation":this._detectOrientation();
+this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case "value":this._animateOff=true;this._refreshValue();this._change(null,0);this._animateOff=false;break;case "values":this._animateOff=true;this._refreshValue();for(c=0;c<f;c+=1)this._change(null,c);this._animateOff=false;break}},_value:function(){var a=this.options.value;return a=this._trimAlignValue(a)},_values:function(a){var b,c;if(arguments.length){b=this.options.values[a];
+return b=this._trimAlignValue(b)}else{b=this.options.values.slice();for(c=0;c<b.length;c+=1)b[c]=this._trimAlignValue(b[c]);return b}},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b;a=a-c;if(Math.abs(c)*2>=b)a+=c>0?b:-b;return parseFloat(a.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var a=
+this.options.range,b=this.options,c=this,f=!this._animateOff?b.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,b.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({width:e-
+g+"%"},{queue:false,duration:b.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},b.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:b.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,b.animate);if(a==="min"&&this.orientation==="horizontal")this.range.stop(1,1)[f?"animate":"css"]({width:e+"%"},
+b.animate);if(a==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:b.animate});if(a==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},b.animate);if(a==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:b.animate})}}});d.extend(d.ui.slider,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Tabs 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&&
+e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=
+d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]||
+(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
+this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected=
+this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");
+if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));
+this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+
+g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",
+function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};
+this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=
+-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";
+d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=
+d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b,
+e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);
+j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();
+if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this.anchors.length?1:-1));e.disabled=d.map(d.grep(e.disabled,function(h){return h!=b}),function(h){return h>=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null,
+this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this},
+load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c,
+"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},
+url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.16"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&&
+a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery);
+;/*
+ * jQuery UI Datepicker 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ * jquery.ui.core.js
+ */
+(function(d,C){function M(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass=
+"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su",
+"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",
+minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false,disabled:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=N(d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}function N(a){return a.bind("mouseout",
+function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
+b.addClass("ui-state-hover");b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.16"}});var B=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},
+setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,
+"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",
+function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b);b.settings.disabled&&this._disableDatepicker(a)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c==
+"focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f==""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():
+d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,
+b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.settings.disabled&&this._disableDatepicker(a);b.dpDiv.css("display","block")}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=
+1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/
+2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=
+d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=
+a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,
+"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==
+a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?
+d.extend({},e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true),i=this._getMinMaxDate(e,"min"),g=this._getMinMaxDate(e,"max");H(e.settings,f);if(i!==null&&f.dateFormat!==C&&f.minDate===C)e.settings.minDate=this._formatDate(e,i);if(g!==null&&f.dateFormat!==C&&f.maxDate===C)e.settings.maxDate=this._formatDate(e,g);this._attachments(d(a),e);this._autoSize(e);this._setDate(e,h);this._updateAlternate(e);
+this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,b);this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");
+b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);c[0]&&d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]);if(a=d.datepicker._get(b,"onSelect")){c=d.datepicker._formatDate(b);a.apply(b.input?b.input[0]:null,[c,b])}else d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,
+a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);c=a.ctrlKey||a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=
+a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,
+"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=d.datepicker._getInst(a.target);if(d.datepicker._get(b,"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==C?a.keyCode:a.charCode);
+return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",
+a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=d.datepicker._get(b,"beforeShow");c=c?c.apply(a,[a,b]):{};if(c!==false){H(b.settings,c);b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value=
+"";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);
+c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing=
+true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});
+a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&
+!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),
+h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=
+this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);
+this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},
+_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):
+0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e["selected"+(c=="M"?
+"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);
+this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");
+if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?
+b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1<a.length&&a.charAt(A+1)==p)&&A++;return p},m=function(p){var D=
+o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&D?4:p=="o"?3:2)+"}");p=b.substring(q).match(p);if(!p)throw"Missing number at position "+q;q+=p[0].length;return parseInt(p[0],10)},n=function(p,D,K){p=d.map(o(p)?K:D,function(w,x){return[[x,w]]}).sort(function(w,x){return-(w[1].length-x[1].length)});var E=-1;d.each(p,function(w,x){w=x[1];if(b.substr(q,w.length).toLowerCase()==w.toLowerCase()){E=x[0];q+=w.length;return false}});if(E!=-1)return E+1;else throw"Unknown name at position "+q;},s=
+function(){if(b.charAt(q)!=a.charAt(A))throw"Unexpected literal at position "+q;q++},q=0,A=0;A<a.length;A++)if(k)if(a.charAt(A)=="'"&&!o("'"))k=false;else s();else switch(a.charAt(A)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":j=m("m");break;case "M":j=n("M",i,g);break;case "y":c=m("y");break;case "@":var v=new Date(m("@"));c=v.getFullYear();j=v.getMonth()+1;l=v.getDate();break;case "!":v=new Date((m("!")-this._ticksTo1970)/1E4);c=v.getFullYear();j=v.getMonth()+
+1;l=v.getDate();break;case "'":if(o("'"))s();else k=true;break;default:s()}if(q<b.length)throw"Extra/unparsed characters found in date: "+b.substring(q);if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",
+COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:
+null)||this._defaults.monthNames;var i=function(o){(o=k+1<a.length&&a.charAt(k+1)==o)&&k++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<n;)m="0"+m;return m},j=function(o,m,n,s){return i(o)?s[m]:n[m]},l="",u=false;if(b)for(var k=0;k<a.length;k++)if(u)if(a.charAt(k)=="'"&&!i("'"))u=false;else l+=a.charAt(k);else switch(a.charAt(k)){case "d":l+=g("d",b.getDate(),2);break;case "D":l+=j("D",b.getDay(),e,f);break;case "o":l+=g("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-
+(new Date(b.getFullYear(),0,0)).getTime())/864E5),3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=j("M",b.getMonth(),h,c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(k)}return l},_possibleChars:function(a){for(var b="",c=false,e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=
+0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+="0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==C?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);
+var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth=f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var e=function(h){var i=new Date;
+i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g=(h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,j=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,k=u.exec(h);k;){switch(k[2]||"d"){case "d":case "D":g+=parseInt(k[1],10);break;case "w":case "W":g+=parseInt(k[1],10)*7;break;case "m":case "M":l+=parseInt(k[1],10);g=
+Math.min(g,d.datepicker._getDaysInMonth(j,l));break;case "y":case "Y":j+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break}k=u.exec(h)}return new Date(j,l,g)};if(b=(b=b==null||b===""?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):new Date(b.getTime()))&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>
+12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&
+a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?
+new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&n<k?k:n;this._daylightSavingAdjust(new Date(m,g,1))>n;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));
+n=this._canAdjustMonth(a,-1,m,g)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', -"+j+", 'M');\" title=\""+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m,
+g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+B+".datepicker._adjustDate('#"+a.id+"', +"+j+", 'M');\" title=\""+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&&
+a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+B+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,s)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+
+B+".datepicker._gotoToday('#"+a.id+"');\">"+j+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x<i[0];x++){var O=
+"";this.maxRows=4;for(var G=0;G<i[1];G++){var P=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",y="";if(l){y+='<div class="ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&
+x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var z=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(t=0;t<7;t++){var r=(t+h)%7;z+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+s[r]+'">'+q[r]+"</span></th>"}y+=z+"</tr></thead><tbody>";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,
+z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q<z;Q++){y+="<tr>";var R=!j?"":'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(r)+"</td>";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&r<k||o&&r>o;R+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(F?" ui-datepicker-other-month":"")+(r.getTime()==
+P.getTime()&&g==a.selectedMonth&&a._keyEvent||E.getTime()==r.getTime()&&E.getTime()==P.getTime()?" "+this._dayOverClass:"")+(L?" "+this._unselectableClass+" ui-state-disabled":"")+(F&&!D?"":" "+I[1]+(r.getTime()==u.getTime()?" "+this._currentClass:"")+(r.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!F||D)&&I[2]?' title="'+I[2]+'"':"")+(L?"":' onclick="DP_jQuery_'+B+".datepicker._selectDay('#"+a.id+"',"+r.getMonth()+","+r.getFullYear()+', this);return false;"')+">"+(F&&!D?"&#xa0;":L?'<span class="ui-state-default">'+
+r.getDate()+"</span>":'<a class="ui-state-default'+(r.getTime()==b.getTime()?" ui-state-highlight":"")+(r.getTime()==u.getTime()?" ui-state-active":"")+(F?" ui-priority-secondary":"")+'" href="#">'+r.getDate()+"</a>")+"</td>";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+"</tr>"}g++;if(g>11){g=0;m++}y+="</tbody></table>"+(l?"</div>"+(i[0]>0&&G==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':
+"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">',o="";if(h||!j)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" >";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&
+(!m||n<=f.getMonth()))o+='<option value="'+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(k+=o+(h||!(j&&l)?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,
+e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+B+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" >";b<=g;b++)a.yearshtml+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";a.yearshtml+="</select>";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?"&#xa0;":"")+o;k+="</div>";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+
+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&b<c?c:b;return b=a&&b>a?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input?
+a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c,
+e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,
+"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this;
+if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a==
+"string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.16";window["DP_jQuery_"+B]=d})(jQuery);
+;/*
+ * jQuery UI Progressbar 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow");
+this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*
+this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.16"})})(jQuery);
+;/*
+ * jQuery UI Effects 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */
+jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
+16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
+a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
+a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor",
+"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,
+0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,
+211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,
+d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})};
+f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,
+[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.16",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=
+0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),
+d=document.activeElement;c.wrap(b);if(c[0]===d||f.contains(c[0],d))f(d).focus();b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(e,g){a[g]=c.css(g);if(isNaN(parseInt(a[g],10)))a[g]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){var a,b=document.activeElement;
+if(c.parent().is(".ui-effects-wrapper")){a=c.parent().replaceWith(c);if(c[0]===b||f.contains(c[0],b))f(b).focus();return a}return c},setTransition:function(c,a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});
+return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,
+arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%","pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/
+2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,
+d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,
+a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,
+d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=
+0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;
+if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,
+a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
+;/*
+ * jQuery UI Effects Blind 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","bottom","left","right"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a,
+g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Bounce 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","bottom","left","right"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/
+3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a);
+b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Clip 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position,
+c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Drop 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e==
+"show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Explode 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(j){j.effects.explode=function(a){return this.queue(function(){var c=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3,d=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3;a.options.mode=a.options.mode=="toggle"?j(this).is(":visible")?"hide":"show":a.options.mode;var b=j(this).show().css("visibility","hidden"),g=b.offset();g.top-=parseInt(b.css("marginTop"),10)||0;g.left-=parseInt(b.css("marginLeft"),10)||0;for(var h=b.outerWidth(true),i=b.outerHeight(true),e=0;e<c;e++)for(var f=
+0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+
+e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery);
+;/*
+ * jQuery UI Effects Fade 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Fold 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],
+10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Highlight 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&&
+this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Pulsate 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
+a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Scale 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.puff=function(b){return this.queue(function(){var a=c(this),e=c.effects.setMode(a,b.options.mode||"hide"),g=parseInt(b.options.percent,10)||150,h=g/100,i={height:a.height(),width:a.width()};c.extend(b.options,{fade:true,mode:e,percent:e=="hide"?g:100,from:e=="hide"?i:{height:i.height*h,width:i.width*h}});a.effect("scale",b.options,b.duration,b.callback);a.dequeue()})};c.effects.scale=function(b){return this.queue(function(){var a=c(this),e=c.extend(true,{},b.options),g=c.effects.setMode(a,
+b.options.mode||"effect"),h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:g=="hide"?0:100),i=b.options.direction||"both",f=b.options.origin;if(g!="effect"){e.origin=f||["middle","center"];e.restore=true}f={height:a.height(),width:a.width()};a.from=b.options.from||(g=="show"?{height:0,width:0}:f);h={y:i!="horizontal"?h/100:1,x:i!="vertical"?h/100:1};a.to={height:f.height*h.y,width:f.width*h.x};if(b.options.fade){if(g=="show"){a.from.opacity=0;a.to.opacity=1}if(g=="hide"){a.from.opacity=
+1;a.to.opacity=0}}e.from=a.from;e.to=a.to;e.mode=g;a.effect("size",e,b.duration,b.callback);a.dequeue()})};c.effects.size=function(b){return this.queue(function(){var a=c(this),e=["position","top","bottom","left","right","width","height","overflow","opacity"],g=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],i=["fontSize"],f=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],k=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],
+p=c.effects.setMode(a,b.options.mode||"effect"),n=b.options.restore||false,m=b.options.scale||"both",l=b.options.origin,j={height:a.height(),width:a.width()};a.from=b.options.from||j;a.to=b.options.to||j;if(l){l=c.effects.getBaseline(l,j);a.from.top=(j.height-a.from.height)*l.y;a.from.left=(j.width-a.from.width)*l.x;a.to.top=(j.height-a.to.height)*l.y;a.to.left=(j.width-a.to.width)*l.x}var d={from:{y:a.from.height/j.height,x:a.from.width/j.width},to:{y:a.to.height/j.height,x:a.to.width/j.width}};
+if(m=="box"||m=="both"){if(d.from.y!=d.to.y){e=e.concat(f);a.from=c.effects.setTransition(a,f,d.from.y,a.from);a.to=c.effects.setTransition(a,f,d.to.y,a.to)}if(d.from.x!=d.to.x){e=e.concat(k);a.from=c.effects.setTransition(a,k,d.from.x,a.from);a.to=c.effects.setTransition(a,k,d.to.x,a.to)}}if(m=="content"||m=="both")if(d.from.y!=d.to.y){e=e.concat(i);a.from=c.effects.setTransition(a,i,d.from.y,a.from);a.to=c.effects.setTransition(a,i,d.to.y,a.to)}c.effects.save(a,n?e:g);a.show();c.effects.createWrapper(a);
+a.css("overflow","hidden").css(a.from);if(m=="content"||m=="both"){f=f.concat(["marginTop","marginBottom"]).concat(i);k=k.concat(["marginLeft","marginRight"]);h=e.concat(f).concat(k);a.find("*[width]").each(function(){child=c(this);n&&c.effects.save(child,h);var o={height:child.height(),width:child.width()};child.from={height:o.height*d.from.y,width:o.width*d.from.x};child.to={height:o.height*d.to.y,width:o.width*d.to.x};if(d.from.y!=d.to.y){child.from=c.effects.setTransition(child,f,d.from.y,child.from);
+child.to=c.effects.setTransition(child,f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a,
+n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Shake 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","bottom","left","right"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]=
+(h=="pos"?"-=":"+=")+e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Slide 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e);
+var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Transfer 1.8.16
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function(e){e.effects.transfer=function(a){return this.queue(function(){var b=e(this),c=e(a.options.to),d=c.offset();c={top:d.top,left:d.left,height:c.innerHeight(),width:c.innerWidth()};d=b.offset();var f=e('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments);
+b.dequeue()})})}})(jQuery);
+; \ No newline at end of file
diff --git a/studio/static/js/jquery.corner.js b/studio/static/js/jquery.corner.js
new file mode 100644
index 0000000..c416613
--- /dev/null
+++ b/studio/static/js/jquery.corner.js
@@ -0,0 +1,249 @@
+/*!
+ * jQuery corner plugin: simple corner rounding
+ * Examples and documentation at: http://jquery.malsup.com/corner/
+ * version 2.12 (23-MAY-2011)
+ * Requires jQuery v1.3.2 or later
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ * Authors: Dave Methvin and Mike Alsup
+ */
+
+/**
+ * corner() takes a single string argument: $('#myDiv').corner("effect corners width")
+ *
+ * effect: name of the effect to apply, such as round, bevel, notch, bite, etc (default is round).
+ * corners: one or more of: top, bottom, tr, tl, br, or bl. (default is all corners)
+ * width: width of the effect; in the case of rounded corners this is the radius.
+ * specify this value using the px suffix such as 10px (yes, it must be pixels).
+ */
+;(function($) {
+
+var style = document.createElement('div').style,
+ moz = style['MozBorderRadius'] !== undefined,
+ webkit = style['WebkitBorderRadius'] !== undefined,
+ radius = style['borderRadius'] !== undefined || style['BorderRadius'] !== undefined,
+ mode = document.documentMode || 0,
+ noBottomFold = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8),
+
+ expr = $.browser.msie && (function() {
+ var div = document.createElement('div');
+ try { div.style.setExpression('width','0+0'); div.style.removeExpression('width'); }
+ catch(e) { return false; }
+ return true;
+ })();
+
+$.support = $.support || {};
+$.support.borderRadius = moz || webkit || radius; // so you can do: if (!$.support.borderRadius) $('#myDiv').corner();
+
+function sz(el, p) {
+ return parseInt($.css(el,p))||0;
+};
+function hex2(s) {
+ s = parseInt(s).toString(16);
+ return ( s.length < 2 ) ? '0'+s : s;
+};
+function gpc(node) {
+ while(node) {
+ var v = $.css(node,'backgroundColor'), rgb;
+ if (v && v != 'transparent' && v != 'rgba(0, 0, 0, 0)') {
+ if (v.indexOf('rgb') >= 0) {
+ rgb = v.match(/\d+/g);
+ return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
+ }
+ return v;
+ }
+ if (node.nodeName.toLowerCase() == 'html')
+ break;
+ node = node.parentNode; // keep walking if transparent
+ }
+ return '#ffffff';
+};
+
+function getWidth(fx, i, width) {
+ switch(fx) {
+ case 'round': return Math.round(width*(1-Math.cos(Math.asin(i/width))));
+ case 'cool': return Math.round(width*(1+Math.cos(Math.asin(i/width))));
+ case 'sharp': return width-i;
+ case 'bite': return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));
+ case 'slide': return Math.round(width*(Math.atan2(i,width/i)));
+ case 'jut': return Math.round(width*(Math.atan2(width,(width-i-1))));
+ case 'curl': return Math.round(width*(Math.atan(i)));
+ case 'tear': return Math.round(width*(Math.cos(i)));
+ case 'wicked': return Math.round(width*(Math.tan(i)));
+ case 'long': return Math.round(width*(Math.sqrt(i)));
+ case 'sculpt': return Math.round(width*(Math.log((width-i-1),width)));
+ case 'dogfold':
+ case 'dog': return (i&1) ? (i+1) : width;
+ case 'dog2': return (i&2) ? (i+1) : width;
+ case 'dog3': return (i&3) ? (i+1) : width;
+ case 'fray': return (i%2)*width;
+ case 'notch': return width;
+ case 'bevelfold':
+ case 'bevel': return i+1;
+ case 'steep': return i/2 + 1;
+ case 'invsteep':return (width-i)/2+1;
+ }
+};
+
+$.fn.corner = function(options) {
+ // in 1.3+ we can fix mistakes with the ready state
+ if (this.length == 0) {
+ if (!$.isReady && this.selector) {
+ var s = this.selector, c = this.context;
+ $(function() {
+ $(s,c).corner(options);
+ });
+ }
+ return this;
+ }
+
+ return this.each(function(index){
+ var $this = $(this),
+ // meta values override options
+ o = [$this.attr($.fn.corner.defaults.metaAttr) || '', options || ''].join(' ').toLowerCase(),
+ keep = /keep/.test(o), // keep borders?
+ cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]), // corner color
+ sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]), // strip color
+ width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10, // corner width
+ re = /round|bevelfold|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dogfold|dog|invsteep|steep/,
+ fx = ((o.match(re)||['round'])[0]),
+ fold = /dogfold|bevelfold/.test(o),
+ edges = { T:0, B:1 },
+ opts = {
+ TL: /top|tl|left/.test(o), TR: /top|tr|right/.test(o),
+ BL: /bottom|bl|left/.test(o), BR: /bottom|br|right/.test(o)
+ },
+ // vars used in func later
+ strip, pad, cssHeight, j, bot, d, ds, bw, i, w, e, c, common, $horz;
+
+ if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR )
+ opts = { TL:1, TR:1, BL:1, BR:1 };
+
+ // support native rounding
+ if ($.fn.corner.defaults.useNative && fx == 'round' && (radius || moz || webkit) && !cc && !sc) {
+ if (opts.TL)
+ $this.css(radius ? 'border-top-left-radius' : moz ? '-moz-border-radius-topleft' : '-webkit-border-top-left-radius', width + 'px');
+ if (opts.TR)
+ $this.css(radius ? 'border-top-right-radius' : moz ? '-moz-border-radius-topright' : '-webkit-border-top-right-radius', width + 'px');
+ if (opts.BL)
+ $this.css(radius ? 'border-bottom-left-radius' : moz ? '-moz-border-radius-bottomleft' : '-webkit-border-bottom-left-radius', width + 'px');
+ if (opts.BR)
+ $this.css(radius ? 'border-bottom-right-radius' : moz ? '-moz-border-radius-bottomright' : '-webkit-border-bottom-right-radius', width + 'px');
+ return;
+ }
+
+ strip = document.createElement('div');
+ $(strip).css({
+ overflow: 'hidden',
+ height: '1px',
+ minHeight: '1px',
+ fontSize: '1px',
+ backgroundColor: sc || 'transparent',
+ borderStyle: 'solid'
+ });
+
+ pad = {
+ T: parseInt($.css(this,'paddingTop'))||0, R: parseInt($.css(this,'paddingRight'))||0,
+ B: parseInt($.css(this,'paddingBottom'))||0, L: parseInt($.css(this,'paddingLeft'))||0
+ };
+
+ if (typeof this.style.zoom != undefined) this.style.zoom = 1; // force 'hasLayout' in IE
+ if (!keep) this.style.border = 'none';
+ strip.style.borderColor = cc || gpc(this.parentNode);
+ cssHeight = $(this).outerHeight();
+
+ for (j in edges) {
+ bot = edges[j];
+ // only add stips if needed
+ if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
+ strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');
+ d = document.createElement('div');
+ $(d).addClass('jquery-corner');
+ ds = d.style;
+
+ bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);
+
+ if (bot && cssHeight != 'auto') {
+ if ($.css(this,'position') == 'static')
+ this.style.position = 'relative';
+ ds.position = 'absolute';
+ ds.bottom = ds.left = ds.padding = ds.margin = '0';
+ if (expr)
+ ds.setExpression('width', 'this.parentNode.offsetWidth');
+ else
+ ds.width = '100%';
+ }
+ else if (!bot && $.browser.msie) {
+ if ($.css(this,'position') == 'static')
+ this.style.position = 'relative';
+ ds.position = 'absolute';
+ ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
+
+ // fix ie6 problem when blocked element has a border width
+ if (expr) {
+ bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth');
+ ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"');
+ }
+ else
+ ds.width = '100%';
+ }
+ else {
+ ds.position = 'relative';
+ ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' :
+ (pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';
+ }
+
+ for (i=0; i < width; i++) {
+ w = Math.max(0,getWidth(fx,i, width));
+ e = strip.cloneNode(false);
+ e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';
+ bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
+ }
+
+ if (fold && $.support.boxModel) {
+ if (bot && noBottomFold) continue;
+ for (c in opts) {
+ if (!opts[c]) continue;
+ if (bot && (c == 'TL' || c == 'TR')) continue;
+ if (!bot && (c == 'BL' || c == 'BR')) continue;
+
+ common = { position: 'absolute', border: 'none', margin: 0, padding: 0, overflow: 'hidden', backgroundColor: strip.style.borderColor };
+ $horz = $('<div/>').css(common).css({ width: width + 'px', height: '1px' });
+ switch(c) {
+ case 'TL': $horz.css({ bottom: 0, left: 0 }); break;
+ case 'TR': $horz.css({ bottom: 0, right: 0 }); break;
+ case 'BL': $horz.css({ top: 0, left: 0 }); break;
+ case 'BR': $horz.css({ top: 0, right: 0 }); break;
+ }
+ d.appendChild($horz[0]);
+
+ var $vert = $('<div/>').css(common).css({ top: 0, bottom: 0, width: '1px', height: width + 'px' });
+ switch(c) {
+ case 'TL': $vert.css({ left: width }); break;
+ case 'TR': $vert.css({ right: width }); break;
+ case 'BL': $vert.css({ left: width }); break;
+ case 'BR': $vert.css({ right: width }); break;
+ }
+ d.appendChild($vert[0]);
+ }
+ }
+ }
+ }
+ });
+};
+
+$.fn.uncorner = function() {
+ if (radius || moz || webkit)
+ this.css(radius ? 'border-radius' : moz ? '-moz-border-radius' : '-webkit-border-radius', 0);
+ $('div.jquery-corner', this).remove();
+ return this;
+};
+
+// expose options
+$.fn.corner.defaults = {
+ useNative: true, // true if plugin should attempt to use native browser support for border radius rounding
+ metaAttr: 'data-corner' // name of meta attribute to use for options
+};
+
+})(jQuery);
diff --git a/studio/static/js/jquery.plugin.svgimg.js b/studio/static/js/jquery.plugin.svgimg.js
new file mode 100644
index 0000000..4e2f9be
--- /dev/null
+++ b/studio/static/js/jquery.plugin.svgimg.js
@@ -0,0 +1,33 @@
+(function(){
+ jQuery.fn.svgimg = function(options) {
+ return $(this).each(function() {
+ var _a, _b, _c, attribute, img, object;
+ img = $(this);
+ img.attr('style', 'display: inline-block');
+ if (img.height() > 0) {
+ if (img.width() > 0) {
+ $('body').addClass('has_svg_images');
+ }
+ } else {
+ if (img.height() > 0) {
+ $('body').addClass('has_svg_images');
+ }
+ }
+ if ($('body').hasClass('has_svg_images')) {
+ return img.removeAttr('style');
+ } else {
+ object = $('<object>');
+ _b = ['height', 'width', 'id', 'class'];
+ for (_a = 0, _c = _b.length; _a < _c; _a++) {
+ attribute = _b[_a];
+ if (img.attr(attribute)) {
+ object.attr(attribute, img.attr(attribute));
+ }
+ }
+ object.attr('data', img.attr('src'));
+ img.after(object);
+ return img.remove();
+ }
+ });
+ };
+})();
diff --git a/studio/static/js/jquery.quicksand.js b/studio/static/js/jquery.quicksand.js
new file mode 100644
index 0000000..626f490
--- /dev/null
+++ b/studio/static/js/jquery.quicksand.js
@@ -0,0 +1,307 @@
+/*
+
+Quicksand 1.2.2
+
+Reorder and filter items with a nice shuffling animation.
+
+Copyright (c) 2010 Jacek Galanciak (razorjack.net) and agilope.com
+Big thanks for Piotr Petrus (riddle.pl) for deep code review and wonderful docs & demos.
+
+Dual licensed under the MIT and GPL version 2 licenses.
+http://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt
+http://github.com/jquery/jquery/blob/master/GPL-LICENSE.txt
+
+Project site: http://razorjack.net/quicksand
+Github site: http://github.com/razorjack/quicksand
+
+*/
+
+(function ($) {
+ $.fn.quicksand = function (collection, customOptions) {
+ var options = {
+ duration: 750,
+ easing: 'swing',
+ attribute: 'data-id', // attribute to recognize same items within source and dest
+ adjustHeight: 'auto', // 'dynamic' animates height during shuffling (slow), 'auto' adjusts it before or after the animation, false leaves height constant
+ useScaling: true, // disable it if you're not using scaling effect or want to improve performance
+ enhancement: function(c) {}, // Visual enhacement (eg. font replacement) function for cloned elements
+ selector: '> *',
+ dx: 0,
+ dy: 0
+ };
+ $.extend(options, customOptions);
+
+ if ($.browser.msie || (typeof($.fn.scale) == 'undefined')) {
+ // Got IE and want scaling effect? Kiss my ass.
+ options.useScaling = false;
+ }
+
+ var callbackFunction;
+ if (typeof(arguments[1]) == 'function') {
+ var callbackFunction = arguments[1];
+ } else if (typeof(arguments[2] == 'function')) {
+ var callbackFunction = arguments[2];
+ }
+
+
+ return this.each(function (i) {
+ var val;
+ var animationQueue = []; // used to store all the animation params before starting the animation; solves initial animation slowdowns
+ var $collection = $(collection).clone(); // destination (target) collection
+ var $sourceParent = $(this); // source, the visible container of source collection
+ var sourceHeight = $(this).css('height'); // used to keep height and document flow during the animation
+
+ var destHeight;
+ var adjustHeightOnCallback = false;
+
+ var offset = $($sourceParent).offset(); // offset of visible container, used in animation calculations
+ var offsets = []; // coordinates of every source collection item
+
+ var $source = $(this).find(options.selector); // source collection items
+
+ // Replace the collection and quit if IE6
+ if ($.browser.msie && $.browser.version.substr(0,1)<7) {
+ $sourceParent.html('').append($collection);
+ return;
+ }
+
+ // Gets called when any animation is finished
+ var postCallbackPerformed = 0; // prevents the function from being called more than one time
+ var postCallback = function () {
+
+ if (!postCallbackPerformed) {
+ postCallbackPerformed = 1;
+
+ // hack:
+ // used to be: $sourceParent.html($dest.html()); // put target HTML into visible source container
+ // but new webkit builds cause flickering when replacing the collections
+ $toDelete = $sourceParent.find('> *');
+ $sourceParent.prepend($dest.find('> *'));
+ $toDelete.remove();
+
+ if (adjustHeightOnCallback) {
+ $sourceParent.css('height', destHeight);
+ }
+ options.enhancement($sourceParent); // Perform custom visual enhancements on a newly replaced collection
+ if (typeof callbackFunction == 'function') {
+ callbackFunction.call(this);
+ }
+ }
+ };
+
+ // Position: relative situations
+ var $correctionParent = $sourceParent.offsetParent();
+ var correctionOffset = $correctionParent.offset();
+ if ($correctionParent.css('position') == 'relative') {
+ if ($correctionParent.get(0).nodeName.toLowerCase() == 'body') {
+
+ } else {
+ correctionOffset.top += (parseFloat($correctionParent.css('border-top-width')) || 0);
+ correctionOffset.left +=( parseFloat($correctionParent.css('border-left-width')) || 0);
+ }
+ } else {
+ correctionOffset.top -= (parseFloat($correctionParent.css('border-top-width')) || 0);
+ correctionOffset.left -= (parseFloat($correctionParent.css('border-left-width')) || 0);
+ correctionOffset.top -= (parseFloat($correctionParent.css('margin-top')) || 0);
+ correctionOffset.left -= (parseFloat($correctionParent.css('margin-left')) || 0);
+ }
+
+ // perform custom corrections from options (use when Quicksand fails to detect proper correction)
+ if (isNaN(correctionOffset.left)) {
+ correctionOffset.left = 0;
+ }
+ if (isNaN(correctionOffset.top)) {
+ correctionOffset.top = 0;
+ }
+
+ correctionOffset.left -= options.dx;
+ correctionOffset.top -= options.dy;
+
+ // keeps nodes after source container, holding their position
+ $sourceParent.css('height', $(this).height());
+
+ // get positions of source collections
+ $source.each(function (i) {
+ offsets[i] = $(this).offset();
+ });
+
+ // stops previous animations on source container
+ $(this).stop();
+ var dx = 0; var dy = 0;
+ $source.each(function (i) {
+ $(this).stop(); // stop animation of collection items
+ var rawObj = $(this).get(0);
+ if (rawObj.style.position == 'absolute') {
+ dx = -options.dx;
+ dy = -options.dy;
+ } else {
+ dx = options.dx;
+ dy = options.dy;
+ }
+
+ rawObj.style.position = 'absolute';
+ rawObj.style.margin = '0';
+
+ rawObj.style.top = (offsets[i].top - parseFloat(rawObj.style.marginTop) - correctionOffset.top + dy) + 'px';
+ rawObj.style.left = (offsets[i].left - parseFloat(rawObj.style.marginLeft) - correctionOffset.left + dx) + 'px';
+ });
+
+ // create temporary container with destination collection
+ var $dest = $($sourceParent).clone();
+ var rawDest = $dest.get(0);
+ rawDest.innerHTML = '';
+ rawDest.setAttribute('id', '');
+ rawDest.style.height = 'auto';
+ rawDest.style.width = $sourceParent.width() + 'px';
+ $dest.append($collection);
+ // insert node into HTML
+ // Note that the node is under visible source container in the exactly same position
+ // The browser render all the items without showing them (opacity: 0.0)
+ // No offset calculations are needed, the browser just extracts position from underlayered destination items
+ // and sets animation to destination positions.
+ $dest.insertBefore($sourceParent);
+ $dest.css('opacity', 0.0);
+ rawDest.style.zIndex = -1;
+
+ rawDest.style.margin = '0';
+ rawDest.style.position = 'absolute';
+ rawDest.style.top = offset.top - correctionOffset.top + 'px';
+ rawDest.style.left = offset.left - correctionOffset.left + 'px';
+
+
+
+
+
+ if (options.adjustHeight === 'dynamic') {
+ // If destination container has different height than source container
+ // the height can be animated, adjusting it to destination height
+ $sourceParent.animate({height: $dest.height()}, options.duration, options.easing);
+ } else if (options.adjustHeight === 'auto') {
+ destHeight = $dest.height();
+ if (parseFloat(sourceHeight) < parseFloat(destHeight)) {
+ // Adjust the height now so that the items don't move out of the container
+ $sourceParent.css('height', destHeight);
+ } else {
+ // Adjust later, on callback
+ adjustHeightOnCallback = true;
+ }
+ }
+
+ // Now it's time to do shuffling animation
+ // First of all, we need to identify same elements within source and destination collections
+ $source.each(function (i) {
+ var destElement = [];
+ if (typeof(options.attribute) == 'function') {
+
+ val = options.attribute($(this));
+ $collection.each(function() {
+ if (options.attribute(this) == val) {
+ destElement = $(this);
+ return false;
+ }
+ });
+ } else {
+ destElement = $collection.filter('[' + options.attribute + '=' + $(this).attr(options.attribute) + ']');
+ }
+ if (destElement.length) {
+ // The item is both in source and destination collections
+ // It it's under different position, let's move it
+ if (!options.useScaling) {
+ animationQueue.push(
+ {
+ element: $(this),
+ animation:
+ {top: destElement.offset().top - correctionOffset.top,
+ left: destElement.offset().left - correctionOffset.left,
+ opacity: 1.0
+ }
+ });
+
+ } else {
+ animationQueue.push({
+ element: $(this),
+ animation: {top: destElement.offset().top - correctionOffset.top,
+ left: destElement.offset().left - correctionOffset.left,
+ opacity: 1.0,
+ scale: '1.0'
+ }
+ });
+
+ }
+ } else {
+ // The item from source collection is not present in destination collections
+ // Let's remove it
+ if (!options.useScaling) {
+ animationQueue.push({element: $(this),
+ animation: {opacity: '0.0'}});
+ } else {
+ animationQueue.push({element: $(this), animation: {opacity: '0.0',
+ scale: '0.0'}});
+ }
+ }
+ });
+
+ $collection.each(function (i) {
+ // Grab all items from target collection not present in visible source collection
+
+ var sourceElement = [];
+ var destElement = [];
+ if (typeof(options.attribute) == 'function') {
+ val = options.attribute($(this));
+ $source.each(function() {
+ if (options.attribute(this) == val) {
+ sourceElement = $(this);
+ return false;
+ }
+ });
+
+ $collection.each(function() {
+ if (options.attribute(this) == val) {
+ destElement = $(this);
+ return false;
+ }
+ });
+ } else {
+ sourceElement = $source.filter('[' + options.attribute + '=' + $(this).attr(options.attribute) + ']');
+ destElement = $collection.filter('[' + options.attribute + '=' + $(this).attr(options.attribute) + ']');
+ }
+
+ var animationOptions;
+ if (sourceElement.length === 0) {
+ // No such element in source collection...
+ if (!options.useScaling) {
+ animationOptions = {
+ opacity: '1.0'
+ };
+ } else {
+ animationOptions = {
+ opacity: '1.0',
+ scale: '1.0'
+ };
+ }
+ // Let's create it
+ d = destElement.clone();
+ var rawDestElement = d.get(0);
+ rawDestElement.style.position = 'absolute';
+ rawDestElement.style.margin = '0';
+ rawDestElement.style.top = destElement.offset().top - correctionOffset.top + 'px';
+ rawDestElement.style.left = destElement.offset().left - correctionOffset.left + 'px';
+ d.css('opacity', 0.0); // IE
+ if (options.useScaling) {
+ d.css('transform', 'scale(0.0)');
+ }
+ d.appendTo($sourceParent);
+
+ animationQueue.push({element: $(d),
+ animation: animationOptions});
+ }
+ });
+
+ $dest.remove();
+ options.enhancement($sourceParent); // Perform custom visual enhancements during the animation
+ for (i = 0; i < animationQueue.length; i++) {
+ animationQueue[i].element.animate(animationQueue[i].animation, options.duration, options.easing, postCallback);
+ }
+ });
+ };
+})(jQuery); \ No newline at end of file
diff --git a/studio/static/js/websdk-1.js b/studio/static/js/websdk-1.js
new file mode 100644
index 0000000..be651e0
--- /dev/null
+++ b/studio/static/js/websdk-1.js
@@ -0,0 +1,6 @@
+/* import jquery-1.6.2.js */
+$(function() {
+ $( "input:button" ).button();
+ $( "input:submit" ).button();
+ /* $( "a", ".demo" ).click(function() { return false; }); */
+ });
diff --git a/studio/static/js/wymeditor/iframe/.svn/entries b/studio/static/js/wymeditor/iframe/.svn/entries
new file mode 100644
index 0000000..247ae9d
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/.svn/entries
@@ -0,0 +1,31 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/iframe
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+default
+dir
+
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/entries b/studio/static/js/wymeditor/iframe/default/.svn/entries
new file mode 100644
index 0000000..cb122d2
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/entries
@@ -0,0 +1,402 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/iframe/default
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+lbl-pre.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+7ad78e30f68e5007ed479516f583e10f
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+177
+
+lbl-blockquote.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+5cd5fb5fe87078c1cae941dad05db2e2
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+196
+
+lbl-h1.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+b3782df976443f4a0086893d4da991d2
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+166
+
+lbl-h2.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+6e4bfdbfc57415b8c650347bd1704154
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+172
+
+wymiframe.html
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+b079d0914f6a73d4812a4967b2d9bd3c
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+867
+
+lbl-h3.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+8915729f6e8b98436f9a1a0723bc2d6a
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+170
+
+lbl-h4.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+03bfccf7f8d474142bafeabf86b4732f
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+172
+
+lbl-h5.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+c0f5d3d29f3cf277960182af39f5028a
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+172
+
+lbl-h6.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+f19136291f975688a3784353ac0fc83c
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+171
+
+wymiframe.css
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+bb4ed9368fc143f7af3936d7e51d8d57
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3273
+
+lbl-p.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+afe885e1e018a8aecee52a08e605c565
+2007-05-02T09:28:40.695596Z
+253
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3607
+
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-blockquote.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-blockquote.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-blockquote.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h1.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h1.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h1.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h2.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h2.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h2.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h3.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h3.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h3.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h4.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h4.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h4.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h5.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h5.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h5.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h6.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h6.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h6.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-p.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-p.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-p.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-pre.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-pre.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-pre.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-base
new file mode 100644
index 0000000..65ea205
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-base
new file mode 100644
index 0000000..dea1da3
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-base
new file mode 100644
index 0000000..f4b4274
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-base
new file mode 100644
index 0000000..bd99f76
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-base
new file mode 100644
index 0000000..e06ab3d
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-base
new file mode 100644
index 0000000..360fc60
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-base
new file mode 100644
index 0000000..73e644a
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-base
new file mode 100644
index 0000000..7f2bf34
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-base
new file mode 100644
index 0000000..e026e47
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.css.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.css.svn-base
new file mode 100644
index 0000000..769520c
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.css.svn-base
@@ -0,0 +1,90 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * wymeditor.css
+ * Main editor css file.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+*/
+
+/* VISUAL FEEDBACK */
+
+/* basic */
+ body { background: #e1e8f1;}
+
+/* make HTML blocs visible */
+ p,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ ul,
+ ol,
+ table,
+ blockquote,
+ pre { background: #FFFFFF no-repeat 2px 2px;
+ padding:8px 5px 5px;
+ margin:10px; }
+ td { background: #F0F4F8; }
+ th { background: #ffffcc; }
+ ul,
+ ol { border-left:20px solid #B9C4D0; padding:0px 5px; }
+ caption { background: #E4E4B0; padding: 5px; font-weight: bold; }
+ table { font-size: 12px; width: 500px; }
+ td { width: 25%; }
+ blockquote { margin-left: 30px; }
+ pre { background-color:transparent; border: 1px solid white; }
+
+/* Gecko min height fix */
+ p { min-height: 1em; } /*min-height is needed under Firefox, because empty parargraphs */
+ *+html p { min-height: auto; } /* but we have to remove it under IE7 because it triggers the 'haslayout' mode */
+ td { height: 1.6em; }
+
+/* labels */
+ p { background-image: url(lbl-p.png); }
+ h1 { background-image: url(lbl-h1.png); }
+ h2 { background-image: url(lbl-h2.png); }
+ h3 { background-image: url(lbl-h3.png); }
+ h4 { background-image: url(lbl-h4.png); }
+ h5 { background-image: url(lbl-h5.png); }
+ h6 { background-image: url(lbl-h6.png); }
+ blockquote{ background-image: url(lbl-blockquote.png); }
+ pre { background-image: url(lbl-pre.png); }
+
+/* specific HTML elements */
+ caption { text-align: left; }
+ img { margin-right: 5px;
+ border-style: solid;
+ border-color: gray;
+ border-width: 0; }
+ a img { border-width: 1px; border-color: blue; }
+ acronym { border: 1px solid gray; }
+ span { background-color: #eef; }
+
+/* visual feedback for non-valid nesting of elements*/
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5, h1 h6, h1 p, h1 pre, h1 address,
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5, h2 h6, h2 p, h2 pre, h2 address,
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5, h3 h6, h3 p, h3 pre, h3 address,
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5, h4 h6, h4 p, h4 pre, h4 address,
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5, h5 h6, h5 p, h5 pre, h5 address,
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4, h6 h6, h6 p, h6 pre, h6 address,
+ p h1, p h2, p h3, p h4, p h5, p h6, p pre, p address,
+ pre h1, pre h2, pre h3, pre h4, pre h5, pre h6, pre p, pre pre, pre address,
+ address h1, address h2, address h3, address h4, address h5, address h6,
+ address p, address pre, address address
+ { background-color: #ff9999 !important;
+ border: 1px solid red !important;
+ font-size: 12px !important;
+ font-weight: normal; }
diff --git a/studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.html.svn-base b/studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.html.svn-base
new file mode 100644
index 0000000..91a690c
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.html.svn-base
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * wymiframe.html
+ * Iframe used by designMode.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+-->
+<html>
+<head>
+<title>WYMeditor iframe</title>
+<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
+<link rel="stylesheet" type="text/css" media="screen" href="wymiframe.css" />
+</head>
+<body class="wym_iframe"></body>
+</html>
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-blockquote.png b/studio/static/js/wymeditor/iframe/default/lbl-blockquote.png
new file mode 100644
index 0000000..65ea205
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-blockquote.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-h1.png b/studio/static/js/wymeditor/iframe/default/lbl-h1.png
new file mode 100644
index 0000000..dea1da3
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-h1.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-h2.png b/studio/static/js/wymeditor/iframe/default/lbl-h2.png
new file mode 100644
index 0000000..f4b4274
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-h2.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-h3.png b/studio/static/js/wymeditor/iframe/default/lbl-h3.png
new file mode 100644
index 0000000..bd99f76
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-h3.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-h4.png b/studio/static/js/wymeditor/iframe/default/lbl-h4.png
new file mode 100644
index 0000000..e06ab3d
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-h4.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-h5.png b/studio/static/js/wymeditor/iframe/default/lbl-h5.png
new file mode 100644
index 0000000..360fc60
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-h5.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-h6.png b/studio/static/js/wymeditor/iframe/default/lbl-h6.png
new file mode 100644
index 0000000..73e644a
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-h6.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-p.png b/studio/static/js/wymeditor/iframe/default/lbl-p.png
new file mode 100644
index 0000000..7f2bf34
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-p.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/lbl-pre.png b/studio/static/js/wymeditor/iframe/default/lbl-pre.png
new file mode 100644
index 0000000..e026e47
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/lbl-pre.png
Binary files differ
diff --git a/studio/static/js/wymeditor/iframe/default/wymiframe.css b/studio/static/js/wymeditor/iframe/default/wymiframe.css
new file mode 100644
index 0000000..769520c
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/wymiframe.css
@@ -0,0 +1,90 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * wymeditor.css
+ * Main editor css file.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+*/
+
+/* VISUAL FEEDBACK */
+
+/* basic */
+ body { background: #e1e8f1;}
+
+/* make HTML blocs visible */
+ p,
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6,
+ ul,
+ ol,
+ table,
+ blockquote,
+ pre { background: #FFFFFF no-repeat 2px 2px;
+ padding:8px 5px 5px;
+ margin:10px; }
+ td { background: #F0F4F8; }
+ th { background: #ffffcc; }
+ ul,
+ ol { border-left:20px solid #B9C4D0; padding:0px 5px; }
+ caption { background: #E4E4B0; padding: 5px; font-weight: bold; }
+ table { font-size: 12px; width: 500px; }
+ td { width: 25%; }
+ blockquote { margin-left: 30px; }
+ pre { background-color:transparent; border: 1px solid white; }
+
+/* Gecko min height fix */
+ p { min-height: 1em; } /*min-height is needed under Firefox, because empty parargraphs */
+ *+html p { min-height: auto; } /* but we have to remove it under IE7 because it triggers the 'haslayout' mode */
+ td { height: 1.6em; }
+
+/* labels */
+ p { background-image: url(lbl-p.png); }
+ h1 { background-image: url(lbl-h1.png); }
+ h2 { background-image: url(lbl-h2.png); }
+ h3 { background-image: url(lbl-h3.png); }
+ h4 { background-image: url(lbl-h4.png); }
+ h5 { background-image: url(lbl-h5.png); }
+ h6 { background-image: url(lbl-h6.png); }
+ blockquote{ background-image: url(lbl-blockquote.png); }
+ pre { background-image: url(lbl-pre.png); }
+
+/* specific HTML elements */
+ caption { text-align: left; }
+ img { margin-right: 5px;
+ border-style: solid;
+ border-color: gray;
+ border-width: 0; }
+ a img { border-width: 1px; border-color: blue; }
+ acronym { border: 1px solid gray; }
+ span { background-color: #eef; }
+
+/* visual feedback for non-valid nesting of elements*/
+ h1 h1, h1 h2, h1 h3, h1 h4, h1 h5, h1 h6, h1 p, h1 pre, h1 address,
+ h2 h1, h2 h2, h2 h3, h2 h4, h2 h5, h2 h6, h2 p, h2 pre, h2 address,
+ h3 h1, h3 h2, h3 h3, h3 h4, h3 h5, h3 h6, h3 p, h3 pre, h3 address,
+ h4 h1, h4 h2, h4 h3, h4 h4, h4 h5, h4 h6, h4 p, h4 pre, h4 address,
+ h5 h1, h5 h2, h5 h3, h5 h4, h5 h5, h5 h6, h5 p, h5 pre, h5 address,
+ h6 h1, h6 h2, h6 h3, h6 h4, h6 h4, h6 h6, h6 p, h6 pre, h6 address,
+ p h1, p h2, p h3, p h4, p h5, p h6, p pre, p address,
+ pre h1, pre h2, pre h3, pre h4, pre h5, pre h6, pre p, pre pre, pre address,
+ address h1, address h2, address h3, address h4, address h5, address h6,
+ address p, address pre, address address
+ { background-color: #ff9999 !important;
+ border: 1px solid red !important;
+ font-size: 12px !important;
+ font-weight: normal; }
diff --git a/studio/static/js/wymeditor/iframe/default/wymiframe.html b/studio/static/js/wymeditor/iframe/default/wymiframe.html
new file mode 100644
index 0000000..91a690c
--- /dev/null
+++ b/studio/static/js/wymeditor/iframe/default/wymiframe.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * wymiframe.html
+ * Iframe used by designMode.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+-->
+<html>
+<head>
+<title>WYMeditor iframe</title>
+<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
+<link rel="stylesheet" type="text/css" media="screen" href="wymiframe.css" />
+</head>
+<body class="wym_iframe"></body>
+</html>
diff --git a/studio/static/js/wymeditor/jquery.wymeditor.explorer.js b/studio/static/js/wymeditor/jquery.wymeditor.explorer.js
new file mode 100644
index 0000000..c177bee
--- /dev/null
+++ b/studio/static/js/wymeditor/jquery.wymeditor.explorer.js
@@ -0,0 +1,218 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.explorer.js
+ * MSIE specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Bermi Ferrer (wymeditor a-t bermi dotorg)
+ * Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+WYMeditor.WymClassExplorer = function(wym) {
+
+ this._wym = wym;
+ this._class = "className";
+ this._newLine = "\r\n";
+
+};
+
+WYMeditor.WymClassExplorer.prototype.initIframe = function(iframe) {
+
+ //This function is executed twice, though it is called once!
+ //But MSIE needs that, otherwise designMode won't work.
+ //Weird.
+
+ this._iframe = iframe;
+ this._doc = iframe.contentWindow.document;
+
+ //add css rules from options
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init html value
+ jQuery(this._doc.body).html(this._wym._html);
+
+ //handle events
+ var wym = this;
+
+ this._doc.body.onfocus = function()
+ {wym._doc.designMode = "on"; wym._doc = iframe.contentWindow.document;};
+ this._doc.onbeforedeactivate = function() {wym.saveCaret();};
+ this._doc.onkeyup = function() {
+ wym.saveCaret();
+ wym.keyup();
+ };
+ this._doc.onclick = function() {wym.saveCaret();};
+
+ this._doc.body.onbeforepaste = function() {
+ wym._iframe.contentWindow.event.returnValue = false;
+ };
+
+ this._doc.body.onpaste = function() {
+ wym._iframe.contentWindow.event.returnValue = false;
+ wym.paste(window.clipboardData.getData("Text"));
+ };
+
+ //callback can't be executed twice, so we check
+ if(this._initialized) {
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+ }
+
+ this._initialized = true;
+
+ //init designMode
+ this._doc.designMode="on";
+ try{
+ // (bermi's note) noticed when running unit tests on IE6
+ // Is this really needed, it trigger an unexisting property on IE6
+ this._doc = iframe.contentWindow.document;
+ }catch(e){}
+};
+
+(function(editorLoadSkin) {
+ WYMeditor.WymClassExplorer.prototype.loadSkin = function() {
+ // Mark container items as unselectable (#203)
+ // Fix for issue explained: http://stackoverflow.com/questions/1470932/ie8-iframe-designmode-loses-selection
+ jQuery(this._box).find(this._options.containerSelector)
+ .attr('unselectable', 'on');
+
+ editorLoadSkin.call(this);
+ };
+})(WYMeditor.editor.prototype.loadSkin);
+
+WYMeditor.WymClassExplorer.prototype._exec = function(cmd,param) {
+
+ switch(cmd) {
+
+ case WYMeditor.INDENT: case WYMeditor.OUTDENT:
+
+ var container = this.findUp(this.container(), WYMeditor.LI);
+ if(container) {
+ var ancestor = container.parentNode.parentNode;
+ if(container.parentNode.childNodes.length>1
+ || ancestor.tagName.toLowerCase() == WYMeditor.OL
+ || ancestor.tagName.toLowerCase() == WYMeditor.UL)
+ this._doc.execCommand(cmd);
+ }
+ break;
+ default:
+ if(param) this._doc.execCommand(cmd,false,param);
+ else this._doc.execCommand(cmd);
+ break;
+ }
+
+};
+
+WYMeditor.WymClassExplorer.prototype.selected = function() {
+
+ var caretPos = this._iframe.contentWindow.document.caretPos;
+ if(caretPos!=null) {
+ if(caretPos.parentElement!=undefined)
+ return(caretPos.parentElement());
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.saveCaret = function() {
+
+ this._doc.caretPos = this._doc.selection.createRange();
+};
+
+WYMeditor.WymClassExplorer.prototype.addCssRule = function(styles, oCss) {
+ // IE doesn't handle combined selectors (#196)
+ var selectors = oCss.name.split(',');
+ for (var i in selectors) {
+ styles.addRule(selectors[i], oCss.css);
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.insert = function(html) {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Overwrite selection with provided html
+ range.pasteHTML(html);
+ } catch (e) { }
+ } else {
+ // Fall back to the internal paste function if there's no selection
+ this.paste(html);
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.wrap = function(left, right) {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Overwrite selection with provided html
+ range.pasteHTML(left + range.text + right);
+ } catch (e) { }
+ }
+};
+
+WYMeditor.WymClassExplorer.prototype.unwrap = function() {
+
+ // Get the current selection
+ var range = this._doc.selection.createRange();
+
+ // Check if the current selection is inside the editor
+ if ( jQuery(range.parentElement()).parents( this._options.iframeBodySelector ).is('*') ) {
+ try {
+ // Unwrap selection
+ var text = range.text;
+ this._exec( 'Cut' );
+ range.pasteHTML( text );
+ } catch (e) { }
+ }
+};
+
+//keyup handler
+WYMeditor.WymClassExplorer.prototype.keyup = function() {
+ this._selected_image = null;
+};
+
+WYMeditor.WymClassExplorer.prototype.setFocusToNode = function(node, toStart) {
+ var range = this._doc.selection.createRange();
+ toStart = toStart ? true : false;
+
+ range.moveToElementText(node);
+ range.collapse(toStart);
+ range.select();
+ node.focus();
+};
+
diff --git a/studio/static/js/wymeditor/jquery.wymeditor.js b/studio/static/js/wymeditor/jquery.wymeditor.js
new file mode 100644
index 0000000..d7c6359
--- /dev/null
+++ b/studio/static/js/wymeditor/jquery.wymeditor.js
@@ -0,0 +1,3860 @@
+/**
+ * @version 0.5-rc1
+ *
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File: jquery.wymeditor.js
+ *
+ * Main JS file with core classes and functions.
+ * See the documentation for more info.
+ *
+ * About: authors
+ *
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Volker Mische (vmx a-t gmx dotde)
+ * Scott Lewis (lewiscot a-t gmail dotcom)
+ * Bermi Ferrer (wymeditor a-t bermi dotorg)
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+/*
+ Namespace: WYMeditor
+ Global WYMeditor namespace.
+*/
+if(!WYMeditor) var WYMeditor = {};
+
+//Wrap the Firebug console in WYMeditor.console
+(function() {
+ if ( !window.console || !console.firebug ) {
+ var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
+ "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+
+ WYMeditor.console = {};
+ for (var i = 0; i < names.length; ++i)
+ WYMeditor.console[names[i]] = function() {}
+
+ } else WYMeditor.console = window.console;
+})();
+
+jQuery.extend(WYMeditor, {
+
+/*
+ Constants: Global WYMeditor constants.
+
+ VERSION - Defines WYMeditor version.
+ INSTANCES - An array of loaded WYMeditor.editor instances.
+ STRINGS - An array of loaded WYMeditor language pairs/values.
+ SKINS - An array of loaded WYMeditor skins.
+ NAME - The "name" attribute.
+ INDEX - A string replaced by the instance index.
+ WYM_INDEX - A string used to get/set the instance index.
+ BASE_PATH - A string replaced by WYMeditor's base path.
+ SKIN_PATH - A string replaced by WYMeditor's skin path.
+ WYM_PATH - A string replaced by WYMeditor's main JS file path.
+ SKINS_DEFAULT_PATH - The skins default base path.
+ SKINS_DEFAULT_CSS - The skins default CSS file.
+ LANG_DEFAULT_PATH - The language files default path.
+ IFRAME_BASE_PATH - A string replaced by the designmode iframe's base path.
+ IFRAME_DEFAULT - The iframe's default base path.
+ JQUERY_PATH - A string replaced by the computed jQuery path.
+ DIRECTION - A string replaced by the text direction (rtl or ltr).
+ LOGO - A string replaced by WYMeditor logo.
+ TOOLS - A string replaced by the toolbar's HTML.
+ TOOLS_ITEMS - A string replaced by the toolbar items.
+ TOOL_NAME - A string replaced by a toolbar item's name.
+ TOOL_TITLE - A string replaced by a toolbar item's title.
+ TOOL_CLASS - A string replaced by a toolbar item's class.
+ CLASSES - A string replaced by the classes panel's HTML.
+ CLASSES_ITEMS - A string replaced by the classes items.
+ CLASS_NAME - A string replaced by a class item's name.
+ CLASS_TITLE - A string replaced by a class item's title.
+ CONTAINERS - A string replaced by the containers panel's HTML.
+ CONTAINERS_ITEMS - A string replaced by the containers items.
+ CONTAINER_NAME - A string replaced by a container item's name.
+ CONTAINER_TITLE - A string replaced by a container item's title.
+ CONTAINER_CLASS - A string replaced by a container item's class.
+ HTML - A string replaced by the HTML view panel's HTML.
+ IFRAME - A string replaced by the designmode iframe.
+ STATUS - A string replaced by the status panel's HTML.
+ DIALOG_TITLE - A string replaced by a dialog's title.
+ DIALOG_BODY - A string replaced by a dialog's HTML body.
+ BODY - The BODY element.
+ STRING - The "string" type.
+ BODY,DIV,P,
+ H1,H2,H3,H4,H5,H6,
+ PRE,BLOCKQUOTE,
+ A,BR,IMG,
+ TABLE,TD,TH,
+ UL,OL,LI - HTML elements string representation.
+ CLASS,HREF,SRC,
+ TITLE,REL,ALT - HTML attributes string representation.
+ DIALOG_LINK - A link dialog type.
+ DIALOG_IMAGE - An image dialog type.
+ DIALOG_TABLE - A table dialog type.
+ DIALOG_PASTE - A 'Paste from Word' dialog type.
+ BOLD - Command: (un)set selection to <strong>.
+ ITALIC - Command: (un)set selection to <em>.
+ CREATE_LINK - Command: open the link dialog or (un)set link.
+ INSERT_IMAGE - Command: open the image dialog or insert an image.
+ INSERT_TABLE - Command: open the table dialog.
+ PASTE - Command: open the paste dialog.
+ INDENT - Command: nest a list item.
+ OUTDENT - Command: unnest a list item.
+ TOGGLE_HTML - Command: display/hide the HTML view.
+ FORMAT_BLOCK - Command: set a block element to another type.
+ PREVIEW - Command: open the preview dialog.
+ UNLINK - Command: unset a link.
+ INSERT_UNORDEREDLIST- Command: insert an unordered list.
+ INSERT_ORDEREDLIST - Command: insert an ordered list.
+ MAIN_CONTAINERS - An array of the main HTML containers used in WYMeditor.
+ BLOCKS - An array of the HTML block elements.
+ KEY - Standard key codes.
+ NODE - Node types.
+
+*/
+
+ VERSION : "0.5-rc1",
+ INSTANCES : [],
+ STRINGS : [],
+ SKINS : [],
+ NAME : "name",
+ INDEX : "{Wym_Index}",
+ WYM_INDEX : "wym_index",
+ BASE_PATH : "{Wym_Base_Path}",
+ CSS_PATH : "{Wym_Css_Path}",
+ WYM_PATH : "{Wym_Wym_Path}",
+ SKINS_DEFAULT_PATH : "skins/",
+ SKINS_DEFAULT_CSS : "skin.css",
+ SKINS_DEFAULT_JS : "skin.js",
+ LANG_DEFAULT_PATH : "lang/",
+ IFRAME_BASE_PATH : "{Wym_Iframe_Base_Path}",
+ IFRAME_DEFAULT : "iframe/default/",
+ JQUERY_PATH : "{Wym_Jquery_Path}",
+ DIRECTION : "{Wym_Direction}",
+ LOGO : "{Wym_Logo}",
+ TOOLS : "{Wym_Tools}",
+ TOOLS_ITEMS : "{Wym_Tools_Items}",
+ TOOL_NAME : "{Wym_Tool_Name}",
+ TOOL_TITLE : "{Wym_Tool_Title}",
+ TOOL_CLASS : "{Wym_Tool_Class}",
+ CLASSES : "{Wym_Classes}",
+ CLASSES_ITEMS : "{Wym_Classes_Items}",
+ CLASS_NAME : "{Wym_Class_Name}",
+ CLASS_TITLE : "{Wym_Class_Title}",
+ CONTAINERS : "{Wym_Containers}",
+ CONTAINERS_ITEMS : "{Wym_Containers_Items}",
+ CONTAINER_NAME : "{Wym_Container_Name}",
+ CONTAINER_TITLE : "{Wym_Containers_Title}",
+ CONTAINER_CLASS : "{Wym_Container_Class}",
+ HTML : "{Wym_Html}",
+ IFRAME : "{Wym_Iframe}",
+ STATUS : "{Wym_Status}",
+ DIALOG_TITLE : "{Wym_Dialog_Title}",
+ DIALOG_BODY : "{Wym_Dialog_Body}",
+ STRING : "string",
+ BODY : "body",
+ DIV : "div",
+ P : "p",
+ H1 : "h1",
+ H2 : "h2",
+ H3 : "h3",
+ H4 : "h4",
+ H5 : "h5",
+ H6 : "h6",
+ PRE : "pre",
+ BLOCKQUOTE : "blockquote",
+ A : "a",
+ BR : "br",
+ IMG : "img",
+ TABLE : "table",
+ TD : "td",
+ TH : "th",
+ UL : "ul",
+ OL : "ol",
+ LI : "li",
+ CLASS : "class",
+ HREF : "href",
+ SRC : "src",
+ TITLE : "title",
+ REL : "rel",
+ ALT : "alt",
+ DIALOG_LINK : "Link",
+ DIALOG_IMAGE : "Image",
+ DIALOG_TABLE : "Table",
+ DIALOG_PASTE : "Paste_From_Word",
+ BOLD : "Bold",
+ ITALIC : "Italic",
+ CREATE_LINK : "CreateLink",
+ INSERT_IMAGE : "InsertImage",
+ INSERT_TABLE : "InsertTable",
+ INSERT_HTML : "InsertHTML",
+ PASTE : "Paste",
+ INDENT : "Indent",
+ OUTDENT : "Outdent",
+ TOGGLE_HTML : "ToggleHtml",
+ FORMAT_BLOCK : "FormatBlock",
+ PREVIEW : "Preview",
+ UNLINK : "Unlink",
+ INSERT_UNORDEREDLIST: "InsertUnorderedList",
+ INSERT_ORDEREDLIST : "InsertOrderedList",
+
+ MAIN_CONTAINERS : new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"),
+
+ BLOCKS : new Array("address", "blockquote", "div", "dl",
+ "fieldset", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr",
+ "noscript", "ol", "p", "pre", "table", "ul", "dd", "dt",
+ "li", "tbody", "td", "tfoot", "th", "thead", "tr"),
+
+ KEY : {
+ BACKSPACE: 8,
+ ENTER: 13,
+ END: 35,
+ HOME: 36,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ CURSOR: new Array(37, 38, 39, 40),
+ DELETE: 46
+ },
+
+ NODE : {
+ ELEMENT: 1,
+ ATTRIBUTE: 2,
+ TEXT: 3
+ },
+
+ /*
+ Class: WYMeditor.editor
+ WYMeditor editor main class, instanciated for each editor occurrence.
+ */
+
+ editor : function(elem, options) {
+
+ /*
+ Constructor: WYMeditor.editor
+
+ Initializes main values (index, elements, paths, ...)
+ and call WYMeditor.editor.init which initializes the editor.
+
+ Parameters:
+
+ elem - The HTML element to be replaced by the editor.
+ options - The hash of options.
+
+ Returns:
+
+ Nothing.
+
+ See Also:
+
+ <WYMeditor.editor.init>
+ */
+
+ //store the instance in the INSTANCES array and store the index
+ this._index = WYMeditor.INSTANCES.push(this) - 1;
+ //store the element replaced by the editor
+ this._element = elem;
+ //store the options
+ this._options = options;
+ //store the element's inner value
+ this._html = jQuery(elem).val();
+
+ //store the HTML option, if any
+ if(this._options.html) this._html = this._options.html;
+ //get or compute the base path (where the main JS file is located)
+ this._options.basePath = this._options.basePath
+ || this.computeBasePath();
+ //get or set the skin path (where the skin files are located)
+ this._options.skinPath = this._options.skinPath
+ || this._options.basePath + WYMeditor.SKINS_DEFAULT_PATH
+ + this._options.skin + '/';
+ //get or compute the main JS file location
+ this._options.wymPath = this._options.wymPath
+ || this.computeWymPath();
+ //get or set the language files path
+ this._options.langPath = this._options.langPath
+ || this._options.basePath + WYMeditor.LANG_DEFAULT_PATH;
+ //get or set the designmode iframe's base path
+ this._options.iframeBasePath = this._options.iframeBasePath
+ || this._options.basePath + WYMeditor.IFRAME_DEFAULT;
+ //get or compute the jQuery JS file location
+ this._options.jQueryPath = this._options.jQueryPath
+ || this.computeJqueryPath();
+
+ //initialize the editor instance
+ this.init();
+
+ }
+
+});
+
+
+/********** JQUERY **********/
+
+/**
+ * Replace an HTML element by WYMeditor
+ *
+ * @example jQuery(".wymeditor").wymeditor(
+ * {
+ *
+ * }
+ * );
+ * @desc Example description here
+ *
+ * @name WYMeditor
+ * @description WYMeditor is a web-based WYSIWYM XHTML editor
+ * @param Hash hash A hash of parameters
+ * @option Integer iExample Description here
+ * @option String sExample Description here
+ *
+ * @type jQuery
+ * @cat Plugins/WYMeditor
+ * @author Jean-Francois Hovinne
+ */
+jQuery.fn.wymeditor = function(options) {
+
+ options = jQuery.extend({
+
+ html: "",
+
+ basePath: false,
+
+ skinPath: false,
+
+ wymPath: false,
+
+ iframeBasePath: false,
+
+ jQueryPath: false,
+
+ styles: false,
+
+ stylesheet: false,
+
+ skin: "default",
+ initSkin: true,
+ loadSkin: true,
+
+ lang: "en",
+
+ direction: "ltr",
+
+ boxHtml: "<div class='wym_box'>"
+ + "<div class='wym_area_top'>"
+ + WYMeditor.TOOLS
+ + "</div>"
+ + "<div class='wym_area_left'></div>"
+ + "<div class='wym_area_right'>"
+ + WYMeditor.CONTAINERS
+ + WYMeditor.CLASSES
+ + "</div>"
+ + "<div class='wym_area_main'>"
+ + WYMeditor.HTML
+ + WYMeditor.IFRAME
+ + WYMeditor.STATUS
+ + "</div>"
+ + "<div class='wym_area_bottom'>"
+ + WYMeditor.LOGO
+ + "</div>"
+ + "</div>",
+
+ logoHtml: "<a class='wym_wymeditor_link' "
+ + "href='http://www.wymeditor.org/'>WYMeditor</a>",
+
+ iframeHtml:"<div class='wym_iframe wym_section'>"
+ + "<iframe "
+ + "src='"
+ + WYMeditor.IFRAME_BASE_PATH
+ + "wymiframe.html' "
+ + "onload='this.contentWindow.parent.WYMeditor.INSTANCES["
+ + WYMeditor.INDEX + "].initIframe(this)'"
+ + "></iframe>"
+ + "</div>",
+
+ editorStyles: [],
+
+ toolsHtml: "<div class='wym_tools wym_section'>"
+ + "<h2>{Tools}</h2>"
+ + "<ul>"
+ + WYMeditor.TOOLS_ITEMS
+ + "</ul>"
+ + "</div>",
+
+ toolsItemHtml: "<li class='"
+ + WYMeditor.TOOL_CLASS
+ + "'><a href='#' name='"
+ + WYMeditor.TOOL_NAME
+ + "' title='"
+ + WYMeditor.TOOL_TITLE
+ + "'>"
+ + WYMeditor.TOOL_TITLE
+ + "</a></li>",
+
+ toolsItems: [
+ {'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'},
+ {'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'},
+ {'name': 'Superscript', 'title': 'Superscript',
+ 'css': 'wym_tools_superscript'},
+ {'name': 'Subscript', 'title': 'Subscript',
+ 'css': 'wym_tools_subscript'},
+ {'name': 'InsertOrderedList', 'title': 'Ordered_List',
+ 'css': 'wym_tools_ordered_list'},
+ {'name': 'InsertUnorderedList', 'title': 'Unordered_List',
+ 'css': 'wym_tools_unordered_list'},
+ {'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'},
+ {'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'},
+ {'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'},
+ {'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'},
+ {'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'},
+ {'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
+ {'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'},
+ {'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'},
+ {'name': 'Paste', 'title': 'Paste_From_Word',
+ 'css': 'wym_tools_paste'},
+ {'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'},
+ {'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'}
+ ],
+
+ containersHtml: "<div class='wym_containers wym_section'>"
+ + "<h2>{Containers}</h2>"
+ + "<ul>"
+ + WYMeditor.CONTAINERS_ITEMS
+ + "</ul>"
+ + "</div>",
+
+ containersItemHtml:"<li class='"
+ + WYMeditor.CONTAINER_CLASS
+ + "'>"
+ + "<a href='#' name='"
+ + WYMeditor.CONTAINER_NAME
+ + "'>"
+ + WYMeditor.CONTAINER_TITLE
+ + "</a></li>",
+
+ containersItems: [
+ {'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'},
+ {'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'},
+ {'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'},
+ {'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'},
+ {'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'},
+ {'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'},
+ {'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'},
+ {'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'},
+ {'name': 'BLOCKQUOTE', 'title': 'Blockquote',
+ 'css': 'wym_containers_blockquote'},
+ {'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'}
+ ],
+
+ classesHtml: "<div class='wym_classes wym_section'>"
+ + "<h2>{Classes}</h2><ul>"
+ + WYMeditor.CLASSES_ITEMS
+ + "</ul></div>",
+
+ classesItemHtml: "<li class='wym_classes_"
+ + WYMeditor.CLASS_NAME
+ + "'><a href='#' name='"
+ + WYMeditor.CLASS_NAME
+ + "'>"
+ + WYMeditor.CLASS_TITLE
+ + "</a></li>",
+
+ classesItems: [],
+
+ statusHtml: "<div class='wym_status wym_section'>"
+ + "<h2>{Status}</h2>"
+ + "</div>",
+
+ htmlHtml: "<div class='wym_html wym_section'>"
+ + "<h2>{Source_Code}</h2>"
+ + "<textarea class='wym_html_val'></textarea>"
+ + "</div>",
+
+ boxSelector: ".wym_box",
+ toolsSelector: ".wym_tools",
+ toolsListSelector: " ul",
+ containersSelector:".wym_containers",
+ classesSelector: ".wym_classes",
+ htmlSelector: ".wym_html",
+ iframeSelector: ".wym_iframe iframe",
+ iframeBodySelector:".wym_iframe",
+ statusSelector: ".wym_status",
+ toolSelector: ".wym_tools a",
+ containerSelector: ".wym_containers a",
+ classSelector: ".wym_classes a",
+ htmlValSelector: ".wym_html_val",
+
+ hrefSelector: ".wym_href",
+ srcSelector: ".wym_src",
+ titleSelector: ".wym_title",
+ relSelector: ".wym_rel",
+ altSelector: ".wym_alt",
+ textSelector: ".wym_text",
+
+ rowsSelector: ".wym_rows",
+ colsSelector: ".wym_cols",
+ captionSelector: ".wym_caption",
+ summarySelector: ".wym_summary",
+
+ submitSelector: "form",
+ cancelSelector: ".wym_cancel",
+ previewSelector: "",
+
+ dialogTypeSelector: ".wym_dialog_type",
+ dialogLinkSelector: ".wym_dialog_link",
+ dialogImageSelector: ".wym_dialog_image",
+ dialogTableSelector: ".wym_dialog_table",
+ dialogPasteSelector: ".wym_dialog_paste",
+ dialogPreviewSelector: ".wym_dialog_preview",
+
+ updateSelector: ".wymupdate",
+ updateEvent: "click",
+
+ dialogFeatures: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ + ",width=560,height=300,top=0,left=0",
+ dialogFeaturesPreview: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ + ",scrollbars=yes,width=560,height=300,top=0,left=0",
+
+ dialogHtml: "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'"
+ + " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>"
+ + "<html dir='"
+ + WYMeditor.DIRECTION
+ + "'><head>"
+ + "<link rel='stylesheet' type='text/css' media='screen'"
+ + " href='"
+ + WYMeditor.CSS_PATH
+ + "' />"
+ + "<title>"
+ + WYMeditor.DIALOG_TITLE
+ + "</title>"
+ + "<script type='text/javascript'"
+ + " src='"
+ + WYMeditor.JQUERY_PATH
+ + "'></script>"
+ + "<script type='text/javascript'"
+ + " src='"
+ + WYMeditor.WYM_PATH
+ + "'></script>"
+ + "</head>"
+ + WYMeditor.DIALOG_BODY
+ + "</html>",
+
+ dialogLinkHtml: "<body class='wym_dialog wym_dialog_link'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_LINK
+ + "' />"
+ + "<legend>{Link}</legend>"
+ + "<div class='row'>"
+ + "<label>{URL}</label>"
+ + "<input type='text' class='wym_href' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Title}</label>"
+ + "<input type='text' class='wym_title' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Relationship}</label>"
+ + "<input type='text' class='wym_rel' value='' size='40' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='submit'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogImageHtml: "<body class='wym_dialog wym_dialog_image'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_IMAGE
+ + "' />"
+ + "<legend>{Image}</legend>"
+ + "<div class='row'>"
+ + "<label>{URL}</label>"
+ + "<input type='text' class='wym_src' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Alternative_Text}</label>"
+ + "<input type='text' class='wym_alt' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Title}</label>"
+ + "<input type='text' class='wym_title' value='' size='40' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='submit'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogTableHtml: "<body class='wym_dialog wym_dialog_table'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_TABLE
+ + "' />"
+ + "<legend>{Table}</legend>"
+ + "<div class='row'>"
+ + "<label>{Caption}</label>"
+ + "<input type='text' class='wym_caption' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Summary}</label>"
+ + "<input type='text' class='wym_summary' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Number_Of_Rows}</label>"
+ + "<input type='text' class='wym_rows' value='3' size='3' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Number_Of_Cols}</label>"
+ + "<input type='text' class='wym_cols' value='2' size='3' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='submit'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogPasteHtml: "<body class='wym_dialog wym_dialog_paste'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_PASTE
+ + "' />"
+ + "<fieldset>"
+ + "<legend>{Paste_From_Word}</legend>"
+ + "<div class='row'>"
+ + "<textarea class='wym_text' rows='10' cols='50'></textarea>"
+ + "</div>"
+ + "<div class='row'>"
+ + "<input class='wym_submit' type='submit'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogPreviewHtml: "<body class='wym_dialog wym_dialog_preview'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + "></body>",
+
+ dialogStyles: [],
+
+ stringDelimiterLeft: "{",
+ stringDelimiterRight:"}",
+
+ preInit: null,
+ preBind: null,
+ postInit: null,
+
+ preInitDialog: null,
+ postInitDialog: null
+
+ }, options);
+
+ return this.each(function() {
+
+ new WYMeditor.editor(jQuery(this),options);
+ });
+};
+
+/* @name extend
+ * @description Returns the WYMeditor instance based on its index
+ */
+jQuery.extend({
+ wymeditors: function(i) {
+ return (WYMeditor.INSTANCES[i]);
+ }
+});
+
+
+/********** WYMeditor **********/
+
+/* @name Wymeditor
+ * @description WYMeditor class
+ */
+
+/* @name init
+ * @description Initializes a WYMeditor instance
+ */
+WYMeditor.editor.prototype.init = function() {
+
+ //load subclass - browser specific
+ //unsupported browsers: do nothing
+ if (jQuery.browser.msie) {
+ var WymClass = new WYMeditor.WymClassExplorer(this);
+ }
+ else if (jQuery.browser.mozilla) {
+ var WymClass = new WYMeditor.WymClassMozilla(this);
+ }
+ else if (jQuery.browser.opera) {
+ var WymClass = new WYMeditor.WymClassOpera(this);
+ }
+ else if (jQuery.browser.safari) {
+ var WymClass = new WYMeditor.WymClassSafari(this);
+ }
+
+ if(WymClass) {
+
+ if(jQuery.isFunction(this._options.preInit)) this._options.preInit(this);
+
+ var SaxListener = new WYMeditor.XhtmlSaxListener();
+ jQuery.extend(SaxListener, WymClass);
+ this.parser = new WYMeditor.XhtmlParser(SaxListener);
+
+ if(this._options.styles || this._options.stylesheet){
+ this.configureEditorUsingRawCss();
+ }
+
+ this.helper = new WYMeditor.XmlHelper();
+
+ //extend the Wymeditor object
+ //don't use jQuery.extend since 1.1.4
+ //jQuery.extend(this, WymClass);
+ for (var prop in WymClass) { this[prop] = WymClass[prop]; }
+
+ //load wymbox
+ this._box = jQuery(this._element).hide().after(this._options.boxHtml).next().addClass('wym_box_' + this._index);
+
+ //store the instance index in wymbox and element replaced by editor instance
+ //but keep it compatible with jQuery < 1.2.3, see #122
+ if( jQuery.isFunction( jQuery.fn.data ) ) {
+ jQuery.data(this._box.get(0), WYMeditor.WYM_INDEX, this._index);
+ jQuery.data(this._element.get(0), WYMeditor.WYM_INDEX, this._index);
+ }
+
+ var h = WYMeditor.Helper;
+
+ //construct the iframe
+ var iframeHtml = this._options.iframeHtml;
+ iframeHtml = h.replaceAll(iframeHtml, WYMeditor.INDEX, this._index);
+ iframeHtml = h.replaceAll(iframeHtml, WYMeditor.IFRAME_BASE_PATH, this._options.iframeBasePath);
+
+ //construct wymbox
+ var boxHtml = jQuery(this._box).html();
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.LOGO, this._options.logoHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS, this._options.toolsHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS,this._options.containersHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES, this._options.classesHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.HTML, this._options.htmlHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.IFRAME, iframeHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.STATUS, this._options.statusHtml);
+
+ //construct tools list
+ var aTools = eval(this._options.toolsItems);
+ var sTools = "";
+
+ for(var i = 0; i < aTools.length; i++) {
+ var oTool = aTools[i];
+ if(oTool.name && oTool.title)
+ var sTool = this._options.toolsItemHtml;
+ var sTool = h.replaceAll(sTool, WYMeditor.TOOL_NAME, oTool.name);
+ sTool = h.replaceAll(sTool, WYMeditor.TOOL_TITLE, this._options.stringDelimiterLeft
+ + oTool.title
+ + this._options.stringDelimiterRight);
+ sTool = h.replaceAll(sTool, WYMeditor.TOOL_CLASS, oTool.css);
+ sTools += sTool;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS_ITEMS, sTools);
+
+ //construct classes list
+ var aClasses = eval(this._options.classesItems);
+ var sClasses = "";
+
+ for(var i = 0; i < aClasses.length; i++) {
+ var oClass = aClasses[i];
+ if(oClass.name && oClass.title)
+ var sClass = this._options.classesItemHtml;
+ sClass = h.replaceAll(sClass, WYMeditor.CLASS_NAME, oClass.name);
+ sClass = h.replaceAll(sClass, WYMeditor.CLASS_TITLE, oClass.title);
+ sClasses += sClass;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES_ITEMS, sClasses);
+
+ //construct containers list
+ var aContainers = eval(this._options.containersItems);
+ var sContainers = "";
+
+ for(var i = 0; i < aContainers.length; i++) {
+ var oContainer = aContainers[i];
+ if(oContainer.name && oContainer.title)
+ var sContainer = this._options.containersItemHtml;
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_NAME, oContainer.name);
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_TITLE,
+ this._options.stringDelimiterLeft
+ + oContainer.title
+ + this._options.stringDelimiterRight);
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_CLASS, oContainer.css);
+ sContainers += sContainer;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS_ITEMS, sContainers);
+
+ //l10n
+ boxHtml = this.replaceStrings(boxHtml);
+
+ //load html in wymbox
+ jQuery(this._box).html(boxHtml);
+
+ //hide the html value
+ jQuery(this._box).find(this._options.htmlSelector).hide();
+
+ //enable the skin
+ this.loadSkin();
+
+ }
+};
+
+WYMeditor.editor.prototype.bindEvents = function() {
+
+ //copy the instance
+ var wym = this;
+
+ //handle click event on tools buttons
+ jQuery(this._box).find(this._options.toolSelector).click(function() {
+ wym._iframe.contentWindow.focus(); //See #154
+ wym.exec(jQuery(this).attr(WYMeditor.NAME));
+ return(false);
+ });
+
+ //handle click event on containers buttons
+ jQuery(this._box).find(this._options.containerSelector).click(function() {
+ wym.container(jQuery(this).attr(WYMeditor.NAME));
+ return(false);
+ });
+
+ //handle keyup event on html value: set the editor value
+ //handle focus/blur events to check if the element has focus, see #147
+ jQuery(this._box).find(this._options.htmlValSelector)
+ .keyup(function() { jQuery(wym._doc.body).html(jQuery(this).val());})
+ .focus(function() { jQuery(this).toggleClass('hasfocus'); })
+ .blur(function() { jQuery(this).toggleClass('hasfocus'); });
+
+ //handle click event on classes buttons
+ jQuery(this._box).find(this._options.classSelector).click(function() {
+
+ var aClasses = eval(wym._options.classesItems);
+ var sName = jQuery(this).attr(WYMeditor.NAME);
+
+ var oClass = WYMeditor.Helper.findByName(aClasses, sName);
+
+ if(oClass) {
+ var jqexpr = oClass.expr;
+ wym.toggleClass(sName, jqexpr);
+ }
+ wym._iframe.contentWindow.focus(); //See #154
+ return(false);
+ });
+
+ //handle event on update element
+ jQuery(this._options.updateSelector)
+ .bind(this._options.updateEvent, function() {
+ wym.update();
+ });
+};
+
+WYMeditor.editor.prototype.ready = function() {
+ return(this._doc != null);
+};
+
+
+/********** METHODS **********/
+
+/* @name box
+ * @description Returns the WYMeditor container
+ */
+WYMeditor.editor.prototype.box = function() {
+ return(this._box);
+};
+
+/* @name html
+ * @description Get/Set the html value
+ */
+WYMeditor.editor.prototype.html = function(html) {
+
+ if(typeof html === 'string') jQuery(this._doc.body).html(html);
+ else return(jQuery(this._doc.body).html());
+};
+
+/* @name xhtml
+ * @description Cleans up the HTML
+ */
+WYMeditor.editor.prototype.xhtml = function() {
+ return this.parser.parse(this.html());
+};
+
+/* @name exec
+ * @description Executes a button command
+ */
+WYMeditor.editor.prototype.exec = function(cmd) {
+
+ //base function for execCommand
+ //open a dialog or exec
+ switch(cmd) {
+ case WYMeditor.CREATE_LINK:
+ var container = this.container();
+ if(container || this._selected_image) this.dialog(WYMeditor.DIALOG_LINK);
+ break;
+
+ case WYMeditor.INSERT_IMAGE:
+ this.dialog(WYMeditor.DIALOG_IMAGE);
+ break;
+
+ case WYMeditor.INSERT_TABLE:
+ this.dialog(WYMeditor.DIALOG_TABLE);
+ break;
+
+ case WYMeditor.PASTE:
+ this.dialog(WYMeditor.DIALOG_PASTE);
+ break;
+
+ case WYMeditor.TOGGLE_HTML:
+ this.update();
+ this.toggleHtml();
+ break;
+
+ case WYMeditor.PREVIEW:
+ this.dialog(WYMeditor.PREVIEW, this._options.dialogFeaturesPreview);
+ break;
+
+ default:
+ this._exec(cmd);
+ break;
+ }
+};
+
+/* @name container
+ * @description Get/Set the selected container
+ */
+WYMeditor.editor.prototype.container = function(sType) {
+
+ if(sType) {
+
+ var container = null;
+
+ if(sType.toLowerCase() == WYMeditor.TH) {
+
+ container = this.container();
+
+ //find the TD or TH container
+ switch(container.tagName.toLowerCase()) {
+
+ case WYMeditor.TD: case WYMeditor.TH:
+ break;
+ default:
+ var aTypes = new Array(WYMeditor.TD,WYMeditor.TH);
+ container = this.findUp(this.container(), aTypes);
+ break;
+ }
+
+ //if it exists, switch
+ if(container!=null) {
+
+ sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD;
+ this.switchTo(container,sType);
+ this.update();
+ }
+ } else {
+
+ //set the container type
+ var aTypes=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5,
+ WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE);
+ container = this.findUp(this.container(), aTypes);
+
+ if(container) {
+
+ var newNode = null;
+
+ //blockquotes must contain a block level element
+ if(sType.toLowerCase() == WYMeditor.BLOCKQUOTE) {
+
+ var blockquote = this.findUp(this.container(), WYMeditor.BLOCKQUOTE);
+
+ if(blockquote == null) {
+
+ newNode = this._doc.createElement(sType);
+ container.parentNode.insertBefore(newNode,container);
+ newNode.appendChild(container);
+ this.setFocusToNode(newNode.firstChild);
+
+ } else {
+
+ var nodes = blockquote.childNodes;
+ var lgt = nodes.length;
+ var firstNode = null;
+
+ if(lgt > 0) firstNode = nodes.item(0);
+ for(var x=0; x<lgt; x++) {
+ blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
+ }
+ blockquote.parentNode.removeChild(blockquote);
+ if(firstNode) this.setFocusToNode(firstNode);
+ }
+ }
+
+ else this.switchTo(container,sType);
+
+ this.update();
+ }
+ }
+ }
+ else return(this.selected());
+};
+
+/* @name toggleClass
+ * @description Toggles class on selected element, or one of its parents
+ */
+WYMeditor.editor.prototype.toggleClass = function(sClass, jqexpr) {
+
+ var container = (this._selected_image
+ ? this._selected_image
+ : jQuery(this.selected()));
+ container = jQuery(container).parentsOrSelf(jqexpr);
+ jQuery(container).toggleClass(sClass);
+
+ if(!jQuery(container).attr(WYMeditor.CLASS)) jQuery(container).removeAttr(this._class);
+
+};
+
+/* @name findUp
+ * @description Returns the first parent or self container, based on its type
+ */
+WYMeditor.editor.prototype.findUp = function(node, filter) {
+
+ //filter is a string or an array of strings
+
+ if(node) {
+
+ var tagname = node.tagName.toLowerCase();
+
+ if(typeof(filter) == WYMeditor.STRING) {
+
+ while(tagname != filter && tagname != WYMeditor.BODY) {
+
+ node = node.parentNode;
+ tagname = node.tagName.toLowerCase();
+ }
+
+ } else {
+
+ var bFound = false;
+
+ while(!bFound && tagname != WYMeditor.BODY) {
+ for(var i = 0; i < filter.length; i++) {
+ if(tagname == filter[i]) {
+ bFound = true;
+ break;
+ }
+ }
+ if(!bFound) {
+ node = node.parentNode;
+ tagname = node.tagName.toLowerCase();
+ }
+ }
+ }
+
+ if(tagname != WYMeditor.BODY) return(node);
+ else return(null);
+
+ } else return(null);
+};
+
+/* @name switchTo
+ * @description Switch the node's type
+ */
+WYMeditor.editor.prototype.switchTo = function(node,sType) {
+
+ var newNode = this._doc.createElement(sType);
+ var html = jQuery(node).html();
+ node.parentNode.replaceChild(newNode,node);
+ jQuery(newNode).html(html);
+ this.setFocusToNode(newNode);
+};
+
+WYMeditor.editor.prototype.replaceStrings = function(sVal) {
+ //check if the language file has already been loaded
+ //if not, get it via a synchronous ajax call
+ if(!WYMeditor.STRINGS[this._options.lang]) {
+ try {
+ eval(jQuery.ajax({url:this._options.langPath
+ + this._options.lang + '.js', async:false}).responseText);
+ } catch(e) {
+ WYMeditor.console.error("WYMeditor: error while parsing language file.");
+ return sVal;
+ }
+ }
+
+ //replace all the strings in sVal and return it
+ for (var key in WYMeditor.STRINGS[this._options.lang]) {
+ sVal = WYMeditor.Helper.replaceAll(sVal, this._options.stringDelimiterLeft + key
+ + this._options.stringDelimiterRight,
+ WYMeditor.STRINGS[this._options.lang][key]);
+ };
+ return(sVal);
+};
+
+WYMeditor.editor.prototype.encloseString = function(sVal) {
+
+ return(this._options.stringDelimiterLeft
+ + sVal
+ + this._options.stringDelimiterRight);
+};
+
+/* @name status
+ * @description Prints a status message
+ */
+WYMeditor.editor.prototype.status = function(sMessage) {
+
+ //print status message
+ jQuery(this._box).find(this._options.statusSelector).html(sMessage);
+};
+
+/* @name update
+ * @description Updates the element and textarea values
+ */
+WYMeditor.editor.prototype.update = function() {
+ var html;
+
+ // Dirty fix to remove stray line breaks (#189)
+ jQuery(this._doc.body).children(WYMeditor.BR).remove();
+
+ html = this.xhtml();
+ jQuery(this._element).val(html);
+ jQuery(this._box).find(this._options.htmlValSelector).not('.hasfocus').val(html); //#147
+};
+
+/* @name dialog
+ * @description Opens a dialog box
+ */
+WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
+
+ var features = dialogFeatures || this._wym._options.dialogFeatures;
+ var wDialog = window.open('', 'dialog', features);
+
+ if(wDialog) {
+
+ var sBodyHtml = "";
+
+ switch( dialogType ) {
+
+ case(WYMeditor.DIALOG_LINK):
+ sBodyHtml = this._options.dialogLinkHtml;
+ break;
+ case(WYMeditor.DIALOG_IMAGE):
+ sBodyHtml = this._options.dialogImageHtml;
+ break;
+ case(WYMeditor.DIALOG_TABLE):
+ sBodyHtml = this._options.dialogTableHtml;
+ break;
+ case(WYMeditor.DIALOG_PASTE):
+ sBodyHtml = this._options.dialogPasteHtml;
+ break;
+ case(WYMeditor.PREVIEW):
+ sBodyHtml = this._options.dialogPreviewHtml;
+ break;
+
+ default:
+ sBodyHtml = bodyHtml;
+ }
+
+ var h = WYMeditor.Helper;
+
+ //construct the dialog
+ var dialogHtml = this._options.dialogHtml;
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.BASE_PATH, this._options.basePath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIRECTION, this._options.direction);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.CSS_PATH, this._options.skinPath + WYMeditor.SKINS_DEFAULT_CSS);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.WYM_PATH, this._options.wymPath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.JQUERY_PATH, this._options.jQueryPath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_TITLE, this.encloseString( dialogType ));
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_BODY, sBodyHtml);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.INDEX, this._index);
+
+ dialogHtml = this.replaceStrings(dialogHtml);
+
+ var doc = wDialog.document;
+ doc.write(dialogHtml);
+ doc.close();
+ }
+};
+
+/* @name toggleHtml
+ * @description Shows/Hides the HTML
+ */
+WYMeditor.editor.prototype.toggleHtml = function() {
+ jQuery(this._box).find(this._options.htmlSelector).toggle();
+};
+
+WYMeditor.editor.prototype.uniqueStamp = function() {
+ var now = new Date();
+ return("wym-" + now.getTime());
+};
+
+/* @name paste
+ * @description Paste text into the editor below the carret,
+ * used for "Paste from Word".
+ * @param String str String to insert, two or more newlines separates
+ * paragraphs. May contain inline HTML.
+ */
+WYMeditor.editor.prototype.paste = function(str) {
+ var container = this.selected(),
+ html = '',
+ paragraphs,
+ focusNode;
+
+ // Split string into paragraphs by two or more newlines
+ paragraphs = str.split(new RegExp(this._newLine + '{2,}', 'g'));
+
+ // Build html
+ for (var i=0, l=paragraphs.length; i < l; i++) {
+ html += '<p>' +
+ ( paragraphs[i].split(this._newLine).join('<br />') ) +
+ '</p>';
+ }
+
+ // Insert where appropriate
+ if (container && container.tagName.toLowerCase() != WYMeditor.BODY) {
+ // No .last() pre jQuery 1.4
+ //focusNode = jQuery(html).insertAfter(container).last()[0];
+ paragraphs = jQuery(html, this._doc).insertAfter(container);
+ focusNode = paragraphs[paragraphs.length - 1];
+ } else {
+ paragraphs = jQuery(html, this._doc).appendTo(this._doc.body);
+ focusNode = paragraphs[paragraphs.length - 1];
+ }
+
+ // Do some minor cleanup (#131)
+ if (jQuery(container).text() == '') {
+ jQuery(container).remove();
+ }
+ // And remove br (if editor was empty)
+ jQuery('body > br', this._doc).remove();
+
+ // Restore focus
+ this.setFocusToNode(focusNode);
+};
+
+WYMeditor.editor.prototype.insert = function(html) {
+ // Do we have a selection?
+ var selection = this._iframe.contentWindow.getSelection(),
+ range,
+ node;
+ if (selection.focusNode != null) {
+ // Overwrite selection with provided html
+ range = selection.getRangeAt(0);
+ node = range.createContextualFragment(html);
+ range.deleteContents();
+ range.insertNode(node);
+ } else {
+ // Fall back to the internal paste function if there's no selection
+ this.paste(html)
+ }
+};
+
+WYMeditor.editor.prototype.wrap = function(left, right) {
+ this.insert(left + this._iframe.contentWindow.getSelection().toString() + right);
+};
+
+WYMeditor.editor.prototype.unwrap = function() {
+ this.insert(this._iframe.contentWindow.getSelection().toString());
+};
+
+WYMeditor.editor.prototype.setFocusToNode = function(node, toStart) {
+ var range = this._doc.createRange(),
+ selection = this._iframe.contentWindow.getSelection();
+ toStart = toStart ? 0 : 1;
+
+ range.selectNodeContents(node);
+ selection.addRange(range);
+ selection.collapse(node, toStart);
+ this._iframe.contentWindow.focus();
+};
+
+WYMeditor.editor.prototype.addCssRules = function(doc, aCss) {
+ var styles = doc.styleSheets[0];
+ if(styles) {
+ for(var i = 0; i < aCss.length; i++) {
+ var oCss = aCss[i];
+ if(oCss.name && oCss.css) this.addCssRule(styles, oCss);
+ }
+ }
+};
+
+/********** CONFIGURATION **********/
+
+WYMeditor.editor.prototype.computeBasePath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src').replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/, '');
+};
+
+WYMeditor.editor.prototype.computeWymPath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src');
+};
+
+WYMeditor.editor.prototype.computeJqueryPath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src');
+};
+
+WYMeditor.editor.prototype.computeCssPath = function() {
+ return jQuery(jQuery.grep(jQuery('link'), function(s){
+ return (s.href && s.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/ ))
+ })).attr('href');
+};
+
+WYMeditor.editor.prototype.configureEditorUsingRawCss = function() {
+
+ var CssParser = new WYMeditor.WymCssParser();
+ if(this._options.stylesheet){
+ CssParser.parse(jQuery.ajax({url: this._options.stylesheet,async:false}).responseText);
+ }else{
+ CssParser.parse(this._options.styles, false);
+ }
+
+ if(this._options.classesItems.length == 0) {
+ this._options.classesItems = CssParser.css_settings.classesItems;
+ }
+ if(this._options.editorStyles.length == 0) {
+ this._options.editorStyles = CssParser.css_settings.editorStyles;
+ }
+ if(this._options.dialogStyles.length == 0) {
+ this._options.dialogStyles = CssParser.css_settings.dialogStyles;
+ }
+};
+
+/********** EVENTS **********/
+
+WYMeditor.editor.prototype.listen = function() {
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ //jQuery(this._doc.body).find("*").bind("mouseup", this.mouseup);
+
+ jQuery(this._doc.body).bind("mousedown", this.mousedown);
+};
+
+WYMeditor.editor.prototype.mousedown = function(evt) {
+ var wym = WYMeditor.INSTANCES[this.ownerDocument.title];
+ wym._selected_image = (evt.target.tagName.toLowerCase() == WYMeditor.IMG) ? evt.target : null;
+};
+
+/********** SKINS **********/
+
+/*
+ * Function: WYMeditor.loadCss
+ * Loads a stylesheet in the document.
+ *
+ * Parameters:
+ * href - The CSS path.
+ */
+WYMeditor.loadCss = function(href) {
+
+ var link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = href;
+
+ var head = jQuery('head').get(0);
+ head.appendChild(link);
+};
+
+/*
+ * Function: WYMeditor.editor.loadSkin
+ * Loads the skin CSS and initialization script (if needed).
+ */
+WYMeditor.editor.prototype.loadSkin = function() {
+
+ //does the user want to automatically load the CSS (default: yes)?
+ //we also test if it hasn't been already loaded by another instance
+ //see below for a better (second) test
+ if(this._options.loadSkin && !WYMeditor.SKINS[this._options.skin]) {
+
+ //check if it hasn't been already loaded
+ //so we don't load it more than once
+ //(we check the existing <link> elements)
+
+ var found = false;
+ var rExp = new RegExp(this._options.skin
+ + '\/' + WYMeditor.SKINS_DEFAULT_CSS + '$');
+
+ jQuery('link').each( function() {
+ if(this.href.match(rExp)) found = true;
+ });
+
+ //load it, using the skin path
+ if(!found) WYMeditor.loadCss( this._options.skinPath
+ + WYMeditor.SKINS_DEFAULT_CSS );
+ }
+
+ //put the classname (ex. wym_skin_default) on wym_box
+ jQuery(this._box).addClass( "wym_skin_" + this._options.skin );
+
+ //does the user want to use some JS to initialize the skin (default: yes)?
+ //also check if it hasn't already been loaded by another instance
+ if(this._options.initSkin && !WYMeditor.SKINS[this._options.skin]) {
+
+ eval(jQuery.ajax({url:this._options.skinPath
+ + WYMeditor.SKINS_DEFAULT_JS, async:false}).responseText);
+ }
+
+ //init the skin, if needed
+ if(WYMeditor.SKINS[this._options.skin]
+ && WYMeditor.SKINS[this._options.skin].init)
+ WYMeditor.SKINS[this._options.skin].init(this);
+
+};
+
+
+/********** DIALOGS **********/
+
+WYMeditor.INIT_DIALOG = function(index) {
+
+ var wym = window.opener.WYMeditor.INSTANCES[index];
+ var doc = window.document;
+ var selected = wym.selected();
+ var dialogType = jQuery(wym._options.dialogTypeSelector).val();
+ var sStamp = wym.uniqueStamp();
+
+ switch(dialogType) {
+
+ case WYMeditor.DIALOG_LINK:
+ //ensure that we select the link to populate the fields
+ if(selected && selected.tagName && selected.tagName.toLowerCase != WYMeditor.A)
+ selected = jQuery(selected).parentsOrSelf(WYMeditor.A);
+
+ //fix MSIE selection if link image has been clicked
+ if(!selected && wym._selected_image)
+ selected = jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A);
+ break;
+
+ }
+
+ //pre-init functions
+ if(jQuery.isFunction(wym._options.preInitDialog))
+ wym._options.preInitDialog(wym,window);
+
+ //add css rules from options
+ var styles = doc.styleSheets[0];
+ var aCss = eval(wym._options.dialogStyles);
+
+ wym.addCssRules(doc, aCss);
+
+ //auto populate fields if selected container (e.g. A)
+ if(selected) {
+ jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF));
+ jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC));
+ jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE));
+ jQuery(wym._options.relSelector).val(jQuery(selected).attr(WYMeditor.REL));
+ jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT));
+ }
+
+ //auto populate image fields if selected image
+ if(wym._selected_image) {
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.srcSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.SRC));
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.titleSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.TITLE));
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.altSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.ALT));
+ }
+
+ jQuery(wym._options.dialogLinkSelector + " "
+ + wym._options.submitSelector).submit(function() {
+
+ var sUrl = jQuery(wym._options.hrefSelector).val();
+ if(sUrl.length > 0) {
+ var link;
+
+ if (selected[0] && selected[0].tagName.toLowerCase() == WYMeditor.A) {
+ link = selected;
+ } else {
+ wym._exec(WYMeditor.CREATE_LINK, sStamp);
+ link = jQuery("a[href=" + sStamp + "]", wym._doc.body);
+ }
+
+ link.attr(WYMeditor.HREF, sUrl)
+ .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val())
+ .attr(WYMeditor.REL, jQuery(wym._options.relSelector).val());
+
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogImageSelector + " "
+ + wym._options.submitSelector).submit(function() {
+
+ var sUrl = jQuery(wym._options.srcSelector).val();
+ if(sUrl.length > 0) {
+
+ wym._exec(WYMeditor.INSERT_IMAGE, sStamp);
+
+ jQuery("img[src$=" + sStamp + "]", wym._doc.body)
+ .attr(WYMeditor.SRC, sUrl)
+ .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val())
+ .attr(WYMeditor.ALT, jQuery(wym._options.altSelector).val());
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogTableSelector + " "
+ + wym._options.submitSelector).submit(function() {
+
+ var iRows = jQuery(wym._options.rowsSelector).val();
+ var iCols = jQuery(wym._options.colsSelector).val();
+
+ if(iRows > 0 && iCols > 0) {
+
+ var table = wym._doc.createElement(WYMeditor.TABLE);
+ var newRow = null;
+ var newCol = null;
+
+ var sCaption = jQuery(wym._options.captionSelector).val();
+
+ //we create the caption
+ var newCaption = table.createCaption();
+ newCaption.innerHTML = sCaption;
+
+ //we create the rows and cells
+ for(x=0; x<iRows; x++) {
+ newRow = table.insertRow(x);
+ for(y=0; y<iCols; y++) {newRow.insertCell(y);}
+ }
+
+ //set the summary attr
+ jQuery(table).attr('summary',
+ jQuery(wym._options.summarySelector).val());
+
+ //append the table after the selected container
+ var node = jQuery(wym.findUp(wym.container(),
+ WYMeditor.MAIN_CONTAINERS)).get(0);
+ if(!node || !node.parentNode) jQuery(wym._doc.body).append(table);
+ else jQuery(node).after(table);
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogPasteSelector + " "
+ + wym._options.submitSelector).submit(function() {
+
+ var sText = jQuery(wym._options.textSelector).val();
+ wym.paste(sText);
+ window.close();
+ });
+
+ jQuery(wym._options.dialogPreviewSelector + " "
+ + wym._options.previewSelector)
+ .html(wym.xhtml());
+
+ //cancel button
+ jQuery(wym._options.cancelSelector).mousedown(function() {
+ window.close();
+ });
+
+ //pre-init functions
+ if(jQuery.isFunction(wym._options.postInitDialog))
+ wym._options.postInitDialog(wym,window);
+
+};
+
+/********** XHTML LEXER/PARSER **********/
+
+/*
+* @name xml
+* @description Use these methods to generate XML and XHTML compliant tags and
+* escape tag attributes correctly
+* @author Bermi Ferrer - http://bermi.org
+* @author David Heinemeier Hansson http://loudthinking.com
+*/
+WYMeditor.XmlHelper = function()
+{
+ this._entitiesDiv = document.createElement('div');
+ return this;
+};
+
+
+/*
+* @name tag
+* @description
+* Returns an empty HTML tag of type *name* which by default is XHTML
+* compliant. Setting *open* to true will create an open tag compatible
+* with HTML 4.0 and below. Add HTML attributes by passing an attributes
+* array to *options*. For attributes with no value like (disabled and
+* readonly), give it a value of true in the *options* array.
+*
+* Examples:
+*
+* this.tag('br')
+* # => <br />
+* this.tag ('br', false, true)
+* # => <br>
+* this.tag ('input', jQuery({type:'text',disabled:true }) )
+* # => <input type="text" disabled="disabled" />
+*/
+WYMeditor.XmlHelper.prototype.tag = function(name, options, open)
+{
+ options = options || false;
+ open = open || false;
+ return '<'+name+(options ? this.tagOptions(options) : '')+(open ? '>' : ' />');
+};
+
+/*
+* @name contentTag
+* @description
+* Returns a XML block tag of type *name* surrounding the *content*. Add
+* XML attributes by passing an attributes array to *options*. For attributes
+* with no value like (disabled and readonly), give it a value of true in
+* the *options* array. You can use symbols or strings for the attribute names.
+*
+* this.contentTag ('p', 'Hello world!' )
+* # => <p>Hello world!</p>
+* this.contentTag('div', this.contentTag('p', "Hello world!"), jQuery({class : "strong"}))
+* # => <div class="strong"><p>Hello world!</p></div>
+* this.contentTag("select", options, jQuery({multiple : true}))
+* # => <select multiple="multiple">...options...</select>
+*/
+WYMeditor.XmlHelper.prototype.contentTag = function(name, content, options)
+{
+ options = options || false;
+ return '<'+name+(options ? this.tagOptions(options) : '')+'>'+content+'</'+name+'>';
+};
+
+/*
+* @name cdataSection
+* @description
+* Returns a CDATA section for the given +content+. CDATA sections
+* are used to escape blocks of text containing characters which would
+* otherwise be recognized as markup. CDATA sections begin with the string
+* <tt>&lt;![CDATA[</tt> and } with (and may not contain) the string
+* <tt>]]></tt>.
+*/
+WYMeditor.XmlHelper.prototype.cdataSection = function(content)
+{
+ return '<![CDATA['+content+']]>';
+};
+
+
+/*
+* @name escapeOnce
+* @description
+* Returns the escaped +xml+ without affecting existing escaped entities.
+*
+* this.escapeOnce( "1 > 2 &amp; 3")
+* # => "1 &gt; 2 &amp; 3"
+*/
+WYMeditor.XmlHelper.prototype.escapeOnce = function(xml)
+{
+ return this._fixDoubleEscape(this.escapeEntities(xml));
+};
+
+/*
+* @name _fixDoubleEscape
+* @description
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc.
+*/
+WYMeditor.XmlHelper.prototype._fixDoubleEscape = function(escaped)
+{
+ return escaped.replace(/&amp;([a-z]+|(#\d+));/ig, "&$1;");
+};
+
+/*
+* @name tagOptions
+* @description
+* Takes an array like the one generated by Tag.parseAttributes
+* [["src", "http://www.editam.com/?a=b&c=d&amp;f=g"], ["title", "Editam, <Simplified> CMS"]]
+* or an object like {src:"http://www.editam.com/?a=b&c=d&amp;f=g", title:"Editam, <Simplified> CMS"}
+* and returns a string properly escaped like
+* ' src = "http://www.editam.com/?a=b&amp;c=d&amp;f=g" title = "Editam, &lt;Simplified&gt; CMS"'
+* which is valid for strict XHTML
+*/
+WYMeditor.XmlHelper.prototype.tagOptions = function(options)
+{
+ var xml = this;
+ xml._formated_options = '';
+
+ for (var key in options) {
+ var formated_options = '';
+ var value = options[key];
+ if(typeof value != 'function' && value.length > 0) {
+
+ if(parseInt(key) == key && typeof value == 'object'){
+ key = value.shift();
+ value = value.pop();
+ }
+ if(key != '' && value != ''){
+ xml._formated_options += ' '+key+'="'+xml.escapeOnce(value)+'"';
+ }
+ }
+ }
+ return xml._formated_options;
+};
+
+/*
+* @name escapeEntities
+* @description
+* Escapes XML/HTML entities <, >, & and ". If seccond parameter is set to false it
+* will not escape ". If set to true it will also escape '
+*/
+WYMeditor.XmlHelper.prototype.escapeEntities = function(string, escape_quotes)
+{
+ this._entitiesDiv.innerHTML = string;
+ this._entitiesDiv.textContent = string;
+ var result = this._entitiesDiv.innerHTML;
+ if(typeof escape_quotes == 'undefined'){
+ if(escape_quotes != false) result = result.replace('"', '&quot;');
+ if(escape_quotes == true) result = result.replace('"', '&#039;');
+ }
+ return result;
+};
+
+/*
+* Parses a string conatining tag attributes and values an returns an array formated like
+* [["src", "http://www.editam.com"], ["title", "Editam, Simplified CMS"]]
+*/
+WYMeditor.XmlHelper.prototype.parseAttributes = function(tag_attributes)
+{
+ // Use a compounded regex to match single quoted, double quoted and unquoted attribute pairs
+ var result = [];
+ var matches = tag_attributes.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);
+ if(matches.toString() != tag_attributes){
+ for (var k in matches) {
+ var v = matches[k];
+ if(typeof v != 'function' && v.length != 0){
+ var re = new RegExp('(\\w+)\\s*'+v);
+ if(match = tag_attributes.match(re) ){
+ var value = v.replace(/^[\s=]+/, "");
+ var delimiter = value.charAt(0);
+ delimiter = delimiter == '"' ? '"' : (delimiter=="'"?"'":'');
+ if(delimiter != ''){
+ value = delimiter == '"' ? value.replace(/^"|"+$/g, '') : value.replace(/^'|'+$/g, '');
+ }
+ tag_attributes = tag_attributes.replace(match[0],'');
+ result.push([match[1] , value]);
+ }
+ }
+ }
+ }
+ return result;
+};
+
+/**
+* XhtmlValidator for validating tag attributes
+*
+* @author Bermi Ferrer - http://bermi.org
+*/
+WYMeditor.XhtmlValidator = {
+ "_attributes":
+ {
+ "core":
+ {
+ "except":[
+ "base",
+ "head",
+ "html",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "class",
+ "id",
+ "style",
+ "title",
+ "accesskey",
+ "tabindex"
+ ]
+ },
+ "language":
+ {
+ "except":[
+ "base",
+ "br",
+ "hr",
+ "iframe",
+ "param",
+ "script"
+ ],
+ "attributes":
+ {
+ "dir":[
+ "ltr",
+ "rtl"
+ ],
+ "0":"lang",
+ "1":"xml:lang"
+ }
+ },
+ "keyboard":
+ {
+ "attributes":
+ {
+ "accesskey":/^(\w){1}$/,
+ "tabindex":/^(\d)+$/
+ }
+ }
+ },
+ "_events":
+ {
+ "window":
+ {
+ "only":[
+ "body"
+ ],
+ "attributes":[
+ "onload",
+ "onunload"
+ ]
+ },
+ "form":
+ {
+ "only":[
+ "form",
+ "input",
+ "textarea",
+ "select",
+ "a",
+ "label",
+ "button"
+ ],
+ "attributes":[
+ "onchange",
+ "onsubmit",
+ "onreset",
+ "onselect",
+ "onblur",
+ "onfocus"
+ ]
+ },
+ "keyboard":
+ {
+ "except":[
+ "base",
+ "bdo",
+ "br",
+ "frame",
+ "frameset",
+ "head",
+ "html",
+ "iframe",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "onkeydown",
+ "onkeypress",
+ "onkeyup"
+ ]
+ },
+ "mouse":
+ {
+ "except":[
+ "base",
+ "bdo",
+ "br",
+ "head",
+ "html",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "onclick",
+ "ondblclick",
+ "onmousedown",
+ "onmousemove",
+ "onmouseover",
+ "onmouseout",
+ "onmouseup"
+ ]
+ }
+ },
+ "_tags":
+ {
+ "a":
+ {
+ "attributes":
+ {
+ "0":"charset",
+ "1":"coords",
+ "2":"href",
+ "3":"hreflang",
+ "4":"name",
+ "5":"rel",
+ "6":"rev",
+ "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/,
+ "7":"type"
+ }
+ },
+ "0":"abbr",
+ "1":"acronym",
+ "2":"address",
+ "area":
+ {
+ "attributes":
+ {
+ "0":"alt",
+ "1":"coords",
+ "2":"href",
+ "nohref":/^(true|false)$/,
+ "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/
+ },
+ "required":[
+ "alt"
+ ]
+ },
+ "3":"b",
+ "base":
+ {
+ "attributes":[
+ "href"
+ ],
+ "required":[
+ "href"
+ ]
+ },
+ "bdo":
+ {
+ "attributes":
+ {
+ "dir":/^(ltr|rtl)$/
+ },
+ "required":[
+ "dir"
+ ]
+ },
+ "4":"big",
+ "blockquote":
+ {
+ "attributes":[
+ "cite"
+ ]
+ },
+ "5":"body",
+ "6":"br",
+ "button":
+ {
+ "attributes":
+ {
+ "disabled":/^(disabled)$/,
+ "type":/^(button|reset|submit)$/,
+ "0":"value"
+ },
+ "inside":"form"
+ },
+ "7":"caption",
+ "8":"cite",
+ "9":"code",
+ "col":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "span":/^(\d)+$/,
+ "valign":/^(top|middle|bottom|baseline)$/,
+ "2":"width"
+ },
+ "inside":"colgroup"
+ },
+ "colgroup":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "span":/^(\d)+$/,
+ "valign":/^(top|middle|bottom|baseline)$/,
+ "2":"width"
+ }
+ },
+ "10":"dd",
+ "del":
+ {
+ "attributes":
+ {
+ "0":"cite",
+ "datetime":/^([0-9]){8}/
+ }
+ },
+ "11":"div",
+ "12":"dfn",
+ "13":"dl",
+ "14":"dt",
+ "15":"em",
+ "fieldset":
+ {
+ "inside":"form"
+ },
+ "form":
+ {
+ "attributes":
+ {
+ "0":"action",
+ "1":"accept",
+ "2":"accept-charset",
+ "3":"enctype",
+ "method":/^(get|post)$/
+ },
+ "required":[
+ "action"
+ ]
+ },
+ "head":
+ {
+ "attributes":[
+ "profile"
+ ]
+ },
+ "16":"h1",
+ "17":"h2",
+ "18":"h3",
+ "19":"h4",
+ "20":"h5",
+ "21":"h6",
+ "22":"hr",
+ "html":
+ {
+ "attributes":[
+ "xmlns"
+ ]
+ },
+ "23":"i",
+ "img":
+ {
+ "attributes":[
+ "alt",
+ "src",
+ "height",
+ "ismap",
+ "longdesc",
+ "usemap",
+ "width"
+ ],
+ "required":[
+ "alt",
+ "src"
+ ]
+ },
+ "input":
+ {
+ "attributes":
+ {
+ "0":"accept",
+ "1":"alt",
+ "checked":/^(checked)$/,
+ "disabled":/^(disabled)$/,
+ "maxlength":/^(\d)+$/,
+ "2":"name",
+ "readonly":/^(readonly)$/,
+ "size":/^(\d)+$/,
+ "3":"src",
+ "type":/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/,
+ "4":"value"
+ },
+ "inside":"form"
+ },
+ "ins":
+ {
+ "attributes":
+ {
+ "0":"cite",
+ "datetime":/^([0-9]){8}/
+ }
+ },
+ "24":"kbd",
+ "label":
+ {
+ "attributes":[
+ "for"
+ ],
+ "inside":"form"
+ },
+ "25":"legend",
+ "26":"li",
+ "link":
+ {
+ "attributes":
+ {
+ "0":"charset",
+ "1":"href",
+ "2":"hreflang",
+ "media":/^(all|braille|print|projection|screen|speech|,|;| )+$/i,
+ //next comment line required by Opera!
+ /*"rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,*/
+ "rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
+ "rev":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
+ "3":"type"
+ },
+ "inside":"head"
+ },
+ "map":
+ {
+ "attributes":[
+ "id",
+ "name"
+ ],
+ "required":[
+ "id"
+ ]
+ },
+ "meta":
+ {
+ "attributes":
+ {
+ "0":"content",
+ "http-equiv":/^(content\-type|expires|refresh|set\-cookie)$/i,
+ "1":"name",
+ "2":"scheme"
+ },
+ "required":[
+ "content"
+ ]
+ },
+ "27":"noscript",
+ "object":
+ {
+ "attributes":[
+ "archive",
+ "classid",
+ "codebase",
+ "codetype",
+ "data",
+ "declare",
+ "height",
+ "name",
+ "standby",
+ "type",
+ "usemap",
+ "width"
+ ]
+ },
+ "28":"ol",
+ "optgroup":
+ {
+ "attributes":
+ {
+ "0":"label",
+ "disabled": /^(disabled)$/
+ },
+ "required":[
+ "label"
+ ]
+ },
+ "option":
+ {
+ "attributes":
+ {
+ "0":"label",
+ "disabled":/^(disabled)$/,
+ "selected":/^(selected)$/,
+ "1":"value"
+ },
+ "inside":"select"
+ },
+ "29":"p",
+ "param":
+ {
+ "attributes":
+ {
+ "0":"type",
+ "valuetype":/^(data|ref|object)$/,
+ "1":"valuetype",
+ "2":"value"
+ },
+ "required":[
+ "name"
+ ]
+ },
+ "30":"pre",
+ "q":
+ {
+ "attributes":[
+ "cite"
+ ]
+ },
+ "31":"samp",
+ "script":
+ {
+ "attributes":
+ {
+ "type":/^(text\/ecmascript|text\/javascript|text\/jscript|text\/vbscript|text\/vbs|text\/xml)$/,
+ "0":"charset",
+ "defer":/^(defer)$/,
+ "1":"src"
+ },
+ "required":[
+ "type"
+ ]
+ },
+ "select":
+ {
+ "attributes":
+ {
+ "disabled":/^(disabled)$/,
+ "multiple":/^(multiple)$/,
+ "0":"name",
+ "1":"size"
+ },
+ "inside":"form"
+ },
+ "32":"small",
+ "33":"span",
+ "34":"strong",
+ "style":
+ {
+ "attributes":
+ {
+ "0":"type",
+ "media":/^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/
+ },
+ "required":[
+ "type"
+ ]
+ },
+ "35":"sub",
+ "36":"sup",
+ "table":
+ {
+ "attributes":
+ {
+ "0":"border",
+ "1":"cellpadding",
+ "2":"cellspacing",
+ "frame":/^(void|above|below|hsides|lhs|rhs|vsides|box|border)$/,
+ "rules":/^(none|groups|rows|cols|all)$/,
+ "3":"summary",
+ "4":"width"
+ }
+ },
+ "tbody":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "td":
+ {
+ "attributes":
+ {
+ "0":"abbr",
+ "align":/^(left|right|center|justify|char)$/,
+ "1":"axis",
+ "2":"char",
+ "3":"charoff",
+ "colspan":/^(\d)+$/,
+ "4":"headers",
+ "rowspan":/^(\d)+$/,
+ "scope":/^(col|colgroup|row|rowgroup)$/,
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "textarea":
+ {
+ "attributes":[
+ "cols",
+ "rows",
+ "disabled",
+ "name",
+ "readonly"
+ ],
+ "required":[
+ "cols",
+ "rows"
+ ],
+ "inside":"form"
+ },
+ "tfoot":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom)$/,
+ "2":"baseline"
+ }
+ },
+ "th":
+ {
+ "attributes":
+ {
+ "0":"abbr",
+ "align":/^(left|right|center|justify|char)$/,
+ "1":"axis",
+ "2":"char",
+ "3":"charoff",
+ "colspan":/^(\d)+$/,
+ "4":"headers",
+ "rowspan":/^(\d)+$/,
+ "scope":/^(col|colgroup|row|rowgroup)$/,
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "thead":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "37":"title",
+ "tr":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify|char)$/,
+ "0":"char",
+ "1":"charoff",
+ "valign":/^(top|middle|bottom|baseline)$/
+ }
+ },
+ "38":"tt",
+ "39":"ul",
+ "40":"var"
+ },
+
+ // Temporary skiped attributes
+ skiped_attributes : [],
+ skiped_attribute_values : [],
+
+ getValidTagAttributes: function(tag, attributes)
+ {
+ var valid_attributes = {};
+ var possible_attributes = this.getPossibleTagAttributes(tag);
+ for(var attribute in attributes) {
+ var value = attributes[attribute];
+ var h = WYMeditor.Helper;
+ if(!h.contains(this.skiped_attributes, attribute) && !h.contains(this.skiped_attribute_values, value)){
+ if (typeof value != 'function' && h.contains(possible_attributes, attribute)) {
+ if (this.doesAttributeNeedsValidation(tag, attribute)) {
+ if(this.validateAttribute(tag, attribute, value)){
+ valid_attributes[attribute] = value;
+ }
+ }else{
+ valid_attributes[attribute] = value;
+ }
+ }
+ }
+ }
+ return valid_attributes;
+ },
+ getUniqueAttributesAndEventsForTag : function(tag)
+ {
+ var result = [];
+
+ if (this._tags[tag] && this._tags[tag]['attributes']) {
+ for (k in this._tags[tag]['attributes']) {
+ result.push(parseInt(k) == k ? this._tags[tag]['attributes'][k] : k);
+ }
+ }
+ return result;
+ },
+ getDefaultAttributesAndEventsForTags : function()
+ {
+ var result = [];
+ for (var key in this._events){
+ result.push(this._events[key]);
+ }
+ for (var key in this._attributes){
+ result.push(this._attributes[key]);
+ }
+ return result;
+ },
+ isValidTag : function(tag)
+ {
+ if(this._tags[tag]){
+ return true;
+ }
+ for(var key in this._tags){
+ if(this._tags[key] == tag){
+ return true;
+ }
+ }
+ return false;
+ },
+ getDefaultAttributesAndEventsForTag : function(tag)
+ {
+ var default_attributes = [];
+ if (this.isValidTag(tag)) {
+ var default_attributes_and_events = this.getDefaultAttributesAndEventsForTags();
+
+ for(var key in default_attributes_and_events) {
+ var defaults = default_attributes_and_events[key];
+ if(typeof defaults == 'object'){
+ var h = WYMeditor.Helper;
+ if ((defaults['except'] && h.contains(defaults['except'], tag)) || (defaults['only'] && !h.contains(defaults['only'], tag))) {
+ continue;
+ }
+
+ var tag_defaults = defaults['attributes'] ? defaults['attributes'] : defaults['events'];
+ for(var k in tag_defaults) {
+ default_attributes.push(typeof tag_defaults[k] != 'string' ? k : tag_defaults[k]);
+ }
+ }
+ }
+ }
+ return default_attributes;
+ },
+ doesAttributeNeedsValidation: function(tag, attribute)
+ {
+ return this._tags[tag] && ((this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute]) || (this._tags[tag]['required'] &&
+ WYMeditor.Helper.contains(this._tags[tag]['required'], attribute)));
+ },
+ validateAttribute : function(tag, attribute, value)
+ {
+ if ( this._tags[tag] &&
+ (this._tags[tag]['attributes'] && this._tags[tag]['attributes'][attribute] && value.length > 0 && !value.match(this._tags[tag]['attributes'][attribute])) || // invalid format
+ (this._tags[tag] && this._tags[tag]['required'] && WYMeditor.Helper.contains(this._tags[tag]['required'], attribute) && value.length == 0) // required attribute
+ ) {
+ return false;
+ }
+ return typeof this._tags[tag] != 'undefined';
+ },
+ getPossibleTagAttributes : function(tag)
+ {
+ if (!this._possible_tag_attributes) {
+ this._possible_tag_attributes = {};
+ }
+ if (!this._possible_tag_attributes[tag]) {
+ this._possible_tag_attributes[tag] = this.getUniqueAttributesAndEventsForTag(tag).concat(this.getDefaultAttributesAndEventsForTag(tag));
+ }
+ return this._possible_tag_attributes[tag];
+ }
+};
+
+
+/**
+* Compounded regular expression. Any of
+* the contained patterns could match and
+* when one does, it's label is returned.
+*
+* Constructor. Starts with no patterns.
+* @param boolean case True for case sensitive, false
+* for insensitive.
+* @access public
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.ParallelRegex = function(case_sensitive)
+{
+ this._case = case_sensitive;
+ this._patterns = [];
+ this._labels = [];
+ this._regex = null;
+ return this;
+};
+
+
+/**
+* Adds a pattern with an optional label.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string label Label of regex to be returned
+* on a match.
+* @access public
+*/
+WYMeditor.ParallelRegex.prototype.addPattern = function(pattern, label)
+{
+ label = label || true;
+ var count = this._patterns.length;
+ this._patterns[count] = pattern;
+ this._labels[count] = label;
+ this._regex = null;
+};
+
+/**
+* Attempts to match all patterns at once against
+* a string.
+* @param string subject String to match against.
+*
+* @return boolean True on success.
+* @return string match First matched portion of
+* subject.
+* @access public
+*/
+WYMeditor.ParallelRegex.prototype.match = function(subject)
+{
+ if (this._patterns.length == 0) {
+ return [false, ''];
+ }
+ var matches = subject.match(this._getCompoundedRegex());
+
+ if(!matches){
+ return [false, ''];
+ }
+ var match = matches[0];
+ for (var i = 1; i < matches.length; i++) {
+ if (matches[i]) {
+ return [this._labels[i-1], match];
+ }
+ }
+ return [true, matches[0]];
+};
+
+/**
+* Compounds the patterns into a single
+* regular expression separated with the
+* "or" operator. Caches the regex.
+* Will automatically escape (, ) and / tokens.
+* @param array patterns List of patterns in order.
+* @access private
+*/
+WYMeditor.ParallelRegex.prototype._getCompoundedRegex = function()
+{
+ if (this._regex == null) {
+ for (var i = 0, count = this._patterns.length; i < count; i++) {
+ this._patterns[i] = '(' + this._untokenizeRegex(this._tokenizeRegex(this._patterns[i]).replace(/([\/\(\)])/g,'\\$1')) + ')';
+ }
+ this._regex = new RegExp(this._patterns.join("|") ,this._getPerlMatchingFlags());
+ }
+ return this._regex;
+};
+
+/**
+* Escape lookahead/lookbehind blocks
+*/
+WYMeditor.ParallelRegex.prototype._tokenizeRegex = function(regex)
+{
+ return regex.
+ replace(/\(\?(i|m|s|x|U)\)/, '~~~~~~Tk1\$1~~~~~~').
+ replace(/\(\?(\-[i|m|s|x|U])\)/, '~~~~~~Tk2\$1~~~~~~').
+ replace(/\(\?\=(.*)\)/, '~~~~~~Tk3\$1~~~~~~').
+ replace(/\(\?\!(.*)\)/, '~~~~~~Tk4\$1~~~~~~').
+ replace(/\(\?\<\=(.*)\)/, '~~~~~~Tk5\$1~~~~~~').
+ replace(/\(\?\<\!(.*)\)/, '~~~~~~Tk6\$1~~~~~~').
+ replace(/\(\?\:(.*)\)/, '~~~~~~Tk7\$1~~~~~~');
+};
+
+/**
+* Unscape lookahead/lookbehind blocks
+*/
+WYMeditor.ParallelRegex.prototype._untokenizeRegex = function(regex)
+{
+ return regex.
+ replace(/~~~~~~Tk1(.{1})~~~~~~/, "(?\$1)").
+ replace(/~~~~~~Tk2(.{2})~~~~~~/, "(?\$1)").
+ replace(/~~~~~~Tk3(.*)~~~~~~/, "(?=\$1)").
+ replace(/~~~~~~Tk4(.*)~~~~~~/, "(?!\$1)").
+ replace(/~~~~~~Tk5(.*)~~~~~~/, "(?<=\$1)").
+ replace(/~~~~~~Tk6(.*)~~~~~~/, "(?<!\$1)").
+ replace(/~~~~~~Tk7(.*)~~~~~~/, "(?:\$1)");
+};
+
+
+/**
+* Accessor for perl regex mode flags to use.
+* @return string Perl regex flags.
+* @access private
+*/
+WYMeditor.ParallelRegex.prototype._getPerlMatchingFlags = function()
+{
+ return (this._case ? "m" : "mi");
+};
+
+
+
+/**
+* States for a stack machine.
+*
+* Constructor. Starts in named state.
+* @param string start Starting state name.
+* @access public
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.StateStack = function(start)
+{
+ this._stack = [start];
+ return this;
+};
+
+/**
+* Accessor for current state.
+* @return string State.
+* @access public
+*/
+WYMeditor.StateStack.prototype.getCurrent = function()
+{
+ return this._stack[this._stack.length - 1];
+};
+
+/**
+* Adds a state to the stack and sets it
+* to be the current state.
+* @param string state New state.
+* @access public
+*/
+WYMeditor.StateStack.prototype.enter = function(state)
+{
+ this._stack.push(state);
+};
+
+/**
+* Leaves the current state and reverts
+* to the previous one.
+* @return boolean False if we drop off
+* the bottom of the list.
+* @access public
+*/
+WYMeditor.StateStack.prototype.leave = function()
+{
+ if (this._stack.length == 1) {
+ return false;
+ }
+ this._stack.pop();
+ return true;
+};
+
+
+// GLOBALS
+WYMeditor.LEXER_ENTER = 1;
+WYMeditor.LEXER_MATCHED = 2;
+WYMeditor.LEXER_UNMATCHED = 3;
+WYMeditor.LEXER_EXIT = 4;
+WYMeditor.LEXER_SPECIAL = 5;
+
+
+/**
+* Accepts text and breaks it into tokens.
+* Some optimisation to make the sure the
+* content is only scanned by the PHP regex
+* parser once. Lexer modes must not start
+* with leading underscores.
+*
+* Sets up the lexer in case insensitive matching
+* by default.
+* @param Parser parser Handling strategy by reference.
+* @param string start Starting handler.
+* @param boolean case True for case sensitive.
+* @access public
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.Lexer = function(parser, start, case_sensitive)
+{
+ start = start || 'accept';
+ this._case = case_sensitive || false;
+ this._regexes = {};
+ this._parser = parser;
+ this._mode = new WYMeditor.StateStack(start);
+ this._mode_handlers = {};
+ this._mode_handlers[start] = start;
+ return this;
+};
+
+/**
+* Adds a token search pattern for a particular
+* parsing mode. The pattern does not change the
+* current mode.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Should only apply this
+* pattern when dealing with
+* this type of input.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addPattern = function(pattern, mode)
+{
+ var mode = mode || "accept";
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern);
+ if (typeof this._mode_handlers[mode] == 'undefined') {
+ this._mode_handlers[mode] = mode;
+ }
+};
+
+/**
+* Adds a pattern that will enter a new parsing
+* mode. Useful for entering parenthesis, strings,
+* tags, etc.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Should only apply this
+* pattern when dealing with
+* this type of input.
+* @param string new_mode Change parsing to this new
+* nested mode.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addEntryPattern = function(pattern, mode, new_mode)
+{
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern, new_mode);
+ if (typeof this._mode_handlers[new_mode] == 'undefined') {
+ this._mode_handlers[new_mode] = new_mode;
+ }
+};
+
+/**
+* Adds a pattern that will exit the current mode
+* and re-enter the previous one.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Mode to leave.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addExitPattern = function(pattern, mode)
+{
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern, "__exit");
+ if (typeof this._mode_handlers[mode] == 'undefined') {
+ this._mode_handlers[mode] = mode;
+ }
+};
+
+/**
+* Adds a pattern that has a special mode. Acts as an entry
+* and exit pattern in one go, effectively calling a special
+* parser handler for this token only.
+* @param string pattern Perl style regex, but ( and )
+* lose the usual meaning.
+* @param string mode Should only apply this
+* pattern when dealing with
+* this type of input.
+* @param string special Use this mode for this one token.
+* @access public
+*/
+WYMeditor.Lexer.prototype.addSpecialPattern = function(pattern, mode, special)
+{
+ if (typeof this._regexes[mode] == 'undefined') {
+ this._regexes[mode] = new WYMeditor.ParallelRegex(this._case);
+ }
+ this._regexes[mode].addPattern(pattern, '_'+special);
+ if (typeof this._mode_handlers[special] == 'undefined') {
+ this._mode_handlers[special] = special;
+ }
+};
+
+/**
+* Adds a mapping from a mode to another handler.
+* @param string mode Mode to be remapped.
+* @param string handler New target handler.
+* @access public
+*/
+WYMeditor.Lexer.prototype.mapHandler = function(mode, handler)
+{
+ this._mode_handlers[mode] = handler;
+};
+
+/**
+* Splits the page text into tokens. Will fail
+* if the handlers report an error or if no
+* content is consumed. If successful then each
+* unparsed and parsed token invokes a call to the
+* held listener.
+* @param string raw Raw HTML text.
+* @return boolean True on success, else false.
+* @access public
+*/
+WYMeditor.Lexer.prototype.parse = function(raw)
+{
+ if (typeof this._parser == 'undefined') {
+ return false;
+ }
+
+ var length = raw.length;
+ var parsed;
+ while (typeof (parsed = this._reduce(raw)) == 'object') {
+ var raw = parsed[0];
+ var unmatched = parsed[1];
+ var matched = parsed[2];
+ var mode = parsed[3];
+
+ if (! this._dispatchTokens(unmatched, matched, mode)) {
+ return false;
+ }
+
+ if (raw == '') {
+ return true;
+ }
+ if (raw.length == length) {
+ return false;
+ }
+ length = raw.length;
+ }
+ if (! parsed ) {
+ return false;
+ }
+
+ return this._invokeParser(raw, WYMeditor.LEXER_UNMATCHED);
+};
+
+/**
+* Sends the matched token and any leading unmatched
+* text to the parser changing the lexer to a new
+* mode if one is listed.
+* @param string unmatched Unmatched leading portion.
+* @param string matched Actual token match.
+* @param string mode Mode after match. A boolean
+* false mode causes no change.
+* @return boolean False if there was any error
+* from the parser.
+* @access private
+*/
+WYMeditor.Lexer.prototype._dispatchTokens = function(unmatched, matched, mode)
+{
+ mode = mode || false;
+
+ if (! this._invokeParser(unmatched, WYMeditor.LEXER_UNMATCHED)) {
+ return false;
+ }
+
+ if (typeof mode == 'boolean') {
+ return this._invokeParser(matched, WYMeditor.LEXER_MATCHED);
+ }
+ if (this._isModeEnd(mode)) {
+ if (! this._invokeParser(matched, WYMeditor.LEXER_EXIT)) {
+ return false;
+ }
+ return this._mode.leave();
+ }
+ if (this._isSpecialMode(mode)) {
+ this._mode.enter(this._decodeSpecial(mode));
+ if (! this._invokeParser(matched, WYMeditor.LEXER_SPECIAL)) {
+ return false;
+ }
+ return this._mode.leave();
+ }
+ this._mode.enter(mode);
+
+ return this._invokeParser(matched, WYMeditor.LEXER_ENTER);
+};
+
+/**
+* Tests to see if the new mode is actually to leave
+* the current mode and pop an item from the matching
+* mode stack.
+* @param string mode Mode to test.
+* @return boolean True if this is the exit mode.
+* @access private
+*/
+WYMeditor.Lexer.prototype._isModeEnd = function(mode)
+{
+ return (mode === "__exit");
+};
+
+/**
+* Test to see if the mode is one where this mode
+* is entered for this token only and automatically
+* leaves immediately afterwoods.
+* @param string mode Mode to test.
+* @return boolean True if this is the exit mode.
+* @access private
+*/
+WYMeditor.Lexer.prototype._isSpecialMode = function(mode)
+{
+ return (mode.substring(0,1) == "_");
+};
+
+/**
+* Strips the magic underscore marking single token
+* modes.
+* @param string mode Mode to decode.
+* @return string Underlying mode name.
+* @access private
+*/
+WYMeditor.Lexer.prototype._decodeSpecial = function(mode)
+{
+ return mode.substring(1);
+};
+
+/**
+* Calls the parser method named after the current
+* mode. Empty content will be ignored. The lexer
+* has a parser handler for each mode in the lexer.
+* @param string content Text parsed.
+* @param boolean is_match Token is recognised rather
+* than unparsed data.
+* @access private
+*/
+WYMeditor.Lexer.prototype._invokeParser = function(content, is_match)
+{
+
+ if (content === '') {
+ return true;
+ }
+ var current = this._mode.getCurrent();
+ var handler = this._mode_handlers[current];
+ var result;
+ eval('result = this._parser.' + handler + '(content, is_match);');
+ return result;
+};
+
+/**
+* Tries to match a chunk of text and if successful
+* removes the recognised chunk and any leading
+* unparsed data. Empty strings will not be matched.
+* @param string raw The subject to parse. This is the
+* content that will be eaten.
+* @return array/boolean Three item list of unparsed
+* content followed by the
+* recognised token and finally the
+* action the parser is to take.
+* True if no match, false if there
+* is a parsing error.
+* @access private
+*/
+WYMeditor.Lexer.prototype._reduce = function(raw)
+{
+ var matched = this._regexes[this._mode.getCurrent()].match(raw);
+ var match = matched[1];
+ var action = matched[0];
+ if (action) {
+ var unparsed_character_count = raw.indexOf(match);
+ var unparsed = raw.substr(0, unparsed_character_count);
+ raw = raw.substring(unparsed_character_count + match.length);
+ return [raw, unparsed, match, action];
+ }
+ return true;
+};
+
+
+
+/**
+* This are the rules for breaking the XHTML code into events
+* handled by the provided parser.
+*
+* @author Marcus Baker (http://lastcraft.com)
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.XhtmlLexer = function(parser)
+{
+ jQuery.extend(this, new WYMeditor.Lexer(parser, 'Text'));
+
+ this.mapHandler('Text', 'Text');
+
+ this.addTokens();
+
+ this.init();
+
+ return this;
+};
+
+
+WYMeditor.XhtmlLexer.prototype.init = function()
+{
+};
+
+WYMeditor.XhtmlLexer.prototype.addTokens = function()
+{
+ this.addCommentTokens('Text');
+ this.addScriptTokens('Text');
+ this.addCssTokens('Text');
+ this.addTagTokens('Text');
+};
+
+WYMeditor.XhtmlLexer.prototype.addCommentTokens = function(scope)
+{
+ this.addEntryPattern("<!--", scope, 'Comment');
+ this.addExitPattern("-->", 'Comment');
+};
+
+WYMeditor.XhtmlLexer.prototype.addScriptTokens = function(scope)
+{
+ this.addEntryPattern("<script", scope, 'Script');
+ this.addExitPattern("</script>", 'Script');
+};
+
+WYMeditor.XhtmlLexer.prototype.addCssTokens = function(scope)
+{
+ this.addEntryPattern("<style", scope, 'Css');
+ this.addExitPattern("</style>", 'Css');
+};
+
+WYMeditor.XhtmlLexer.prototype.addTagTokens = function(scope)
+{
+ this.addSpecialPattern("<\\s*[a-z0-9:\-]+\\s*>", scope, 'OpeningTag');
+ this.addEntryPattern("<[a-z0-9:\-]+"+'[\\\/ \\\>]+', scope, 'OpeningTag');
+ this.addInTagDeclarationTokens('OpeningTag');
+
+ this.addSpecialPattern("</\\s*[a-z0-9:\-]+\\s*>", scope, 'ClosingTag');
+
+};
+
+WYMeditor.XhtmlLexer.prototype.addInTagDeclarationTokens = function(scope)
+{
+ this.addSpecialPattern('\\s+', scope, 'Ignore');
+
+ this.addAttributeTokens(scope);
+
+ this.addExitPattern('/>', scope);
+ this.addExitPattern('>', scope);
+
+};
+
+WYMeditor.XhtmlLexer.prototype.addAttributeTokens = function(scope)
+{
+ this.addSpecialPattern("\\s*[a-z-_0-9]*:?[a-z-_0-9]+\\s*(?=\=)\\s*", scope, 'TagAttributes');
+
+ this.addEntryPattern('=\\s*"', scope, 'DoubleQuotedAttribute');
+ this.addPattern("\\\\\"", 'DoubleQuotedAttribute');
+ this.addExitPattern('"', 'DoubleQuotedAttribute');
+
+ this.addEntryPattern("=\\s*'", scope, 'SingleQuotedAttribute');
+ this.addPattern("\\\\'", 'SingleQuotedAttribute');
+ this.addExitPattern("'", 'SingleQuotedAttribute');
+
+ this.addSpecialPattern('=\\s*[^>\\s]*', scope, 'UnquotedAttribute');
+};
+
+
+
+/**
+* XHTML Parser.
+*
+* This XHTML parser will trigger the events available on on
+* current SaxListener
+*
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.XhtmlParser = function(Listener, mode)
+{
+ var mode = mode || 'Text';
+ this._Lexer = new WYMeditor.XhtmlLexer(this);
+ this._Listener = Listener;
+ this._mode = mode;
+ this._matches = [];
+ this._last_match = '';
+ this._current_match = '';
+
+ return this;
+};
+
+WYMeditor.XhtmlParser.prototype.parse = function(raw)
+{
+ this._Lexer.parse(this.beforeParsing(raw));
+ return this.afterParsing(this._Listener.getResult());
+};
+
+WYMeditor.XhtmlParser.prototype.beforeParsing = function(raw)
+{
+ if(raw.match(/class="MsoNormal"/) || raw.match(/ns = "urn:schemas-microsoft-com/)){
+ // Usefull for cleaning up content pasted from other sources (MSWord)
+ this._Listener.avoidStylingTagsAndAttributes();
+ }
+ return this._Listener.beforeParsing(raw);
+};
+
+WYMeditor.XhtmlParser.prototype.afterParsing = function(parsed)
+{
+ if(this._Listener._avoiding_tags_implicitly){
+ this._Listener.allowStylingTagsAndAttributes();
+ }
+ return this._Listener.afterParsing(parsed);
+};
+
+
+WYMeditor.XhtmlParser.prototype.Ignore = function(match, state)
+{
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.Text = function(text)
+{
+ this._Listener.addContent(text);
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.Comment = function(match, status)
+{
+ return this._addNonTagBlock(match, status, 'addComment');
+};
+
+WYMeditor.XhtmlParser.prototype.Script = function(match, status)
+{
+ return this._addNonTagBlock(match, status, 'addScript');
+};
+
+WYMeditor.XhtmlParser.prototype.Css = function(match, status)
+{
+ return this._addNonTagBlock(match, status, 'addCss');
+};
+
+WYMeditor.XhtmlParser.prototype._addNonTagBlock = function(match, state, type)
+{
+ switch (state){
+ case WYMeditor.LEXER_ENTER:
+ this._non_tag = match;
+ break;
+ case WYMeditor.LEXER_UNMATCHED:
+ this._non_tag += match;
+ break;
+ case WYMeditor.LEXER_EXIT:
+ switch(type) {
+ case 'addComment':
+ this._Listener.addComment(this._non_tag+match);
+ break;
+ case 'addScript':
+ this._Listener.addScript(this._non_tag+match);
+ break;
+ case 'addCss':
+ this._Listener.addCss(this._non_tag+match);
+ break;
+ }
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.OpeningTag = function(match, state)
+{
+ switch (state){
+ case WYMeditor.LEXER_ENTER:
+ this._tag = this.normalizeTag(match);
+ this._tag_attributes = {};
+ break;
+ case WYMeditor.LEXER_SPECIAL:
+ this._callOpenTagListener(this.normalizeTag(match));
+ break;
+ case WYMeditor.LEXER_EXIT:
+ this._callOpenTagListener(this._tag, this._tag_attributes);
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.ClosingTag = function(match, state)
+{
+ this._callCloseTagListener(this.normalizeTag(match));
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype._callOpenTagListener = function(tag, attributes)
+{
+ var attributes = attributes || {};
+ this.autoCloseUnclosedBeforeNewOpening(tag);
+
+ if(this._Listener.isBlockTag(tag)){
+ this._Listener._tag_stack.push(tag);
+ this._Listener.fixNestingBeforeOpeningBlockTag(tag, attributes);
+ this._Listener.openBlockTag(tag, attributes);
+ this._increaseOpenTagCounter(tag);
+ }else if(this._Listener.isInlineTag(tag)){
+ this._Listener.inlineTag(tag, attributes);
+ }else{
+ this._Listener.openUnknownTag(tag, attributes);
+ this._increaseOpenTagCounter(tag);
+ }
+ this._Listener.last_tag = tag;
+ this._Listener.last_tag_opened = true;
+ this._Listener.last_tag_attributes = attributes;
+};
+
+WYMeditor.XhtmlParser.prototype._callCloseTagListener = function(tag)
+{
+ if(this._decreaseOpenTagCounter(tag)){
+ this.autoCloseUnclosedBeforeTagClosing(tag);
+
+ if(this._Listener.isBlockTag(tag)){
+ var expected_tag = this._Listener._tag_stack.pop();
+ if(expected_tag == false){
+ return;
+ }else if(expected_tag != tag){
+ tag = expected_tag;
+ }
+ this._Listener.closeBlockTag(tag);
+ }else{
+ this._Listener.closeUnknownTag(tag);
+ }
+ }else{
+ this._Listener.closeUnopenedTag(tag);
+ }
+ this._Listener.last_tag = tag;
+ this._Listener.last_tag_opened = false;
+};
+
+WYMeditor.XhtmlParser.prototype._increaseOpenTagCounter = function(tag)
+{
+ this._Listener._open_tags[tag] = this._Listener._open_tags[tag] || 0;
+ this._Listener._open_tags[tag]++;
+};
+
+WYMeditor.XhtmlParser.prototype._decreaseOpenTagCounter = function(tag)
+{
+ if(this._Listener._open_tags[tag]){
+ this._Listener._open_tags[tag]--;
+ if(this._Listener._open_tags[tag] == 0){
+ this._Listener._open_tags[tag] = undefined;
+ }
+ return true;
+ }
+ return false;
+};
+
+WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeNewOpening = function(new_tag)
+{
+ this._autoCloseUnclosed(new_tag, false);
+};
+
+WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeTagClosing = function(tag)
+{
+ this._autoCloseUnclosed(tag, true);
+};
+
+WYMeditor.XhtmlParser.prototype._autoCloseUnclosed = function(new_tag, closing)
+{
+ var closing = closing || false;
+ if(this._Listener._open_tags){
+ for (var tag in this._Listener._open_tags) {
+ var counter = this._Listener._open_tags[tag];
+ if(counter > 0 && this._Listener.shouldCloseTagAutomatically(tag, new_tag, closing)){
+ this._callCloseTagListener(tag, true);
+ }
+ }
+ }
+};
+
+WYMeditor.XhtmlParser.prototype.getTagReplacements = function()
+{
+ return this._Listener.getTagReplacements();
+};
+
+WYMeditor.XhtmlParser.prototype.normalizeTag = function(tag)
+{
+ tag = tag.replace(/^([\s<\/>]*)|([\s<\/>]*)$/gm,'').toLowerCase();
+ var tags = this._Listener.getTagReplacements();
+ if(tags[tag]){
+ return tags[tag];
+ }
+ return tag;
+};
+
+WYMeditor.XhtmlParser.prototype.TagAttributes = function(match, state)
+{
+ if(WYMeditor.LEXER_SPECIAL == state){
+ this._current_attribute = match;
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.DoubleQuotedAttribute = function(match, state)
+{
+ if(WYMeditor.LEXER_UNMATCHED == state){
+ this._tag_attributes[this._current_attribute] = match;
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.SingleQuotedAttribute = function(match, state)
+{
+ if(WYMeditor.LEXER_UNMATCHED == state){
+ this._tag_attributes[this._current_attribute] = match;
+ }
+ return true;
+};
+
+WYMeditor.XhtmlParser.prototype.UnquotedAttribute = function(match, state)
+{
+ this._tag_attributes[this._current_attribute] = match.replace(/^=/,'');
+ return true;
+};
+
+
+
+/**
+* XHTML Sax parser.
+*
+* @author Bermi Ferrer (http://bermi.org)
+*/
+WYMeditor.XhtmlSaxListener = function()
+{
+ this.output = '';
+ this.helper = new WYMeditor.XmlHelper();
+ this._open_tags = {};
+ this.validator = WYMeditor.XhtmlValidator;
+ this._tag_stack = [];
+ this.avoided_tags = [];
+
+ this.entities = {
+ '&nbsp;':'&#160;','&iexcl;':'&#161;','&cent;':'&#162;',
+ '&pound;':'&#163;','&curren;':'&#164;','&yen;':'&#165;',
+ '&brvbar;':'&#166;','&sect;':'&#167;','&uml;':'&#168;',
+ '&copy;':'&#169;','&ordf;':'&#170;','&laquo;':'&#171;',
+ '&not;':'&#172;','&shy;':'&#173;','&reg;':'&#174;',
+ '&macr;':'&#175;','&deg;':'&#176;','&plusmn;':'&#177;',
+ '&sup2;':'&#178;','&sup3;':'&#179;','&acute;':'&#180;',
+ '&micro;':'&#181;','&para;':'&#182;','&middot;':'&#183;',
+ '&cedil;':'&#184;','&sup1;':'&#185;','&ordm;':'&#186;',
+ '&raquo;':'&#187;','&frac14;':'&#188;','&frac12;':'&#189;',
+ '&frac34;':'&#190;','&iquest;':'&#191;','&Agrave;':'&#192;',
+ '&Aacute;':'&#193;','&Acirc;':'&#194;','&Atilde;':'&#195;',
+ '&Auml;':'&#196;','&Aring;':'&#197;','&AElig;':'&#198;',
+ '&Ccedil;':'&#199;','&Egrave;':'&#200;','&Eacute;':'&#201;',
+ '&Ecirc;':'&#202;','&Euml;':'&#203;','&Igrave;':'&#204;',
+ '&Iacute;':'&#205;','&Icirc;':'&#206;','&Iuml;':'&#207;',
+ '&ETH;':'&#208;','&Ntilde;':'&#209;','&Ograve;':'&#210;',
+ '&Oacute;':'&#211;','&Ocirc;':'&#212;','&Otilde;':'&#213;',
+ '&Ouml;':'&#214;','&times;':'&#215;','&Oslash;':'&#216;',
+ '&Ugrave;':'&#217;','&Uacute;':'&#218;','&Ucirc;':'&#219;',
+ '&Uuml;':'&#220;','&Yacute;':'&#221;','&THORN;':'&#222;',
+ '&szlig;':'&#223;','&agrave;':'&#224;','&aacute;':'&#225;',
+ '&acirc;':'&#226;','&atilde;':'&#227;','&auml;':'&#228;',
+ '&aring;':'&#229;','&aelig;':'&#230;','&ccedil;':'&#231;',
+ '&egrave;':'&#232;','&eacute;':'&#233;','&ecirc;':'&#234;',
+ '&euml;':'&#235;','&igrave;':'&#236;','&iacute;':'&#237;',
+ '&icirc;':'&#238;','&iuml;':'&#239;','&eth;':'&#240;',
+ '&ntilde;':'&#241;','&ograve;':'&#242;','&oacute;':'&#243;',
+ '&ocirc;':'&#244;','&otilde;':'&#245;','&ouml;':'&#246;',
+ '&divide;':'&#247;','&oslash;':'&#248;','&ugrave;':'&#249;',
+ '&uacute;':'&#250;','&ucirc;':'&#251;','&uuml;':'&#252;',
+ '&yacute;':'&#253;','&thorn;':'&#254;','&yuml;':'&#255;',
+ '&OElig;':'&#338;','&oelig;':'&#339;','&Scaron;':'&#352;',
+ '&scaron;':'&#353;','&Yuml;':'&#376;','&fnof;':'&#402;',
+ '&circ;':'&#710;','&tilde;':'&#732;','&Alpha;':'&#913;',
+ '&Beta;':'&#914;','&Gamma;':'&#915;','&Delta;':'&#916;',
+ '&Epsilon;':'&#917;','&Zeta;':'&#918;','&Eta;':'&#919;',
+ '&Theta;':'&#920;','&Iota;':'&#921;','&Kappa;':'&#922;',
+ '&Lambda;':'&#923;','&Mu;':'&#924;','&Nu;':'&#925;',
+ '&Xi;':'&#926;','&Omicron;':'&#927;','&Pi;':'&#928;',
+ '&Rho;':'&#929;','&Sigma;':'&#931;','&Tau;':'&#932;',
+ '&Upsilon;':'&#933;','&Phi;':'&#934;','&Chi;':'&#935;',
+ '&Psi;':'&#936;','&Omega;':'&#937;','&alpha;':'&#945;',
+ '&beta;':'&#946;','&gamma;':'&#947;','&delta;':'&#948;',
+ '&epsilon;':'&#949;','&zeta;':'&#950;','&eta;':'&#951;',
+ '&theta;':'&#952;','&iota;':'&#953;','&kappa;':'&#954;',
+ '&lambda;':'&#955;','&mu;':'&#956;','&nu;':'&#957;',
+ '&xi;':'&#958;','&omicron;':'&#959;','&pi;':'&#960;',
+ '&rho;':'&#961;','&sigmaf;':'&#962;','&sigma;':'&#963;',
+ '&tau;':'&#964;','&upsilon;':'&#965;','&phi;':'&#966;',
+ '&chi;':'&#967;','&psi;':'&#968;','&omega;':'&#969;',
+ '&thetasym;':'&#977;','&upsih;':'&#978;','&piv;':'&#982;',
+ '&ensp;':'&#8194;','&emsp;':'&#8195;','&thinsp;':'&#8201;',
+ '&zwnj;':'&#8204;','&zwj;':'&#8205;','&lrm;':'&#8206;',
+ '&rlm;':'&#8207;','&ndash;':'&#8211;','&mdash;':'&#8212;',
+ '&lsquo;':'&#8216;','&rsquo;':'&#8217;','&sbquo;':'&#8218;',
+ '&ldquo;':'&#8220;','&rdquo;':'&#8221;','&bdquo;':'&#8222;',
+ '&dagger;':'&#8224;','&Dagger;':'&#8225;','&bull;':'&#8226;',
+ '&hellip;':'&#8230;','&permil;':'&#8240;','&prime;':'&#8242;',
+ '&Prime;':'&#8243;','&lsaquo;':'&#8249;','&rsaquo;':'&#8250;',
+ '&oline;':'&#8254;','&frasl;':'&#8260;','&euro;':'&#8364;',
+ '&image;':'&#8465;','&weierp;':'&#8472;','&real;':'&#8476;',
+ '&trade;':'&#8482;','&alefsym;':'&#8501;','&larr;':'&#8592;',
+ '&uarr;':'&#8593;','&rarr;':'&#8594;','&darr;':'&#8595;',
+ '&harr;':'&#8596;','&crarr;':'&#8629;','&lArr;':'&#8656;',
+ '&uArr;':'&#8657;','&rArr;':'&#8658;','&dArr;':'&#8659;',
+ '&hArr;':'&#8660;','&forall;':'&#8704;','&part;':'&#8706;',
+ '&exist;':'&#8707;','&empty;':'&#8709;','&nabla;':'&#8711;',
+ '&isin;':'&#8712;','&notin;':'&#8713;','&ni;':'&#8715;',
+ '&prod;':'&#8719;','&sum;':'&#8721;','&minus;':'&#8722;',
+ '&lowast;':'&#8727;','&radic;':'&#8730;','&prop;':'&#8733;',
+ '&infin;':'&#8734;','&ang;':'&#8736;','&and;':'&#8743;',
+ '&or;':'&#8744;','&cap;':'&#8745;','&cup;':'&#8746;',
+ '&int;':'&#8747;','&there4;':'&#8756;','&sim;':'&#8764;',
+ '&cong;':'&#8773;','&asymp;':'&#8776;','&ne;':'&#8800;',
+ '&equiv;':'&#8801;','&le;':'&#8804;','&ge;':'&#8805;',
+ '&sub;':'&#8834;','&sup;':'&#8835;','&nsub;':'&#8836;',
+ '&sube;':'&#8838;','&supe;':'&#8839;','&oplus;':'&#8853;',
+ '&otimes;':'&#8855;','&perp;':'&#8869;','&sdot;':'&#8901;',
+ '&lceil;':'&#8968;','&rceil;':'&#8969;','&lfloor;':'&#8970;',
+ '&rfloor;':'&#8971;','&lang;':'&#9001;','&rang;':'&#9002;',
+ '&loz;':'&#9674;','&spades;':'&#9824;','&clubs;':'&#9827;',
+ '&hearts;':'&#9829;','&diams;':'&#9830;'};
+
+ this.block_tags = ["a", "abbr", "acronym", "address", "area", "b",
+ "base", "bdo", "big", "blockquote", "body", "button",
+ "caption", "cite", "code", "col", "colgroup", "dd", "del", "div",
+ "dfn", "dl", "dt", "em", "fieldset", "form", "head", "h1", "h2",
+ "h3", "h4", "h5", "h6", "html", "i", "ins",
+ "kbd", "label", "legend", "li", "map", "noscript",
+ "object", "ol", "optgroup", "option", "p", "param", "pre", "q",
+ "samp", "script", "select", "small", "span", "strong", "style",
+ "sub", "sup", "table", "tbody", "td", "textarea", "tfoot", "th",
+ "thead", "title", "tr", "tt", "ul", "var", "extends"];
+
+
+ this.inline_tags = ["br", "hr", "img", "input"];
+
+ return this;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.shouldCloseTagAutomatically = function(tag, now_on_tag, closing)
+{
+ var closing = closing || false;
+ if(tag == 'td'){
+ if((closing && now_on_tag == 'tr') || (!closing && now_on_tag == 'td')){
+ return true;
+ }
+ }
+ if(tag == 'option'){
+ if((closing && now_on_tag == 'select') || (!closing && now_on_tag == 'option')){
+ return true;
+ }
+ }
+ return false;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.beforeParsing = function(raw)
+{
+ this.output = '';
+ return raw;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.afterParsing = function(xhtml)
+{
+ xhtml = this.replaceNamedEntities(xhtml);
+ xhtml = this.joinRepeatedEntities(xhtml);
+ xhtml = this.removeEmptyTags(xhtml);
+ xhtml = this.removeBrInPre(xhtml);
+ return xhtml;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.replaceNamedEntities = function(xhtml)
+{
+ for (var entity in this.entities) {
+ xhtml = xhtml.replace(new RegExp(entity, 'g'), this.entities[entity]);
+ }
+ return xhtml;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.joinRepeatedEntities = function(xhtml)
+{
+ var tags = 'em|strong|sub|sup|acronym|pre|del|address';
+ return xhtml.replace(new RegExp('<\/('+tags+')><\\1>' ,''),'').
+ replace(new RegExp('(\s*<('+tags+')>\s*){2}(.*)(\s*<\/\\2>\s*){2}' ,''),'<\$2>\$3<\$2>');
+};
+
+WYMeditor.XhtmlSaxListener.prototype.removeEmptyTags = function(xhtml)
+{
+ return xhtml.replace(new RegExp('<('+this.block_tags.join("|").replace(/\|td/,'').replace(/\|th/, '')+')>(<br \/>|&#160;|&nbsp;|\\s)*<\/\\1>' ,'g'),'');
+};
+
+WYMeditor.XhtmlSaxListener.prototype.removeBrInPre = function(xhtml)
+{
+ var matches = xhtml.match(new RegExp('<pre[^>]*>(.*?)<\/pre>','gmi'));
+ if(matches) {
+ for(var i=0; i<matches.length; i++) {
+ xhtml = xhtml.replace(matches[i], matches[i].replace(new RegExp('<br \/>', 'g'), String.fromCharCode(13,10)));
+ }
+ }
+ return xhtml;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.getResult = function()
+{
+ return this.output;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.getTagReplacements = function()
+{
+ return {'b':'strong', 'i':'em'};
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addContent = function(text)
+{
+ this.output += text;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addComment = function(text)
+{
+ if(this.remove_comments){
+ this.output += text;
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addScript = function(text)
+{
+ if(!this.remove_scripts){
+ this.output += text;
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype.addCss = function(text)
+{
+ if(!this.remove_embeded_styles){
+ this.output += text;
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype.openBlockTag = function(tag, attributes)
+{
+ this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes), true);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.inlineTag = function(tag, attributes)
+{
+ this.output += this.helper.tag(tag, this.validator.getValidTagAttributes(tag, attributes));
+};
+
+WYMeditor.XhtmlSaxListener.prototype.openUnknownTag = function(tag, attributes)
+{
+ //this.output += this.helper.tag(tag, attributes, true);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.closeBlockTag = function(tag)
+{
+ this.output = this.output.replace(/<br \/>$/, '')+this._getClosingTagContent('before', tag)+"</"+tag+">"+this._getClosingTagContent('after', tag);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.closeUnknownTag = function(tag)
+{
+ //this.output += "</"+tag+">";
+};
+
+WYMeditor.XhtmlSaxListener.prototype.closeUnopenedTag = function(tag)
+{
+ this.output += "</"+tag+">";
+};
+
+WYMeditor.XhtmlSaxListener.prototype.avoidStylingTagsAndAttributes = function()
+{
+ this.avoided_tags = ['div','span'];
+ this.validator.skiped_attributes = ['style'];
+ this.validator.skiped_attribute_values = ['MsoNormal','main1']; // MS Word attributes for class
+ this._avoiding_tags_implicitly = true;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.allowStylingTagsAndAttributes = function()
+{
+ this.avoided_tags = [];
+ this.validator.skiped_attributes = [];
+ this.validator.skiped_attribute_values = [];
+ this._avoiding_tags_implicitly = false;
+};
+
+WYMeditor.XhtmlSaxListener.prototype.isBlockTag = function(tag)
+{
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.block_tags, tag);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.isInlineTag = function(tag)
+{
+ return !WYMeditor.Helper.contains(this.avoided_tags, tag) && WYMeditor.Helper.contains(this.inline_tags, tag);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag = function(tag, content)
+{
+ this._insertContentWhenClosingTag('after', tag, content);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.insertContentBeforeClosingTag = function(tag, content)
+{
+ this._insertContentWhenClosingTag('before', tag, content);
+};
+
+WYMeditor.XhtmlSaxListener.prototype.fixNestingBeforeOpeningBlockTag = function(tag, attributes)
+{
+ if(tag != 'li' && (tag == 'ul' || tag == 'ol') && this.last_tag && !this.last_tag_opened && this.last_tag == 'li'){
+ this.output = this.output.replace(/<\/li>$/, '');
+ this.insertContentAfterClosingTag(tag, '</li>');
+ }
+};
+
+WYMeditor.XhtmlSaxListener.prototype._insertContentWhenClosingTag = function(position, tag, content)
+{
+ if(!this['_insert_'+position+'_closing']){
+ this['_insert_'+position+'_closing'] = [];
+ }
+ if(!this['_insert_'+position+'_closing'][tag]){
+ this['_insert_'+position+'_closing'][tag] = [];
+ }
+ this['_insert_'+position+'_closing'][tag].push(content);
+};
+
+WYMeditor.XhtmlSaxListener.prototype._getClosingTagContent = function(position, tag)
+{
+ if( this['_insert_'+position+'_closing'] &&
+ this['_insert_'+position+'_closing'][tag] &&
+ this['_insert_'+position+'_closing'][tag].length > 0){
+ return this['_insert_'+position+'_closing'][tag].pop();
+ }
+ return '';
+};
+
+
+/********** CSS PARSER **********/
+
+
+WYMeditor.WymCssLexer = function(parser, only_wym_blocks)
+{
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? true : only_wym_blocks);
+
+ jQuery.extend(this, new WYMeditor.Lexer(parser, (only_wym_blocks?'Ignore':'WymCss')));
+
+ this.mapHandler('WymCss', 'Ignore');
+
+ if(only_wym_blocks == true){
+ this.addEntryPattern("/\\\x2a[<\\s]*WYMeditor[>\\s]*\\\x2a/", 'Ignore', 'WymCss');
+ this.addExitPattern("/\\\x2a[<\/\\s]*WYMeditor[>\\s]*\\\x2a/", 'WymCss');
+ }
+
+ this.addSpecialPattern("[\\sa-z1-6]*\\\x2e[a-z-_0-9]+", 'WymCss', 'WymCssStyleDeclaration');
+
+ this.addEntryPattern("/\\\x2a", 'WymCss', 'WymCssComment');
+ this.addExitPattern("\\\x2a/", 'WymCssComment');
+
+ this.addEntryPattern("\x7b", 'WymCss', 'WymCssStyle');
+ this.addExitPattern("\x7d", 'WymCssStyle');
+
+ this.addEntryPattern("/\\\x2a", 'WymCssStyle', 'WymCssFeedbackStyle');
+ this.addExitPattern("\\\x2a/", 'WymCssFeedbackStyle');
+
+ return this;
+};
+
+WYMeditor.WymCssParser = function()
+{
+ this._in_style = false;
+ this._has_title = false;
+ this.only_wym_blocks = true;
+ this.css_settings = {'classesItems':[], 'editorStyles':[], 'dialogStyles':[]};
+ return this;
+};
+
+WYMeditor.WymCssParser.prototype.parse = function(raw, only_wym_blocks)
+{
+ var only_wym_blocks = (typeof only_wym_blocks == 'undefined' ? this.only_wym_blocks : only_wym_blocks);
+ this._Lexer = new WYMeditor.WymCssLexer(this, only_wym_blocks);
+ this._Lexer.parse(raw);
+};
+
+WYMeditor.WymCssParser.prototype.Ignore = function(match, state)
+{
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssComment = function(text, status)
+{
+ if(text.match(/end[a-z0-9\s]*wym[a-z0-9\s]*/mi)){
+ return false;
+ }
+ if(status == WYMeditor.LEXER_UNMATCHED){
+ if(!this._in_style){
+ this._has_title = true;
+ this._current_item = {'title':WYMeditor.Helper.trim(text)};
+ }else{
+ if(this._current_item[this._current_element]){
+ if(!this._current_item[this._current_element].expressions){
+ this._current_item[this._current_element].expressions = [text];
+ }else{
+ this._current_item[this._current_element].expressions.push(text);
+ }
+ }
+ }
+ this._in_style = true;
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssStyle = function(match, status)
+{
+ if(status == WYMeditor.LEXER_UNMATCHED){
+ match = WYMeditor.Helper.trim(match);
+ if(match != ''){
+ this._current_item[this._current_element].style = match;
+ }
+ }else if (status == WYMeditor.LEXER_EXIT){
+ this._in_style = false;
+ this._has_title = false;
+ this.addStyleSetting(this._current_item);
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssFeedbackStyle = function(match, status)
+{
+ if(status == WYMeditor.LEXER_UNMATCHED){
+ this._current_item[this._current_element].feedback_style = match.replace(/^([\s\/\*]*)|([\s\/\*]*)$/gm,'');
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.WymCssStyleDeclaration = function(match)
+{
+ match = match.replace(/^([\s\.]*)|([\s\.*]*)$/gm, '');
+
+ var tag = '';
+ if(match.indexOf('.') > 0){
+ var parts = match.split('.');
+ this._current_element = parts[1];
+ var tag = parts[0];
+ }else{
+ this._current_element = match;
+ }
+
+ if(!this._has_title){
+ this._current_item = {'title':(!tag?'':tag.toUpperCase()+': ')+this._current_element};
+ this._has_title = true;
+ }
+
+ if(!this._current_item[this._current_element]){
+ this._current_item[this._current_element] = {'name':this._current_element};
+ }
+ if(tag){
+ if(!this._current_item[this._current_element].tags){
+ this._current_item[this._current_element].tags = [tag];
+ }else{
+ this._current_item[this._current_element].tags.push(tag);
+ }
+ }
+ return true;
+};
+
+WYMeditor.WymCssParser.prototype.addStyleSetting = function(style_details)
+{
+ for (var name in style_details){
+ var details = style_details[name];
+ if(typeof details == 'object' && name != 'title'){
+
+ this.css_settings.classesItems.push({
+ 'name': WYMeditor.Helper.trim(details.name),
+ 'title': style_details.title,
+ 'expr' : WYMeditor.Helper.trim((details.expressions||details.tags).join(', '))
+ });
+ if(details.feedback_style){
+ this.css_settings.editorStyles.push({
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
+ 'css': details.feedback_style
+ });
+ }
+ if(details.style){
+ this.css_settings.dialogStyles.push({
+ 'name': '.'+ WYMeditor.Helper.trim(details.name),
+ 'css': details.style
+ });
+ }
+ }
+ }
+};
+
+/********** HELPERS **********/
+
+// Returns true if it is a text node with whitespaces only
+jQuery.fn.isPhantomNode = function() {
+ if (this[0].nodeType == 3)
+ return !(/[^\t\n\r ]/.test(this[0].data));
+
+ return false;
+};
+
+WYMeditor.isPhantomNode = function(n) {
+ if (n.nodeType == 3)
+ return !(/[^\t\n\r ]/.test(n.data));
+
+ return false;
+};
+
+WYMeditor.isPhantomString = function(str) {
+ return !(/[^\t\n\r ]/.test(str));
+};
+
+// Returns the Parents or the node itself
+// jqexpr = a jQuery expression
+jQuery.fn.parentsOrSelf = function(jqexpr) {
+ var n = this;
+
+ if (n[0].nodeType == 3)
+ n = n.parents().slice(0,1);
+
+// if (n.is(jqexpr)) // XXX should work, but doesn't (probably a jQuery bug)
+ if (n.filter(jqexpr).size() == 1)
+ return n;
+ else
+ return n.parents(jqexpr).slice(0,1);
+};
+
+// String & array helpers
+
+WYMeditor.Helper = {
+
+ //replace all instances of 'old' by 'rep' in 'str' string
+ replaceAll: function(str, old, rep) {
+ var rExp = new RegExp(old, "g");
+ return(str.replace(rExp, rep));
+ },
+
+ //insert 'inserted' at position 'pos' in 'str' string
+ insertAt: function(str, inserted, pos) {
+ return(str.substr(0,pos) + inserted + str.substring(pos));
+ },
+
+ //trim 'str' string
+ trim: function(str) {
+ return str.replace(/^(\s*)|(\s*)$/gm,'');
+ },
+
+ //return true if 'arr' array contains 'elem', or false
+ contains: function(arr, elem) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === elem) return true;
+ }
+ return false;
+ },
+
+ //return 'item' position in 'arr' array, or -1
+ indexOf: function(arr, item) {
+ var ret=-1;
+ for(var i = 0; i < arr.length; i++) {
+ if (arr[i] == item) {
+ ret = i;
+ break;
+ }
+ }
+ return(ret);
+ },
+
+ //return 'item' object in 'arr' array, checking its 'name' property, or null
+ findByName: function(arr, name) {
+ for(var i = 0; i < arr.length; i++) {
+ var item = arr[i];
+ if(item.name == name) return(item);
+ }
+ return(null);
+ }
+};
+
+
diff --git a/studio/static/js/wymeditor/jquery.wymeditor.min.js b/studio/static/js/wymeditor/jquery.wymeditor.min.js
new file mode 100644
index 0000000..b57a18e
--- /dev/null
+++ b/studio/static/js/wymeditor/jquery.wymeditor.min.js
@@ -0,0 +1 @@
+if(!WYMeditor){var WYMeditor={}}(function(){if(!window.console||!console.firebug){var b=["log","debug","info","warn","error","assert","dir","dirxml","group","groupEnd","time","timeEnd","count","trace","profile","profileEnd"];WYMeditor.console={};for(var a=0;a<b.length;++a){WYMeditor.console[b[a]]=function(){}}}else{WYMeditor.console=window.console}})();jQuery.extend(WYMeditor,{VERSION:"0.5-rc1",INSTANCES:[],STRINGS:[],SKINS:[],NAME:"name",INDEX:"{Wym_Index}",WYM_INDEX:"wym_index",BASE_PATH:"{Wym_Base_Path}",CSS_PATH:"{Wym_Css_Path}",WYM_PATH:"{Wym_Wym_Path}",SKINS_DEFAULT_PATH:"skins/",SKINS_DEFAULT_CSS:"skin.css",SKINS_DEFAULT_JS:"skin.js",LANG_DEFAULT_PATH:"lang/",IFRAME_BASE_PATH:"{Wym_Iframe_Base_Path}",IFRAME_DEFAULT:"iframe/default/",JQUERY_PATH:"{Wym_Jquery_Path}",DIRECTION:"{Wym_Direction}",LOGO:"{Wym_Logo}",TOOLS:"{Wym_Tools}",TOOLS_ITEMS:"{Wym_Tools_Items}",TOOL_NAME:"{Wym_Tool_Name}",TOOL_TITLE:"{Wym_Tool_Title}",TOOL_CLASS:"{Wym_Tool_Class}",CLASSES:"{Wym_Classes}",CLASSES_ITEMS:"{Wym_Classes_Items}",CLASS_NAME:"{Wym_Class_Name}",CLASS_TITLE:"{Wym_Class_Title}",CONTAINERS:"{Wym_Containers}",CONTAINERS_ITEMS:"{Wym_Containers_Items}",CONTAINER_NAME:"{Wym_Container_Name}",CONTAINER_TITLE:"{Wym_Containers_Title}",CONTAINER_CLASS:"{Wym_Container_Class}",HTML:"{Wym_Html}",IFRAME:"{Wym_Iframe}",STATUS:"{Wym_Status}",DIALOG_TITLE:"{Wym_Dialog_Title}",DIALOG_BODY:"{Wym_Dialog_Body}",STRING:"string",BODY:"body",DIV:"div",P:"p",H1:"h1",H2:"h2",H3:"h3",H4:"h4",H5:"h5",H6:"h6",PRE:"pre",BLOCKQUOTE:"blockquote",A:"a",BR:"br",IMG:"img",TABLE:"table",TD:"td",TH:"th",UL:"ul",OL:"ol",LI:"li",CLASS:"class",HREF:"href",SRC:"src",TITLE:"title",REL:"rel",ALT:"alt",DIALOG_LINK:"Link",DIALOG_IMAGE:"Image",DIALOG_TABLE:"Table",DIALOG_PASTE:"Paste_From_Word",BOLD:"Bold",ITALIC:"Italic",CREATE_LINK:"CreateLink",INSERT_IMAGE:"InsertImage",INSERT_TABLE:"InsertTable",INSERT_HTML:"InsertHTML",PASTE:"Paste",INDENT:"Indent",OUTDENT:"Outdent",TOGGLE_HTML:"ToggleHtml",FORMAT_BLOCK:"FormatBlock",PREVIEW:"Preview",UNLINK:"Unlink",INSERT_UNORDEREDLIST:"InsertUnorderedList",INSERT_ORDEREDLIST:"InsertOrderedList",MAIN_CONTAINERS:new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"),BLOCKS:new Array("address","blockquote","div","dl","fieldset","form","h1","h2","h3","h4","h5","h6","hr","noscript","ol","p","pre","table","ul","dd","dt","li","tbody","td","tfoot","th","thead","tr"),KEY:{BACKSPACE:8,ENTER:13,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,CURSOR:new Array(37,38,39,40),DELETE:46},NODE:{ELEMENT:1,ATTRIBUTE:2,TEXT:3},editor:function(b,a){this._index=WYMeditor.INSTANCES.push(this)-1;this._element=b;this._options=a;this._html=jQuery(b).val();if(this._options.html){this._html=this._options.html}this._options.basePath=this._options.basePath||this.computeBasePath();this._options.skinPath=this._options.skinPath||this._options.basePath+WYMeditor.SKINS_DEFAULT_PATH+this._options.skin+"/";this._options.wymPath=this._options.wymPath||this.computeWymPath();this._options.langPath=this._options.langPath||this._options.basePath+WYMeditor.LANG_DEFAULT_PATH;this._options.iframeBasePath=this._options.iframeBasePath||this._options.basePath+WYMeditor.IFRAME_DEFAULT;this._options.jQueryPath=this._options.jQueryPath||this.computeJqueryPath();this.init()}});jQuery.fn.wymeditor=function(a){a=jQuery.extend({html:"",basePath:false,skinPath:false,wymPath:false,iframeBasePath:false,jQueryPath:false,styles:false,stylesheet:false,skin:"default",initSkin:true,loadSkin:true,lang:"en",direction:"ltr",boxHtml:"<div class='wym_box'><div class='wym_area_top'>"+WYMeditor.TOOLS+"</div><div class='wym_area_left'></div><div class='wym_area_right'>"+WYMeditor.CONTAINERS+WYMeditor.CLASSES+"</div><div class='wym_area_main'>"+WYMeditor.HTML+WYMeditor.IFRAME+WYMeditor.STATUS+"</div><div class='wym_area_bottom'>"+WYMeditor.LOGO+"</div></div>",logoHtml:"<a class='wym_wymeditor_link' href='http://www.wymeditor.org/'>WYMeditor</a>",iframeHtml:"<div class='wym_iframe wym_section'><iframe src='"+WYMeditor.IFRAME_BASE_PATH+"wymiframe.html' onload='this.contentWindow.parent.WYMeditor.INSTANCES["+WYMeditor.INDEX+"].initIframe(this)'></iframe></div>",editorStyles:[],toolsHtml:"<div class='wym_tools wym_section'><h2>{Tools}</h2><ul>"+WYMeditor.TOOLS_ITEMS+"</ul></div>",toolsItemHtml:"<li class='"+WYMeditor.TOOL_CLASS+"'><a href='#' name='"+WYMeditor.TOOL_NAME+"' title='"+WYMeditor.TOOL_TITLE+"'>"+WYMeditor.TOOL_TITLE+"</a></li>",toolsItems:[{name:"Bold",title:"Strong",css:"wym_tools_strong"},{name:"Italic",title:"Emphasis",css:"wym_tools_emphasis"},{name:"Superscript",title:"Superscript",css:"wym_tools_superscript"},{name:"Subscript",title:"Subscript",css:"wym_tools_subscript"},{name:"InsertOrderedList",title:"Ordered_List",css:"wym_tools_ordered_list"},{name:"InsertUnorderedList",title:"Unordered_List",css:"wym_tools_unordered_list"},{name:"Indent",title:"Indent",css:"wym_tools_indent"},{name:"Outdent",title:"Outdent",css:"wym_tools_outdent"},{name:"Undo",title:"Undo",css:"wym_tools_undo"},{name:"Redo",title:"Redo",css:"wym_tools_redo"},{name:"CreateLink",title:"Link",css:"wym_tools_link"},{name:"Unlink",title:"Unlink",css:"wym_tools_unlink"},{name:"InsertImage",title:"Image",css:"wym_tools_image"},{name:"InsertTable",title:"Table",css:"wym_tools_table"},{name:"Paste",title:"Paste_From_Word",css:"wym_tools_paste"},{name:"ToggleHtml",title:"HTML",css:"wym_tools_html"},{name:"Preview",title:"Preview",css:"wym_tools_preview"}],containersHtml:"<div class='wym_containers wym_section'><h2>{Containers}</h2><ul>"+WYMeditor.CONTAINERS_ITEMS+"</ul></div>",containersItemHtml:"<li class='"+WYMeditor.CONTAINER_CLASS+"'><a href='#' name='"+WYMeditor.CONTAINER_NAME+"'>"+WYMeditor.CONTAINER_TITLE+"</a></li>",containersItems:[{name:"P",title:"Paragraph",css:"wym_containers_p"},{name:"H1",title:"Heading_1",css:"wym_containers_h1"},{name:"H2",title:"Heading_2",css:"wym_containers_h2"},{name:"H3",title:"Heading_3",css:"wym_containers_h3"},{name:"H4",title:"Heading_4",css:"wym_containers_h4"},{name:"H5",title:"Heading_5",css:"wym_containers_h5"},{name:"H6",title:"Heading_6",css:"wym_containers_h6"},{name:"PRE",title:"Preformatted",css:"wym_containers_pre"},{name:"BLOCKQUOTE",title:"Blockquote",css:"wym_containers_blockquote"},{name:"TH",title:"Table_Header",css:"wym_containers_th"}],classesHtml:"<div class='wym_classes wym_section'><h2>{Classes}</h2><ul>"+WYMeditor.CLASSES_ITEMS+"</ul></div>",classesItemHtml:"<li class='wym_classes_"+WYMeditor.CLASS_NAME+"'><a href='#' name='"+WYMeditor.CLASS_NAME+"'>"+WYMeditor.CLASS_TITLE+"</a></li>",classesItems:[],statusHtml:"<div class='wym_status wym_section'><h2>{Status}</h2></div>",htmlHtml:"<div class='wym_html wym_section'><h2>{Source_Code}</h2><textarea class='wym_html_val'></textarea></div>",boxSelector:".wym_box",toolsSelector:".wym_tools",toolsListSelector:" ul",containersSelector:".wym_containers",classesSelector:".wym_classes",htmlSelector:".wym_html",iframeSelector:".wym_iframe iframe",iframeBodySelector:".wym_iframe",statusSelector:".wym_status",toolSelector:".wym_tools a",containerSelector:".wym_containers a",classSelector:".wym_classes a",htmlValSelector:".wym_html_val",hrefSelector:".wym_href",srcSelector:".wym_src",titleSelector:".wym_title",relSelector:".wym_rel",altSelector:".wym_alt",textSelector:".wym_text",rowsSelector:".wym_rows",colsSelector:".wym_cols",captionSelector:".wym_caption",summarySelector:".wym_summary",submitSelector:"form",cancelSelector:".wym_cancel",previewSelector:"",dialogTypeSelector:".wym_dialog_type",dialogLinkSelector:".wym_dialog_link",dialogImageSelector:".wym_dialog_image",dialogTableSelector:".wym_dialog_table",dialogPasteSelector:".wym_dialog_paste",dialogPreviewSelector:".wym_dialog_preview",updateSelector:".wymupdate",updateEvent:"click",dialogFeatures:"menubar=no,titlebar=no,toolbar=no,resizable=no,width=560,height=300,top=0,left=0",dialogFeaturesPreview:"menubar=no,titlebar=no,toolbar=no,resizable=no,scrollbars=yes,width=560,height=300,top=0,left=0",dialogHtml:"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'><html dir='"+WYMeditor.DIRECTION+"'><head><link rel='stylesheet' type='text/css' media='screen' href='"+WYMeditor.CSS_PATH+"' /><title>"+WYMeditor.DIALOG_TITLE+"</title><script type='text/javascript' src='"+WYMeditor.JQUERY_PATH+"'><\/script><script type='text/javascript' src='"+WYMeditor.WYM_PATH+"'><\/script></head>"+WYMeditor.DIALOG_BODY+"</html>",dialogLinkHtml:"<body class='wym_dialog wym_dialog_link' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><fieldset><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_LINK+"' /><legend>{Link}</legend><div class='row'><label>{URL}</label><input type='text' class='wym_href' value='' size='40' /></div><div class='row'><label>{Title}</label><input type='text' class='wym_title' value='' size='40' /></div><div class='row'><label>{Relationship}</label><input type='text' class='wym_rel' value='' size='40' /></div><div class='row row-indent'><input class='wym_submit' type='submit' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogImageHtml:"<body class='wym_dialog wym_dialog_image' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><fieldset><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_IMAGE+"' /><legend>{Image}</legend><div class='row'><label>{URL}</label><input type='text' class='wym_src' value='' size='40' /></div><div class='row'><label>{Alternative_Text}</label><input type='text' class='wym_alt' value='' size='40' /></div><div class='row'><label>{Title}</label><input type='text' class='wym_title' value='' size='40' /></div><div class='row row-indent'><input class='wym_submit' type='submit' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogTableHtml:"<body class='wym_dialog wym_dialog_table' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><fieldset><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_TABLE+"' /><legend>{Table}</legend><div class='row'><label>{Caption}</label><input type='text' class='wym_caption' value='' size='40' /></div><div class='row'><label>{Summary}</label><input type='text' class='wym_summary' value='' size='40' /></div><div class='row'><label>{Number_Of_Rows}</label><input type='text' class='wym_rows' value='3' size='3' /></div><div class='row'><label>{Number_Of_Cols}</label><input type='text' class='wym_cols' value='2' size='3' /></div><div class='row row-indent'><input class='wym_submit' type='submit' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogPasteHtml:"<body class='wym_dialog wym_dialog_paste' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'><form><input type='hidden' class='wym_dialog_type' value='"+WYMeditor.DIALOG_PASTE+"' /><fieldset><legend>{Paste_From_Word}</legend><div class='row'><textarea class='wym_text' rows='10' cols='50'></textarea></div><div class='row'><input class='wym_submit' type='submit' value='{Submit}' /><input class='wym_cancel' type='button'value='{Cancel}' /></div></fieldset></form></body>",dialogPreviewHtml:"<body class='wym_dialog wym_dialog_preview' onload='WYMeditor.INIT_DIALOG("+WYMeditor.INDEX+")'></body>",dialogStyles:[],stringDelimiterLeft:"{",stringDelimiterRight:"}",preInit:null,preBind:null,postInit:null,preInitDialog:null,postInitDialog:null},a);return this.each(function(){new WYMeditor.editor(jQuery(this),a)})};jQuery.extend({wymeditors:function(a){return(WYMeditor.INSTANCES[a])}});WYMeditor.editor.prototype.init=function(){if(jQuery.browser.msie){var WymClass=new WYMeditor.WymClassExplorer(this)}else{if(jQuery.browser.mozilla){var WymClass=new WYMeditor.WymClassMozilla(this)}else{if(jQuery.browser.opera){var WymClass=new WYMeditor.WymClassOpera(this)}else{if(jQuery.browser.safari){var WymClass=new WYMeditor.WymClassSafari(this)}}}}if(WymClass){if(jQuery.isFunction(this._options.preInit)){this._options.preInit(this)}var SaxListener=new WYMeditor.XhtmlSaxListener();jQuery.extend(SaxListener,WymClass);this.parser=new WYMeditor.XhtmlParser(SaxListener);if(this._options.styles||this._options.stylesheet){this.configureEditorUsingRawCss()}this.helper=new WYMeditor.XmlHelper();for(var prop in WymClass){this[prop]=WymClass[prop]}this._box=jQuery(this._element).hide().after(this._options.boxHtml).next().addClass("wym_box_"+this._index);if(jQuery.isFunction(jQuery.fn.data)){jQuery.data(this._box.get(0),WYMeditor.WYM_INDEX,this._index);jQuery.data(this._element.get(0),WYMeditor.WYM_INDEX,this._index)}var h=WYMeditor.Helper;var iframeHtml=this._options.iframeHtml;iframeHtml=h.replaceAll(iframeHtml,WYMeditor.INDEX,this._index);iframeHtml=h.replaceAll(iframeHtml,WYMeditor.IFRAME_BASE_PATH,this._options.iframeBasePath);var boxHtml=jQuery(this._box).html();boxHtml=h.replaceAll(boxHtml,WYMeditor.LOGO,this._options.logoHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.TOOLS,this._options.toolsHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.CONTAINERS,this._options.containersHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.CLASSES,this._options.classesHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.HTML,this._options.htmlHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.IFRAME,iframeHtml);boxHtml=h.replaceAll(boxHtml,WYMeditor.STATUS,this._options.statusHtml);var aTools=eval(this._options.toolsItems);var sTools="";for(var i=0;i<aTools.length;i++){var oTool=aTools[i];if(oTool.name&&oTool.title){var sTool=this._options.toolsItemHtml}var sTool=h.replaceAll(sTool,WYMeditor.TOOL_NAME,oTool.name);sTool=h.replaceAll(sTool,WYMeditor.TOOL_TITLE,this._options.stringDelimiterLeft+oTool.title+this._options.stringDelimiterRight);sTool=h.replaceAll(sTool,WYMeditor.TOOL_CLASS,oTool.css);sTools+=sTool}boxHtml=h.replaceAll(boxHtml,WYMeditor.TOOLS_ITEMS,sTools);var aClasses=eval(this._options.classesItems);var sClasses="";for(var i=0;i<aClasses.length;i++){var oClass=aClasses[i];if(oClass.name&&oClass.title){var sClass=this._options.classesItemHtml}sClass=h.replaceAll(sClass,WYMeditor.CLASS_NAME,oClass.name);sClass=h.replaceAll(sClass,WYMeditor.CLASS_TITLE,oClass.title);sClasses+=sClass}boxHtml=h.replaceAll(boxHtml,WYMeditor.CLASSES_ITEMS,sClasses);var aContainers=eval(this._options.containersItems);var sContainers="";for(var i=0;i<aContainers.length;i++){var oContainer=aContainers[i];if(oContainer.name&&oContainer.title){var sContainer=this._options.containersItemHtml}sContainer=h.replaceAll(sContainer,WYMeditor.CONTAINER_NAME,oContainer.name);sContainer=h.replaceAll(sContainer,WYMeditor.CONTAINER_TITLE,this._options.stringDelimiterLeft+oContainer.title+this._options.stringDelimiterRight);sContainer=h.replaceAll(sContainer,WYMeditor.CONTAINER_CLASS,oContainer.css);sContainers+=sContainer}boxHtml=h.replaceAll(boxHtml,WYMeditor.CONTAINERS_ITEMS,sContainers);boxHtml=this.replaceStrings(boxHtml);jQuery(this._box).html(boxHtml);jQuery(this._box).find(this._options.htmlSelector).hide();this.loadSkin()}};WYMeditor.editor.prototype.bindEvents=function(){var wym=this;jQuery(this._box).find(this._options.toolSelector).click(function(){wym._iframe.contentWindow.focus();wym.exec(jQuery(this).attr(WYMeditor.NAME));return(false)});jQuery(this._box).find(this._options.containerSelector).click(function(){wym.container(jQuery(this).attr(WYMeditor.NAME));return(false)});jQuery(this._box).find(this._options.htmlValSelector).keyup(function(){jQuery(wym._doc.body).html(jQuery(this).val())}).focus(function(){jQuery(this).toggleClass("hasfocus")}).blur(function(){jQuery(this).toggleClass("hasfocus")});jQuery(this._box).find(this._options.classSelector).click(function(){var aClasses=eval(wym._options.classesItems);var sName=jQuery(this).attr(WYMeditor.NAME);var oClass=WYMeditor.Helper.findByName(aClasses,sName);if(oClass){var jqexpr=oClass.expr;wym.toggleClass(sName,jqexpr)}wym._iframe.contentWindow.focus();return(false)});jQuery(this._options.updateSelector).bind(this._options.updateEvent,function(){wym.update()})};WYMeditor.editor.prototype.ready=function(){return(this._doc!=null)};WYMeditor.editor.prototype.box=function(){return(this._box)};WYMeditor.editor.prototype.html=function(a){if(typeof a==="string"){jQuery(this._doc.body).html(a)}else{return(jQuery(this._doc.body).html())}};WYMeditor.editor.prototype.xhtml=function(){return this.parser.parse(this.html())};WYMeditor.editor.prototype.exec=function(b){switch(b){case WYMeditor.CREATE_LINK:var a=this.container();if(a||this._selected_image){this.dialog(WYMeditor.DIALOG_LINK)}break;case WYMeditor.INSERT_IMAGE:this.dialog(WYMeditor.DIALOG_IMAGE);break;case WYMeditor.INSERT_TABLE:this.dialog(WYMeditor.DIALOG_TABLE);break;case WYMeditor.PASTE:this.dialog(WYMeditor.DIALOG_PASTE);break;case WYMeditor.TOGGLE_HTML:this.update();this.toggleHtml();break;case WYMeditor.PREVIEW:this.dialog(WYMeditor.PREVIEW,this._options.dialogFeaturesPreview);break;default:this._exec(b);break}};WYMeditor.editor.prototype.container=function(a){if(a){var d=null;if(a.toLowerCase()==WYMeditor.TH){d=this.container();switch(d.tagName.toLowerCase()){case WYMeditor.TD:case WYMeditor.TH:break;default:var e=new Array(WYMeditor.TD,WYMeditor.TH);d=this.findUp(this.container(),e);break}if(d!=null){a=(d.tagName.toLowerCase()==WYMeditor.TD)?WYMeditor.TH:WYMeditor.TD;this.switchTo(d,a);this.update()}}else{var e=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5,WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE);d=this.findUp(this.container(),e);if(d){var i=null;if(a.toLowerCase()==WYMeditor.BLOCKQUOTE){var f=this.findUp(this.container(),WYMeditor.BLOCKQUOTE);if(f==null){i=this._doc.createElement(a);d.parentNode.insertBefore(i,d);i.appendChild(d);this.setFocusToNode(i.firstChild)}else{var c=f.childNodes;var g=c.length;var b=null;if(g>0){b=c.item(0)}for(var h=0;h<g;h++){f.parentNode.insertBefore(c.item(0),f)}f.parentNode.removeChild(f);if(b){this.setFocusToNode(b)}}}else{this.switchTo(d,a)}this.update()}}}else{return(this.selected())}};WYMeditor.editor.prototype.toggleClass=function(c,b){var a=(this._selected_image?this._selected_image:jQuery(this.selected()));a=jQuery(a).parentsOrSelf(b);jQuery(a).toggleClass(c);if(!jQuery(a).attr(WYMeditor.CLASS)){jQuery(a).removeAttr(this._class)}};WYMeditor.editor.prototype.findUp=function(d,c){if(d){var e=d.tagName.toLowerCase();if(typeof(c)==WYMeditor.STRING){while(e!=c&&e!=WYMeditor.BODY){d=d.parentNode;e=d.tagName.toLowerCase()}}else{var b=false;while(!b&&e!=WYMeditor.BODY){for(var a=0;a<c.length;a++){if(e==c[a]){b=true;break}}if(!b){d=d.parentNode;e=d.tagName.toLowerCase()}}}if(e!=WYMeditor.BODY){return(d)}else{return(null)}}else{return(null)}};WYMeditor.editor.prototype.switchTo=function(c,d){var b=this._doc.createElement(d);var a=jQuery(c).html();c.parentNode.replaceChild(b,c);jQuery(b).html(a);this.setFocusToNode(b)};WYMeditor.editor.prototype.replaceStrings=function(sVal){if(!WYMeditor.STRINGS[this._options.lang]){try{eval(jQuery.ajax({url:this._options.langPath+this._options.lang+".js",async:false}).responseText)}catch(e){WYMeditor.console.error("WYMeditor: error while parsing language file.");return sVal}}for(var key in WYMeditor.STRINGS[this._options.lang]){sVal=WYMeditor.Helper.replaceAll(sVal,this._options.stringDelimiterLeft+key+this._options.stringDelimiterRight,WYMeditor.STRINGS[this._options.lang][key])}return(sVal)};WYMeditor.editor.prototype.encloseString=function(a){return(this._options.stringDelimiterLeft+a+this._options.stringDelimiterRight)};WYMeditor.editor.prototype.status=function(a){jQuery(this._box).find(this._options.statusSelector).html(a)};WYMeditor.editor.prototype.update=function(){var a;jQuery(this._doc.body).children(WYMeditor.BR).remove();a=this.xhtml();jQuery(this._element).val(a);jQuery(this._box).find(this._options.htmlValSelector).not(".hasfocus").val(a)};WYMeditor.editor.prototype.dialog=function(j,c,f){var a=c||this._wym._options.dialogFeatures;var d=window.open("","dialog",a);if(d){var b="";switch(j){case (WYMeditor.DIALOG_LINK):b=this._options.dialogLinkHtml;break;case (WYMeditor.DIALOG_IMAGE):b=this._options.dialogImageHtml;break;case (WYMeditor.DIALOG_TABLE):b=this._options.dialogTableHtml;break;case (WYMeditor.DIALOG_PASTE):b=this._options.dialogPasteHtml;break;case (WYMeditor.PREVIEW):b=this._options.dialogPreviewHtml;break;default:b=f}var e=WYMeditor.Helper;var g=this._options.dialogHtml;g=e.replaceAll(g,WYMeditor.BASE_PATH,this._options.basePath);g=e.replaceAll(g,WYMeditor.DIRECTION,this._options.direction);g=e.replaceAll(g,WYMeditor.CSS_PATH,this._options.skinPath+WYMeditor.SKINS_DEFAULT_CSS);g=e.replaceAll(g,WYMeditor.WYM_PATH,this._options.wymPath);g=e.replaceAll(g,WYMeditor.JQUERY_PATH,this._options.jQueryPath);g=e.replaceAll(g,WYMeditor.DIALOG_TITLE,this.encloseString(j));g=e.replaceAll(g,WYMeditor.DIALOG_BODY,b);g=e.replaceAll(g,WYMeditor.INDEX,this._index);g=this.replaceStrings(g);var i=d.document;i.write(g);i.close()}};WYMeditor.editor.prototype.toggleHtml=function(){jQuery(this._box).find(this._options.htmlSelector).toggle()};WYMeditor.editor.prototype.uniqueStamp=function(){var a=new Date();return("wym-"+a.getTime())};WYMeditor.editor.prototype.paste=function(g){var b=this.selected(),d="",e,f;e=g.split(new RegExp(this._newLine+"{2,}","g"));for(var c=0,a=e.length;c<a;c++){d+="<p>"+(e[c].split(this._newLine).join("<br />"))+"</p>"}if(b&&b.tagName.toLowerCase()!=WYMeditor.BODY){e=jQuery(d,this._doc).insertAfter(b);f=e[e.length-1]}else{e=jQuery(d,this._doc).appendTo(this._doc.body);f=e[e.length-1]}if(jQuery(b).text()==""){jQuery(b).remove()}jQuery("body > br",this._doc).remove();this.setFocusToNode(f)};WYMeditor.editor.prototype.insert=function(b){var c=this._iframe.contentWindow.getSelection(),a,d;if(c.focusNode!=null){a=c.getRangeAt(0);d=a.createContextualFragment(b);a.deleteContents();a.insertNode(d)}else{this.paste(b)}};WYMeditor.editor.prototype.wrap=function(b,a){this.insert(b+this._iframe.contentWindow.getSelection().toString()+a)};WYMeditor.editor.prototype.unwrap=function(){this.insert(this._iframe.contentWindow.getSelection().toString())};WYMeditor.editor.prototype.setFocusToNode=function(d,b){var a=this._doc.createRange(),c=this._iframe.contentWindow.getSelection();b=b?0:1;a.selectNodeContents(d);c.addRange(a);c.collapse(d,b);this._iframe.contentWindow.focus()};WYMeditor.editor.prototype.addCssRules=function(e,c){var b=e.styleSheets[0];if(b){for(var a=0;a<c.length;a++){var d=c[a];if(d.name&&d.css){this.addCssRule(b,d)}}}};WYMeditor.editor.prototype.computeBasePath=function(){return jQuery(jQuery.grep(jQuery("script"),function(a){return(a.src&&a.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/))})).attr("src").replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/,"")};WYMeditor.editor.prototype.computeWymPath=function(){return jQuery(jQuery.grep(jQuery("script"),function(a){return(a.src&&a.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/))})).attr("src")};WYMeditor.editor.prototype.computeJqueryPath=function(){return jQuery(jQuery.grep(jQuery("script"),function(a){return(a.src&&a.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/))})).attr("src")};WYMeditor.editor.prototype.computeCssPath=function(){return jQuery(jQuery.grep(jQuery("link"),function(a){return(a.href&&a.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/))})).attr("href")};WYMeditor.editor.prototype.configureEditorUsingRawCss=function(){var a=new WYMeditor.WymCssParser();if(this._options.stylesheet){a.parse(jQuery.ajax({url:this._options.stylesheet,async:false}).responseText)}else{a.parse(this._options.styles,false)}if(this._options.classesItems.length==0){this._options.classesItems=a.css_settings.classesItems}if(this._options.editorStyles.length==0){this._options.editorStyles=a.css_settings.editorStyles}if(this._options.dialogStyles.length==0){this._options.dialogStyles=a.css_settings.dialogStyles}};WYMeditor.editor.prototype.listen=function(){jQuery(this._doc.body).bind("mousedown",this.mousedown)};WYMeditor.editor.prototype.mousedown=function(a){var b=WYMeditor.INSTANCES[this.ownerDocument.title];b._selected_image=(a.target.tagName.toLowerCase()==WYMeditor.IMG)?a.target:null};WYMeditor.loadCss=function(a){var c=document.createElement("link");c.rel="stylesheet";c.href=a;var b=jQuery("head").get(0);b.appendChild(c)};WYMeditor.editor.prototype.loadSkin=function(){if(this._options.loadSkin&&!WYMeditor.SKINS[this._options.skin]){var found=false;var rExp=new RegExp(this._options.skin+"/"+WYMeditor.SKINS_DEFAULT_CSS+"$");jQuery("link").each(function(){if(this.href.match(rExp)){found=true}});if(!found){WYMeditor.loadCss(this._options.skinPath+WYMeditor.SKINS_DEFAULT_CSS)}}jQuery(this._box).addClass("wym_skin_"+this._options.skin);if(this._options.initSkin&&!WYMeditor.SKINS[this._options.skin]){eval(jQuery.ajax({url:this._options.skinPath+WYMeditor.SKINS_DEFAULT_JS,async:false}).responseText)}if(WYMeditor.SKINS[this._options.skin]&&WYMeditor.SKINS[this._options.skin].init){WYMeditor.SKINS[this._options.skin].init(this)}};WYMeditor.INIT_DIALOG=function(index){var wym=window.opener.WYMeditor.INSTANCES[index];var doc=window.document;var selected=wym.selected();var dialogType=jQuery(wym._options.dialogTypeSelector).val();var sStamp=wym.uniqueStamp();switch(dialogType){case WYMeditor.DIALOG_LINK:if(selected&&selected.tagName&&selected.tagName.toLowerCase!=WYMeditor.A){selected=jQuery(selected).parentsOrSelf(WYMeditor.A)}if(!selected&&wym._selected_image){selected=jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A)}break}if(jQuery.isFunction(wym._options.preInitDialog)){wym._options.preInitDialog(wym,window)}var styles=doc.styleSheets[0];var aCss=eval(wym._options.dialogStyles);wym.addCssRules(doc,aCss);if(selected){jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF));jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC));jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE));jQuery(wym._options.relSelector).val(jQuery(selected).attr(WYMeditor.REL));jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT))}if(wym._selected_image){jQuery(wym._options.dialogImageSelector+" "+wym._options.srcSelector).val(jQuery(wym._selected_image).attr(WYMeditor.SRC));jQuery(wym._options.dialogImageSelector+" "+wym._options.titleSelector).val(jQuery(wym._selected_image).attr(WYMeditor.TITLE));jQuery(wym._options.dialogImageSelector+" "+wym._options.altSelector).val(jQuery(wym._selected_image).attr(WYMeditor.ALT))}jQuery(wym._options.dialogLinkSelector+" "+wym._options.submitSelector).submit(function(){var sUrl=jQuery(wym._options.hrefSelector).val();if(sUrl.length>0){var link;if(selected[0]&&selected[0].tagName.toLowerCase()==WYMeditor.A){link=selected}else{wym._exec(WYMeditor.CREATE_LINK,sStamp);link=jQuery("a[href="+sStamp+"]",wym._doc.body)}link.attr(WYMeditor.HREF,sUrl).attr(WYMeditor.TITLE,jQuery(wym._options.titleSelector).val()).attr(WYMeditor.REL,jQuery(wym._options.relSelector).val())}window.close()});jQuery(wym._options.dialogImageSelector+" "+wym._options.submitSelector).submit(function(){var sUrl=jQuery(wym._options.srcSelector).val();if(sUrl.length>0){wym._exec(WYMeditor.INSERT_IMAGE,sStamp);jQuery("img[src$="+sStamp+"]",wym._doc.body).attr(WYMeditor.SRC,sUrl).attr(WYMeditor.TITLE,jQuery(wym._options.titleSelector).val()).attr(WYMeditor.ALT,jQuery(wym._options.altSelector).val())}window.close()});jQuery(wym._options.dialogTableSelector+" "+wym._options.submitSelector).submit(function(){var iRows=jQuery(wym._options.rowsSelector).val();var iCols=jQuery(wym._options.colsSelector).val();if(iRows>0&&iCols>0){var table=wym._doc.createElement(WYMeditor.TABLE);var newRow=null;var newCol=null;var sCaption=jQuery(wym._options.captionSelector).val();var newCaption=table.createCaption();newCaption.innerHTML=sCaption;for(x=0;x<iRows;x++){newRow=table.insertRow(x);for(y=0;y<iCols;y++){newRow.insertCell(y)}}jQuery(table).attr("summary",jQuery(wym._options.summarySelector).val());var node=jQuery(wym.findUp(wym.container(),WYMeditor.MAIN_CONTAINERS)).get(0);if(!node||!node.parentNode){jQuery(wym._doc.body).append(table)}else{jQuery(node).after(table)}}window.close()});jQuery(wym._options.dialogPasteSelector+" "+wym._options.submitSelector).submit(function(){var sText=jQuery(wym._options.textSelector).val();wym.paste(sText);window.close()});jQuery(wym._options.dialogPreviewSelector+" "+wym._options.previewSelector).html(wym.xhtml());jQuery(wym._options.cancelSelector).mousedown(function(){window.close()});if(jQuery.isFunction(wym._options.postInitDialog)){wym._options.postInitDialog(wym,window)}};WYMeditor.XmlHelper=function(){this._entitiesDiv=document.createElement("div");return this};WYMeditor.XmlHelper.prototype.tag=function(c,b,a){b=b||false;a=a||false;return"<"+c+(b?this.tagOptions(b):"")+(a?">":" />")};WYMeditor.XmlHelper.prototype.contentTag=function(b,c,a){a=a||false;return"<"+b+(a?this.tagOptions(a):"")+">"+c+"</"+b+">"};WYMeditor.XmlHelper.prototype.cdataSection=function(a){return"<![CDATA["+a+"]]>"};WYMeditor.XmlHelper.prototype.escapeOnce=function(a){return this._fixDoubleEscape(this.escapeEntities(a))};WYMeditor.XmlHelper.prototype._fixDoubleEscape=function(a){return a.replace(/&amp;([a-z]+|(#\d+));/ig,"&$1;")};WYMeditor.XmlHelper.prototype.tagOptions=function(b){var a=this;a._formated_options="";for(var c in b){var d="";var e=b[c];if(typeof e!="function"&&e.length>0){if(parseInt(c)==c&&typeof e=="object"){c=e.shift();e=e.pop()}if(c!=""&&e!=""){a._formated_options+=" "+c+'="'+a.escapeOnce(e)+'"'}}}return a._formated_options};WYMeditor.XmlHelper.prototype.escapeEntities=function(c,b){this._entitiesDiv.innerHTML=c;this._entitiesDiv.textContent=c;var a=this._entitiesDiv.innerHTML;if(typeof b=="undefined"){if(b!=false){a=a.replace('"',"&quot;")}if(b==true){a=a.replace('"',"&#039;")}}return a};WYMeditor.XmlHelper.prototype.parseAttributes=function(h){var a=[];var g=h.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);if(g.toString()!=h){for(var d in g){var c=g[d];if(typeof c!="function"&&c.length!=0){var e=new RegExp("(\\w+)\\s*"+c);if(match=h.match(e)){var f=c.replace(/^[\s=]+/,"");var b=f.charAt(0);b=b=='"'?'"':(b=="'"?"'":"");if(b!=""){f=b=='"'?f.replace(/^"|"+$/g,""):f.replace(/^'|'+$/g,"")}h=h.replace(match[0],"");a.push([match[1],f])}}}}return a};WYMeditor.XhtmlValidator={_attributes:{core:{except:["base","head","html","meta","param","script","style","title"],attributes:["class","id","style","title","accesskey","tabindex"]},language:{except:["base","br","hr","iframe","param","script"],attributes:{dir:["ltr","rtl"],"0":"lang","1":"xml:lang"}},keyboard:{attributes:{accesskey:/^(\w){1}$/,tabindex:/^(\d)+$/}}},_events:{window:{only:["body"],attributes:["onload","onunload"]},form:{only:["form","input","textarea","select","a","label","button"],attributes:["onchange","onsubmit","onreset","onselect","onblur","onfocus"]},keyboard:{except:["base","bdo","br","frame","frameset","head","html","iframe","meta","param","script","style","title"],attributes:["onkeydown","onkeypress","onkeyup"]},mouse:{except:["base","bdo","br","head","html","meta","param","script","style","title"],attributes:["onclick","ondblclick","onmousedown","onmousemove","onmouseover","onmouseout","onmouseup"]}},_tags:{a:{attributes:{"0":"charset","1":"coords","2":"href","3":"hreflang","4":"name","5":"rel","6":"rev",shape:/^(rect|rectangle|circ|circle|poly|polygon)$/,"7":"type"}},"0":"abbr","1":"acronym","2":"address",area:{attributes:{"0":"alt","1":"coords","2":"href",nohref:/^(true|false)$/,shape:/^(rect|rectangle|circ|circle|poly|polygon)$/},required:["alt"]},"3":"b",base:{attributes:["href"],required:["href"]},bdo:{attributes:{dir:/^(ltr|rtl)$/},required:["dir"]},"4":"big",blockquote:{attributes:["cite"]},"5":"body","6":"br",button:{attributes:{disabled:/^(disabled)$/,type:/^(button|reset|submit)$/,"0":"value"},inside:"form"},"7":"caption","8":"cite","9":"code",col:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",span:/^(\d)+$/,valign:/^(top|middle|bottom|baseline)$/,"2":"width"},inside:"colgroup"},colgroup:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",span:/^(\d)+$/,valign:/^(top|middle|bottom|baseline)$/,"2":"width"}},"10":"dd",del:{attributes:{"0":"cite",datetime:/^([0-9]){8}/}},"11":"div","12":"dfn","13":"dl","14":"dt","15":"em",fieldset:{inside:"form"},form:{attributes:{"0":"action","1":"accept","2":"accept-charset","3":"enctype",method:/^(get|post)$/},required:["action"]},head:{attributes:["profile"]},"16":"h1","17":"h2","18":"h3","19":"h4","20":"h5","21":"h6","22":"hr",html:{attributes:["xmlns"]},"23":"i",img:{attributes:["alt","src","height","ismap","longdesc","usemap","width"],required:["alt","src"]},input:{attributes:{"0":"accept","1":"alt",checked:/^(checked)$/,disabled:/^(disabled)$/,maxlength:/^(\d)+$/,"2":"name",readonly:/^(readonly)$/,size:/^(\d)+$/,"3":"src",type:/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/,"4":"value"},inside:"form"},ins:{attributes:{"0":"cite",datetime:/^([0-9]){8}/}},"24":"kbd",label:{attributes:["for"],inside:"form"},"25":"legend","26":"li",link:{attributes:{"0":"charset","1":"href","2":"hreflang",media:/^(all|braille|print|projection|screen|speech|,|;| )+$/i,rel:/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,rev:/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,"3":"type"},inside:"head"},map:{attributes:["id","name"],required:["id"]},meta:{attributes:{"0":"content","http-equiv":/^(content\-type|expires|refresh|set\-cookie)$/i,"1":"name","2":"scheme"},required:["content"]},"27":"noscript",object:{attributes:["archive","classid","codebase","codetype","data","declare","height","name","standby","type","usemap","width"]},"28":"ol",optgroup:{attributes:{"0":"label",disabled:/^(disabled)$/},required:["label"]},option:{attributes:{"0":"label",disabled:/^(disabled)$/,selected:/^(selected)$/,"1":"value"},inside:"select"},"29":"p",param:{attributes:{"0":"type",valuetype:/^(data|ref|object)$/,"1":"valuetype","2":"value"},required:["name"]},"30":"pre",q:{attributes:["cite"]},"31":"samp",script:{attributes:{type:/^(text\/ecmascript|text\/javascript|text\/jscript|text\/vbscript|text\/vbs|text\/xml)$/,"0":"charset",defer:/^(defer)$/,"1":"src"},required:["type"]},select:{attributes:{disabled:/^(disabled)$/,multiple:/^(multiple)$/,"0":"name","1":"size"},inside:"form"},"32":"small","33":"span","34":"strong",style:{attributes:{"0":"type",media:/^(screen|tty|tv|projection|handheld|print|braille|aural|all)$/},required:["type"]},"35":"sub","36":"sup",table:{attributes:{"0":"border","1":"cellpadding","2":"cellspacing",frame:/^(void|above|below|hsides|lhs|rhs|vsides|box|border)$/,rules:/^(none|groups|rows|cols|all)$/,"3":"summary","4":"width"}},tbody:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom|baseline)$/}},td:{attributes:{"0":"abbr",align:/^(left|right|center|justify|char)$/,"1":"axis","2":"char","3":"charoff",colspan:/^(\d)+$/,"4":"headers",rowspan:/^(\d)+$/,scope:/^(col|colgroup|row|rowgroup)$/,valign:/^(top|middle|bottom|baseline)$/}},textarea:{attributes:["cols","rows","disabled","name","readonly"],required:["cols","rows"],inside:"form"},tfoot:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom)$/,"2":"baseline"}},th:{attributes:{"0":"abbr",align:/^(left|right|center|justify|char)$/,"1":"axis","2":"char","3":"charoff",colspan:/^(\d)+$/,"4":"headers",rowspan:/^(\d)+$/,scope:/^(col|colgroup|row|rowgroup)$/,valign:/^(top|middle|bottom|baseline)$/}},thead:{attributes:{align:/^(right|left|center|justify)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom|baseline)$/}},"37":"title",tr:{attributes:{align:/^(right|left|center|justify|char)$/,"0":"char","1":"charoff",valign:/^(top|middle|bottom|baseline)$/}},"38":"tt","39":"ul","40":"var"},skiped_attributes:[],skiped_attribute_values:[],getValidTagAttributes:function(a,b){var c={};var g=this.getPossibleTagAttributes(a);for(var e in b){var f=b[e];var d=WYMeditor.Helper;if(!d.contains(this.skiped_attributes,e)&&!d.contains(this.skiped_attribute_values,f)){if(typeof f!="function"&&d.contains(g,e)){if(this.doesAttributeNeedsValidation(a,e)){if(this.validateAttribute(a,e,f)){c[e]=f}}else{c[e]=f}}}}return c},getUniqueAttributesAndEventsForTag:function(b){var a=[];if(this._tags[b]&&this._tags[b]["attributes"]){for(k in this._tags[b]["attributes"]){a.push(parseInt(k)==k?this._tags[b]["attributes"][k]:k)}}return a},getDefaultAttributesAndEventsForTags:function(){var a=[];for(var b in this._events){a.push(this._events[b])}for(var b in this._attributes){a.push(this._attributes[b])}return a},isValidTag:function(a){if(this._tags[a]){return true}for(var b in this._tags){if(this._tags[b]==a){return true}}return false},getDefaultAttributesAndEventsForTag:function(a){var i=[];if(this.isValidTag(a)){var g=this.getDefaultAttributesAndEventsForTags();for(var d in g){var f=g[d];if(typeof f=="object"){var e=WYMeditor.Helper;if((f.except&&e.contains(f.except,a))||(f.only&&!e.contains(f.only,a))){continue}var c=f.attributes?f.attributes:f.events;for(var b in c){i.push(typeof c[b]!="string"?b:c[b])}}}}return i},doesAttributeNeedsValidation:function(a,b){return this._tags[a]&&((this._tags[a]["attributes"]&&this._tags[a]["attributes"][b])||(this._tags[a]["required"]&&WYMeditor.Helper.contains(this._tags[a]["required"],b)))},validateAttribute:function(a,b,c){if(this._tags[a]&&(this._tags[a]["attributes"]&&this._tags[a]["attributes"][b]&&c.length>0&&!c.match(this._tags[a]["attributes"][b]))||(this._tags[a]&&this._tags[a]["required"]&&WYMeditor.Helper.contains(this._tags[a]["required"],b)&&c.length==0)){return false}return typeof this._tags[a]!="undefined"},getPossibleTagAttributes:function(a){if(!this._possible_tag_attributes){this._possible_tag_attributes={}}if(!this._possible_tag_attributes[a]){this._possible_tag_attributes[a]=this.getUniqueAttributesAndEventsForTag(a).concat(this.getDefaultAttributesAndEventsForTag(a))}return this._possible_tag_attributes[a]}};WYMeditor.ParallelRegex=function(a){this._case=a;this._patterns=[];this._labels=[];this._regex=null;return this};WYMeditor.ParallelRegex.prototype.addPattern=function(c,a){a=a||true;var b=this._patterns.length;this._patterns[b]=c;this._labels[b]=a;this._regex=null};WYMeditor.ParallelRegex.prototype.match=function(c){if(this._patterns.length==0){return[false,""]}var d=c.match(this._getCompoundedRegex());if(!d){return[false,""]}var a=d[0];for(var b=1;b<d.length;b++){if(d[b]){return[this._labels[b-1],a]}}return[true,d[0]]};WYMeditor.ParallelRegex.prototype._getCompoundedRegex=function(){if(this._regex==null){for(var a=0,b=this._patterns.length;a<b;a++){this._patterns[a]="("+this._untokenizeRegex(this._tokenizeRegex(this._patterns[a]).replace(/([\/\(\)])/g,"\\$1"))+")"}this._regex=new RegExp(this._patterns.join("|"),this._getPerlMatchingFlags())}return this._regex};WYMeditor.ParallelRegex.prototype._tokenizeRegex=function(a){return a.replace(/\(\?(i|m|s|x|U)\)/,"~~~~~~Tk1$1~~~~~~").replace(/\(\?(\-[i|m|s|x|U])\)/,"~~~~~~Tk2$1~~~~~~").replace(/\(\?\=(.*)\)/,"~~~~~~Tk3$1~~~~~~").replace(/\(\?\!(.*)\)/,"~~~~~~Tk4$1~~~~~~").replace(/\(\?\<\=(.*)\)/,"~~~~~~Tk5$1~~~~~~").replace(/\(\?\<\!(.*)\)/,"~~~~~~Tk6$1~~~~~~").replace(/\(\?\:(.*)\)/,"~~~~~~Tk7$1~~~~~~")};WYMeditor.ParallelRegex.prototype._untokenizeRegex=function(a){return a.replace(/~~~~~~Tk1(.{1})~~~~~~/,"(?$1)").replace(/~~~~~~Tk2(.{2})~~~~~~/,"(?$1)").replace(/~~~~~~Tk3(.*)~~~~~~/,"(?=$1)").replace(/~~~~~~Tk4(.*)~~~~~~/,"(?!$1)").replace(/~~~~~~Tk5(.*)~~~~~~/,"(?<=$1)").replace(/~~~~~~Tk6(.*)~~~~~~/,"(?<!$1)").replace(/~~~~~~Tk7(.*)~~~~~~/,"(?:$1)")};WYMeditor.ParallelRegex.prototype._getPerlMatchingFlags=function(){return(this._case?"m":"mi")};WYMeditor.StateStack=function(a){this._stack=[a];return this};WYMeditor.StateStack.prototype.getCurrent=function(){return this._stack[this._stack.length-1]};WYMeditor.StateStack.prototype.enter=function(a){this._stack.push(a)};WYMeditor.StateStack.prototype.leave=function(){if(this._stack.length==1){return false}this._stack.pop();return true};WYMeditor.LEXER_ENTER=1;WYMeditor.LEXER_MATCHED=2;WYMeditor.LEXER_UNMATCHED=3;WYMeditor.LEXER_EXIT=4;WYMeditor.LEXER_SPECIAL=5;WYMeditor.Lexer=function(c,b,a){b=b||"accept";this._case=a||false;this._regexes={};this._parser=c;this._mode=new WYMeditor.StateStack(b);this._mode_handlers={};this._mode_handlers[b]=b;return this};WYMeditor.Lexer.prototype.addPattern=function(a,b){var b=b||"accept";if(typeof this._regexes[b]=="undefined"){this._regexes[b]=new WYMeditor.ParallelRegex(this._case)}this._regexes[b].addPattern(a);if(typeof this._mode_handlers[b]=="undefined"){this._mode_handlers[b]=b}};WYMeditor.Lexer.prototype.addEntryPattern=function(b,c,a){if(typeof this._regexes[c]=="undefined"){this._regexes[c]=new WYMeditor.ParallelRegex(this._case)}this._regexes[c].addPattern(b,a);if(typeof this._mode_handlers[a]=="undefined"){this._mode_handlers[a]=a}};WYMeditor.Lexer.prototype.addExitPattern=function(a,b){if(typeof this._regexes[b]=="undefined"){this._regexes[b]=new WYMeditor.ParallelRegex(this._case)}this._regexes[b].addPattern(a,"__exit");if(typeof this._mode_handlers[b]=="undefined"){this._mode_handlers[b]=b}};WYMeditor.Lexer.prototype.addSpecialPattern=function(b,c,a){if(typeof this._regexes[c]=="undefined"){this._regexes[c]=new WYMeditor.ParallelRegex(this._case)}this._regexes[c].addPattern(b,"_"+a);if(typeof this._mode_handlers[a]=="undefined"){this._mode_handlers[a]=a}};WYMeditor.Lexer.prototype.mapHandler=function(b,a){this._mode_handlers[b]=a};WYMeditor.Lexer.prototype.parse=function(d){if(typeof this._parser=="undefined"){return false}var e=d.length;var c;while(typeof(c=this._reduce(d))=="object"){var d=c[0];var b=c[1];var a=c[2];var f=c[3];if(!this._dispatchTokens(b,a,f)){return false}if(d==""){return true}if(d.length==e){return false}e=d.length}if(!c){return false}return this._invokeParser(d,WYMeditor.LEXER_UNMATCHED)};WYMeditor.Lexer.prototype._dispatchTokens=function(b,a,c){c=c||false;if(!this._invokeParser(b,WYMeditor.LEXER_UNMATCHED)){return false}if(typeof c=="boolean"){return this._invokeParser(a,WYMeditor.LEXER_MATCHED)}if(this._isModeEnd(c)){if(!this._invokeParser(a,WYMeditor.LEXER_EXIT)){return false}return this._mode.leave()}if(this._isSpecialMode(c)){this._mode.enter(this._decodeSpecial(c));if(!this._invokeParser(a,WYMeditor.LEXER_SPECIAL)){return false}return this._mode.leave()}this._mode.enter(c);return this._invokeParser(a,WYMeditor.LEXER_ENTER)};WYMeditor.Lexer.prototype._isModeEnd=function(a){return(a==="__exit")};WYMeditor.Lexer.prototype._isSpecialMode=function(a){return(a.substring(0,1)=="_")};WYMeditor.Lexer.prototype._decodeSpecial=function(a){return a.substring(1)};WYMeditor.Lexer.prototype._invokeParser=function(content,is_match){if(content===""){return true}var current=this._mode.getCurrent();var handler=this._mode_handlers[current];var result;eval("result = this._parser."+handler+"(content, is_match);");return result};WYMeditor.Lexer.prototype._reduce=function(c){var a=this._regexes[this._mode.getCurrent()].match(c);var b=a[1];var e=a[0];if(e){var f=c.indexOf(b);var d=c.substr(0,f);c=c.substring(f+b.length);return[c,d,b,e]}return true};WYMeditor.XhtmlLexer=function(a){jQuery.extend(this,new WYMeditor.Lexer(a,"Text"));this.mapHandler("Text","Text");this.addTokens();this.init();return this};WYMeditor.XhtmlLexer.prototype.init=function(){};WYMeditor.XhtmlLexer.prototype.addTokens=function(){this.addCommentTokens("Text");this.addScriptTokens("Text");this.addCssTokens("Text");this.addTagTokens("Text")};WYMeditor.XhtmlLexer.prototype.addCommentTokens=function(a){this.addEntryPattern("<!--",a,"Comment");this.addExitPattern("-->","Comment")};WYMeditor.XhtmlLexer.prototype.addScriptTokens=function(a){this.addEntryPattern("<script",a,"Script");this.addExitPattern("<\/script>","Script")};WYMeditor.XhtmlLexer.prototype.addCssTokens=function(a){this.addEntryPattern("<style",a,"Css");this.addExitPattern("</style>","Css")};WYMeditor.XhtmlLexer.prototype.addTagTokens=function(a){this.addSpecialPattern("<\\s*[a-z0-9:-]+\\s*>",a,"OpeningTag");this.addEntryPattern("<[a-z0-9:-]+[\\/ \\>]+",a,"OpeningTag");this.addInTagDeclarationTokens("OpeningTag");this.addSpecialPattern("</\\s*[a-z0-9:-]+\\s*>",a,"ClosingTag")};WYMeditor.XhtmlLexer.prototype.addInTagDeclarationTokens=function(a){this.addSpecialPattern("\\s+",a,"Ignore");this.addAttributeTokens(a);this.addExitPattern("/>",a);this.addExitPattern(">",a)};WYMeditor.XhtmlLexer.prototype.addAttributeTokens=function(a){this.addSpecialPattern("\\s*[a-z-_0-9]*:?[a-z-_0-9]+\\s*(?==)\\s*",a,"TagAttributes");this.addEntryPattern('=\\s*"',a,"DoubleQuotedAttribute");this.addPattern('\\\\"',"DoubleQuotedAttribute");this.addExitPattern('"',"DoubleQuotedAttribute");this.addEntryPattern("=\\s*'",a,"SingleQuotedAttribute");this.addPattern("\\\\'","SingleQuotedAttribute");this.addExitPattern("'","SingleQuotedAttribute");this.addSpecialPattern("=\\s*[^>\\s]*",a,"UnquotedAttribute")};WYMeditor.XhtmlParser=function(a,b){var b=b||"Text";this._Lexer=new WYMeditor.XhtmlLexer(this);this._Listener=a;this._mode=b;this._matches=[];this._last_match="";this._current_match="";return this};WYMeditor.XhtmlParser.prototype.parse=function(a){this._Lexer.parse(this.beforeParsing(a));return this.afterParsing(this._Listener.getResult())};WYMeditor.XhtmlParser.prototype.beforeParsing=function(a){if(a.match(/class="MsoNormal"/)||a.match(/ns = "urn:schemas-microsoft-com/)){this._Listener.avoidStylingTagsAndAttributes()}return this._Listener.beforeParsing(a)};WYMeditor.XhtmlParser.prototype.afterParsing=function(a){if(this._Listener._avoiding_tags_implicitly){this._Listener.allowStylingTagsAndAttributes()}return this._Listener.afterParsing(a)};WYMeditor.XhtmlParser.prototype.Ignore=function(a,b){return true};WYMeditor.XhtmlParser.prototype.Text=function(a){this._Listener.addContent(a);return true};WYMeditor.XhtmlParser.prototype.Comment=function(b,a){return this._addNonTagBlock(b,a,"addComment")};WYMeditor.XhtmlParser.prototype.Script=function(b,a){return this._addNonTagBlock(b,a,"addScript")};WYMeditor.XhtmlParser.prototype.Css=function(b,a){return this._addNonTagBlock(b,a,"addCss")};WYMeditor.XhtmlParser.prototype._addNonTagBlock=function(a,c,b){switch(c){case WYMeditor.LEXER_ENTER:this._non_tag=a;break;case WYMeditor.LEXER_UNMATCHED:this._non_tag+=a;break;case WYMeditor.LEXER_EXIT:switch(b){case"addComment":this._Listener.addComment(this._non_tag+a);break;case"addScript":this._Listener.addScript(this._non_tag+a);break;case"addCss":this._Listener.addCss(this._non_tag+a);break}}return true};WYMeditor.XhtmlParser.prototype.OpeningTag=function(a,b){switch(b){case WYMeditor.LEXER_ENTER:this._tag=this.normalizeTag(a);this._tag_attributes={};break;case WYMeditor.LEXER_SPECIAL:this._callOpenTagListener(this.normalizeTag(a));break;case WYMeditor.LEXER_EXIT:this._callOpenTagListener(this._tag,this._tag_attributes)}return true};WYMeditor.XhtmlParser.prototype.ClosingTag=function(a,b){this._callCloseTagListener(this.normalizeTag(a));return true};WYMeditor.XhtmlParser.prototype._callOpenTagListener=function(a,b){var b=b||{};this.autoCloseUnclosedBeforeNewOpening(a);if(this._Listener.isBlockTag(a)){this._Listener._tag_stack.push(a);this._Listener.fixNestingBeforeOpeningBlockTag(a,b);this._Listener.openBlockTag(a,b);this._increaseOpenTagCounter(a)}else{if(this._Listener.isInlineTag(a)){this._Listener.inlineTag(a,b)}else{this._Listener.openUnknownTag(a,b);this._increaseOpenTagCounter(a)}}this._Listener.last_tag=a;this._Listener.last_tag_opened=true;this._Listener.last_tag_attributes=b};WYMeditor.XhtmlParser.prototype._callCloseTagListener=function(a){if(this._decreaseOpenTagCounter(a)){this.autoCloseUnclosedBeforeTagClosing(a);if(this._Listener.isBlockTag(a)){var b=this._Listener._tag_stack.pop();if(b==false){return}else{if(b!=a){a=b}}this._Listener.closeBlockTag(a)}else{this._Listener.closeUnknownTag(a)}}else{this._Listener.closeUnopenedTag(a)}this._Listener.last_tag=a;this._Listener.last_tag_opened=false};WYMeditor.XhtmlParser.prototype._increaseOpenTagCounter=function(a){this._Listener._open_tags[a]=this._Listener._open_tags[a]||0;this._Listener._open_tags[a]++};WYMeditor.XhtmlParser.prototype._decreaseOpenTagCounter=function(a){if(this._Listener._open_tags[a]){this._Listener._open_tags[a]--;if(this._Listener._open_tags[a]==0){this._Listener._open_tags[a]=undefined}return true}return false};WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeNewOpening=function(a){this._autoCloseUnclosed(a,false)};WYMeditor.XhtmlParser.prototype.autoCloseUnclosedBeforeTagClosing=function(a){this._autoCloseUnclosed(a,true)};WYMeditor.XhtmlParser.prototype._autoCloseUnclosed=function(c,d){var d=d||false;if(this._Listener._open_tags){for(var a in this._Listener._open_tags){var b=this._Listener._open_tags[a];if(b>0&&this._Listener.shouldCloseTagAutomatically(a,c,d)){this._callCloseTagListener(a,true)}}}};WYMeditor.XhtmlParser.prototype.getTagReplacements=function(){return this._Listener.getTagReplacements()};WYMeditor.XhtmlParser.prototype.normalizeTag=function(a){a=a.replace(/^([\s<\/>]*)|([\s<\/>]*)$/gm,"").toLowerCase();var b=this._Listener.getTagReplacements();if(b[a]){return b[a]}return a};WYMeditor.XhtmlParser.prototype.TagAttributes=function(a,b){if(WYMeditor.LEXER_SPECIAL==b){this._current_attribute=a}return true};WYMeditor.XhtmlParser.prototype.DoubleQuotedAttribute=function(a,b){if(WYMeditor.LEXER_UNMATCHED==b){this._tag_attributes[this._current_attribute]=a}return true};WYMeditor.XhtmlParser.prototype.SingleQuotedAttribute=function(a,b){if(WYMeditor.LEXER_UNMATCHED==b){this._tag_attributes[this._current_attribute]=a}return true};WYMeditor.XhtmlParser.prototype.UnquotedAttribute=function(a,b){this._tag_attributes[this._current_attribute]=a.replace(/^=/,"");return true};WYMeditor.XhtmlSaxListener=function(){this.output="";this.helper=new WYMeditor.XmlHelper();this._open_tags={};this.validator=WYMeditor.XhtmlValidator;this._tag_stack=[];this.avoided_tags=[];this.entities={"&nbsp;":"&#160;","&iexcl;":"&#161;","&cent;":"&#162;","&pound;":"&#163;","&curren;":"&#164;","&yen;":"&#165;","&brvbar;":"&#166;","&sect;":"&#167;","&uml;":"&#168;","&copy;":"&#169;","&ordf;":"&#170;","&laquo;":"&#171;","&not;":"&#172;","&shy;":"&#173;","&reg;":"&#174;","&macr;":"&#175;","&deg;":"&#176;","&plusmn;":"&#177;","&sup2;":"&#178;","&sup3;":"&#179;","&acute;":"&#180;","&micro;":"&#181;","&para;":"&#182;","&middot;":"&#183;","&cedil;":"&#184;","&sup1;":"&#185;","&ordm;":"&#186;","&raquo;":"&#187;","&frac14;":"&#188;","&frac12;":"&#189;","&frac34;":"&#190;","&iquest;":"&#191;","&Agrave;":"&#192;","&Aacute;":"&#193;","&Acirc;":"&#194;","&Atilde;":"&#195;","&Auml;":"&#196;","&Aring;":"&#197;","&AElig;":"&#198;","&Ccedil;":"&#199;","&Egrave;":"&#200;","&Eacute;":"&#201;","&Ecirc;":"&#202;","&Euml;":"&#203;","&Igrave;":"&#204;","&Iacute;":"&#205;","&Icirc;":"&#206;","&Iuml;":"&#207;","&ETH;":"&#208;","&Ntilde;":"&#209;","&Ograve;":"&#210;","&Oacute;":"&#211;","&Ocirc;":"&#212;","&Otilde;":"&#213;","&Ouml;":"&#214;","&times;":"&#215;","&Oslash;":"&#216;","&Ugrave;":"&#217;","&Uacute;":"&#218;","&Ucirc;":"&#219;","&Uuml;":"&#220;","&Yacute;":"&#221;","&THORN;":"&#222;","&szlig;":"&#223;","&agrave;":"&#224;","&aacute;":"&#225;","&acirc;":"&#226;","&atilde;":"&#227;","&auml;":"&#228;","&aring;":"&#229;","&aelig;":"&#230;","&ccedil;":"&#231;","&egrave;":"&#232;","&eacute;":"&#233;","&ecirc;":"&#234;","&euml;":"&#235;","&igrave;":"&#236;","&iacute;":"&#237;","&icirc;":"&#238;","&iuml;":"&#239;","&eth;":"&#240;","&ntilde;":"&#241;","&ograve;":"&#242;","&oacute;":"&#243;","&ocirc;":"&#244;","&otilde;":"&#245;","&ouml;":"&#246;","&divide;":"&#247;","&oslash;":"&#248;","&ugrave;":"&#249;","&uacute;":"&#250;","&ucirc;":"&#251;","&uuml;":"&#252;","&yacute;":"&#253;","&thorn;":"&#254;","&yuml;":"&#255;","&OElig;":"&#338;","&oelig;":"&#339;","&Scaron;":"&#352;","&scaron;":"&#353;","&Yuml;":"&#376;","&fnof;":"&#402;","&circ;":"&#710;","&tilde;":"&#732;","&Alpha;":"&#913;","&Beta;":"&#914;","&Gamma;":"&#915;","&Delta;":"&#916;","&Epsilon;":"&#917;","&Zeta;":"&#918;","&Eta;":"&#919;","&Theta;":"&#920;","&Iota;":"&#921;","&Kappa;":"&#922;","&Lambda;":"&#923;","&Mu;":"&#924;","&Nu;":"&#925;","&Xi;":"&#926;","&Omicron;":"&#927;","&Pi;":"&#928;","&Rho;":"&#929;","&Sigma;":"&#931;","&Tau;":"&#932;","&Upsilon;":"&#933;","&Phi;":"&#934;","&Chi;":"&#935;","&Psi;":"&#936;","&Omega;":"&#937;","&alpha;":"&#945;","&beta;":"&#946;","&gamma;":"&#947;","&delta;":"&#948;","&epsilon;":"&#949;","&zeta;":"&#950;","&eta;":"&#951;","&theta;":"&#952;","&iota;":"&#953;","&kappa;":"&#954;","&lambda;":"&#955;","&mu;":"&#956;","&nu;":"&#957;","&xi;":"&#958;","&omicron;":"&#959;","&pi;":"&#960;","&rho;":"&#961;","&sigmaf;":"&#962;","&sigma;":"&#963;","&tau;":"&#964;","&upsilon;":"&#965;","&phi;":"&#966;","&chi;":"&#967;","&psi;":"&#968;","&omega;":"&#969;","&thetasym;":"&#977;","&upsih;":"&#978;","&piv;":"&#982;","&ensp;":"&#8194;","&emsp;":"&#8195;","&thinsp;":"&#8201;","&zwnj;":"&#8204;","&zwj;":"&#8205;","&lrm;":"&#8206;","&rlm;":"&#8207;","&ndash;":"&#8211;","&mdash;":"&#8212;","&lsquo;":"&#8216;","&rsquo;":"&#8217;","&sbquo;":"&#8218;","&ldquo;":"&#8220;","&rdquo;":"&#8221;","&bdquo;":"&#8222;","&dagger;":"&#8224;","&Dagger;":"&#8225;","&bull;":"&#8226;","&hellip;":"&#8230;","&permil;":"&#8240;","&prime;":"&#8242;","&Prime;":"&#8243;","&lsaquo;":"&#8249;","&rsaquo;":"&#8250;","&oline;":"&#8254;","&frasl;":"&#8260;","&euro;":"&#8364;","&image;":"&#8465;","&weierp;":"&#8472;","&real;":"&#8476;","&trade;":"&#8482;","&alefsym;":"&#8501;","&larr;":"&#8592;","&uarr;":"&#8593;","&rarr;":"&#8594;","&darr;":"&#8595;","&harr;":"&#8596;","&crarr;":"&#8629;","&lArr;":"&#8656;","&uArr;":"&#8657;","&rArr;":"&#8658;","&dArr;":"&#8659;","&hArr;":"&#8660;","&forall;":"&#8704;","&part;":"&#8706;","&exist;":"&#8707;","&empty;":"&#8709;","&nabla;":"&#8711;","&isin;":"&#8712;","&notin;":"&#8713;","&ni;":"&#8715;","&prod;":"&#8719;","&sum;":"&#8721;","&minus;":"&#8722;","&lowast;":"&#8727;","&radic;":"&#8730;","&prop;":"&#8733;","&infin;":"&#8734;","&ang;":"&#8736;","&and;":"&#8743;","&or;":"&#8744;","&cap;":"&#8745;","&cup;":"&#8746;","&int;":"&#8747;","&there4;":"&#8756;","&sim;":"&#8764;","&cong;":"&#8773;","&asymp;":"&#8776;","&ne;":"&#8800;","&equiv;":"&#8801;","&le;":"&#8804;","&ge;":"&#8805;","&sub;":"&#8834;","&sup;":"&#8835;","&nsub;":"&#8836;","&sube;":"&#8838;","&supe;":"&#8839;","&oplus;":"&#8853;","&otimes;":"&#8855;","&perp;":"&#8869;","&sdot;":"&#8901;","&lceil;":"&#8968;","&rceil;":"&#8969;","&lfloor;":"&#8970;","&rfloor;":"&#8971;","&lang;":"&#9001;","&rang;":"&#9002;","&loz;":"&#9674;","&spades;":"&#9824;","&clubs;":"&#9827;","&hearts;":"&#9829;","&diams;":"&#9830;"};this.block_tags=["a","abbr","acronym","address","area","b","base","bdo","big","blockquote","body","button","caption","cite","code","col","colgroup","dd","del","div","dfn","dl","dt","em","fieldset","form","head","h1","h2","h3","h4","h5","h6","html","i","ins","kbd","label","legend","li","map","noscript","object","ol","optgroup","option","p","param","pre","q","samp","script","select","small","span","strong","style","sub","sup","table","tbody","td","textarea","tfoot","th","thead","title","tr","tt","ul","var","extends"];this.inline_tags=["br","hr","img","input"];return this};WYMeditor.XhtmlSaxListener.prototype.shouldCloseTagAutomatically=function(a,c,b){var b=b||false;if(a=="td"){if((b&&c=="tr")||(!b&&c=="td")){return true}}if(a=="option"){if((b&&c=="select")||(!b&&c=="option")){return true}}return false};WYMeditor.XhtmlSaxListener.prototype.beforeParsing=function(a){this.output="";return a};WYMeditor.XhtmlSaxListener.prototype.afterParsing=function(a){a=this.replaceNamedEntities(a);a=this.joinRepeatedEntities(a);a=this.removeEmptyTags(a);a=this.removeBrInPre(a);return a};WYMeditor.XhtmlSaxListener.prototype.replaceNamedEntities=function(b){for(var a in this.entities){b=b.replace(new RegExp(a,"g"),this.entities[a])}return b};WYMeditor.XhtmlSaxListener.prototype.joinRepeatedEntities=function(b){var a="em|strong|sub|sup|acronym|pre|del|address";return b.replace(new RegExp("</("+a+")><\\1>",""),"").replace(new RegExp("(s*<("+a+")>s*){2}(.*)(s*</\\2>s*){2}",""),"<$2>$3<$2>")};WYMeditor.XhtmlSaxListener.prototype.removeEmptyTags=function(a){return a.replace(new RegExp("<("+this.block_tags.join("|").replace(/\|td/,"").replace(/\|th/,"")+")>(<br />|&#160;|&nbsp;|\\s)*</\\1>","g"),"")};WYMeditor.XhtmlSaxListener.prototype.removeBrInPre=function(c){var b=c.match(new RegExp("<pre[^>]*>(.*?)</pre>","gmi"));if(b){for(var a=0;a<b.length;a++){c=c.replace(b[a],b[a].replace(new RegExp("<br />","g"),String.fromCharCode(13,10)))}}return c};WYMeditor.XhtmlSaxListener.prototype.getResult=function(){return this.output};WYMeditor.XhtmlSaxListener.prototype.getTagReplacements=function(){return{b:"strong",i:"em"}};WYMeditor.XhtmlSaxListener.prototype.addContent=function(a){this.output+=a};WYMeditor.XhtmlSaxListener.prototype.addComment=function(a){if(this.remove_comments){this.output+=a}};WYMeditor.XhtmlSaxListener.prototype.addScript=function(a){if(!this.remove_scripts){this.output+=a}};WYMeditor.XhtmlSaxListener.prototype.addCss=function(a){if(!this.remove_embeded_styles){this.output+=a}};WYMeditor.XhtmlSaxListener.prototype.openBlockTag=function(a,b){this.output+=this.helper.tag(a,this.validator.getValidTagAttributes(a,b),true)};WYMeditor.XhtmlSaxListener.prototype.inlineTag=function(a,b){this.output+=this.helper.tag(a,this.validator.getValidTagAttributes(a,b))};WYMeditor.XhtmlSaxListener.prototype.openUnknownTag=function(a,b){};WYMeditor.XhtmlSaxListener.prototype.closeBlockTag=function(a){this.output=this.output.replace(/<br \/>$/,"")+this._getClosingTagContent("before",a)+"</"+a+">"+this._getClosingTagContent("after",a)};WYMeditor.XhtmlSaxListener.prototype.closeUnknownTag=function(a){};WYMeditor.XhtmlSaxListener.prototype.closeUnopenedTag=function(a){this.output+="</"+a+">"};WYMeditor.XhtmlSaxListener.prototype.avoidStylingTagsAndAttributes=function(){this.avoided_tags=["div","span"];this.validator.skiped_attributes=["style"];this.validator.skiped_attribute_values=["MsoNormal","main1"];this._avoiding_tags_implicitly=true};WYMeditor.XhtmlSaxListener.prototype.allowStylingTagsAndAttributes=function(){this.avoided_tags=[];this.validator.skiped_attributes=[];this.validator.skiped_attribute_values=[];this._avoiding_tags_implicitly=false};WYMeditor.XhtmlSaxListener.prototype.isBlockTag=function(a){return !WYMeditor.Helper.contains(this.avoided_tags,a)&&WYMeditor.Helper.contains(this.block_tags,a)};WYMeditor.XhtmlSaxListener.prototype.isInlineTag=function(a){return !WYMeditor.Helper.contains(this.avoided_tags,a)&&WYMeditor.Helper.contains(this.inline_tags,a)};WYMeditor.XhtmlSaxListener.prototype.insertContentAfterClosingTag=function(a,b){this._insertContentWhenClosingTag("after",a,b)};WYMeditor.XhtmlSaxListener.prototype.insertContentBeforeClosingTag=function(a,b){this._insertContentWhenClosingTag("before",a,b)};WYMeditor.XhtmlSaxListener.prototype.fixNestingBeforeOpeningBlockTag=function(a,b){if(a!="li"&&(a=="ul"||a=="ol")&&this.last_tag&&!this.last_tag_opened&&this.last_tag=="li"){this.output=this.output.replace(/<\/li>$/,"");this.insertContentAfterClosingTag(a,"</li>")}};WYMeditor.XhtmlSaxListener.prototype._insertContentWhenClosingTag=function(b,a,c){if(!this["_insert_"+b+"_closing"]){this["_insert_"+b+"_closing"]=[]}if(!this["_insert_"+b+"_closing"][a]){this["_insert_"+b+"_closing"][a]=[]}this["_insert_"+b+"_closing"][a].push(c)};WYMeditor.XhtmlSaxListener.prototype._getClosingTagContent=function(b,a){if(this["_insert_"+b+"_closing"]&&this["_insert_"+b+"_closing"][a]&&this["_insert_"+b+"_closing"][a].length>0){return this["_insert_"+b+"_closing"][a].pop()}return""};WYMeditor.WymCssLexer=function(b,a){var a=(typeof a=="undefined"?true:a);jQuery.extend(this,new WYMeditor.Lexer(b,(a?"Ignore":"WymCss")));this.mapHandler("WymCss","Ignore");if(a==true){this.addEntryPattern("/\\\x2a[<\\s]*WYMeditor[>\\s]*\\\x2a/","Ignore","WymCss");this.addExitPattern("/\\\x2a[</\\s]*WYMeditor[>\\s]*\\\x2a/","WymCss")}this.addSpecialPattern("[\\sa-z1-6]*\\\x2e[a-z-_0-9]+","WymCss","WymCssStyleDeclaration");this.addEntryPattern("/\\\x2a","WymCss","WymCssComment");this.addExitPattern("\\\x2a/","WymCssComment");this.addEntryPattern("\x7b","WymCss","WymCssStyle");this.addExitPattern("\x7d","WymCssStyle");this.addEntryPattern("/\\\x2a","WymCssStyle","WymCssFeedbackStyle");this.addExitPattern("\\\x2a/","WymCssFeedbackStyle");return this};WYMeditor.WymCssParser=function(){this._in_style=false;this._has_title=false;this.only_wym_blocks=true;this.css_settings={classesItems:[],editorStyles:[],dialogStyles:[]};return this};WYMeditor.WymCssParser.prototype.parse=function(a,b){var b=(typeof b=="undefined"?this.only_wym_blocks:b);this._Lexer=new WYMeditor.WymCssLexer(this,b);this._Lexer.parse(a)};WYMeditor.WymCssParser.prototype.Ignore=function(a,b){return true};WYMeditor.WymCssParser.prototype.WymCssComment=function(b,a){if(b.match(/end[a-z0-9\s]*wym[a-z0-9\s]*/mi)){return false}if(a==WYMeditor.LEXER_UNMATCHED){if(!this._in_style){this._has_title=true;this._current_item={title:WYMeditor.Helper.trim(b)}}else{if(this._current_item[this._current_element]){if(!this._current_item[this._current_element].expressions){this._current_item[this._current_element].expressions=[b]}else{this._current_item[this._current_element].expressions.push(b)}}}this._in_style=true}return true};WYMeditor.WymCssParser.prototype.WymCssStyle=function(b,a){if(a==WYMeditor.LEXER_UNMATCHED){b=WYMeditor.Helper.trim(b);if(b!=""){this._current_item[this._current_element].style=b}}else{if(a==WYMeditor.LEXER_EXIT){this._in_style=false;this._has_title=false;this.addStyleSetting(this._current_item)}}return true};WYMeditor.WymCssParser.prototype.WymCssFeedbackStyle=function(b,a){if(a==WYMeditor.LEXER_UNMATCHED){this._current_item[this._current_element].feedback_style=b.replace(/^([\s\/\*]*)|([\s\/\*]*)$/gm,"")}return true};WYMeditor.WymCssParser.prototype.WymCssStyleDeclaration=function(b){b=b.replace(/^([\s\.]*)|([\s\.*]*)$/gm,"");var a="";if(b.indexOf(".")>0){var c=b.split(".");this._current_element=c[1];var a=c[0]}else{this._current_element=b}if(!this._has_title){this._current_item={title:(!a?"":a.toUpperCase()+": ")+this._current_element};this._has_title=true}if(!this._current_item[this._current_element]){this._current_item[this._current_element]={name:this._current_element}}if(a){if(!this._current_item[this._current_element].tags){this._current_item[this._current_element].tags=[a]}else{this._current_item[this._current_element].tags.push(a)}}return true};WYMeditor.WymCssParser.prototype.addStyleSetting=function(a){for(var b in a){var c=a[b];if(typeof c=="object"&&b!="title"){this.css_settings.classesItems.push({name:WYMeditor.Helper.trim(c.name),title:a.title,expr:WYMeditor.Helper.trim((c.expressions||c.tags).join(", "))});if(c.feedback_style){this.css_settings.editorStyles.push({name:"."+WYMeditor.Helper.trim(c.name),css:c.feedback_style})}if(c.style){this.css_settings.dialogStyles.push({name:"."+WYMeditor.Helper.trim(c.name),css:c.style})}}}};jQuery.fn.isPhantomNode=function(){if(this[0].nodeType==3){return !(/[^\t\n\r ]/.test(this[0].data))}return false};WYMeditor.isPhantomNode=function(a){if(a.nodeType==3){return !(/[^\t\n\r ]/.test(a.data))}return false};WYMeditor.isPhantomString=function(a){return !(/[^\t\n\r ]/.test(a))};jQuery.fn.parentsOrSelf=function(b){var a=this;if(a[0].nodeType==3){a=a.parents().slice(0,1)}if(a.filter(b).size()==1){return a}else{return a.parents(b).slice(0,1)}};WYMeditor.Helper={replaceAll:function(d,a,c){var b=new RegExp(a,"g");return(d.replace(b,c))},insertAt:function(b,a,c){return(b.substr(0,c)+a+b.substring(c))},trim:function(a){return a.replace(/^(\s*)|(\s*)$/gm,"")},contains:function(a,c){for(var b=0;b<a.length;b++){if(a[b]===c){return true}}return false},indexOf:function(a,d){var b=-1;for(var c=0;c<a.length;c++){if(a[c]==d){b=c;break}}return(b)},findByName:function(a,b){for(var c=0;c<a.length;c++){var d=a[c];if(d.name==b){return(d)}}return(null)}};WYMeditor.WymClassExplorer=function(a){this._wym=a;this._class="className";this._newLine="\r\n"};WYMeditor.WymClassExplorer.prototype.initIframe=function(iframe){this._iframe=iframe;this._doc=iframe.contentWindow.document;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);jQuery(this._doc.body).html(this._wym._html);var wym=this;this._doc.body.onfocus=function(){wym._doc.designMode="on";wym._doc=iframe.contentWindow.document};this._doc.onbeforedeactivate=function(){wym.saveCaret()};this._doc.onkeyup=function(){wym.saveCaret();wym.keyup()};this._doc.onclick=function(){wym.saveCaret()};this._doc.body.onbeforepaste=function(){wym._iframe.contentWindow.event.returnValue=false};this._doc.body.onpaste=function(){wym._iframe.contentWindow.event.returnValue=false;wym.paste(window.clipboardData.getData("Text"))};if(this._initialized){if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()}this._initialized=true;this._doc.designMode="on";try{this._doc=iframe.contentWindow.document}catch(e){}};(function(a){WYMeditor.WymClassExplorer.prototype.loadSkin=function(){jQuery(this._box).find(this._options.containerSelector).attr("unselectable","on");a.call(this)}})(WYMeditor.editor.prototype.loadSkin);WYMeditor.WymClassExplorer.prototype._exec=function(c,d){switch(c){case WYMeditor.INDENT:case WYMeditor.OUTDENT:var a=this.findUp(this.container(),WYMeditor.LI);if(a){var b=a.parentNode.parentNode;if(a.parentNode.childNodes.length>1||b.tagName.toLowerCase()==WYMeditor.OL||b.tagName.toLowerCase()==WYMeditor.UL){this._doc.execCommand(c)}}break;default:if(d){this._doc.execCommand(c,false,d)}else{this._doc.execCommand(c)}break}};WYMeditor.WymClassExplorer.prototype.selected=function(){var a=this._iframe.contentWindow.document.caretPos;if(a!=null){if(a.parentElement!=undefined){return(a.parentElement())}}};WYMeditor.WymClassExplorer.prototype.saveCaret=function(){this._doc.caretPos=this._doc.selection.createRange()};WYMeditor.WymClassExplorer.prototype.addCssRule=function(c,d){var b=d.name.split(",");for(var a in b){c.addRule(b[a],d.css)}};WYMeditor.WymClassExplorer.prototype.insert=function(b){var a=this._doc.selection.createRange();if(jQuery(a.parentElement()).parents(this._options.iframeBodySelector).is("*")){try{a.pasteHTML(b)}catch(c){}}else{this.paste(b)}};WYMeditor.WymClassExplorer.prototype.wrap=function(d,b){var a=this._doc.selection.createRange();if(jQuery(a.parentElement()).parents(this._options.iframeBodySelector).is("*")){try{a.pasteHTML(d+a.text+b)}catch(c){}}};WYMeditor.WymClassExplorer.prototype.unwrap=function(){var a=this._doc.selection.createRange();if(jQuery(a.parentElement()).parents(this._options.iframeBodySelector).is("*")){try{var c=a.text;this._exec("Cut");a.pasteHTML(c)}catch(b){}}};WYMeditor.WymClassExplorer.prototype.keyup=function(){this._selected_image=null};WYMeditor.WymClassExplorer.prototype.setFocusToNode=function(c,b){var a=this._doc.selection.createRange();b=b?true:false;a.moveToElementText(c);a.collapse(b);a.select();c.focus()};WYMeditor.WymClassMozilla=function(a){this._wym=a;this._class="class";this._newLine="\n"};WYMeditor.WymClassMozilla.prototype.initIframe=function(iframe){var wym=this;this._iframe=iframe;this._doc=iframe.contentDocument;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);this.html(this._wym._html);this.enableDesignMode();if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();jQuery(this._doc).bind("keydown",this.keydown);jQuery(this._doc).bind("keyup",this.keyup);jQuery(this._doc).bind("focus",function(){wym.enableDesignMode.call(wym)});if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()};WYMeditor.WymClassMozilla.prototype.html=function(a){if(typeof a==="string"){try{this._doc.designMode="off"}catch(b){}a=a.replace(/<em(\b[^>]*)>/gi,"<i$1>").replace(/<\/em>/gi,"</i>").replace(/<strong(\b[^>]*)>/gi,"<b$1>").replace(/<\/strong>/gi,"</b>");jQuery(this._doc.body).html(a);this.enableDesignMode()}else{return(jQuery(this._doc.body).html())}};WYMeditor.WymClassMozilla.prototype._exec=function(e,g){if(!this.selected()){return(false)}switch(e){case WYMeditor.INDENT:case WYMeditor.OUTDENT:var f=this.selected();var d=this._iframe.contentWindow.getSelection();var b=d.anchorNode;if(b.nodeName=="#text"){b=b.parentNode}f=this.findUp(f,WYMeditor.BLOCKS);b=this.findUp(b,WYMeditor.BLOCKS);if(f&&f==b&&f.tagName.toLowerCase()==WYMeditor.LI){var c=f.parentNode.parentNode;if(f.parentNode.childNodes.length>1||c.tagName.toLowerCase()==WYMeditor.OL||c.tagName.toLowerCase()==WYMeditor.UL){this._doc.execCommand(e,"",null)}}break;default:if(g){this._doc.execCommand(e,"",g)}else{this._doc.execCommand(e,"",null)}}var a=this.selected();if(a.tagName.toLowerCase()==WYMeditor.BODY){this._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}};WYMeditor.WymClassMozilla.prototype.selected=function(){var b=this._iframe.contentWindow.getSelection();var a=b.focusNode;if(a){if(a.nodeName=="#text"){return(a.parentNode)}else{return(a)}}else{return(null)}};WYMeditor.WymClassMozilla.prototype.addCssRule=function(a,b){a.insertRule(b.name+" {"+b.css+"}",a.cssRules.length)};WYMeditor.WymClassMozilla.prototype.keydown=function(b){var c=WYMeditor.INSTANCES[this.title];var a=null;if(b.ctrlKey){if(b.keyCode==66){c._exec(WYMeditor.BOLD);return false}if(b.keyCode==73){c._exec(WYMeditor.ITALIC);return false}}else{if(b.keyCode==13){if(!b.shiftKey){a=c.selected();if(a&&a.tagName.toLowerCase()==WYMeditor.PRE){b.preventDefault();c.insert("<p></p>")}}}}};WYMeditor.WymClassMozilla.prototype.keyup=function(b){var d=WYMeditor.INSTANCES[this.title];d._selected_image=null;var a=null;if(b.keyCode==13&&!b.shiftKey){jQuery(d._doc.body).children(WYMeditor.BR).remove()}if(b.keyCode!=8&&b.keyCode!=17&&b.keyCode!=46&&b.keyCode!=224&&!b.metaKey&&!b.ctrlKey){a=d.selected();var c=a.tagName.toLowerCase();if(c=="strong"||c=="b"||c=="em"||c=="i"||c=="sub"||c=="sup"||c=="a"){c=a.parentNode.tagName.toLowerCase()}if(c==WYMeditor.BODY){d._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}}};WYMeditor.WymClassMozilla.prototype.enableDesignMode=function(){if(this._doc.designMode=="off"){try{this._doc.designMode="on";this._doc.execCommand("styleWithCSS","",false)}catch(a){}}};WYMeditor.WymClassMozilla.prototype.openBlockTag=function(a,c){var c=this.validator.getValidTagAttributes(a,c);if(a=="span"&&c.style){var b=this.getTagForStyle(c.style);if(b){a=b;this._tag_stack.pop();this._tag_stack.push(a);c.style=""}}this.output+=this.helper.tag(a,c,true)};WYMeditor.WymClassMozilla.prototype.getTagForStyle=function(a){if(/bold/.test(a)){return"strong"}if(/italic/.test(a)){return"em"}if(/sub/.test(a)){return"sub"}if(/super/.test(a)){return"sup"}return false};WYMeditor.WymClassOpera=function(a){this._wym=a;this._class="class";this._newLine="\r\n"};WYMeditor.WymClassOpera.prototype.initIframe=function(iframe){this._iframe=iframe;this._doc=iframe.contentWindow.document;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);this._doc.designMode="on";this.html(this._wym._html);if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();jQuery(this._doc).bind("keydown",this.keydown);jQuery(this._doc).bind("keyup",this.keyup);if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()};WYMeditor.WymClassOpera.prototype._exec=function(a,b){if(b){this._doc.execCommand(a,false,b)}else{this._doc.execCommand(a)}};WYMeditor.WymClassOpera.prototype.selected=function(){var b=this._iframe.contentWindow.getSelection();var a=b.focusNode;if(a){if(a.nodeName=="#text"){return(a.parentNode)}else{return(a)}}else{return(null)}};WYMeditor.WymClassOpera.prototype.addCssRule=function(a,b){a.insertRule(b.name+" {"+b.css+"}",a.cssRules.length)};WYMeditor.WymClassOpera.prototype.keydown=function(a){var c=WYMeditor.INSTANCES[this.title];var b=c._iframe.contentWindow.getSelection();startNode=b.getRangeAt(0).startContainer;if(!jQuery(startNode).parentsOrSelf(WYMeditor.MAIN_CONTAINERS.join(","))[0]&&!jQuery(startNode).parentsOrSelf("li")&&a.keyCode!=WYMeditor.KEY.ENTER&&a.keyCode!=WYMeditor.KEY.LEFT&&a.keyCode!=WYMeditor.KEY.UP&&a.keyCode!=WYMeditor.KEY.RIGHT&&a.keyCode!=WYMeditor.KEY.DOWN&&a.keyCode!=WYMeditor.KEY.BACKSPACE&&a.keyCode!=WYMeditor.KEY.DELETE){c._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}};WYMeditor.WymClassOpera.prototype.keyup=function(a){var b=WYMeditor.INSTANCES[this.title];b._selected_image=null};WYMeditor.WymClassSafari=function(a){this._wym=a;this._class="class";this._newLine="\n"};WYMeditor.WymClassSafari.prototype.initIframe=function(iframe){this._iframe=iframe;this._doc=iframe.contentDocument;var styles=this._doc.styleSheets[0];var aCss=eval(this._options.editorStyles);this.addCssRules(this._doc,aCss);this._doc.title=this._wym._index;jQuery("html",this._doc).attr("dir",this._options.direction);this._doc.designMode="on";this.html(this._wym._html);if(jQuery.isFunction(this._options.preBind)){this._options.preBind(this)}this._wym.bindEvents();jQuery(this._doc).bind("keydown",this.keydown);jQuery(this._doc).bind("keyup",this.keyup);if(jQuery.isFunction(this._options.postInit)){this._options.postInit(this)}this.listen()};WYMeditor.WymClassSafari.prototype._exec=function(e,g){if(!this.selected()){return(false)}switch(e){case WYMeditor.INDENT:case WYMeditor.OUTDENT:var f=this.selected();var d=this._iframe.contentWindow.getSelection();var b=d.anchorNode;if(b.nodeName=="#text"){b=b.parentNode}f=this.findUp(f,WYMeditor.BLOCKS);b=this.findUp(b,WYMeditor.BLOCKS);if(f&&f==b&&f.tagName.toLowerCase()==WYMeditor.LI){var c=f.parentNode.parentNode;if(f.parentNode.childNodes.length>1||c.tagName.toLowerCase()==WYMeditor.OL||c.tagName.toLowerCase()==WYMeditor.UL){this._doc.execCommand(e,"",null)}}break;case WYMeditor.INSERT_ORDEREDLIST:case WYMeditor.INSERT_UNORDEREDLIST:this._doc.execCommand(e,"",null);var f=this.selected();var a=this.findUp(f,WYMeditor.MAIN_CONTAINERS);if(a){jQuery(a).replaceWith(jQuery(a).html())}break;default:if(g){this._doc.execCommand(e,"",g)}else{this._doc.execCommand(e,"",null)}}var a=this.selected();if(a&&a.tagName.toLowerCase()==WYMeditor.BODY){this._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}};WYMeditor.WymClassSafari.prototype.selected=function(){var b=this._iframe.contentWindow.getSelection();var a=b.focusNode;if(a){if(a.nodeName=="#text"){return(a.parentNode)}else{return(a)}}else{return(null)}};WYMeditor.WymClassSafari.prototype.addCssRule=function(a,b){a.insertRule(b.name+" {"+b.css+"}",a.cssRules.length)};WYMeditor.WymClassSafari.prototype.keydown=function(b){var a=WYMeditor.INSTANCES[this.title];if(b.ctrlKey){if(b.keyCode==66){a._exec(WYMeditor.BOLD);b.preventDefault()}if(b.keyCode==73){a._exec(WYMeditor.ITALIC);b.preventDefault()}}else{if(b.shiftKey&&b.keyCode==13){a._exec("InsertLineBreak");b.preventDefault()}}};WYMeditor.WymClassSafari.prototype.keyup=function(b){var d=WYMeditor.INSTANCES[this.title];d._selected_image=null;var a=null;if(b.keyCode==13&&!b.shiftKey){jQuery(d._doc.body).children(WYMeditor.BR).remove();a=d.selected();if(a&&a.tagName.toLowerCase()==WYMeditor.PRE){d._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}}if(b.keyCode!=8&&b.keyCode!=17&&b.keyCode!=46&&b.keyCode!=224&&!b.metaKey&&!b.ctrlKey){a=d.selected();var c=a.tagName.toLowerCase();if(c=="strong"||c=="b"||c=="em"||c=="i"||c=="sub"||c=="sup"||c=="a"||c=="span"){c=a.parentNode.tagName.toLowerCase()}if(c==WYMeditor.BODY||c==WYMeditor.DIV){d._exec(WYMeditor.FORMAT_BLOCK,WYMeditor.P)}}};WYMeditor.WymClassSafari.prototype.openBlockTag=function(a,c){var c=this.validator.getValidTagAttributes(a,c);if(a=="span"&&c.style){var b=this.getTagForStyle(c.style);if(b){a=b;this._tag_stack.pop();this._tag_stack.push(a);c.style="";if(typeof c["class"]=="string"){c["class"]=c["class"].replace(/apple-style-span/gi,"")}}}this.output+=this.helper.tag(a,c,true)};WYMeditor.WymClassSafari.prototype.getTagForStyle=function(a){if(/bold/.test(a)){return"strong"}if(/italic/.test(a)){return"em"}if(/sub/.test(a)){return"sub"}if(/super/.test(a)){return"sup"}return false}; \ No newline at end of file
diff --git a/studio/static/js/wymeditor/jquery.wymeditor.mozilla.js b/studio/static/js/wymeditor/jquery.wymeditor.mozilla.js
new file mode 100644
index 0000000..3aa84b9
--- /dev/null
+++ b/studio/static/js/wymeditor/jquery.wymeditor.mozilla.js
@@ -0,0 +1,278 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.mozilla.js
+ * Gecko specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Volker Mische (vmx a-t gmx dotde)
+ * Bermi Ferrer (wymeditor a-t bermi dotorg)
+ * Frédéric Palluel-Lafleur (fpalluel a-t gmail dotcom)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+WYMeditor.WymClassMozilla = function(wym) {
+
+ this._wym = wym;
+ this._class = "class";
+ this._newLine = "\n";
+};
+
+WYMeditor.WymClassMozilla.prototype.initIframe = function(iframe) {
+ var wym = this;
+
+ this._iframe = iframe;
+ this._doc = iframe.contentDocument;
+
+ //add css rules from options
+
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init html value
+ this.html(this._wym._html);
+
+ //init designMode
+ this.enableDesignMode();
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //bind editor keydown events
+ jQuery(this._doc).bind("keydown", this.keydown);
+
+ //bind editor keyup events
+ jQuery(this._doc).bind("keyup", this.keyup);
+
+ //bind editor focus events (used to reset designmode - Gecko bug)
+ jQuery(this._doc).bind("focus", function () {
+ // Fix scope
+ wym.enableDesignMode.call(wym);
+ });
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+};
+
+/* @name html
+ * @description Get/Set the html value
+ */
+WYMeditor.WymClassMozilla.prototype.html = function(html) {
+
+ if(typeof html === 'string') {
+
+ //disable designMode
+ try { this._doc.designMode = "off"; } catch(e) { };
+
+ //replace em by i and strong by bold
+ //(designMode issue)
+ html = html.replace(/<em(\b[^>]*)>/gi, "<i$1>")
+ .replace(/<\/em>/gi, "</i>")
+ .replace(/<strong(\b[^>]*)>/gi, "<b$1>")
+ .replace(/<\/strong>/gi, "</b>");
+
+ //update the html body
+ jQuery(this._doc.body).html(html);
+
+ //re-init designMode
+ this.enableDesignMode();
+ }
+ else return(jQuery(this._doc.body).html());
+};
+
+WYMeditor.WymClassMozilla.prototype._exec = function(cmd,param) {
+
+ if(!this.selected()) return(false);
+
+ switch(cmd) {
+
+ case WYMeditor.INDENT: case WYMeditor.OUTDENT:
+
+ var focusNode = this.selected();
+ var sel = this._iframe.contentWindow.getSelection();
+ var anchorNode = sel.anchorNode;
+ if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode;
+
+ focusNode = this.findUp(focusNode, WYMeditor.BLOCKS);
+ anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS);
+
+ if(focusNode && focusNode == anchorNode
+ && focusNode.tagName.toLowerCase() == WYMeditor.LI) {
+
+ var ancestor = focusNode.parentNode.parentNode;
+
+ if(focusNode.parentNode.childNodes.length>1
+ || ancestor.tagName.toLowerCase() == WYMeditor.OL
+ || ancestor.tagName.toLowerCase() == WYMeditor.UL)
+ this._doc.execCommand(cmd,'',null);
+ }
+
+ break;
+
+ default:
+
+ if(param) this._doc.execCommand(cmd,'',param);
+ else this._doc.execCommand(cmd,'',null);
+ }
+
+ //set to P if parent = BODY
+ var container = this.selected();
+ if(container.tagName.toLowerCase() == WYMeditor.BODY)
+ this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+};
+
+/* @name selected
+ * @description Returns the selected container
+ */
+WYMeditor.WymClassMozilla.prototype.selected = function() {
+
+ var sel = this._iframe.contentWindow.getSelection();
+ var node = sel.focusNode;
+ if(node) {
+ if(node.nodeName == "#text") return(node.parentNode);
+ else return(node);
+ } else return(null);
+};
+
+WYMeditor.WymClassMozilla.prototype.addCssRule = function(styles, oCss) {
+
+ styles.insertRule(oCss.name + " {" + oCss.css + "}",
+ styles.cssRules.length);
+};
+
+
+//keydown handler, mainly used for keyboard shortcuts
+WYMeditor.WymClassMozilla.prototype.keydown = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+ var container = null;
+
+ if(evt.ctrlKey){
+ if(evt.keyCode == 66){
+ //CTRL+b => STRONG
+ wym._exec(WYMeditor.BOLD);
+ return false;
+ }
+ if(evt.keyCode == 73){
+ //CTRL+i => EMPHASIS
+ wym._exec(WYMeditor.ITALIC);
+ return false;
+ }
+ }
+
+ else if(evt.keyCode == 13) {
+ if(!evt.shiftKey){
+ //fix PRE bug #73
+ container = wym.selected();
+ if(container && container.tagName.toLowerCase() == WYMeditor.PRE) {
+ evt.preventDefault();
+ wym.insert('<p></p>');
+ }
+ }
+ }
+};
+
+//keyup handler, mainly used for cleanups
+WYMeditor.WymClassMozilla.prototype.keyup = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+
+ wym._selected_image = null;
+ var container = null;
+
+ if(evt.keyCode == 13 && !evt.shiftKey) {
+
+ //RETURN key
+ //cleanup <br><br> between paragraphs
+ jQuery(wym._doc.body).children(WYMeditor.BR).remove();
+ }
+
+ if(evt.keyCode != 8
+ && evt.keyCode != 17
+ && evt.keyCode != 46
+ && evt.keyCode != 224
+ && !evt.metaKey
+ && !evt.ctrlKey) {
+
+ //NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND
+ //text nodes replaced by P
+
+ container = wym.selected();
+ var name = container.tagName.toLowerCase();
+
+ //fix forbidden main containers
+ if(
+ name == "strong" ||
+ name == "b" ||
+ name == "em" ||
+ name == "i" ||
+ name == "sub" ||
+ name == "sup" ||
+ name == "a"
+
+ ) name = container.parentNode.tagName.toLowerCase();
+
+ if(name == WYMeditor.BODY) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+ }
+};
+
+WYMeditor.WymClassMozilla.prototype.enableDesignMode = function() {
+ if(this._doc.designMode == "off") {
+ try {
+ this._doc.designMode = "on";
+ this._doc.execCommand("styleWithCSS", '', false);
+ } catch(e) { }
+ }
+};
+
+WYMeditor.WymClassMozilla.prototype.openBlockTag = function(tag, attributes)
+{
+ var attributes = this.validator.getValidTagAttributes(tag, attributes);
+
+ // Handle Mozilla styled spans
+ if (tag == 'span' && attributes.style) {
+ var new_tag = this.getTagForStyle(attributes.style);
+ if (new_tag) {
+ tag = new_tag;
+ this._tag_stack.pop();
+ this._tag_stack.push(tag);
+ attributes.style = '';
+ }
+ }
+
+ this.output += this.helper.tag(tag, attributes, true);
+};
+
+WYMeditor.WymClassMozilla.prototype.getTagForStyle = function(style) {
+
+ if(/bold/.test(style)) return 'strong';
+ if(/italic/.test(style)) return 'em';
+ if(/sub/.test(style)) return 'sub';
+ if(/super/.test(style)) return 'sup';
+ return false;
+};
+
diff --git a/studio/static/js/wymeditor/jquery.wymeditor.opera.js b/studio/static/js/wymeditor/jquery.wymeditor.opera.js
new file mode 100644
index 0000000..53c2232
--- /dev/null
+++ b/studio/static/js/wymeditor/jquery.wymeditor.opera.js
@@ -0,0 +1,119 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.opera.js
+ * Opera specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+WYMeditor.WymClassOpera = function(wym) {
+
+ this._wym = wym;
+ this._class = "class";
+ this._newLine = "\r\n";
+};
+
+WYMeditor.WymClassOpera.prototype.initIframe = function(iframe) {
+
+ this._iframe = iframe;
+ this._doc = iframe.contentWindow.document;
+
+ //add css rules from options
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init designMode
+ this._doc.designMode = "on";
+
+ //init html value
+ this.html(this._wym._html);
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //bind editor keydown events
+ jQuery(this._doc).bind("keydown", this.keydown);
+
+ //bind editor events
+ jQuery(this._doc).bind("keyup", this.keyup);
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+};
+
+WYMeditor.WymClassOpera.prototype._exec = function(cmd,param) {
+
+ if(param) this._doc.execCommand(cmd,false,param);
+ else this._doc.execCommand(cmd);
+
+};
+
+WYMeditor.WymClassOpera.prototype.selected = function() {
+
+ var sel=this._iframe.contentWindow.getSelection();
+ var node=sel.focusNode;
+ if(node) {
+ if(node.nodeName=="#text")return(node.parentNode);
+ else return(node);
+ } else return(null);
+};
+
+WYMeditor.WymClassOpera.prototype.addCssRule = function(styles, oCss) {
+
+ styles.insertRule(oCss.name + " {" + oCss.css + "}",
+ styles.cssRules.length);
+};
+
+//keydown handler
+WYMeditor.WymClassOpera.prototype.keydown = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+ var sel = wym._iframe.contentWindow.getSelection();
+ startNode = sel.getRangeAt(0).startContainer;
+
+ //Get a P instead of no container
+ if(!jQuery(startNode).parentsOrSelf(
+ WYMeditor.MAIN_CONTAINERS.join(","))[0]
+ && !jQuery(startNode).parentsOrSelf('li')
+ && evt.keyCode != WYMeditor.KEY.ENTER
+ && evt.keyCode != WYMeditor.KEY.LEFT
+ && evt.keyCode != WYMeditor.KEY.UP
+ && evt.keyCode != WYMeditor.KEY.RIGHT
+ && evt.keyCode != WYMeditor.KEY.DOWN
+ && evt.keyCode != WYMeditor.KEY.BACKSPACE
+ && evt.keyCode != WYMeditor.KEY.DELETE)
+ wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+
+};
+
+//keyup handler
+WYMeditor.WymClassOpera.prototype.keyup = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+ wym._selected_image = null;
+};
diff --git a/studio/static/js/wymeditor/jquery.wymeditor.pack.js b/studio/static/js/wymeditor/jquery.wymeditor.pack.js
new file mode 100644
index 0000000..427b621
--- /dev/null
+++ b/studio/static/js/wymeditor/jquery.wymeditor.pack.js
@@ -0,0 +1 @@
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(!f)o f={};(j(){if(!2n.4N||!4N.rN){o ae=["rM","rL","rK","rJ","9r","rI","3C","rH","rG","rF","rE","rD","5a","rC","cR","rB"];f.4N={};1j(o i=0;i<ae.Y;++i)f.4N[ae[i]]=j(){}}X f.4N=2n.4N})();D.55(f,{rA:"0.5-rz",2x:[],7K:[],5i:[],7P:"G",3Q:"{ry}",9X:"rx",dI:"{rw}",9n:"{rv}",9m:"{ru}",f1:"dB/",7w:"2Z.1b",dr:"2Z.js",f0:"3L/",9W:"{rt}",eZ:"1L/3U/",9l:"{rs}",9o:"{rr}",9V:"{rq}",9U:"{rp}",9M:"{ro}",9O:"{rn}",7S:"{rm}",9N:"{rl}",9S:"{rk}",9J:"{rj}",7R:"{ri}",9K:"{rh}",9T:"{rg}",9D:"{rf}",9G:"{rd}",9F:"{rc}",9E:"{rb}",7T:"{ra}",9R:"{r9}",9Q:"{r8}",9k:"{r7}",9j:"{r6}",dS:"3T",3w:"1i",ai:"N",P:"p",9A:"h1",9z:"h2",9y:"h3",9x:"h4",9w:"h5",9v:"h6",5r:"43",6e:"2H",A:"a",6o:"br",dz:"6V",dj:"3k",6g:"4E",5m:"5L",6s:"2G",6t:"ol",6v:"li",dT:"J",97:"1R",7u:"1V",64:"O",96:"5X",7t:"5c",68:"a5",7H:"a4",7G:"a2",7F:"a0",82:"eY",81:"eX",98:"eQ",94:"eP",dY:"eO",r5:"r4",dX:"eN",6x:"ad",6w:"ac",dW:"eM",4r:"r3",7E:"aa",r2:"ab",ak:"eT",al:"eU",6r:1g 6f("p","h1","h2","h3","h4","h5","h6","43","2H"),5v:1g 6f("6Q","2H","N","dl","2T","1M","h1","h2","h3","h4","h5","h6","hr","8w","ol","p","43","3k","2G","dd","dt","li","8v","4E","8u","5L","8t","6U"),3W:{ao:8,at:13,r1:35,r0:36,as:37,ar:38,aq:39,ap:40,qZ:1g 6f(37,38,39,40),an:46},qY:{qX:1,qW:2,qV:3},1h:j(5G,1S){c.3g=f.2x.1Y(c)-1;c.7J=5G;c.C=1S;c.4Q=D(5G).1A();if(c.C.Q)c.4Q=c.C.Q;c.C.4K=c.C.4K||c.dE();c.C.5j=c.C.5j||c.C.4K+f.f1+c.C.2Z+\'/\';c.C.7D=c.C.7D||c.dD();c.C.9s=c.C.9s||c.C.4K+f.f0;c.C.7U=c.C.7U||c.C.4K+f.eZ;c.C.7C=c.C.7C||c.dC();c.58()}});D.fn.5l=j(1S){1S=D.55({Q:"",4K:L,5j:L,7D:L,7U:L,7C:L,1E:L,4g:L,2Z:"3U",ds:S,52:S,3L:"en",4R:"8V",1x:"<N J=\'eL\'>"+"<N J=\'qU\'>"+f.9U+"</N>"+"<N J=\'qT\'></N>"+"<N J=\'qS\'>"+f.9T+f.9S+"</N>"+"<N J=\'qR\'>"+f.7T+f.9R+f.9Q+"</N>"+"<N J=\'qQ\'>"+f.9V+"</N>"+"</N>",ef:"<a J=\'qP\' "+"1R=\'8T://eB.5l.eA/\'>f</a>",4o:"<N J=\'a9 5p\'>"+"<1L "+"1V=\'"+f.9W+"qO.Q\' "+"4J=\'c.1I.qN.f.2x["+f.3Q+"].5y(c)\'"+"></1L>"+"</N>",3h:[],ee:"<N J=\'a8 5p\'>"+"<h2>{qM}</h2>"+"<2G>"+f.9M+"</2G>"+"</N>",e8:"<li J=\'"+f.9N+"\'><a 1R=\'#\' G=\'"+f.9O+"\' O=\'"+f.7S+"\'>"+f.7S+"</a></li>",e9:[{\'G\':\'eY\',\'O\':\'qL\',\'1b\':\'qK\'},{\'G\':\'eX\',\'O\':\'qJ\',\'1b\':\'qI\'},{\'G\':\'eW\',\'O\':\'eW\',\'1b\':\'qH\'},{\'G\':\'eV\',\'O\':\'eV\',\'1b\':\'qG\'},{\'G\':\'eU\',\'O\':\'qF\',\'1b\':\'qE\'},{\'G\':\'eT\',\'O\':\'qD\',\'1b\':\'qC\'},{\'G\':\'ad\',\'O\':\'ad\',\'1b\':\'qB\'},{\'G\':\'ac\',\'O\':\'ac\',\'1b\':\'qA\'},{\'G\':\'eS\',\'O\':\'eS\',\'1b\':\'qz\'},{\'G\':\'eR\',\'O\':\'eR\',\'1b\':\'qy\'},{\'G\':\'eQ\',\'O\':\'a5\',\'1b\':\'qx\'},{\'G\':\'ab\',\'O\':\'ab\',\'1b\':\'qw\'},{\'G\':\'eP\',\'O\':\'a4\',\'1b\':\'qv\'},{\'G\':\'eO\',\'O\':\'a2\',\'1b\':\'qu\'},{\'G\':\'eN\',\'O\':\'a0\',\'1b\':\'qt\'},{\'G\':\'eM\',\'O\':\'7T\',\'1b\':\'qs\'},{\'G\':\'aa\',\'O\':\'aa\',\'1b\':\'qr\'}],ed:"<N J=\'a7 5p\'>"+"<h2>{qq}</h2>"+"<2G>"+f.9D+"</2G>"+"</N>",e5:"<li J=\'"+f.9E+"\'>"+"<a 1R=\'#\' G=\'"+f.9G+"\'>"+f.9F+"</a></li>",e6:[{\'G\':\'P\',\'O\':\'qp\',\'1b\':\'qo\'},{\'G\':\'9A\',\'O\':\'qn\',\'1b\':\'qm\'},{\'G\':\'9z\',\'O\':\'ql\',\'1b\':\'qk\'},{\'G\':\'9y\',\'O\':\'qj\',\'1b\':\'qi\'},{\'G\':\'9x\',\'O\':\'qh\',\'1b\':\'qg\'},{\'G\':\'9w\',\'O\':\'qf\',\'1b\':\'qe\'},{\'G\':\'9v\',\'O\':\'qd\',\'1b\':\'qc\'},{\'G\':\'5r\',\'O\':\'qb\',\'1b\':\'qa\'},{\'G\':\'6e\',\'O\':\'q9\',\'1b\':\'q8\'},{\'G\':\'5m\',\'O\':\'q7\',\'1b\':\'q6\'}],ec:"<N J=\'a6 5p\'>"+"<h2>{q5}</h2><2G>"+f.9J+"</2G></N>",e7:"<li J=\'q4"+f.7R+"\'><a 1R=\'#\' G=\'"+f.7R+"\'>"+f.9K+"</a></li>",3Y:[],ea:"<N J=\'eJ 5p\'>"+"<h2>{q3}</h2>"+"</N>",eb:"<N J=\'eK 5p\'>"+"<h2>{q2}</h2>"+"<4F J=\'eI\'></4F>"+"</N>",q1:".eL",q0:".a8",pZ:" 2G",pY:".a7",pX:".a6",9g:".eK",pW:".a9 1L",6z:".a9",dR:".eJ",e3:".a8 a",88:".a7 a",e2:".a6 a",9q:".eI",99:".ew",7v:".es",63:".a3",95:".ev",7s:".er",dc:".eh",dm:".ek",dk:".ej",di:".eo",df:".el",62:"1M",d8:".6j",d9:"",dq:".6k",do:".ex",67:".eu",dn:".ep",de:".ei",da:".eg",e1:".pV",e0:"7Q",7I:"eH=no,eG=no,eF=no,eE=no"+",4I=eD,7m=eC,3q=0,2m=0",dV:"eH=no,eG=no,eF=no,eE=no"+",pU=pT,4I=eD,7m=eC,3q=0,2m=0",1H:"<!pS Q pR \'-//pQ//ez pP 1.0 pO//pN\'"+" \'8T://eB.pM.eA/pL/ey/ez/ey-pK.pJ\'>"+"<Q 3C=\'"+f.9o+"\'><2S>"+"<2w 5X=\'4g\' 1d=\'V/1b\' 8R=\'7k\'"+" 1R=\'"+f.9n+"\' />"+"<O>"+f.9k+"</O>"+"<2j 1d=\'V/8S\'"+" 1V=\'"+f.9l+"\'></2j>"+"<2j 1d=\'V/8S\'"+" 1V=\'"+f.9m+"\'></2j>"+"</2S>"+f.9j+"</Q>",dO:"<1i J=\'6i ex\'"+" 4J=\'f.5h("+f.3Q+")\'"+">"+"<1M>"+"<2T>"+"<1z 1d=\'5Y\' J=\'6k\' R=\'"+f.68+"\' />"+"<3l>{a5}</3l>"+"<N J=\'1N\'>"+"<1n>{et}</1n>"+"<1z 1d=\'V\' J=\'ew\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N\'>"+"<1n>{eq}</1n>"+"<1z 1d=\'V\' J=\'a3\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N\'>"+"<1n>{pI}</1n>"+"<1z 1d=\'V\' J=\'ev\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N 1N-a1\'>"+"<1z J=\'7Y\' 1d=\'3r\'"+" R=\'{7X}\' />"+"<1z J=\'6j\' 1d=\'3K\'"+"R=\'{7W}\' />"+"</N>"+"</2T>"+"</1M>"+"</1i>",dN:"<1i J=\'6i eu\'"+" 4J=\'f.5h("+f.3Q+")\'"+">"+"<1M>"+"<2T>"+"<1z 1d=\'5Y\' J=\'6k\' R=\'"+f.7H+"\' />"+"<3l>{a4}</3l>"+"<N J=\'1N\'>"+"<1n>{et}</1n>"+"<1z 1d=\'V\' J=\'es\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N\'>"+"<1n>{pH}</1n>"+"<1z 1d=\'V\' J=\'er\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N\'>"+"<1n>{eq}</1n>"+"<1z 1d=\'V\' J=\'a3\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N 1N-a1\'>"+"<1z J=\'7Y\' 1d=\'3r\'"+" R=\'{7X}\' />"+"<1z J=\'6j\' 1d=\'3K\'"+"R=\'{7W}\' />"+"</N>"+"</2T>"+"</1M>"+"</1i>",dM:"<1i J=\'6i ep\'"+" 4J=\'f.5h("+f.3Q+")\'"+">"+"<1M>"+"<2T>"+"<1z 1d=\'5Y\' J=\'6k\' R=\'"+f.7G+"\' />"+"<3l>{a2}</3l>"+"<N J=\'1N\'>"+"<1n>{pG}</1n>"+"<1z 1d=\'V\' J=\'eo\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N\'>"+"<1n>{pF}</1n>"+"<1z 1d=\'V\' J=\'el\' R=\'\' 2u=\'40\' />"+"</N>"+"<N J=\'1N\'>"+"<1n>{pE}</1n>"+"<1z 1d=\'V\' J=\'ek\' R=\'3\' 2u=\'3\' />"+"</N>"+"<N J=\'1N\'>"+"<1n>{pD}</1n>"+"<1z 1d=\'V\' J=\'ej\' R=\'2\' 2u=\'3\' />"+"</N>"+"<N J=\'1N 1N-a1\'>"+"<1z J=\'7Y\' 1d=\'3r\'"+" R=\'{7X}\' />"+"<1z J=\'6j\' 1d=\'3K\'"+"R=\'{7W}\' />"+"</N>"+"</2T>"+"</1M>"+"</1i>",dL:"<1i J=\'6i ei\'"+" 4J=\'f.5h("+f.3Q+")\'"+">"+"<1M>"+"<1z 1d=\'5Y\' J=\'6k\' R=\'"+f.7F+"\' />"+"<2T>"+"<3l>{a0}</3l>"+"<N J=\'1N\'>"+"<4F J=\'eh\' 7i=\'10\' 7j=\'50\'></4F>"+"</N>"+"<N J=\'1N\'>"+"<1z J=\'7Y\' 1d=\'3r\'"+" R=\'{7X}\' />"+"<1z J=\'6j\' 1d=\'3K\'"+"R=\'{7W}\' />"+"</N>"+"</2T>"+"</1M>"+"</1i>",dK:"<1i J=\'6i eg\'"+" 4J=\'f.5h("+f.3Q+")\'"+"></1i>",4z:[],6c:"{",6b:"}",9Z:1f,3B:1f,3z:1f,9a:1f,90:1f},1S);u c.dw(j(){1g f.1h(D(c),1S)})};D.55({pC:j(i){u(f.2x[i])}});f.1h.B.58=j(){if(D.7V.pB){o 4p=1g f.2t(c)}X if(D.7V.pA){o 4p=1g f.2C(c)}X if(D.7V.pz){o 4p=1g f.3V(c)}X if(D.7V.py){o 4p=1g f.3c(c)}if(4p){if(D.2A(c.C.9Z))c.C.9Z(c);o 9Y=1g f.1m();D.55(9Y,4p);c.42=1g f.1w(9Y);if(c.C.1E||c.C.4g){c.dA()}c.4O=1g f.2V();1j(o 6Z in 4p){c[6Z]=4p[6Z]}c.2f=D(c.7J).e4().6J(c.C.1x).8U().du(\'px\'+c.3g);if(D.2A(D.fn.4y)){D.4y(c.2f.5Z(0),f.9X,c.3g);D.4y(c.7J.5Z(0),f.9X,c.3g)}o h=f.1P;o 4o=c.C.4o;4o=h.1l(4o,f.3Q,c.3g);4o=h.1l(4o,f.9W,c.C.7U);o 1x=D(c.2f).Q();1x=h.1l(1x,f.9V,c.C.ef);1x=h.1l(1x,f.9U,c.C.ee);1x=h.1l(1x,f.9T,c.C.ed);1x=h.1l(1x,f.9S,c.C.ec);1x=h.1l(1x,f.7T,c.C.eb);1x=h.1l(1x,f.9R,4o);1x=h.1l(1x,f.9Q,c.C.ea);o 9P=2B(c.C.e9);o 9L="";1j(o i=0;i<9P.Y;i++){o 5o=9P[i];if(5o.G&&5o.O)o 4n=c.C.e8;o 4n=h.1l(4n,f.9O,5o.G);4n=h.1l(4n,f.7S,c.C.6c+5o.O+c.C.6b);4n=h.1l(4n,f.9N,5o.1b);9L+=4n}1x=h.1l(1x,f.9M,9L);o 6h=2B(c.C.3Y);o 9I="";1j(o i=0;i<6h.Y;i++){o 4l=6h[i];if(4l.G&&4l.O)o 4k=c.C.e7;4k=h.1l(4k,f.7R,4l.G);4k=h.1l(4k,f.9K,4l.O);9I+=4k}1x=h.1l(1x,f.9J,9I);o 9H=2B(c.C.e6);o 9C="";1j(o i=0;i<9H.Y;i++){o 5n=9H[i];if(5n.G&&5n.O)o 4m=c.C.e5;4m=h.1l(4m,f.9G,5n.G);4m=h.1l(4m,f.9F,c.C.6c+5n.O+c.C.6b);4m=h.1l(4m,f.9E,5n.1b);9C+=4m}1x=h.1l(1x,f.9D,9C);1x=c.9i(1x);D(c.2f).Q(1x);D(c.2f).3E(c.C.9g).e4();c.52()}};f.1h.B.5x=j(){o F=c;D(c.2f).3E(c.C.e3).7Q(j(){F.1K.1I.4W();F.dZ(D(c).1r(f.7P));u(L)});D(c.2f).3E(c.C.88).7Q(j(){F.K(D(c).1r(f.7P));u(L)});D(c.2f).3E(c.C.9q).2y(j(){D(F.I.1i).Q(D(c).1A())}).4W(j(){D(c).6d(\'9p\')}).pw(j(){D(c).6d(\'9p\')});D(c.2f).3E(c.C.e2).7Q(j(){o 6h=2B(F.C.3Y);o 9B=D(c).1r(f.7P);o 4l=f.1P.aH(6h,9B);if(4l){o 4x=4l.aO;F.6d(9B,4x)}F.1K.1I.4W();u(L)});D(c.C.e1).3A(c.C.e0,j(){F.6a()})};f.1h.B.pv=j(){u(c.I!=1f)};f.1h.B.co=j(){u(c.2f)};f.1h.B.Q=j(Q){if(1s Q===\'3T\')D(c.I.1i).Q(Q);X u(D(c.I.1i).Q())};f.1h.B.1v=j(){u c.42.3Z(c.Q())};f.1h.B.dZ=j(1F){3f(1F){1k f.98:o K=c.K();if(K||c.2l)c.4L(f.68);1p;1k f.94:c.4L(f.7H);1p;1k f.dY:c.4L(f.7G);1p;1k f.dX:c.4L(f.7F);1p;1k f.dW:c.6a();c.dG();1p;1k f.7E:c.4L(f.7E,c.C.dV);1p;3U:c.1O(1F);1p}};f.1h.B.K=j(3t){if(3t){o K=1f;if(3t.1q()==f.5m){K=c.K();3f(K.1t.1q()){1k f.6g:1k f.5m:1p;3U:o 7O=1g 6f(f.6g,f.5m);K=c.2K(c.K(),7O);1p}if(K!=1f){3t=(K.1t.1q()==f.6g)?f.5m:f.6g;c.9t(K,3t);c.6a()}}X{o 7O=1g 6f(f.P,f.9A,f.9z,f.9y,f.9x,f.9w,f.9v,f.5r,f.6e);K=c.2K(c.K(),7O);if(K){o 3R=1f;if(3t.1q()==f.6e){o 2H=c.2K(c.K(),f.6e);if(2H==1f){3R=c.I.61(3t);K.1B.dU(3R,K);3R.dx(K);c.4Z(3R.pu)}X{o 7N=2H.6u;o 9u=7N.Y;o 7M=1f;if(9u>0)7M=7N.4v(0);1j(o x=0;x<9u;x++){2H.1B.dU(7N.4v(0),2H)}2H.1B.pt(2H);if(7M)c.4Z(7M)}}X c.9t(K,3t);c.6a()}}}X u(c.1c())};f.1h.B.6d=j(4k,4x){o K=(c.2l?c.2l:D(c.1c()));K=D(K).4V(4x);D(K).6d(4k);if(!D(K).1r(f.dT))D(K).ps(c.5z)};f.1h.B.2K=j(Z,53){if(Z){o 4j=Z.1t.1q();if(1s(53)==f.dS){7c(4j!=53&&4j!=f.3w){Z=Z.1B;4j=Z.1t.1q()}}X{o 7L=L;7c(!7L&&4j!=f.3w){1j(o i=0;i<53.Y;i++){if(4j==53[i]){7L=S;1p}}if(!7L){Z=Z.1B;4j=Z.1t.1q()}}}if(4j!=f.3w)u(Z);X u(1f)}X u(1f)};f.1h.B.9t=j(Z,3t){o 3R=c.I.61(3t);o Q=D(Z).Q();Z.1B.pr(3R,Z);D(3R).Q(Q);c.4Z(3R)};f.1h.B.9i=j(4M){if(!f.7K[c.C.3L]){4u{2B(D.9e({9d:c.C.9s+c.C.3L+\'.js\',9c:L}).9b)}4t(e){f.4N.9r("f: 9r 7c pq d3 cP.");u 4M}}1j(o 1U in f.7K[c.C.3L]){4M=f.1P.1l(4M,c.C.6c+1U+c.C.6b,f.7K[c.C.3L][1U])};u(4M)};f.1h.B.dH=j(4M){u(c.C.6c+4M+c.C.6b)};f.1h.B.2p=j(dQ){D(c.2f).3E(c.C.dR).Q(dQ)};f.1h.B.6a=j(){o Q;D(c.I.1i).80(f.6o).5s();Q=c.1v();D(c.7J).1A(Q);D(c.2f).3E(c.C.9q).bt(\'.9p\').1A(Q);};f.1h.B.4L=j(69,7I,dJ){o dP=7I||c.1W.C.7I;o 9h=2n.60(\'\',\'4L\',dP);if(9h){o 4i="";3f(69){1k(f.68):4i=c.C.dO;1p;1k(f.7H):4i=c.C.dN;1p;1k(f.7G):4i=c.C.dM;1p;1k(f.7F):4i=c.C.dL;1p;1k(f.7E):4i=c.C.dK;1p;3U:4i=dJ}o h=f.1P;o 1H=c.C.1H;1H=h.1l(1H,f.dI,c.C.4K);1H=h.1l(1H,f.9o,c.C.4R);1H=h.1l(1H,f.9n,c.C.5j+f.7w);1H=h.1l(1H,f.9m,c.C.7D);1H=h.1l(1H,f.9l,c.C.7C);1H=h.1l(1H,f.9k,c.dH(69));1H=h.1l(1H,f.9j,4i);1H=h.1l(1H,f.3Q,c.3g);1H=c.9i(1H);o 4h=9h.3D;4h.pp(1H);4h.5f()}};f.1h.B.dG=j(){D(c.2f).3E(c.C.9g).po()};f.1h.B.dp=j(){o dF=1g pn();u("F-"+dF.pm())};f.1h.B.5E=j(2M){o K=c.1c(),Q=\'\',3s,1u;3s=2M.5F(1g 2N(c.4U+\'{2,}\',\'g\'));1j(o i=0,l=3s.Y;i<l;i++){Q+=\'<p>\'+(3s[i].5F(c.4U).5A(\'<br />\'))+\'</p>\'}if(K&&K.1t.1q()!=f.3w){3s=D(Q,c.I).pl(K);1u=3s[3s.Y-1]}X{3s=D(Q,c.I).pk(c.I.1i);1u=3s[3s.Y-1]}if(D(K).V()==\'\'){D(K).5s()}D(\'1i > br\',c.I).5s();c.4Z(1u)};f.1h.B.5B=j(Q){o 2L=c.1K.1I.3e(),1D,Z;if(2L.1u!=1f){1D=2L.au(0);Z=1D.pj(Q);1D.ph();1D.pg(Z)}X{c.5E(Q)}};f.1h.B.az=j(2m,2D){c.5B(2m+c.1K.1I.3e().8W()+2D)};f.1h.B.ay=j(){c.5B(c.1K.1I.3e().8W())};f.1h.B.4Z=j(Z,3X){o 1D=c.I.4Y(),2L=c.1K.1I.3e();3X=3X?0:1;1D.pf(Z);2L.pe(1D);2L.ax(Z,3X);c.1K.1I.4W()};f.1h.B.4S=j(4h,2s){o 1E=4h.4T[0];if(1E){1j(o i=0;i<2s.Y;i++){o 1Z=2s[i];if(1Z.G&&1Z.1b)c.5t(1E,1Z)}}};f.1h.B.dE=j(){u D(D.7x(D(\'2j\'),j(s){u(s.1V&&s.1V.M(/7B\\.5l(\\.7A|\\.7z|\\.7y)?\\.js(\\?.*)?$/))})).1r(\'1V\').W(/7B\\.5l(\\.7A|\\.7z|\\.7y)?\\.js(\\?.*)?$/,\'\')};f.1h.B.dD=j(){u D(D.7x(D(\'2j\'),j(s){u(s.1V&&s.1V.M(/7B\\.5l(\\.7A|\\.7z|\\.7y)?\\.js(\\?.*)?$/))})).1r(\'1V\')};f.1h.B.dC=j(){u D(D.7x(D(\'2j\'),j(s){u(s.1V&&s.1V.M(/7B(-(.*)){0,1}(\\.7A|\\.7z|\\.7y)?\\.js(\\?.*)?$/))})).1r(\'1V\')};f.1h.B.pd=j(){u D(D.7x(D(\'2w\'),j(s){u(s.1R&&s.1R.M(/5l\\/dB\\/(.*)7k\\.1b(\\?.*)?$/))})).1r(\'1R\')};f.1h.B.dA=j(){o 5k=1g f.3G();if(c.C.4g){5k.3Z(D.9e({9d:c.C.4g,9c:L}).9b)}X{5k.3Z(c.C.1E,L)}if(c.C.3Y.Y==0){c.C.3Y=5k.4A.3Y}if(c.C.3h.Y==0){c.C.3h=5k.4A.3h}if(c.C.4z.Y==0){c.C.4z=5k.4A.4z}};f.1h.B.5w=j(){D(c.I.1i).3A("7r",c.7r)};f.1h.B.7r=j(1e){o F=f.2x[c.pc.O];F.2l=(1e.dy.1t.1q()==f.dz)?1e.dy:1f};f.dv=j(1R){o 2w=3D.61(\'2w\');2w.5X=\'4g\';2w.1R=1R;o 2S=D(\'2S\').5Z(0);2S.dx(2w)};f.1h.B.52=j(){if(c.C.52&&!f.5i[c.C.2Z]){o 9f=L;o 6C=1g 2N(c.C.2Z+\'\\/\'+f.7w+\'$\');D(\'2w\').dw(j(){if(c.1R.M(6C))9f=S});if(!9f)f.dv(c.C.5j+f.7w)}D(c.2f).du("pb"+c.C.2Z);if(c.C.ds&&!f.5i[c.C.2Z]){2B(D.9e({9d:c.C.5j+f.dr,9c:L}).9b)}if(f.5i[c.C.2Z]&&f.5i[c.C.2Z].58)f.5i[c.C.2Z].58(c)};f.5h=j(7o){o F=2n.pa.f.2x[7o];o 4h=2n.3D;o 1c=F.1c();o 69=D(F.C.dq).1A();o 65=F.dp();3f(69){1k f.68:if(1c&&1c.1t&&1c.1t.1q!=f.A)1c=D(1c).4V(f.A);if(!1c&&F.2l)1c=D(F.2l).4V(f.A);1p}if(D.2A(F.C.9a))F.C.9a(F,2n);o 1E=4h.4T[0];o 2s=2B(F.C.4z);F.4S(4h,2s);if(1c){D(F.C.99).1A(D(1c).1r(f.97));D(F.C.7v).1A(D(1c).1r(f.7u));D(F.C.63).1A(D(1c).1r(f.64));D(F.C.95).1A(D(1c).1r(f.96));D(F.C.7s).1A(D(1c).1r(f.7t))}if(F.2l){D(F.C.67+" "+F.C.7v).1A(D(F.2l).1r(f.7u));D(F.C.67+" "+F.C.63).1A(D(F.2l).1r(f.64));D(F.C.67+" "+F.C.7s).1A(D(F.2l).1r(f.7t))}D(F.C.do+" "+F.C.62).3r(j(){o 5g=D(F.C.99).1A();if(5g.Y>0){o 2w;if(1c[0]&&1c[0].1t.1q()==f.A){2w=1c}X{F.1O(f.98,65);2w=D("a[1R="+65+"]",F.I.1i)}2w.1r(f.97,5g).1r(f.64,D(F.C.63).1A()).1r(f.96,D(F.C.95).1A())}2n.5f()});D(F.C.67+" "+F.C.62).3r(j(){o 5g=D(F.C.7v).1A();if(5g.Y>0){F.1O(f.94,65);D("6V[1V$="+65+"]",F.I.1i).1r(f.7u,5g).1r(f.64,D(F.C.63).1A()).1r(f.7t,D(F.C.7s).1A())}2n.5f()});D(F.C.dn+" "+F.C.62).3r(j(){o 93=D(F.C.dm).1A();o 92=D(F.C.dk).1A();if(93>0&&92>0){o 3k=F.I.61(f.dj);o 91=1f;o p9=1f;o dg=D(F.C.di).1A();o dh=3k.p8();dh.8X=dg;1j(x=0;x<93;x++){91=3k.p7(x);1j(y=0;y<92;y++){91.p6(y)}}D(3k).1r(\'cm\',D(F.C.df).1A());o Z=D(F.2K(F.K(),f.6r)).5Z(0);if(!Z||!Z.1B)D(F.I.1i).p5(3k);X D(Z).6J(3k)}2n.5f()});D(F.C.de+" "+F.C.62).3r(j(){o db=D(F.C.dc).1A();F.5E(db);2n.5f()});D(F.C.da+" "+F.C.d9).Q(F.1v());D(F.C.d8).7r(j(){2n.5f()});if(D.2A(F.C.90))F.C.90(F,2n)};f.2V=j(){c.7q=3D.61(\'N\');u c};f.2V.B.E=j(G,1S,60){1S=1S||L;60=60||L;u\'<\'+G+(1S?c.8Z(1S):\'\')+(60?\'>\':\' />\')};f.2V.B.p4=j(G,2c,1S){1S=1S||L;u\'<\'+G+(1S?c.8Z(1S):\'\')+\'>\'+2c+\'</\'+G+\'>\'};f.2V.B.p3=j(2c){u\'<![p2[\'+2c+\']]>\'};f.2V.B.d5=j(3P){u c.d7(c.d4(3P))};f.2V.B.d7=j(d6){u d6.W(/&p1;([a-z]+|(#\\d+));/ig,"&$1;")};f.2V.B.8Z=j(1S){o 3P=c;3P.8Y=\'\';1j(o 1U in 1S){o p0=\'\';o R=1S[1U];if(1s R!=\'j\'&&R.Y>0){if(cf(1U)==1U&&1s R==\'4B\'){1U=R.oZ();R=R.4P()}if(1U!=\'\'&&R!=\'\'){3P.8Y+=\' \'+1U+\'="\'+3P.d5(R)+\'"\'}}}u 3P.8Y};f.2V.B.d4=j(3T,7p){c.7q.8X=3T;c.7q.oY=3T;o 1Q=c.7q.8X;if(1s 7p==\'2e\'){if(7p!=L)1Q=1Q.W(\'"\',\'&oX;\');if(7p==S)1Q=1Q.W(\'"\',\'&#oW;\')}u 1Q};f.2V.B.oV=j(5d){o 1Q=[];o 2i=5d.5F(/((=\\s*")(")("))|((=\\s*\\\')(\\\')(\\\'))|((=\\s*[^>\\s]*))/g);if(2i.8W()!=5d){1j(o k in 2i){o v=2i[k];if(1s v!=\'j\'&&v.Y!=0){o re=1g 2N(\'(\\\\w+)\\\\s*\'+v);if(M=5d.M(re)){o R=v.W(/^[\\s=]+/,"");o 5e=R.oU(0);5e=5e==\'"\'?\'"\':(5e=="\'"?"\'":\'\');if(5e!=\'\'){R=5e==\'"\'?R.W(/^"|"+$/g,\'\'):R.W(/^\'|\'+$/g,\'\')}5d=5d.W(M[0],\'\');1Q.1Y([M[1],R])}}}}u 1Q};f.bu={"8N":{"oT":{"5b":["57","2S","Q","7n","1T","2j","1o","O"],"H":["J","id","1o","O","d2","d1"]},"d3":{"5b":["57","br","hr","1L","1T","2j"],"H":{"3C":["8V","cS"],"0":"3L","1":"3P:3L"}},"d0":{"H":{"d2":/^(\\w){1}$/,"d1":/^(\\d)+$/}}},"8O":{"2n":{"7g":["1i"],"H":["4J","oS"]},"1M":{"7g":["1M","1z","4F","4X","a","1n","3K"],"H":["oR","oQ","oP","oO","oN","aG"]},"d0":{"5b":["57","6X","br","cp","oM","2S","Q","1L","7n","1T","2j","1o","O"],"H":["oL","oK","aF"]},"oJ":{"5b":["57","6X","br","2S","Q","7n","1T","2j","1o","O"],"H":["aE","oI","oH","oG","oF","oE","oD"]}},"1J":{"a":{"H":{"0":"7l","1":"cZ","2":"1R","3":"cM","4":"G","5":"5X","6":"cL","cY":/^(cX|cW|8x|cV|cU|cT)$/,"7":"1d"}},"0":"6Y","1":"8q","2":"6Q","bp":{"H":{"0":"5c","1":"cZ","2":"1R","oC":/^(S|L)$/,"cY":/^(cX|cW|8x|cV|cU|cT)$/},"2d":["5c"]},"3":"b","57":{"H":["1R"],"2d":["1R"]},"6X":{"H":{"3C":/^(8V|cS)$/},"2d":["3C"]},"4":"bo","2H":{"H":["56"]},"5":"1i","6":"br","3K":{"H":{"2Y":/^(2Y)$/,"1d":/^(3K|cO|3r)$/,"0":"R"},"3O":"1M"},"7":"bn","8":"56","9":"bm","6W":{"H":{"4f":/^(2D|2m|4e|4d)$/,"0":"2X","1":"4c","3u":/^(\\d)+$/,"4b":/^(3q|4a|49|48)$/,"2":"4I"},"3O":"5N"},"5N":{"H":{"4f":/^(2D|2m|4e|4d)$/,"0":"2X","1":"4c","3u":/^(\\d)+$/,"4b":/^(3q|4a|49|48)$/,"2":"4I"}},"10":"dd","8p":{"H":{"0":"56","cN":/^([0-9]){8}/}},"11":"N","12":"bl","13":"dl","14":"dt","15":"em","2T":{"3O":"1M"},"1M":{"H":{"0":"5Q","1":"5T","2":"5T-7l","3":"oB","oA":/^(5Z|oz)$/},"2d":["5Q"]},"2S":{"H":["cR"]},"16":"h1","17":"h2","18":"h3","19":"h4","20":"h5","21":"h6","22":"hr","Q":{"H":["oy"]},"23":"i","6V":{"H":["5c","1V","7m","ox","ow","cw","4I"],"2d":["5c","1V"]},"1z":{"H":{"0":"5T","1":"5c","cQ":/^(cQ)$/,"2Y":/^(2Y)$/,"ov":/^(\\d)+$/,"2":"G","8P":/^(8P)$/,"2u":/^(\\d)+$/,"3":"1V","1d":/^(3K|ou|cP|5Y|bs|ot|os|cO|3r|V)$/,"4":"R"},"3O":"1M"},"bk":{"H":{"0":"56","cN":/^([0-9]){8}/}},"24":"bj","1n":{"H":["1j"],"3O":"1M"},"25":"3l","26":"li","2w":{"H":{"0":"7l","1":"1R","2":"cM","8R":/^(8Q|cq|cr|cs|7k|oq|,|;|)+$/i,"5X":/^(cK|cJ|cI|cH|cG|cF|cE|cD|cC|7o|8U|cB|cA|3p|4g|cz||cy|cx)+$/i,"cL":/^(cK|cJ|cI|cH|cG|cF|cE|cD|cC|7o|8U|cB|cA|3p|4g|cz||cy|cx)+$/i,"3":"1d"},"3O":"2S"},"bi":{"H":["id","G"],"2d":["id"]},"7n":{"H":{"0":"2c","8T-bq":/^(2c\\-1d|op|oo|om\\-ok)$/i,"1":"G","2":"oj"},"2d":["2c"]},"27":"8w","4B":{"H":["oi","oh","og","of","4y","oe","7m","G","od","1d","cw","4I"]},"28":"ol","bh":{"H":{"0":"1n","2Y":/^(2Y)$/},"2d":["1n"]},"6T":{"H":{"0":"1n","2Y":/^(2Y)$/,"1c":/^(1c)$/,"1":"R"},"3O":"4X"},"29":"p","1T":{"H":{"0":"1d","cv":/^(4y|oc|4B)$/,"1":"cv","2":"R"},"2d":["G"]},"30":"43","q":{"H":["56"]},"31":"bg","2j":{"H":{"1d":/^(V\\/ob|V\\/8S|V\\/oa|V\\/o9|V\\/o8|V\\/3P)$/,"0":"7l","cu":/^(cu)$/,"1":"1V"},"2d":["1d"]},"4X":{"H":{"2Y":/^(2Y)$/,"ct":/^(ct)$/,"0":"G","1":"2u"},"3O":"1M"},"32":"bf","33":"3u","34":"3b","1o":{"H":{"0":"1d","8R":/^(7k|o7|o6|cs|o5|cr|cq|o4|8Q)$/},"2d":["1d"]},"35":"3a","36":"3S","3k":{"H":{"0":"cn","1":"o3","2":"o2","cp":/^(o1|o0|nZ|nY|nX|nW|nV|co|cn)$/,"nU":/^(nT|nS|7i|7j|8Q)$/,"3":"cm","4":"4I"}},"8v":{"H":{"4f":/^(2D|2m|4e|4d)$/,"0":"2X","1":"4c","4b":/^(3q|4a|49|48)$/}},"4E":{"H":{"0":"6Y","4f":/^(2m|2D|4e|4d|2X)$/,"1":"cl","2":"2X","3":"4c","ck":/^(\\d)+$/,"4":"cj","ci":/^(\\d)+$/,"1G":/^(6W|5N|1N|ch)$/,"4b":/^(3q|4a|49|48)$/}},"4F":{"H":["7j","7i","2Y","G","8P"],"2d":["7j","7i"],"3O":"1M"},"8u":{"H":{"4f":/^(2D|2m|4e|4d)$/,"0":"2X","1":"4c","4b":/^(3q|4a|49)$/,"2":"48"}},"5L":{"H":{"0":"6Y","4f":/^(2m|2D|4e|4d|2X)$/,"1":"cl","2":"2X","3":"4c","ck":/^(\\d)+$/,"4":"cj","ci":/^(\\d)+$/,"1G":/^(6W|5N|1N|ch)$/,"4b":/^(3q|4a|49|48)$/}},"8t":{"H":{"4f":/^(2D|2m|4e|4d)$/,"0":"2X","1":"4c","4b":/^(3q|4a|49|48)$/}},"37":"O","6U":{"H":{"4f":/^(2D|2m|4e|4d|2X)$/,"0":"2X","1":"4c","4b":/^(3q|4a|49|48)$/}},"38":"be","39":"2G","40":"o"},6L:[],6K:[],5q:j(E,H){o 7h={};o cg=c.ca(E);1j(o 2k in H){o R=H[2k];o h=f.1P;if(!h.2E(c.6L,2k)&&!h.2E(c.6K,R)){if(1s R!=\'j\'&&h.2E(cg,2k)){if(c.cc(E,2k)){if(c.cb(E,2k,R)){7h[2k]=R}}X{7h[2k]=R}}}}u 7h},c9:j(E){o 1Q=[];if(c.1J[E]&&c.1J[E][\'H\']){1j(k in c.1J[E][\'H\']){1Q.1Y(cf(k)==k?c.1J[E][\'H\'][k]:k)}}u 1Q},cd:j(){o 1Q=[];1j(o 1U in c.8O){1Q.1Y(c.8O[1U])}1j(o 1U in c.8N){1Q.1Y(c.8N[1U])}u 1Q},ce:j(E){if(c.1J[E]){u S}1j(o 1U in c.1J){if(c.1J[1U]==E){u S}}u L},c8:j(E){o 8L=[];if(c.ce(E)){o 8M=c.cd();1j(o 1U in 8M){o 3N=8M[1U];if(1s 3N==\'4B\'){o h=f.1P;if((3N[\'5b\']&&h.2E(3N[\'5b\'],E))||(3N[\'7g\']&&!h.2E(3N[\'7g\'],E))){nR}o 7f=3N[\'H\']?3N[\'H\']:3N[\'nQ\'];1j(o k in 7f){8L.1Y(1s 7f[k]!=\'3T\'?k:7f[k])}}}}u 8L},cc:j(E,2k){u c.1J[E]&&((c.1J[E][\'H\']&&c.1J[E][\'H\'][2k])||(c.1J[E][\'2d\']&&f.1P.2E(c.1J[E][\'2d\'],2k)))},cb:j(E,2k,R){if(c.1J[E]&&(c.1J[E][\'H\']&&c.1J[E][\'H\'][2k]&&R.Y>0&&!R.M(c.1J[E][\'H\'][2k]))||(c.1J[E]&&c.1J[E][\'2d\']&&f.1P.2E(c.1J[E][\'2d\'],2k)&&R.Y==0)){u L}u 1s c.1J[E]!=\'2e\'},ca:j(E){if(!c.5W){c.5W={}}if(!c.5W[E]){c.5W[E]=c.c9(E).nP(c.c8(E))}u c.5W[E]}};f.2W=j(7d){c.4H=7d;c.47=[];c.8K=[];c.5V=1f;u c};f.2W.B.45=j(3o,1n){1n=1n||S;o 5a=c.47.Y;c.47[5a]=3o;c.8K[5a]=1n;c.5V=1f};f.2W.B.M=j(c7){if(c.47.Y==0){u[L,\'\']}o 2i=c7.M(c.c6());if(!2i){u[L,\'\']}o M=2i[0];1j(o i=1;i<2i.Y;i++){if(2i[i]){u[c.8K[i-1],M]}}u[S,2i[0]]};f.2W.B.c6=j(){if(c.5V==1f){1j(o i=0,5a=c.47.Y;i<5a;i++){c.47[i]=\'(\'+c.c4(c.c5(c.47[i]).W(/([\\/\\(\\)])/g,\'\\\\$1\'))+\')\'}c.5V=1g 2N(c.47.5A("|"),c.bW())}u c.5V};f.2W.B.c5=j(7e){u 7e.W(/\\(\\?(i|m|s|x|U)\\)/,\'~~~~~~c3\\$1~~~~~~\').W(/\\(\\?(\\-[i|m|s|x|U])\\)/,\'~~~~~~c2\\$1~~~~~~\').W(/\\(\\?\\=(.*)\\)/,\'~~~~~~c1\\$1~~~~~~\').W(/\\(\\?\\!(.*)\\)/,\'~~~~~~c0\\$1~~~~~~\').W(/\\(\\?\\<\\=(.*)\\)/,\'~~~~~~bZ\\$1~~~~~~\').W(/\\(\\?\\<\\!(.*)\\)/,\'~~~~~~bY\\$1~~~~~~\').W(/\\(\\?\\:(.*)\\)/,\'~~~~~~bX\\$1~~~~~~\')};f.2W.B.c4=j(7e){u 7e.W(/~~~~~~c3(.{1})~~~~~~/,"(?\\$1)").W(/~~~~~~c2(.{2})~~~~~~/,"(?\\$1)").W(/~~~~~~c1(.*)~~~~~~/,"(?=\\$1)").W(/~~~~~~c0(.*)~~~~~~/,"(?!\\$1)").W(/~~~~~~bZ(.*)~~~~~~/,"(?<=\\$1)").W(/~~~~~~bY(.*)~~~~~~/,"(?<!\\$1)").W(/~~~~~~bX(.*)~~~~~~/,"(?:\\$1)")};f.2W.B.bW=j(){u(c.4H?"m":"mi")};f.5U=j(3p){c.59=[3p];u c};f.5U.B.8G=j(){u c.59[c.59.Y-1]};f.5U.B.8I=j(2b){c.59.1Y(2b)};f.5U.B.8J=j(){if(c.59.Y==1){u L}c.59.4P();u S};f.77=1;f.bU=2;f.3H=3;f.5I=4;f.74=5;f.2h=j(42,3p,7d){3p=3p||\'5T\';c.4H=7d||L;c.2q={};c.8H=42;c.44=1g f.5U(3p);c.2I={};c.2I[3p]=3p;u c};f.2h.B.45=j(3o,T){o T=T||"5T";if(1s c.2q[T]==\'2e\'){c.2q[T]=1g f.2W(c.4H)}c.2q[T].45(3o);if(1s c.2I[T]==\'2e\'){c.2I[T]=T}};f.2h.B.2P=j(3o,T,5S){if(1s c.2q[T]==\'2e\'){c.2q[T]=1g f.2W(c.4H)}c.2q[T].45(3o,5S);if(1s c.2I[5S]==\'2e\'){c.2I[5S]=5S}};f.2h.B.2F=j(3o,T){if(1s c.2q[T]==\'2e\'){c.2q[T]=1g f.2W(c.4H)}c.2q[T].45(3o,"bS");if(1s c.2I[T]==\'2e\'){c.2I[T]=T}};f.2h.B.4D=j(3o,T,5R){if(1s c.2q[T]==\'2e\'){c.2q[T]=1g f.2W(c.4H)}c.2q[T].45(3o,\'bQ\'+5R);if(1s c.2I[5R]==\'2e\'){c.2I[5R]=5R}};f.2h.B.8i=j(T,7a){c.2I[T]=7a};f.2h.B.3Z=j(1y){if(1s c.8H==\'2e\'){u L}o Y=1y.Y;o 3M;7c(1s(3M=c.bM(1y))==\'4B\'){o 1y=3M[0];o 7b=3M[1];o 3n=3M[2];o T=3M[3];if(!c.bV(7b,3n,T)){u L}if(1y==\'\'){u S}if(1y.Y==Y){u L}Y=1y.Y}if(!3M){u L}u c.4G(1y,f.3H)};f.2h.B.bV=j(7b,3n,T){T=T||L;if(!c.4G(7b,f.3H)){u L}if(1s T==\'nO\'){u c.4G(3n,f.bU)}if(c.bT(T)){if(!c.4G(3n,f.5I)){u L}u c.44.8J()}if(c.bR(T)){c.44.8I(c.bP(T));if(!c.4G(3n,f.74)){u L}u c.44.8J()}c.44.8I(T);u c.4G(3n,f.77)};f.2h.B.bT=j(T){u(T==="bS")};f.2h.B.bR=j(T){u(T.6B(0,1)=="bQ")};f.2h.B.bP=j(T){u T.6B(1)};f.2h.B.4G=j(2c,bN){if(2c===\'\'){u S}o bO=c.44.8G();o 7a=c.2I[bO];o 1Q;2B(\'1Q = c.8H.\'+7a+\'(2c, bN);\');u 1Q};f.2h.B.bM=j(1y){o 3n=c.2q[c.44.8G()].M(1y);o M=3n[1];o 5Q=3n[0];if(5Q){o 8F=1y.8a(M);o bL=1y.aJ(0,8F);1y=1y.6B(8F+M.Y);u[1y,bL,M,5Q]}u S};f.3m=j(42){D.55(c,1g f.2h(42,\'3i\'));c.8i(\'3i\',\'3i\');c.bK();c.58();u c};f.3m.B.58=j(){};f.3m.B.bK=j(){c.bJ(\'3i\');c.bI(\'3i\');c.bH(\'3i\');c.bG(\'3i\')};f.3m.B.bJ=j(1G){c.2P("<!--",1G,\'8E\');c.2F("-->",\'8E\')};f.3m.B.bI=j(1G){c.2P("<2j",1G,\'8D\');c.2F("</2j>",\'8D\')};f.3m.B.bH=j(1G){c.2P("<1o",1G,\'8C\');c.2F("</1o>",\'8C\')};f.3m.B.bG=j(1G){c.4D("<\\\\s*[a-5J-9:\\-]+\\\\s*>",1G,\'78\');c.2P("<[a-5J-9:\\-]+"+\'[\\\\\\/ \\\\\\>]+\',1G,\'78\');c.bF(\'78\');c.4D("</\\\\s*[a-5J-9:\\-]+\\\\s*>",1G,\'bB\')};f.3m.B.bF=j(1G){c.4D(\'\\\\s+\',1G,\'54\');c.bE(1G);c.2F(\'/>\',1G);c.2F(\'>\',1G)};f.3m.B.bE=j(1G){c.4D("\\\\s*[a-z-8h-9]*:?[a-z-8h-9]+\\\\s*(?=\\=)\\\\s*",1G,\'bw\');c.2P(\'=\\\\s*"\',1G,\'72\');c.45("\\\\\\\\\\"",\'72\');c.2F(\'"\',\'72\');c.2P("=\\\\s*\'",1G,\'71\');c.45("\\\\\\\\\'",\'71\');c.2F("\'",\'71\');c.4D(\'=\\\\s*[^>\\\\s]*\',1G,\'bv\')};f.1w=j(bD,T){o T=T||\'3i\';c.6H=1g f.3m(c);c.1a=bD;c.44=T;c.nN=[];c.nM=\'\';c.nL=\'\';u c};f.1w.B.3Z=j(1y){c.6H.3Z(c.6S(1y));u c.6R(c.1a.b6())};f.1w.B.6S=j(1y){if(1y.M(/J="aY"/)||1y.M(/ns = "nK:nJ-nI-nH/)){c.1a.aZ()}u c.1a.6S(1y)};f.1w.B.6R=j(3M){if(c.1a.8o){c.1a.aX()}u c.1a.6R(3M)};f.1w.B.54=j(M,2b){u S};f.1w.B.3i=j(V){c.1a.b5(V);u S};f.1w.B.8E=j(M,2p){u c.79(M,2p,\'6O\')};f.1w.B.8D=j(M,2p){u c.79(M,2p,\'6N\')};f.1w.B.8C=j(M,2p){u c.79(M,2p,\'6M\')};f.1w.B.79=j(M,2b,1d){3f(2b){1k f.77:c.5P=M;1p;1k f.3H:c.5P+=M;1p;1k f.5I:3f(1d){1k\'6O\':c.1a.6O(c.5P+M);1p;1k\'6N\':c.1a.6N(c.5P+M);1p;1k\'6M\':c.1a.6M(c.5P+M);1p}}u S};f.1w.B.78=j(M,2b){3f(2b){1k f.77:c.bC=c.75(M);c.5O={};1p;1k f.74:c.8B(c.75(M));1p;1k f.5I:c.8B(c.bC,c.5O)}u S};f.1w.B.bB=j(M,2b){c.8y(c.75(M));u S};f.1w.B.8B=j(E,H){o H=H||{};c.bz(E);if(c.1a.8n(E)){c.1a.4q.1Y(E);c.1a.aT(E,H);c.1a.6m(E,H);c.8A(E)}X if(c.1a.aW(E)){c.1a.b4(E,H)}X{c.1a.b3(E,H);c.8A(E)}c.1a.6I=E;c.1a.8l=S;c.1a.nG=H};f.1w.B.8y=j(E){if(c.bA(E)){c.by(E);if(c.1a.8n(E)){o 76=c.1a.4q.4P();if(76==L){u}X if(76!=E){E=76}c.1a.b2(E)}X{c.1a.b1(E)}}X{c.1a.b0(E)}c.1a.6I=E;c.1a.8l=L};f.1w.B.8A=j(E){c.1a.2U[E]=c.1a.2U[E]||0;c.1a.2U[E]++};f.1w.B.bA=j(E){if(c.1a.2U[E]){c.1a.2U[E]--;if(c.1a.2U[E]==0){c.1a.2U[E]=2e}u S}u L};f.1w.B.bz=j(3d){c.8z(3d,L)};f.1w.B.by=j(E){c.8z(E,S)};f.1w.B.8z=j(3d,2R){o 2R=2R||L;if(c.1a.2U){1j(o E in c.1a.2U){o bx=c.1a.2U[E];if(bx>0&&c.1a.bd(E,3d,2R)){c.8y(E,S)}}}};f.1w.B.6P=j(){u c.1a.6P()};f.1w.B.75=j(E){E=E.W(/^([\\s<\\/>]*)|([\\s<\\/>]*)$/gm,\'\').1q();o 3j=c.1a.6P();if(3j[E]){u 3j[E]}u E};f.1w.B.bw=j(M,2b){if(f.74==2b){c.70=M}u S};f.1w.B.72=j(M,2b){if(f.3H==2b){c.5O[c.70]=M}u S};f.1w.B.71=j(M,2b){if(f.3H==2b){c.5O[c.70]=M}u S};f.1w.B.bv=j(M,2b){c.5O[c.70]=M.W(/^=/,\'\');u S};f.1m=j(){c.1X=\'\';c.4O=1g f.2V();c.2U={};c.3v=f.bu;c.4q=[];c.5K=[];c.8s={\'&b8;\':\'&#b9;\',\'&nF;\':\'&#nE;\',\'&nD;\':\'&#nC;\',\'&nB;\':\'&#nA;\',\'&nz;\':\'&#ny;\',\'&nx;\':\'&#nw;\',\'&nv;\':\'&#nt;\',\'&nr;\':\'&#nq;\',\'&np;\':\'&#nn;\',\'&nm;\':\'&#nl;\',\'&nk;\':\'&#nj;\',\'&nh;\':\'&#ng;\',\'&bt;\':\'&#nf;\',\'&nd;\':\'&#nc;\',\'&nb;\':\'&#na;\',\'&n9;\':\'&#n8;\',\'&n7;\':\'&#n6;\',\'&n5;\':\'&#n4;\',\'&n3;\':\'&#n2;\',\'&n1;\':\'&#n0;\',\'&mZ;\':\'&#mY;\',\'&mX;\':\'&#mW;\',\'&mV;\':\'&#mU;\',\'&mT;\':\'&#mS;\',\'&mR;\':\'&#mQ;\',\'&mP;\':\'&#mO;\',\'&mN;\':\'&#mM;\',\'&mL;\':\'&#mK;\',\'&mJ;\':\'&#mI;\',\'&mH;\':\'&#mG;\',\'&mF;\':\'&#mE;\',\'&mD;\':\'&#mC;\',\'&mB;\':\'&#mA;\',\'&mz;\':\'&#my;\',\'&mx;\':\'&#mw;\',\'&mv;\':\'&#mt;\',\'&ms;\':\'&#mr;\',\'&mq;\':\'&#mp;\',\'&mo;\':\'&#mn;\',\'&mm;\':\'&#ml;\',\'&mk;\':\'&#mj;\',\'&mh;\':\'&#mg;\',\'&mf;\':\'&#me;\',\'&md;\':\'&#mc;\',\'&mb;\':\'&#ma;\',\'&m9;\':\'&#m8;\',\'&m7;\':\'&#m6;\',\'&m5;\':\'&#m4;\',\'&m3;\':\'&#m2;\',\'&m1;\':\'&#m0;\',\'&lZ;\':\'&#lY;\',\'&lX;\':\'&#lW;\',\'&lV;\':\'&#lU;\',\'&lT;\':\'&#lS;\',\'&lR;\':\'&#lQ;\',\'&lP;\':\'&#lO;\',\'&lN;\':\'&#lM;\',\'&lL;\':\'&#lK;\',\'&lJ;\':\'&#lI;\',\'&lH;\':\'&#lG;\',\'&lF;\':\'&#lE;\',\'&lD;\':\'&#lC;\',\'&lB;\':\'&#lA;\',\'&lz;\':\'&#ly;\',\'&lx;\':\'&#7Z;\',\'&lw;\':\'&#lv;\',\'&lu;\':\'&#lt;\',\'&ls;\':\'&#lr;\',\'&lq;\':\'&#lp;\',\'&lo;\':\'&#ln;\',\'&lm;\':\'&#ll;\',\'&lk;\':\'&#lj;\',\'&lh;\':\'&#lg;\',\'&lf;\':\'&#ld;\',\'&lc;\':\'&#lb;\',\'&la;\':\'&#l9;\',\'&l8;\':\'&#l7;\',\'&l6;\':\'&#l5;\',\'&l4;\':\'&#l3;\',\'&l2;\':\'&#l1;\',\'&l0;\':\'&#kZ;\',\'&kY;\':\'&#kX;\',\'&kW;\':\'&#kV;\',\'&kU;\':\'&#kT;\',\'&kS;\':\'&#kR;\',\'&kQ;\':\'&#kP;\',\'&kO;\':\'&#kN;\',\'&kM;\':\'&#kL;\',\'&kK;\':\'&#kJ;\',\'&kI;\':\'&#kH;\',\'&kG;\':\'&#kF;\',\'&kE;\':\'&#kD;\',\'&kC;\':\'&#kB;\',\'&kA;\':\'&#kz;\',\'&ky;\':\'&#kx;\',\'&kw;\':\'&#kv;\',\'&ku;\':\'&#kt;\',\'&ks;\':\'&#kr;\',\'&kq;\':\'&#kp;\',\'&ko;\':\'&#kn;\',\'&km;\':\'&#kl;\',\'&kk;\':\'&#kj;\',\'&8x;\':\'&#ki;\',\'&kh;\':\'&#kg;\',\'&kf;\':\'&#ke;\',\'&kd;\':\'&#kc;\',\'&kb;\':\'&#ka;\',\'&k9;\':\'&#k8;\',\'&k7;\':\'&#k6;\',\'&k5;\':\'&#k4;\',\'&k3;\':\'&#k2;\',\'&k1;\':\'&#k0;\',\'&jZ;\':\'&#jY;\',\'&jX;\':\'&#jW;\',\'&jV;\':\'&#jU;\',\'&jT;\':\'&#jS;\',\'&jR;\':\'&#jQ;\',\'&jP;\':\'&#jO;\',\'&jN;\':\'&#jM;\',\'&jL;\':\'&#jK;\',\'&jJ;\':\'&#jI;\',\'&jH;\':\'&#jG;\',\'&jF;\':\'&#jE;\',\'&jD;\':\'&#jC;\',\'&jB;\':\'&#jA;\',\'&jz;\':\'&#jy;\',\'&jx;\':\'&#jw;\',\'&jv;\':\'&#ju;\',\'&jt;\':\'&#jr;\',\'&jq;\':\'&#jp;\',\'&jo;\':\'&#jn;\',\'&jm;\':\'&#jl;\',\'&jk;\':\'&#jj;\',\'&ji;\':\'&#jh;\',\'&jg;\':\'&#jf;\',\'&je;\':\'&#jd;\',\'&jc;\':\'&#jb;\',\'&ja;\':\'&#j9;\',\'&j8;\':\'&#j7;\',\'&mu;\':\'&#j6;\',\'&nu;\':\'&#j5;\',\'&j4;\':\'&#j3;\',\'&j2;\':\'&#j1;\',\'&pi;\':\'&#j0;\',\'&iZ;\':\'&#iY;\',\'&iX;\':\'&#iW;\',\'&iV;\':\'&#iU;\',\'&iT;\':\'&#iS;\',\'&iR;\':\'&#iQ;\',\'&iP;\':\'&#iO;\',\'&iN;\':\'&#iM;\',\'&iL;\':\'&#iK;\',\'&iJ;\':\'&#iI;\',\'&iH;\':\'&#iG;\',\'&iF;\':\'&#iE;\',\'&iD;\':\'&#iC;\',\'&iB;\':\'&#iA;\',\'&iz;\':\'&#iy;\',\'&ix;\':\'&#iw;\',\'&iv;\':\'&#iu;\',\'&it;\':\'&#ir;\',\'&iq;\':\'&#ip;\',\'&io;\':\'&#im;\',\'&il;\':\'&#ik;\',\'&ij;\':\'&#ii;\',\'&ih;\':\'&#ie;\',\'&ic;\':\'&#ib;\',\'&ia;\':\'&#i9;\',\'&i8;\':\'&#i7;\',\'&i6;\':\'&#i5;\',\'&i4;\':\'&#i3;\',\'&i2;\':\'&#i1;\',\'&i0;\':\'&#hZ;\',\'&hY;\':\'&#hX;\',\'&hW;\':\'&#hV;\',\'&hU;\':\'&#hT;\',\'&hS;\':\'&#hR;\',\'&hQ;\':\'&#hP;\',\'&hO;\':\'&#hN;\',\'&hM;\':\'&#hL;\',\'&hK;\':\'&#hJ;\',\'&hI;\':\'&#hH;\',\'&hG;\':\'&#hF;\',\'&bs;\':\'&#hE;\',\'&hD;\':\'&#hC;\',\'&hB;\':\'&#hA;\',\'&hz;\':\'&#hy;\',\'&hx;\':\'&#hw;\',\'&hv;\':\'&#hu;\',\'&ht;\':\'&#hs;\',\'&hq;\':\'&#hp;\',\'&ho;\':\'&#hn;\',\'&hm;\':\'&#hl;\',\'&hk;\':\'&#hj;\',\'&hi;\':\'&#hh;\',\'&hg;\':\'&#hf;\',\'&he;\':\'&#hd;\',\'&hc;\':\'&#hb;\',\'&ha;\':\'&#h9;\',\'&h8;\':\'&#h7;\',\'&h0;\':\'&#gZ;\',\'&gY;\':\'&#gX;\',\'&gW;\':\'&#gV;\',\'&gU;\':\'&#gT;\',\'&gS;\':\'&#gR;\',\'&gQ;\':\'&#gP;\',\'&ni;\':\'&#gO;\',\'&gN;\':\'&#gM;\',\'&gL;\':\'&#gK;\',\'&gJ;\':\'&#gI;\',\'&gH;\':\'&#gG;\',\'&gF;\':\'&#gE;\',\'&6Z;\':\'&#gD;\',\'&gC;\':\'&#gB;\',\'&gA;\':\'&#gz;\',\'&gy;\':\'&#gx;\',\'&or;\':\'&#gw;\',\'&gv;\':\'&#gu;\',\'&gt;\':\'&#gs;\',\'&gr;\':\'&#gq;\',\'&gp;\':\'&#go;\',\'&gn;\':\'&#gl;\',\'&gk;\':\'&#gj;\',\'&gh;\':\'&#gg;\',\'&ne;\':\'&#gf;\',\'&bq;\':\'&#gd;\',\'&le;\':\'&#gc;\',\'&ge;\':\'&#gb;\',\'&3a;\':\'&#ga;\',\'&3S;\':\'&#g9;\',\'&g8;\':\'&#g7;\',\'&g6;\':\'&#g5;\',\'&g4;\':\'&#g3;\',\'&g2;\':\'&#g1;\',\'&g0;\':\'&#fZ;\',\'&fY;\':\'&#fX;\',\'&fW;\':\'&#fV;\',\'&fU;\':\'&#fT;\',\'&fS;\':\'&#fR;\',\'&fQ;\':\'&#fP;\',\'&fO;\':\'&#fN;\',\'&3L;\':\'&#fM;\',\'&fL;\':\'&#fK;\',\'&fJ;\':\'&#fI;\',\'&fH;\':\'&#fG;\',\'&fF;\':\'&#fE;\',\'&fD;\':\'&#fC;\',\'&fB;\':\'&#fA;\'};c.8m=["a","6Y","8q","6Q","bp","b","57","6X","bo","2H","1i","3K","bn","56","bm","6W","5N","dd","8p","N","bl","dl","dt","em","2T","1M","2S","h1","h2","h3","h4","h5","h6","Q","i","bk","bj","1n","3l","li","bi","8w","4B","ol","bh","6T","p","1T","43","q","bg","2j","4X","bf","3u","3b","1o","3a","3S","3k","8v","4E","4F","8u","5L","8t","O","6U","be","2G","o","fz"];c.aV=["br","hr","6V","1z"];u c};f.1m.B.bd=j(E,5M,2R){o 2R=2R||L;if(E==\'4E\'){if((2R&&5M==\'6U\')||(!2R&&5M==\'4E\')){u S}}if(E==\'6T\'){if((2R&&5M==\'4X\')||(!2R&&5M==\'6T\')){u S}}u L};f.1m.B.6S=j(1y){c.1X=\'\';u 1y};f.1m.B.6R=j(1v){1v=c.bc(1v);1v=c.bb(1v);1v=c.ba(1v);1v=c.b7(1v);u 1v};f.1m.B.bc=j(1v){1j(o 8r in c.8s){1v=1v.W(1g 2N(8r,\'g\'),c.8s[8r])}u 1v};f.1m.B.bb=j(1v){o 3j=\'em|3b|3a|3S|8q|43|8p|6Q\';u 1v.W(1g 2N(\'<\\/(\'+3j+\')><\\\\1>\',\'\'),\'\').W(1g 2N(\'(\\s*<(\'+3j+\')>\\s*){2}(.*)(\\s*<\\/\\\\2>\\s*){2}\',\'\'),\'<\\$2>\\$3<\\$2>\')};f.1m.B.ba=j(1v){u 1v.W(1g 2N(\'<(\'+c.8m.5A("|").W(/\\|4E/,\'\').W(/\\|5L/,\'\')+\')>(<br \\/>|&#b9;|&b8;|\\\\s)*<\\/\\\\1>\',\'g\'),\'\')};f.1m.B.b7=j(1v){o 2i=1v.M(1g 2N(\'<43[^>]*>(.*?)<\\/43>\',\'fy\'));if(2i){1j(o i=0;i<2i.Y;i++){1v=1v.W(2i[i],2i[i].W(1g 2N(\'<br \\/>\',\'g\'),fx.fw(13,10)))}}u 1v};f.1m.B.b6=j(){u c.1X};f.1m.B.6P=j(){u{\'b\':\'3b\',\'i\':\'em\'}};f.1m.B.b5=j(V){c.1X+=V};f.1m.B.6O=j(V){if(c.fv){c.1X+=V}};f.1m.B.6N=j(V){if(!c.fu){c.1X+=V}};f.1m.B.6M=j(V){if(!c.ft){c.1X+=V}};f.1m.B.6m=j(E,H){c.1X+=c.4O.E(E,c.3v.5q(E,H),S)};f.1m.B.b4=j(E,H){c.1X+=c.4O.E(E,c.3v.5q(E,H))};f.1m.B.b3=j(E,H){};f.1m.B.b2=j(E){c.1X=c.1X.W(/<br \\/>$/,\'\')+c.8j(\'aU\',E)+"</"+E+">"+c.8j(\'6J\',E)};f.1m.B.b1=j(E){};f.1m.B.b0=j(E){c.1X+="</"+E+">"};f.1m.B.aZ=j(){c.5K=[\'N\',\'3u\'];c.3v.6L=[\'1o\'];c.3v.6K=[\'aY\',\'fs\'];c.8o=S};f.1m.B.aX=j(){c.5K=[];c.3v.6L=[];c.3v.6K=[];c.8o=L};f.1m.B.8n=j(E){u!f.1P.2E(c.5K,E)&&f.1P.2E(c.8m,E)};f.1m.B.aW=j(E){u!f.1P.2E(c.5K,E)&&f.1P.2E(c.aV,E)};f.1m.B.aS=j(E,2c){c.8k(\'6J\',E,2c)};f.1m.B.fr=j(E,2c){c.8k(\'aU\',E,2c)};f.1m.B.aT=j(E,H){if(E!=\'li\'&&(E==\'2G\'||E==\'ol\')&&c.6I&&!c.8l&&c.6I==\'li\'){c.1X=c.1X.W(/<\\/li>$/,\'\');c.aS(E,\'</li>\')}};f.1m.B.8k=j(2Q,E,2c){if(!c[\'3J\'+2Q+\'3I\']){c[\'3J\'+2Q+\'3I\']=[]}if(!c[\'3J\'+2Q+\'3I\'][E]){c[\'3J\'+2Q+\'3I\'][E]=[]}c[\'3J\'+2Q+\'3I\'][E].1Y(2c)};f.1m.B.8j=j(2Q,E){if(c[\'3J\'+2Q+\'3I\']&&c[\'3J\'+2Q+\'3I\'][E]&&c[\'3J\'+2Q+\'3I\'][E].Y>0){u c[\'3J\'+2Q+\'3I\'][E].4P()}u\'\'};f.aR=j(42,2v){o 2v=(1s 2v==\'2e\'?S:2v);D.55(c,1g f.2h(42,(2v?\'54\':\'4C\')));c.8i(\'4C\',\'54\');if(2v==S){c.2P("/\\\\\\41[<\\\\s]*f[>\\\\s]*\\\\\\41/",\'54\',\'4C\');c.2F("/\\\\\\41[<\\/\\\\s]*f[>\\\\s]*\\\\\\41/",\'4C\')}c.4D("[\\\\fq-fp-6]*\\\\\\fo[a-z-8h-9]+",\'4C\',\'aQ\');c.2P("/\\\\\\41",\'4C\',\'8g\');c.2F("\\\\\\41/",\'8g\');c.2P("\\fm",\'4C\',\'6G\');c.2F("\\fl",\'6G\');c.2P("/\\\\\\41",\'6G\',\'8f\');c.2F("\\\\\\41/",\'8f\');u c};f.3G=j(){c.6F=L;c.5H=L;c.2v=S;c.4A={\'3Y\':[],\'3h\':[],\'4z\':[]};u c};f.3G.B.3Z=j(1y,2v){o 2v=(1s 2v==\'2e\'?c.2v:2v);c.6H=1g f.aR(c,2v);c.6H.3Z(1y)};f.3G.B.54=j(M,2b){u S};f.3G.B.8g=j(V,2p){if(V.M(/fk[a-5J-9\\s]*F[a-5J-9\\s]*/mi)){u L}if(2p==f.3H){if(!c.6F){c.5H=S;c.2o={\'O\':f.1P.4w(V)}}X{if(c.2o[c.2g]){if(!c.2o[c.2g].6D){c.2o[c.2g].6D=[V]}X{c.2o[c.2g].6D.1Y(V)}}}c.6F=S}u S};f.3G.B.6G=j(M,2p){if(2p==f.3H){M=f.1P.4w(M);if(M!=\'\'){c.2o[c.2g].1o=M}}X if(2p==f.5I){c.6F=L;c.5H=L;c.aP(c.2o)}u S};f.3G.B.8f=j(M,2p){if(2p==f.3H){c.2o[c.2g].8d=M.W(/^([\\s\\/\\*]*)|([\\s\\/\\*]*)$/gm,\'\')}u S};f.3G.B.aQ=j(M){M=M.W(/^([\\s\\.]*)|([\\s\\.*]*)$/gm,\'\');o E=\'\';if(M.8a(\'.\')>0){o 8e=M.5F(\'.\');c.2g=8e[1];o E=8e[0]}X{c.2g=M}if(!c.5H){c.2o={\'O\':(!E?\'\':E.fj()+\': \')+c.2g};c.5H=S}if(!c.2o[c.2g]){c.2o[c.2g]={\'G\':c.2g}}if(E){if(!c.2o[c.2g].3j){c.2o[c.2g].3j=[E]}X{c.2o[c.2g].3j.1Y(E)}}u S};f.3G.B.aP=j(6E){1j(o G in 6E){o 2O=6E[G];if(1s 2O==\'4B\'&&G!=\'O\'){c.4A.3Y.1Y({\'G\':f.1P.4w(2O.G),\'O\':6E.O,\'aO\':f.1P.4w((2O.6D||2O.3j).5A(\', \'))});if(2O.8d){c.4A.3h.1Y({\'G\':\'.\'+f.1P.4w(2O.G),\'1b\':2O.8d})}if(2O.1o){c.4A.4z.1Y({\'G\':\'.\'+f.1P.4w(2O.G),\'1b\':2O.1o})}}}};D.fn.aN=j(){if(c[0].8c==3)u!(/[^\\t\\n\\r ]/.2J(c[0].4y));u L};f.aN=j(n){if(n.8c==3)u!(/[^\\t\\n\\r ]/.2J(n.4y));u L};f.fi=j(2M){u!(/[^\\t\\n\\r ]/.2J(2M))};D.fn.4V=j(4x){o n=c;if(n[0].8c==3)n=n.5C().aM(0,1);if(n.53(4x).2u()==1)u n;X u n.5C(4x).aM(0,1)};f.1P={1l:j(2M,aL,aK){o 6C=1g 2N(aL,"g");u(2M.W(6C,aK))},fh:j(2M,aI,8b){u(2M.aJ(0,8b)+aI+2M.6B(8b))},4w:j(2M){u 2M.W(/^(\\s*)|(\\s*)$/gm,\'\')},2E:j(3F,5G){1j(o i=0;i<3F.Y;i++){if(3F[i]===5G)u S}u L},8a:j(3F,4v){o 89=-1;1j(o i=0;i<3F.Y;i++){if(3F[i]==4v){89=i;1p}}u(89)},aH:j(3F,G){1j(o i=0;i<3F.Y;i++){o 4v=3F[i];if(4v.G==G)u(4v)}u(1f)}};f.2t=j(F){c.1W=F;c.5z="fg";c.4U="\\r\\n"};f.2t.B.5y=j(1L){c.1K=1L;c.I=1L.1I.3D;o 1E=c.I.4T[0];o 2s=2B(c.C.3h);c.4S(c.I,2s);c.I.O=c.1W.3g;D(\'Q\',c.I).1r(\'3C\',c.C.4R);D(c.I.1i).Q(c.1W.4Q);o F=c;c.I.1i.aG=j(){F.I.4s="on";F.I=1L.1I.3D};c.I.ff=j(){F.6A()};c.I.aF=j(){F.6A();F.2y()};c.I.aE=j(){F.6A()};c.I.1i.fe=j(){F.1K.1I.aD.aC=L};c.I.1i.fd=j(){F.1K.1I.aD.aC=L;F.5E(2n.fc.fb("3i"))};if(c.aB){if(D.2A(c.C.3B))c.C.3B(c);c.1W.5x();if(D.2A(c.C.3z))c.C.3z(c);c.5w()}c.aB=S;c.I.4s="on";4u{c.I=1L.1I.3D}4t(e){}};(j(aA){f.2t.B.52=j(){D(c.2f).3E(c.C.88).1r(\'fa\',\'on\');aA.aw(c)}})(f.1h.B.52);f.2t.B.1O=j(1F,1T){3f(1F){1k f.6x:1k f.6w:o K=c.2K(c.K(),f.6v);if(K){o 3y=K.1B.1B;if(K.1B.6u.Y>1||3y.1t.1q()==f.6t||3y.1t.1q()==f.6s)c.I.2r(1F)}1p;3U:if(1T)c.I.2r(1F,L,1T);X c.I.2r(1F);1p}};f.2t.B.1c=j(){o 51=c.1K.1I.3D.51;if(51!=1f){if(51.5D!=2e)u(51.5D())}};f.2t.B.6A=j(){c.I.51=c.I.2L.4Y()};f.2t.B.5t=j(1E,1Z){o 87=1Z.G.5F(\',\');1j(o i in 87){1E.f9(87[i],1Z.1b)}};f.2t.B.5B=j(Q){o 1D=c.I.2L.4Y();if(D(1D.5D()).5C(c.C.6z).is(\'*\')){4u{1D.86(Q)}4t(e){}}X{c.5E(Q)}};f.2t.B.az=j(2m,2D){o 1D=c.I.2L.4Y();if(D(1D.5D()).5C(c.C.6z).is(\'*\')){4u{1D.86(2m+1D.V+2D)}4t(e){}}};f.2t.B.ay=j(){o 1D=c.I.2L.4Y();if(D(1D.5D()).5C(c.C.6z).is(\'*\')){4u{o V=1D.V;c.1O(\'f8\');1D.86(V)}4t(e){}}};f.2t.B.2y=j(){c.2l=1f};f.2t.B.4Z=j(Z,3X){o 1D=c.I.2L.4Y();3X=3X?S:L;1D.f7(Z);1D.ax(3X);1D.4X();Z.4W()};f.2C=j(F){c.1W=F;c.5z="J";c.4U="\\n"};f.2C.B.5y=j(1L){o F=c;c.1K=1L;c.I=1L.am;o 1E=c.I.4T[0];o 2s=2B(c.C.3h);c.4S(c.I,2s);c.I.O=c.1W.3g;D(\'Q\',c.I).1r(\'3C\',c.C.4R);c.Q(c.1W.4Q);c.6y();if(D.2A(c.C.3B))c.C.3B(c);c.1W.5x();D(c.I).3A("3x",c.3x);D(c.I).3A("2y",c.2y);D(c.I).3A("4W",j(){F.6y.aw(F)});if(D.2A(c.C.3z))c.C.3z(c);c.5w()};f.2C.B.Q=j(Q){if(1s Q===\'3T\'){4u{c.I.4s="av"}4t(e){};Q=Q.W(/<em(\\b[^>]*)>/gi,"<i$1>").W(/<\\/em>/gi,"</i>").W(/<3b(\\b[^>]*)>/gi,"<b$1>").W(/<\\/3b>/gi,"</b>");D(c.I.1i).Q(Q);c.6y()}X u(D(c.I.1i).Q())};f.2C.B.1O=j(1F,1T){if(!c.1c())u(L);3f(1F){1k f.6x:1k f.6w:o 1u=c.1c();o 2z=c.1K.1I.3e();o 2a=2z.2a;if(2a.5u=="#V")2a=2a.1B;1u=c.2K(1u,f.5v);2a=c.2K(2a,f.5v);if(1u&&1u==2a&&1u.1t.1q()==f.6v){o 3y=1u.1B.1B;if(1u.1B.6u.Y>1||3y.1t.1q()==f.6t||3y.1t.1q()==f.6s)c.I.2r(1F,\'\',1f)}1p;3U:if(1T)c.I.2r(1F,\'\',1T);X c.I.2r(1F,\'\',1f)}o K=c.1c();if(K.1t.1q()==f.3w)c.1O(f.4r,f.P)};f.2C.B.1c=j(){o 2z=c.1K.1I.3e();o Z=2z.1u;if(Z){if(Z.5u=="#V")u(Z.1B);X u(Z)}X u(1f)};f.2C.B.5t=j(1E,1Z){1E.84(1Z.G+" {"+1Z.1b+"}",1E.83.Y)};f.2C.B.3x=j(1e){o F=f.2x[c.O];o K=1f;if(1e.6n){if(1e.1C==66){F.1O(f.82);u L}if(1e.1C==73){F.1O(f.81);u L}}X if(1e.1C==13){if(!1e.6p){K=F.1c();if(K&&K.1t.1q()==f.5r){1e.6q();F.5B(\'<p></p>\')}}}};f.2C.B.2y=j(1e){o F=f.2x[c.O];F.2l=1f;o K=1f;if(1e.1C==13&&!1e.6p){D(F.I.1i).80(f.6o).5s()}if(1e.1C!=8&&1e.1C!=17&&1e.1C!=46&&1e.1C!=7Z&&!1e.aj&&!1e.6n){K=F.1c();o G=K.1t.1q();if(G=="3b"||G=="b"||G=="em"||G=="i"||G=="3a"||G=="3S"||G=="a")G=K.1B.1t.1q();if(G==f.3w)F.1O(f.4r,f.P)}};f.2C.B.6y=j(){if(c.I.4s=="av"){4u{c.I.4s="on";c.I.2r("f6",\'\',L)}4t(e){}}};f.2C.B.6m=j(E,H){o H=c.3v.5q(E,H);if(E==\'3u\'&&H.1o){o 3d=c.6l(H.1o);if(3d){E=3d;c.4q.4P();c.4q.1Y(E);H.1o=\'\'}}c.1X+=c.4O.E(E,H,S)};f.2C.B.6l=j(1o){if(/ah/.2J(1o))u\'3b\';if(/ag/.2J(1o))u\'em\';if(/3a/.2J(1o))u\'3a\';if(/af/.2J(1o))u\'3S\';u L};f.3V=j(F){c.1W=F;c.5z="J";c.4U="\\r\\n"};f.3V.B.5y=j(1L){c.1K=1L;c.I=1L.1I.3D;o 1E=c.I.4T[0];o 2s=2B(c.C.3h);c.4S(c.I,2s);c.I.O=c.1W.3g;D(\'Q\',c.I).1r(\'3C\',c.C.4R);c.I.4s="on";c.Q(c.1W.4Q);if(D.2A(c.C.3B))c.C.3B(c);c.1W.5x();D(c.I).3A("3x",c.3x);D(c.I).3A("2y",c.2y);if(D.2A(c.C.3z))c.C.3z(c);c.5w()};f.3V.B.1O=j(1F,1T){if(1T)c.I.2r(1F,L,1T);X c.I.2r(1F)};f.3V.B.1c=j(){o 2z=c.1K.1I.3e();o Z=2z.1u;if(Z){if(Z.5u=="#V")u(Z.1B);X u(Z)}X u(1f)};f.3V.B.5t=j(1E,1Z){1E.84(1Z.G+" {"+1Z.1b+"}",1E.83.Y)};f.3V.B.3x=j(1e){o F=f.2x[c.O];o 2z=F.1K.1I.3e();85=2z.au(0).f5;if(!D(85).4V(f.6r.5A(","))[0]&&!D(85).4V(\'li\')&&1e.1C!=f.3W.at&&1e.1C!=f.3W.as&&1e.1C!=f.3W.ar&&1e.1C!=f.3W.aq&&1e.1C!=f.3W.ap&&1e.1C!=f.3W.ao&&1e.1C!=f.3W.an)F.1O(f.4r,f.P)};f.3V.B.2y=j(1e){o F=f.2x[c.O];F.2l=1f};f.3c=j(F){c.1W=F;c.5z="J";c.4U="\\n"};f.3c.B.5y=j(1L){c.1K=1L;c.I=1L.am;o 1E=c.I.4T[0];o 2s=2B(c.C.3h);c.4S(c.I,2s);c.I.O=c.1W.3g;D(\'Q\',c.I).1r(\'3C\',c.C.4R);c.I.4s="on";c.Q(c.1W.4Q);if(D.2A(c.C.3B))c.C.3B(c);c.1W.5x();D(c.I).3A("3x",c.3x);D(c.I).3A("2y",c.2y);if(D.2A(c.C.3z))c.C.3z(c);c.5w()};f.3c.B.1O=j(1F,1T){if(!c.1c())u(L);3f(1F){1k f.6x:1k f.6w:o 1u=c.1c();o 2z=c.1K.1I.3e();o 2a=2z.2a;if(2a.5u=="#V")2a=2a.1B;1u=c.2K(1u,f.5v);2a=c.2K(2a,f.5v);if(1u&&1u==2a&&1u.1t.1q()==f.6v){o 3y=1u.1B.1B;if(1u.1B.6u.Y>1||3y.1t.1q()==f.6t||3y.1t.1q()==f.6s)c.I.2r(1F,\'\',1f)}1p;1k f.al:1k f.ak:c.I.2r(1F,\'\',1f);o 1u=c.1c();o K=c.2K(1u,f.6r);if(K)D(K).f4(D(K).Q());1p;3U:if(1T)c.I.2r(1F,\'\',1T);X c.I.2r(1F,\'\',1f)}o K=c.1c();if(K&&K.1t.1q()==f.3w)c.1O(f.4r,f.P)};f.3c.B.1c=j(){o 2z=c.1K.1I.3e();o Z=2z.1u;if(Z){if(Z.5u=="#V")u(Z.1B);X u(Z)}X u(1f)};f.3c.B.5t=j(1E,1Z){1E.84(1Z.G+" {"+1Z.1b+"}",1E.83.Y)};f.3c.B.3x=j(e){o F=f.2x[c.O];if(e.6n){if(e.1C==66){F.1O(f.82);e.6q()}if(e.1C==73){F.1O(f.81);e.6q()}}X if(e.6p&&e.1C==13){F.1O(\'f3\');e.6q()}};f.3c.B.2y=j(1e){o F=f.2x[c.O];F.2l=1f;o K=1f;if(1e.1C==13&&!1e.6p){D(F.I.1i).80(f.6o).5s();K=F.1c();if(K&&K.1t.1q()==f.5r)F.1O(f.4r,f.P);}if(1e.1C!=8&&1e.1C!=17&&1e.1C!=46&&1e.1C!=7Z&&!1e.aj&&!1e.6n){K=F.1c();o G=K.1t.1q();if(G=="3b"||G=="b"||G=="em"||G=="i"||G=="3a"||G=="3S"||G=="a"||G=="3u")G=K.1B.1t.1q();if(G==f.3w||G==f.ai)F.1O(f.4r,f.P);}};f.3c.B.6m=j(E,H){o H=c.3v.5q(E,H);if(E==\'3u\'&&H.1o){o 3d=c.6l(H.1o);if(3d){E=3d;c.4q.4P();c.4q.1Y(E);H.1o=\'\';if(1s H[\'J\']==\'3T\'){H[\'J\']=H[\'J\'].W(/f2-1o-3u/gi,\'\')}}}c.1X+=c.4O.E(E,H,S)};f.3c.B.6l=j(1o){if(/ah/.2J(1o))u\'3b\';if(/ag/.2J(1o))u\'em\';if(/3a/.2J(1o))u\'3a\';if(/af/.2J(1o))u\'3S\';u L};',62,1724,'||||||||||||this|||WYMeditor||||function|||||var||||||return|||||||prototype|_options|jQuery|tag|wym|name|attributes|_doc|class|container|false|match|div|title||html|value|true|mode||text|replace|else|length|node|||||||||||_Listener|css|selected|type|evt|null|new|editor|body|for|case|replaceAll|XhtmlSaxListener|label|style|break|toLowerCase|attr|typeof|tagName|focusNode|xhtml|XhtmlParser|boxHtml|raw|input|val|parentNode|keyCode|range|styles|cmd|scope|dialogHtml|contentWindow|_tags|_iframe|iframe|form|row|_exec|Helper|result|href|options|param|key|src|_wym|output|push|oCss|||||||||||anchorNode|state|content|required|undefined|_box|_current_element|Lexer|matches|script|attribute|_selected_image|left|window|_current_item|status|_regexes|execCommand|aCss|WymClassExplorer|size|only_wym_blocks|link|INSTANCES|keyup|sel|isFunction|eval|WymClassMozilla|right|contains|addExitPattern|ul|blockquote|_mode_handlers|test|findUp|selection|str|RegExp|details|addEntryPattern|position|closing|head|fieldset|_open_tags|XmlHelper|ParallelRegex|char|disabled|skin|||||||||||sub|strong|WymClassSafari|new_tag|getSelection|switch|_index|editorStyles|Text|tags|table|legend|XhtmlLexer|matched|pattern|start|top|submit|paragraphs|sType|span|validator|BODY|keydown|ancestor|postInit|bind|preBind|dir|document|find|arr|WymCssParser|LEXER_UNMATCHED|_closing|_insert_|button|lang|parsed|defaults|inside|xml|INDEX|newNode|sup|string|default|WymClassOpera|KEY|toStart|classesItems|parse||x2a|parser|pre|_mode|addPattern||_patterns|baseline|bottom|middle|valign|charoff|justify|center|align|stylesheet|doc|sBodyHtml|tagname|sClass|oClass|sContainer|sTool|iframeHtml|WymClass|_tag_stack|FORMAT_BLOCK|designMode|catch|try|item|trim|jqexpr|data|dialogStyles|css_settings|object|WymCss|addSpecialPattern|td|textarea|_invokeParser|_case|width|onload|basePath|dialog|sVal|console|helper|pop|_html|direction|addCssRules|styleSheets|_newLine|parentsOrSelf|focus|select|createRange|setFocusToNode||caretPos|loadSkin|filter|Ignore|extend|cite|base|init|_stack|count|except|alt|tag_attributes|delimiter|close|sUrl|INIT_DIALOG|SKINS|skinPath|CssParser|wymeditor|TH|oContainer|oTool|wym_section|getValidTagAttributes|PRE|remove|addCssRule|nodeName|BLOCKS|listen|bindEvents|initIframe|_class|join|insert|parents|parentElement|paste|split|elem|_has_title|LEXER_EXIT|z0|avoided_tags|th|now_on_tag|colgroup|_tag_attributes|_non_tag|action|special|new_mode|accept|StateStack|_regex|_possible_tag_attributes|rel|hidden|get|open|createElement|submitSelector|titleSelector|TITLE|sStamp||dialogImageSelector|DIALOG_LINK|dialogType|update|stringDelimiterRight|stringDelimiterLeft|toggleClass|BLOCKQUOTE|Array|TD|aClasses|wym_dialog|wym_cancel|wym_dialog_type|getTagForStyle|openBlockTag|ctrlKey|BR|shiftKey|preventDefault|MAIN_CONTAINERS|UL|OL|childNodes|LI|OUTDENT|INDENT|enableDesignMode|iframeBodySelector|saveCaret|substring|rExp|expressions|style_details|_in_style|WymCssStyle|_Lexer|last_tag|after|skiped_attribute_values|skiped_attributes|addCss|addScript|addComment|getTagReplacements|address|afterParsing|beforeParsing|option|tr|img|col|bdo|abbr|prop|_current_attribute|SingleQuotedAttribute|DoubleQuotedAttribute||LEXER_SPECIAL|normalizeTag|expected_tag|LEXER_ENTER|OpeningTag|_addNonTagBlock|handler|unmatched|while|case_sensitive|regex|tag_defaults|only|valid_attributes|rows|cols|screen|charset|height|meta|index|escape_quotes|_entitiesDiv|mousedown|altSelector|ALT|SRC|srcSelector|SKINS_DEFAULT_CSS|grep|packed|min|pack|jquery|jQueryPath|wymPath|PREVIEW|DIALOG_PASTE|DIALOG_TABLE|DIALOG_IMAGE|dialogFeatures|_element|STRINGS|bFound|firstNode|nodes|aTypes|NAME|click|CLASS_NAME|TOOL_TITLE|HTML|iframeBasePath|browser|Cancel|Submit|wym_submit|224|children|ITALIC|BOLD|cssRules|insertRule|startNode|pasteHTML|selectors|containerSelector|ret|indexOf|pos|nodeType|feedback_style|parts|WymCssFeedbackStyle|WymCssComment|_0|mapHandler|_getClosingTagContent|_insertContentWhenClosingTag|last_tag_opened|block_tags|isBlockTag|_avoiding_tags_implicitly|del|acronym|entity|entities|thead|tfoot|tbody|noscript|circ|_callCloseTagListener|_autoCloseUnclosed|_increaseOpenTagCounter|_callOpenTagListener|Css|Script|Comment|unparsed_character_count|getCurrent|_parser|enter|leave|_labels|default_attributes|default_attributes_and_events|_attributes|_events|readonly|all|media|javascript|http|next|ltr|toString|innerHTML|_formated_options|tagOptions|postInitDialog|newRow|iCols|iRows|INSERT_IMAGE|relSelector|REL|HREF|CREATE_LINK|hrefSelector|preInitDialog|responseText|async|url|ajax|found|htmlSelector|wDialog|replaceStrings|DIALOG_BODY|DIALOG_TITLE|JQUERY_PATH|WYM_PATH|CSS_PATH|DIRECTION|hasfocus|htmlValSelector|error|langPath|switchTo|lgt|H6|H5|H4|H3|H2|H1|sName|sContainers|CONTAINERS_ITEMS|CONTAINER_CLASS|CONTAINER_TITLE|CONTAINER_NAME|aContainers|sClasses|CLASSES_ITEMS|CLASS_TITLE|sTools|TOOLS_ITEMS|TOOL_CLASS|TOOL_NAME|aTools|STATUS|IFRAME|CLASSES|CONTAINERS|TOOLS|LOGO|IFRAME_BASE_PATH|WYM_INDEX|SaxListener|preInit|Paste_From_Word|indent|Table|wym_title|Image|Link|wym_classes|wym_containers|wym_tools|wym_iframe|Preview|Unlink|Outdent|Indent|names|super|italic|bold|DIV|metaKey|INSERT_UNORDEREDLIST|INSERT_ORDEREDLIST|contentDocument|DELETE|BACKSPACE|DOWN|RIGHT|UP|LEFT|ENTER|getRangeAt|off|call|collapse|unwrap|wrap|editorLoadSkin|_initialized|returnValue|event|onclick|onkeyup|onfocus|findByName|inserted|substr|rep|old|slice|isPhantomNode|expr|addStyleSetting|WymCssStyleDeclaration|WymCssLexer|insertContentAfterClosingTag|fixNestingBeforeOpeningBlockTag|before|inline_tags|isInlineTag|allowStylingTagsAndAttributes|MsoNormal|avoidStylingTagsAndAttributes|closeUnopenedTag|closeUnknownTag|closeBlockTag|openUnknownTag|inlineTag|addContent|getResult|removeBrInPre|nbsp|160|removeEmptyTags|joinRepeatedEntities|replaceNamedEntities|shouldCloseTagAutomatically|tt|small|samp|optgroup|map|kbd|ins|dfn|code|caption|big|area|equiv||image|not|XhtmlValidator|UnquotedAttribute|TagAttributes|counter|autoCloseUnclosedBeforeTagClosing|autoCloseUnclosedBeforeNewOpening|_decreaseOpenTagCounter|ClosingTag|_tag|Listener|addAttributeTokens|addInTagDeclarationTokens|addTagTokens|addCssTokens|addScriptTokens|addCommentTokens|addTokens|unparsed|_reduce|is_match|current|_decodeSpecial|_|_isSpecialMode|__exit|_isModeEnd|LEXER_MATCHED|_dispatchTokens|_getPerlMatchingFlags|Tk7|Tk6|Tk5|Tk4|Tk3|Tk2|Tk1|_untokenizeRegex|_tokenizeRegex|_getCompoundedRegex|subject|getDefaultAttributesAndEventsForTag|getUniqueAttributesAndEventsForTag|getPossibleTagAttributes|validateAttribute|doesAttributeNeedsValidation|getDefaultAttributesAndEventsForTags|isValidTag|parseInt|possible_attributes|rowgroup|rowspan|headers|colspan|axis|summary|border|box|frame|braille|print|projection|multiple|defer|valuetype|usemap|icon|shortcut|subsection|section|prev|home|help|glossary|copyright|contents|chapter|bookmark|appendix|alternate|rev|hreflang|datetime|reset|file|checked|profile|rtl|polygon|poly|circle|rectangle|rect|shape|coords|keyboard|tabindex|accesskey|language|escapeEntities|escapeOnce|escaped|_fixDoubleEscape|cancelSelector|previewSelector|dialogPreviewSelector|sText|textSelector||dialogPasteSelector|summarySelector|sCaption|newCaption|captionSelector|TABLE|colsSelector||rowsSelector|dialogTableSelector|dialogLinkSelector|uniqueStamp|dialogTypeSelector|SKINS_DEFAULT_JS|initSkin||addClass|loadCss|each|appendChild|target|IMG|configureEditorUsingRawCss|skins|computeJqueryPath|computeWymPath|computeBasePath|now|toggleHtml|encloseString|BASE_PATH|bodyHtml|dialogPreviewHtml|dialogPasteHtml|dialogTableHtml|dialogImageHtml|dialogLinkHtml|features|sMessage|statusSelector|STRING|CLASS|insertBefore|dialogFeaturesPreview|TOGGLE_HTML|PASTE|INSERT_TABLE|exec|updateEvent|updateSelector|classSelector|toolSelector|hide|containersItemHtml|containersItems|classesItemHtml|toolsItemHtml|toolsItems|statusHtml|htmlHtml|classesHtml|containersHtml|toolsHtml|logoHtml|wym_dialog_preview|wym_text|wym_dialog_paste|wym_cols|wym_rows|wym_summary|||wym_caption|wym_dialog_table|Title|wym_alt|wym_src|URL|wym_dialog_image|wym_rel|wym_href|wym_dialog_link|xhtml1|DTD|org|www|300|560|resizable|toolbar|titlebar|menubar|wym_html_val|wym_status|wym_html|wym_box|ToggleHtml|Paste|InsertTable|InsertImage|CreateLink|Redo|Undo|InsertUnorderedList|InsertOrderedList|Subscript|Superscript|Italic|Bold|IFRAME_DEFAULT|LANG_DEFAULT_PATH|SKINS_DEFAULT_PATH|apple|InsertLineBreak|replaceWith|startContainer|styleWithCSS|moveToElementText|Cut|addRule|unselectable|getData|clipboardData|onpaste|onbeforepaste|onbeforedeactivate|className|insertAt|isPhantomString|toUpperCase|end|x7d|x7b||x2e|z1|sa|insertContentBeforeClosingTag|main1|remove_embeded_styles|remove_scripts|remove_comments|fromCharCode|String|gmi|extends|9830|diams|9829|hearts|9827|clubs|9824|spades|9674|loz|9002|rang|9001|8971|rfloor|8970|lfloor|8969|rceil|8968|lceil|8901|sdot|8869|perp|8855|otimes|8853|oplus|8839|supe|8838|sube|8836|nsub|8835|8834|8805|8804|8801||8800|8776|asymp||8773|cong|8764||sim|8756|there4|8747|int|8746|cup|8745|cap|8744|8743|and|8736|ang|8734|infin|8733|8730|radic|8727|lowast|8722|minus|8721|sum|8719|prod|8715|8713|notin|8712|isin|8711|nabla|8709|empty|8707|exist|8706|part|||||||8704|forall|8660|hArr|8659|dArr|8658|rArr|8657|uArr|8656|lArr|8629|crarr|8596|harr|8595|darr|8594|rarr||8593|uarr|8592|larr|8501|alefsym|8482|trade|8476|real|8472|weierp|8465|8364|euro|8260|frasl|8254|oline|8250|rsaquo|8249|lsaquo|8243|Prime|8242|prime|8240|permil|8230|hellip|8226|bull|8225|Dagger|8224|dagger|8222|bdquo|8221|rdquo|8220|ldquo|8218|sbquo|8217|rsquo||8216|||lsquo|8212|mdash|8211|ndash|8207||rlm|8206|lrm|8205||zwj|8204|zwnj|8201|thinsp|8195|emsp|8194|ensp|982|piv|978|upsih|977|thetasym|969|omega|968|psi|967|chi|966|phi|965|upsilon|964|tau|963|sigma|962|sigmaf|961|rho|960|959|omicron|958|xi|957|956|955|lambda|954|kappa|953|iota|952|theta|951|eta|950|zeta|949|epsilon|948|delta|947|gamma|946|beta|945||alpha|937|Omega|936|Psi|935|Chi|934|Phi|933|Upsilon|932|Tau|931|Sigma|929|Rho|928|Pi|927|Omicron|926|Xi|925|Nu|924|Mu|923|Lambda|922|Kappa|921|Iota|920|Theta|919|Eta|918|Zeta|917|Epsilon|916|Delta|915|Gamma|914|Beta|913|Alpha|732|tilde|710|402|fnof|376|Yuml|353|scaron|352|Scaron|339|oelig|338|OElig|255|yuml|254|thorn|253|yacute|252|uuml|251|ucirc|250|uacute|249|ugrave|248|oslash|247|divide|246|ouml|245|otilde|244|ocirc|243|oacute|242|ograve|241|ntilde|240|eth|239|iuml|238|icirc|237|iacute|236|igrave|235|euml|234|ecirc|233||eacute|232|egrave||231|ccedil|230|aelig|229|aring|228|auml|227|atilde|226|acirc|225|aacute|agrave|223|szlig|222|THORN|221|Yacute|220|Uuml|219|Ucirc|218|Uacute|217|Ugrave|216|Oslash|215|times|214|Ouml|213|Otilde|212|Ocirc|211|Oacute|210|Ograve|209|Ntilde|208|ETH|207|Iuml|206|Icirc|205|Iacute|204|Igrave|203|Euml|202|Ecirc|201|Eacute||200|Egrave|199|Ccedil|198|AElig|197|Aring|196|Auml|195||Atilde|194|Acirc|193|Aacute|192|Agrave|191|iquest|190|frac34|189|frac12|188|frac14|187|raquo|186|ordm|185|sup1|184|cedil|183|middot|182|para|181|micro|180|acute|179|sup3|178|sup2|177|plusmn|176|deg|175|macr|174|reg|173|shy||172|171|laquo||170|ordf|169|copy|168||uml|167|sect||166||brvbar|165|yen|164|curren|163|pound|162|cent|161|iexcl|last_tag_attributes|com|microsoft|schemas|urn|_current_match|_last_match|_matches|boolean|concat|events|continue|groups|none|rules|vsides|rhs|lhs|hsides|below|above|void|cellspacing|cellpadding|aural|handheld|tv|tty|vbs|vbscript|jscript|ecmascript|ref|standby|declare|codetype|codebase|classid|archive|scheme|cookie||set||refresh|expires|speech||radio|password|checkbox|maxlength|longdesc|ismap|xmlns|post|method|enctype|nohref|onmouseup|onmouseout|onmouseover|onmousemove|onmousedown|ondblclick|mouse|onkeypress|onkeydown|frameset|onblur|onselect|onreset|onsubmit|onchange|onunload|core|charAt|parseAttributes|039|quot|textContent|shift|formated_options|amp|CDATA|cdataSection|contentTag|append|insertCell|insertRow|createCaption|newCol|opener|wym_skin_|ownerDocument|computeCssPath|addRange|selectNodeContents|insertNode|deleteContents||createContextualFragment|appendTo|insertAfter|getTime|Date|toggle|write|parsing|replaceChild|removeAttr|removeChild|firstChild|ready|blur|wym_box_|safari|opera|mozilla|msie|wymeditors|Number_Of_Cols|Number_Of_Rows|Summary|Caption|Alternative_Text|Relationship|dtd|strict|TR|w3|EN|Strict|XHTML|W3C|PUBLIC|DOCTYPE|yes|scrollbars|wymupdate|iframeSelector|classesSelector|containersSelector|toolsListSelector|toolsSelector|boxSelector|Source_Code|Status|wym_classes_|Classes|wym_containers_th|Table_Header|wym_containers_blockquote|Blockquote|wym_containers_pre|Preformatted|wym_containers_h6|Heading_6|wym_containers_h5|Heading_5|wym_containers_h4|Heading_4|wym_containers_h3|Heading_3|wym_containers_h2|Heading_2|wym_containers_h1|Heading_1|wym_containers_p|Paragraph|Containers|wym_tools_preview|wym_tools_html|wym_tools_paste|wym_tools_table|wym_tools_image|wym_tools_unlink|wym_tools_link|wym_tools_redo|wym_tools_undo|wym_tools_outdent|wym_tools_indent|wym_tools_unordered_list|Unordered_List|wym_tools_ordered_list|Ordered_List|wym_tools_subscript|wym_tools_superscript|wym_tools_emphasis|Emphasis|wym_tools_strong|Strong|Tools|parent|wymiframe|wym_wymeditor_link|wym_area_bottom|wym_area_main|wym_area_right|wym_area_left|wym_area_top|TEXT|ATTRIBUTE|ELEMENT|NODE|CURSOR|HOME|END|UNLINK|FormatBlock|InsertHTML|INSERT_HTML|Wym_Dialog_Body|Wym_Dialog_Title|Wym_Status|Wym_Iframe|Wym_Html|Wym_Container_Class|Wym_Containers_Title|Wym_Container_Name||Wym_Containers_Items|Wym_Containers|Wym_Class_Title|Wym_Class_Name|Wym_Classes_Items|Wym_Classes|Wym_Tool_Class|Wym_Tool_Title|Wym_Tool_Name|Wym_Tools_Items|Wym_Tools|Wym_Logo|Wym_Direction|Wym_Jquery_Path|Wym_Iframe_Base_Path|Wym_Wym_Path|Wym_Css_Path|Wym_Base_Path|wym_index|Wym_Index|rc1|VERSION|profileEnd|trace|timeEnd|time|groupEnd|group|dirxml|assert|warn|info|debug|log|firebug'.split('|'),0,{}))
diff --git a/studio/static/js/wymeditor/jquery.wymeditor.safari.js b/studio/static/js/wymeditor/jquery.wymeditor.safari.js
new file mode 100644
index 0000000..e4b9739
--- /dev/null
+++ b/studio/static/js/wymeditor/jquery.wymeditor.safari.js
@@ -0,0 +1,247 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.safari.js
+ * Safari specific class and functions.
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Scott Lewis (lewiscot a-t gmail dotcom)
+ */
+
+WYMeditor.WymClassSafari = function(wym) {
+
+ this._wym = wym;
+ this._class = "class";
+ this._newLine = "\n";
+};
+
+WYMeditor.WymClassSafari.prototype.initIframe = function(iframe) {
+
+ this._iframe = iframe;
+ this._doc = iframe.contentDocument;
+
+ //add css rules from options
+
+ var styles = this._doc.styleSheets[0];
+ var aCss = eval(this._options.editorStyles);
+
+ this.addCssRules(this._doc, aCss);
+
+ this._doc.title = this._wym._index;
+
+ //set the text direction
+ jQuery('html', this._doc).attr('dir', this._options.direction);
+
+ //init designMode
+ this._doc.designMode = "on";
+
+ //init html value
+ this.html(this._wym._html);
+
+ //pre-bind functions
+ if(jQuery.isFunction(this._options.preBind)) this._options.preBind(this);
+
+ //bind external events
+ this._wym.bindEvents();
+
+ //bind editor keydown events
+ jQuery(this._doc).bind("keydown", this.keydown);
+
+ //bind editor keyup events
+ jQuery(this._doc).bind("keyup", this.keyup);
+
+ //post-init functions
+ if(jQuery.isFunction(this._options.postInit)) this._options.postInit(this);
+
+ //add event listeners to doc elements, e.g. images
+ this.listen();
+};
+
+WYMeditor.WymClassSafari.prototype._exec = function(cmd,param) {
+
+ if(!this.selected()) return(false);
+
+ switch(cmd) {
+
+ case WYMeditor.INDENT: case WYMeditor.OUTDENT:
+
+ var focusNode = this.selected();
+ var sel = this._iframe.contentWindow.getSelection();
+ var anchorNode = sel.anchorNode;
+ if(anchorNode.nodeName == "#text") anchorNode = anchorNode.parentNode;
+
+ focusNode = this.findUp(focusNode, WYMeditor.BLOCKS);
+ anchorNode = this.findUp(anchorNode, WYMeditor.BLOCKS);
+
+ if(focusNode && focusNode == anchorNode
+ && focusNode.tagName.toLowerCase() == WYMeditor.LI) {
+
+ var ancestor = focusNode.parentNode.parentNode;
+
+ if(focusNode.parentNode.childNodes.length>1
+ || ancestor.tagName.toLowerCase() == WYMeditor.OL
+ || ancestor.tagName.toLowerCase() == WYMeditor.UL)
+ this._doc.execCommand(cmd,'',null);
+ }
+
+ break;
+
+ case WYMeditor.INSERT_ORDEREDLIST: case WYMeditor.INSERT_UNORDEREDLIST:
+
+ this._doc.execCommand(cmd,'',null);
+
+ //Safari creates lists in e.g. paragraphs.
+ //Find the container, and remove it.
+ var focusNode = this.selected();
+ var container = this.findUp(focusNode, WYMeditor.MAIN_CONTAINERS);
+ if(container) jQuery(container).replaceWith(jQuery(container).html());
+
+ break;
+
+ default:
+
+ if(param) this._doc.execCommand(cmd,'',param);
+ else this._doc.execCommand(cmd,'',null);
+ }
+
+ //set to P if parent = BODY
+ var container = this.selected();
+ if(container && container.tagName.toLowerCase() == WYMeditor.BODY)
+ this._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P);
+
+};
+
+/* @name selected
+ * @description Returns the selected container
+ */
+WYMeditor.WymClassSafari.prototype.selected = function() {
+
+ var sel = this._iframe.contentWindow.getSelection();
+ var node = sel.focusNode;
+ if(node) {
+ if(node.nodeName == "#text") return(node.parentNode);
+ else return(node);
+ } else return(null);
+};
+
+WYMeditor.WymClassSafari.prototype.addCssRule = function(styles, oCss) {
+
+ styles.insertRule(oCss.name + " {" + oCss.css + "}",
+ styles.cssRules.length);
+};
+
+
+//keydown handler, mainly used for keyboard shortcuts
+WYMeditor.WymClassSafari.prototype.keydown = function(e) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+
+ if(e.ctrlKey){
+ if(e.keyCode == 66){
+ //CTRL+b => STRONG
+ wym._exec(WYMeditor.BOLD);
+ e.preventDefault();
+ }
+ if(e.keyCode == 73){
+ //CTRL+i => EMPHASIS
+ wym._exec(WYMeditor.ITALIC);
+ e.preventDefault();
+ }
+ } else if(e.shiftKey && e.keyCode == 13) {
+ wym._exec('InsertLineBreak');
+ e.preventDefault();
+ }
+};
+
+//keyup handler, mainly used for cleanups
+WYMeditor.WymClassSafari.prototype.keyup = function(evt) {
+
+ //'this' is the doc
+ var wym = WYMeditor.INSTANCES[this.title];
+
+ wym._selected_image = null;
+ var container = null;
+
+ if(evt.keyCode == 13 && !evt.shiftKey) {
+
+ //RETURN key
+ //cleanup <br><br> between paragraphs
+ jQuery(wym._doc.body).children(WYMeditor.BR).remove();
+
+ //fix PRE bug #73
+ container = wym.selected();
+ if(container && container.tagName.toLowerCase() == WYMeditor.PRE)
+ wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //create P after PRE
+ }
+
+ if(evt.keyCode != 8
+ && evt.keyCode != 17
+ && evt.keyCode != 46
+ && evt.keyCode != 224
+ && !evt.metaKey
+ && !evt.ctrlKey) {
+
+ //NOT BACKSPACE, NOT DELETE, NOT CTRL, NOT COMMAND
+ //text nodes replaced by P
+
+ container = wym.selected();
+ var name = container.tagName.toLowerCase();
+
+ //fix forbidden main containers
+ if(
+ name == "strong" ||
+ name == "b" ||
+ name == "em" ||
+ name == "i" ||
+ name == "sub" ||
+ name == "sup" ||
+ name == "a" ||
+ name == "span" //fix #110
+
+ ) name = container.parentNode.tagName.toLowerCase();
+
+ if(name == WYMeditor.BODY || name == WYMeditor.DIV) wym._exec(WYMeditor.FORMAT_BLOCK, WYMeditor.P); //fix #110 for DIV
+ }
+};
+
+WYMeditor.WymClassSafari.prototype.openBlockTag = function(tag, attributes)
+{
+ var attributes = this.validator.getValidTagAttributes(tag, attributes);
+
+ // Handle Safari styled spans
+ if (tag == 'span' && attributes.style) {
+ var new_tag = this.getTagForStyle(attributes.style);
+ if (new_tag) {
+ tag = new_tag;
+ this._tag_stack.pop();
+ this._tag_stack.push(tag);
+ attributes.style = '';
+
+ // Should fix #125 - also removed the xhtml() override
+ if(typeof attributes['class'] == 'string') {
+ attributes['class'] = attributes['class'].replace(/apple-style-span/gi, '');
+ }
+ }
+ }
+
+ this.output += this.helper.tag(tag, attributes, true);
+};
+
+WYMeditor.WymClassSafari.prototype.getTagForStyle = function(style) {
+
+ if(/bold/.test(style)) return 'strong';
+ if(/italic/.test(style)) return 'em';
+ if(/sub/.test(style)) return 'sub';
+ if(/super/.test(style)) return 'sup';
+ return false;
+};
diff --git a/studio/static/js/wymeditor/lang/.svn/entries b/studio/static/js/wymeditor/lang/.svn/entries
new file mode 100644
index 0000000..2f4e246
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/entries
@@ -0,0 +1,912 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/lang
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2010-06-16T22:14:30.896087Z
+671
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+cs.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+8407c00954064ee8a4bb1c5534e9b0cf
+2008-10-06T17:56:42.178306Z
+556
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1683
+
+fr.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+de2243ddfc69cc11cde635d51c203f58
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1540
+
+es.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+15ddb1092fb54b243ebc27306c4ff185
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1548
+
+hr.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+d9f0b6c76b689f76243dcbafb6c4ca8b
+2009-03-12T10:43:31.861532Z
+585
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1517
+
+nl.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+16ecb01b57a7c6e627195c095ebd9664
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1537
+
+cy.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+26e73f813aac75243a4e2e2aa9eefc2d
+2010-03-31T20:26:06.993041Z
+651
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1542
+
+nn.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+f389e5577207f6125d3d95148f7f56ca
+2008-07-08T21:01:27.213276Z
+514
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1504
+
+pl.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+4170572c911e8dc00fe78b97eb034a87
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1563
+
+it.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+c267a9efc265501508b0c20beda2a202
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1523
+
+hu.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+87ec9def97fa4085049b2039af1e3cf5
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1553
+
+ca.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+ef8f5b258b41e4b85281c0ae810139bd
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1552
+
+pt.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+8de734032fe9edea9d37f4334cbbb64d
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1547
+
+pt-br.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+9f32ebcb89424e390b6d57668894987d
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1543
+
+tr.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+976fd75344b1893f9de9262bf43263f8
+2008-11-09T19:53:01.458305Z
+567
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1537
+
+ru.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+28782e8ac2861dfa57a39bbb6ab7c883
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1956
+
+fa.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+29d7bb6c7da2781c88503d9a75d7d93a
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2253
+
+de.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+c47599295200bd1ae30bc51c749c49f0
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1694
+
+bg.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+f1d85ab79383acf1698f7042773717e2
+2008-12-16T20:10:42.882950Z
+575
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1973
+
+sv.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+0d1671c35bb10efefecd9ac9d161abfc
+2010-06-16T22:14:30.896087Z
+671
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1518
+
+ja.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+db365bd23b03b84f9eaf7e0df5fe791a
+2010-06-11T10:15:56.250894Z
+666
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1311
+
+he.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+f119d047232bcdb436a238b716060138
+2008-09-10T20:50:59.861136Z
+539
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1812
+
+fi.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+c7fa6c1e747480b1695e86bff08c3f99
+2009-03-14T15:08:06.269083Z
+586
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1543
+
+nb.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+b51b573772a041858126df92a8a7cc3c
+2008-07-08T21:01:27.213276Z
+514
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1501
+
+zh_cn.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+83ba8a5406c3cb0ee1c480cb236ee6fa
+2008-06-05T19:53:16.438140Z
+508
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1206
+
+gl.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+3c1001043b99fed6be195252296a98ce
+2009-06-08T21:00:39.401508Z
+646
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1546
+
+en.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+ee76c7f82c1f407ac5ca8464dfcc6724
+2010-06-16T22:14:30.896087Z
+671
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1508
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/bg.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/bg.js.svn-base
new file mode 100644
index 0000000..576bca5
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/bg.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['bg'] = {
+ Strong: 'Получер',
+ Emphasis: 'Курсив',
+ Superscript: 'Горен индекс',
+ Subscript: 'Долен индекс',
+ Ordered_List: 'Подреден списък',
+ Unordered_List: 'Неподреден списък',
+ Indent: 'Блок навътре',
+ Outdent: 'Блок навън',
+ Undo: 'Стъпка назад',
+ Redo: 'Стъпка напред',
+ Link: 'Създай хипервръзка',
+ Unlink: 'Премахни хипервръзката',
+ Image: 'Изображение',
+ Table: 'Таблица',
+ HTML: 'HTML',
+ Paragraph: 'Абзац',
+ Heading_1: 'Заглавие 1',
+ Heading_2: 'Заглавие 2',
+ Heading_3: 'Заглавие 3',
+ Heading_4: 'Заглавие 4',
+ Heading_5: 'Заглавие 5',
+ Heading_6: 'Заглавие 6',
+ Preformatted: 'Преформатиран',
+ Blockquote: 'Цитат',
+ Table_Header: 'Заглавие на таблицата',
+ URL: 'URL',
+ Title: 'Заглавие',
+ Alternative_Text: 'Алтернативен текст',
+ Caption: 'Етикет',
+ Summary: 'Общо',
+ Number_Of_Rows: 'Брой редове',
+ Number_Of_Cols: 'Брой колони',
+ Submit: 'Изпрати',
+ Cancel: 'Отмени',
+ Choose: 'Затвори',
+ Preview: 'Предварителен преглед',
+ Paste_From_Word: 'Вмъкни от MS WORD',
+ Tools: 'Инструменти',
+ Containers: 'Контейнери',
+ Classes: 'Класове',
+ Status: 'Статус',
+ Source_Code: 'Източник, код'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/ca.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/ca.js.svn-base
new file mode 100644
index 0000000..c342406
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/ca.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['ca'] = {
+ Strong: 'Ressaltar',
+ Emphasis: 'Emfatitzar',
+ Superscript: 'Superindex',
+ Subscript: 'Subindex',
+ Ordered_List: 'Llistat ordenat',
+ Unordered_List: 'Llistat sense ordenar',
+ Indent: 'Indentat',
+ Outdent: 'Sense indentar',
+ Undo: 'Desfer',
+ Redo: 'Refer',
+ Link: 'Enllaçar',
+ Unlink: 'Eliminar enllaç',
+ Image: 'Imatge',
+ Table: 'Taula',
+ HTML: 'HTML',
+ Paragraph: 'Paràgraf',
+ Heading_1: 'Capçalera 1',
+ Heading_2: 'Capçalera 2',
+ Heading_3: 'Capçalera 3',
+ Heading_4: 'Capçalera 4',
+ Heading_5: 'Capçalera 5',
+ Heading_6: 'Capçalera 6',
+ Preformatted: 'Pre-formatejat',
+ Blockquote: 'Cita',
+ Table_Header: 'Capçalera de la taula',
+ URL: 'URL',
+ Title: 'Títol',
+ Alternative_Text: 'Text alternatiu',
+ Caption: 'Llegenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Nombre de files',
+ Number_Of_Cols: 'Nombre de columnes',
+ Submit: 'Enviar',
+ Cancel: 'Cancel·lar',
+ Choose: 'Triar',
+ Preview: 'Vista prèvia',
+ Paste_From_Word: 'Pegar des de Word',
+ Tools: 'Eines',
+ Containers: 'Contenidors',
+ Classes: 'Classes',
+ Status: 'Estat',
+ Source_Code: 'Codi font'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/cs.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/cs.js.svn-base
new file mode 100644
index 0000000..3939d71
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/cs.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['cs'] = {
+ Strong: 'Tučné',
+ Emphasis: 'Kurzíva',
+ Superscript: 'Horní index',
+ Subscript: 'Dolní index',
+ Ordered_List: 'Číslovaný seznam',
+ Unordered_List: 'Nečíslovaný seznam',
+ Indent: 'Zvětšit odsazení',
+ Outdent: 'Zmenšit odsazení',
+ Undo: 'Zpět',
+ Redo: 'Znovu',
+ Link: 'Vytvořit odkaz',
+ Unlink: 'Zrušit odkaz',
+ Image: 'Obrázek',
+ Table: 'Tabulka',
+ HTML: 'HTML',
+ Paragraph: 'Odstavec',
+ Heading_1: 'Nadpis 1. úrovně',
+ Heading_2: 'Nadpis 2. úrovně',
+ Heading_3: 'Nadpis 3. úrovně',
+ Heading_4: 'Nadpis 4. úrovně',
+ Heading_5: 'Nadpis 5. úrovně',
+ Heading_6: 'Nadpis 6. úrovně',
+ Preformatted: 'Předformátovaný text',
+ Blockquote: 'Citace',
+ Table_Header: 'Hlavičková buňka tabulky',
+ URL: 'Adresa',
+ Title: 'Text po najetí myší',
+ Alternative_Text: 'Text pro případ nezobrazení obrázku',
+ Caption: 'Titulek tabulky',
+ Summary: 'Shrnutí obsahu',
+ Number_Of_Rows: 'Počet řádek',
+ Number_Of_Cols: 'Počet sloupců',
+ Submit: 'Vytvořit',
+ Cancel: 'Zrušit',
+ Choose: 'Vybrat',
+ Preview: 'Náhled',
+ Paste_From_Word: 'Vložit z Wordu',
+ Tools: 'Nástroje',
+ Containers: 'Typy obsahu',
+ Classes: 'Třídy',
+ Status: 'Stav',
+ Source_Code: 'Zdrojový kód'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/cy.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/cy.js.svn-base
new file mode 100644
index 0000000..7d15b79
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/cy.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['cy'] = {
+ Strong: 'Bras',
+ Emphasis: 'Italig',
+ Superscript: 'Uwchsgript',
+ Subscript: 'Is-sgript',
+ Ordered_List: 'Rhestr mewn Trefn',
+ Unordered_List: 'Pwyntiau Bwled',
+ Indent: 'Mewnoli',
+ Outdent: 'Alloli',
+ Undo: 'Dadwneud',
+ Redo: 'Ailwneud',
+ Link: 'Cysylltu',
+ Unlink: 'Datgysylltu',
+ Image: 'Delwedd',
+ Table: 'Tabl',
+ HTML: 'HTML',
+ Paragraph: 'Paragraff',
+ Heading_1: 'Pennawd 1',
+ Heading_2: 'Pennawd 2',
+ Heading_3: 'Pennawd 3',
+ Heading_4: 'Pennawd 4',
+ Heading_5: 'Pennawd 5',
+ Heading_6: 'Pennawd 6',
+ Preformatted: 'Rhagfformat',
+ Blockquote: 'Bloc Dyfyniad',
+ Table_Header: 'Pennyn Tabl',
+ URL: 'URL',
+ Title: 'Teitl',
+ Alternative_Text: 'Testun Amgen',
+ Caption: 'Pennawd',
+ Summary: 'Crynodeb',
+ Number_Of_Rows: 'Nifer y rhesi',
+ Number_Of_Cols: 'Nifer y colofnau',
+ Submit: 'Anfon',
+ Cancel: 'Diddymu',
+ Choose: 'Dewis',
+ Preview: 'Rhagolwg',
+ Paste_From_Word: 'Gludo o Word',
+ Tools: 'Offer',
+ Containers: 'Cynhwysyddion',
+ Classes: 'Dosbarthiadau',
+ Status: 'Statws',
+ Source_Code: 'Cod ffynhonnell'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/de.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/de.js.svn-base
new file mode 100644
index 0000000..a1e01e1
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/de.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['de'] = {
+ Strong: 'Fett',
+ Emphasis: 'Kursiv',
+ Superscript: 'Text hochstellen',
+ Subscript: 'Text tiefstellen',
+ Ordered_List: 'Geordnete Liste einfügen',
+ Unordered_List: 'Ungeordnete Liste einfügen',
+ Indent: 'Einzug erhöhen',
+ Outdent: 'Einzug vermindern',
+ Undo: 'Befehle rückgängig machen',
+ Redo: 'Befehle wiederherstellen',
+ Link: 'Hyperlink einfügen',
+ Unlink: 'Hyperlink entfernen',
+ Image: 'Bild einfügen',
+ Table: 'Tabelle einfügen',
+ HTML: 'HTML anzeigen/verstecken',
+ Paragraph: 'Absatz',
+ Heading_1: 'Überschrift 1',
+ Heading_2: 'Überschrift 2',
+ Heading_3: 'Überschrift 3',
+ Heading_4: 'Überschrift 4',
+ Heading_5: 'Überschrift 5',
+ Heading_6: 'Überschrift 6',
+ Preformatted: 'Vorformatiert',
+ Blockquote: 'Zitat',
+ Table_Header: 'Tabellenüberschrift',
+ URL: 'URL',
+ Title: 'Titel',
+ Alternative_Text: 'Alternativer Text',
+ Caption: 'Tabellenüberschrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Anzahl Zeilen',
+ Number_Of_Cols: 'Anzahl Spalten',
+ Submit: 'Absenden',
+ Cancel: 'Abbrechen',
+ Choose: 'Auswählen',
+ Preview: 'Vorschau',
+ Paste_From_Word: 'Aus Word einfügen',
+ Tools: 'Werkzeuge',
+ Containers: 'Inhaltstyp',
+ Classes: 'Klassen',
+ Status: 'Status',
+ Source_Code: 'Quellcode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/en.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/en.js.svn-base
new file mode 100644
index 0000000..dc7eb21
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/en.js.svn-base
@@ -0,0 +1,46 @@
+WYMeditor.STRINGS['en'] = {
+ Strong: 'Strong',
+ Emphasis: 'Emphasis',
+ Superscript: 'Superscript',
+ Subscript: 'Subscript',
+ Ordered_List: 'Ordered List',
+ Unordered_List: 'Unordered List',
+ Indent: 'Indent',
+ Outdent: 'Outdent',
+ Undo: 'Undo',
+ Redo: 'Redo',
+ Link: 'Link',
+ Unlink: 'Unlink',
+ Image: 'Image',
+ Table: 'Table',
+ HTML: 'HTML',
+ Paragraph: 'Paragraph',
+ Heading_1: 'Heading 1',
+ Heading_2: 'Heading 2',
+ Heading_3: 'Heading 3',
+ Heading_4: 'Heading 4',
+ Heading_5: 'Heading 5',
+ Heading_6: 'Heading 6',
+ Preformatted: 'Preformatted',
+ Blockquote: 'Blockquote',
+ Table_Header: 'Table Header',
+ URL: 'URL',
+ Title: 'Title',
+ Relationship: 'Relationship',
+ Alternative_Text: 'Alternative text',
+ Caption: 'Caption',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Number of rows',
+ Number_Of_Cols: 'Number of cols',
+ Submit: 'Submit',
+ Cancel: 'Cancel',
+ Choose: 'Choose',
+ Preview: 'Preview',
+ Paste_From_Word: 'Paste from Word',
+ Tools: 'Tools',
+ Containers: 'Containers',
+ Classes: 'Classes',
+ Status: 'Status',
+ Source_Code: 'Source code'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/es.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/es.js.svn-base
new file mode 100644
index 0000000..cdb03c1
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/es.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['es'] = {
+ Strong: 'Resaltar',
+ Emphasis: 'Enfatizar',
+ Superscript: 'Superindice',
+ Subscript: 'Subindice',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista sin ordenar',
+ Indent: 'Indentado',
+ Outdent: 'Sin indentar',
+ Undo: 'Deshacer',
+ Redo: 'Rehacer',
+ Link: 'Enlazar',
+ Unlink: 'Eliminar enlace',
+ Image: 'Imagen',
+ Table: 'Tabla',
+ HTML: 'HTML',
+ Paragraph: 'Párrafo',
+ Heading_1: 'Cabecera 1',
+ Heading_2: 'Cabecera 2',
+ Heading_3: 'Cabecera 3',
+ Heading_4: 'Cabecera 4',
+ Heading_5: 'Cabecera 5',
+ Heading_6: 'Cabecera 6',
+ Preformatted: 'Preformateado',
+ Blockquote: 'Cita',
+ Table_Header: 'Cabecera de la tabla',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Leyenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de filas',
+ Number_Of_Cols: 'Número de columnas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Seleccionar',
+ Preview: 'Vista previa',
+ Paste_From_Word: 'Pegar desde Word',
+ Tools: 'Herramientas',
+ Containers: 'Contenedores',
+ Classes: 'Clases',
+ Status: 'Estado',
+ Source_Code: 'Código fuente'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/fa.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/fa.js.svn-base
new file mode 100644
index 0000000..9d70fcb
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/fa.js.svn-base
@@ -0,0 +1,46 @@
+//Translation To Persian: Ghassem Tofighi (http://ght.ir)
+WYMeditor.STRINGS['fa'] = {
+ Strong: 'پررنگ',//Strong
+ Emphasis: 'ایتالیک',//Emphasis
+ Superscript: 'بالانويس‌ ',//Superscript
+ Subscript: 'زيرنويس‌',//Subscript
+ Ordered_List: 'لیست مرتب',//Ordered List
+ Unordered_List: 'لیست نامرتب',//Unordered List
+ Indent: 'افزودن دندانه',//Indent
+ Outdent: 'کاهش دندانه',//Outdent
+ Undo: 'واگردانی',//Undo
+ Redo: 'تکرار',//Redo
+ Link: 'ساختن پیوند',//Link
+ Unlink: 'برداشتن پیوند',//Unlink
+ Image: 'تصویر',//Image
+ Table: 'جدول',//Table
+ HTML: 'HTML',//HTML
+ Paragraph: 'پاراگراف',//Paragraph
+ Heading_1: 'سرتیتر ۱',//Heading 1
+ Heading_2: 'سرتیتر ۲',//Heading 2
+ Heading_3: 'سرتیتر ۳',//Heading 3
+ Heading_4: 'سرتیتر ۴',//Heading 4
+ Heading_5: 'سرتیتر ۵',//Heading 5
+ Heading_6: 'سرتیتر ۶',//Heading 6
+ Preformatted: 'قالب آماده',//Preformatted
+ Blockquote: 'نقل قول',//Blockquote
+ Table_Header: 'سرجدول',//Table Header
+ URL: 'آدرس اینترنتی',//URL
+ Title: 'عنوان',//Title
+ Alternative_Text: 'متن جایگزین',//Alternative text
+ Caption: 'عنوان',//Caption
+ Summary: 'Summary',
+ Number_Of_Rows: 'تعداد سطرها',//Number of rows
+ Number_Of_Cols: 'تعداد ستون‌ها',//Number of cols
+ Submit: 'فرستادن',//Submit
+ Cancel: 'لغو',//Cancel
+ Choose: 'انتخاب',//Choose
+ Preview: 'پیش‌نمایش',//Preview
+ Paste_From_Word: 'انتقال از ورد',//Paste from Word
+ Tools: 'ابزار',//Tools
+ Containers: '‌قالب‌ها',//Containers
+ Classes: 'کلاس‌ها',//Classes
+ Status: 'وضعیت',//Status
+ Source_Code: 'کد مبدأ'//Source code
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/fi.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/fi.js.svn-base
new file mode 100644
index 0000000..fe1eab4
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/fi.js.svn-base
@@ -0,0 +1,44 @@
+WYMeditor.STRINGS['fi'] = {
+ Strong: 'Lihavoitu',
+ Emphasis: 'Korostus',
+ Superscript: 'Yläindeksi',
+ Subscript: 'Alaindeksi',
+ Ordered_List: 'Numeroitu lista',
+ Unordered_List: 'Luettelomerkit',
+ Indent: 'Suurenna sisennystä',
+ Outdent: 'Pienennä sisennystä',
+ Undo: 'Kumoa',
+ Redo: 'Toista',
+ Link: 'Linkitä',
+ Unlink: 'Poista linkitys',
+ Image: 'Kuva',
+ Table: 'Taulukko',
+ HTML: 'HTML',
+ Paragraph: 'Kappale',
+ Heading_1: 'Otsikko 1',
+ Heading_2: 'Otsikko 2',
+ Heading_3: 'Otsikko 3',
+ Heading_4: 'Otsikko 4',
+ Heading_5: 'Otsikko 5',
+ Heading_6: 'Otsikko 6',
+ Preformatted: 'Esimuotoilu',
+ Blockquote: 'Sitaatti',
+ Table_Header: 'Taulukon otsikko',
+ URL: 'URL',
+ Title: 'Otsikko',
+ Alternative_Text: 'Vaihtoehtoinen teksti',
+ Caption: 'Kuvateksti',
+ Summary: 'Yhteenveto',
+ Number_Of_Rows: 'Rivien määrä',
+ Number_Of_Cols: 'Palstojen määrä',
+ Submit: 'Lähetä',
+ Cancel: 'Peruuta',
+ Choose: 'Valitse',
+ Preview: 'Esikatsele',
+ Paste_From_Word: 'Tuo Wordista',
+ Tools: 'Työkalut',
+ Containers: 'Muotoilut',
+ Classes: 'Luokat',
+ Status: 'Tila',
+ Source_Code: 'Lähdekoodi'
+};
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/fr.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/fr.js.svn-base
new file mode 100644
index 0000000..9b6deb9
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/fr.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['fr'] = {
+ Strong: 'Mise en évidence',
+ Emphasis: 'Emphase',
+ Superscript: 'Exposant',
+ Subscript: 'Indice',
+ Ordered_List: 'Liste Ordonnée',
+ Unordered_List: 'Liste Non-Ordonnée',
+ Indent: 'Imbriqué',
+ Outdent: 'Non-imbriqué',
+ Undo: 'Annuler',
+ Redo: 'Rétablir',
+ Link: 'Lien',
+ Unlink: 'Supprimer le Lien',
+ Image: 'Image',
+ Table: 'Tableau',
+ HTML: 'HTML',
+ Paragraph: 'Paragraphe',
+ Heading_1: 'Titre 1',
+ Heading_2: 'Titre 2',
+ Heading_3: 'Titre 3',
+ Heading_4: 'Titre 4',
+ Heading_5: 'Titre 5',
+ Heading_6: 'Titre 6',
+ Preformatted: 'Pré-formatté',
+ Blockquote: 'Citation',
+ Table_Header: 'Cellule de titre',
+ URL: 'URL',
+ Title: 'Titre',
+ Alternative_Text: 'Texte alternatif',
+ Caption: 'Légende',
+ Summary: 'Résumé',
+ Number_Of_Rows: 'Nombre de lignes',
+ Number_Of_Cols: 'Nombre de colonnes',
+ Submit: 'Envoyer',
+ Cancel: 'Annuler',
+ Choose: 'Choisir',
+ Preview: 'Prévisualisation',
+ Paste_From_Word: 'Copier depuis Word',
+ Tools: 'Outils',
+ Containers: 'Type de texte',
+ Classes: 'Type de contenu',
+ Status: 'Infos',
+ Source_Code: 'Code source'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/gl.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/gl.js.svn-base
new file mode 100644
index 0000000..d4786b8
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/gl.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['gl'] = {
+ Strong: 'Moita énfase',
+ Emphasis: 'Énfase',
+ Superscript: 'Superíndice',
+ Subscript: 'Subíndice',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista sen ordenar',
+ Indent: 'Aniñar',
+ Outdent: 'Desaniñar',
+ Undo: 'Desfacer',
+ Redo: 'Refacer',
+ Link: 'Ligazón',
+ Unlink: 'Desligar',
+ Image: 'Imaxe',
+ Table: 'Táboa',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Preformatado',
+ Blockquote: 'Cita en parágrafo',
+ Table_Header: 'Cabeceira da táboa',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Título',
+ Summary: 'Resumo',
+ Number_Of_Rows: 'Número de filas',
+ Number_Of_Cols: 'Número de columnas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Escoller',
+ Preview: 'Previsualizar',
+ Paste_From_Word: 'Colar dende Word',
+ Tools: 'Ferramentas',
+ Containers: 'Contenedores',
+ Classes: 'Clases',
+ Status: 'Estado',
+ Source_Code: 'Código fonte'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/he.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/he.js.svn-base
new file mode 100644
index 0000000..97c9675
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/he.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['he'] = {
+ Strong: 'חזק',
+ Emphasis: 'מובלט',
+ Superscript: 'כתב עילי',
+ Subscript: 'כתב תחתי',
+ Ordered_List: 'רשימה ממוספרת',
+ Unordered_List: 'רשימה לא ממוספרת',
+ Indent: 'הזחה פנימה',
+ Outdent: 'הזחה החוצה',
+ Undo: 'בטל פעולה',
+ Redo: 'בצע מחדש פעולה',
+ Link: 'קישור',
+ Unlink: 'בטל קישור',
+ Image: 'תמונה',
+ Table: 'טבלה',
+ HTML: 'קוד HTML',
+ Paragraph: 'פסקה',
+ Heading_1: 'כותרת 1 ; תג &lt;h1&gt;',
+ Heading_2: 'כותרת 2 ; תג &lt;h2&gt;',
+ Heading_3: 'כותרת 3 ; תג &lt;h3&gt;',
+ Heading_4: 'כותרת 4 ; תג &lt;h4&gt;',
+ Heading_5: 'כותרת 5 ; תג &lt;h5&gt;',
+ Heading_6: 'כותרת 6 ; תג &lt;h6&gt;',
+ Preformatted: 'משמר רווחים',
+ Blockquote: 'ציטוט',
+ Table_Header: 'כותרת טבלה',
+ URL: 'קישור (URL)',
+ Title: 'כותרת',
+ Alternative_Text: 'טקסט חלופי',
+ Caption: 'כותרת',
+ Summary: 'סיכום',
+ Number_Of_Rows: 'מספר שורות',
+ Number_Of_Cols: 'מספר טורים',
+ Submit: 'שלח',
+ Cancel: 'בטל',
+ Choose: 'בחר',
+ Preview: 'תצוגה מקדימה',
+ Paste_From_Word: 'העתק מ-Word',
+ Tools: 'כלים',
+ Containers: 'מיכלים',
+ Classes: 'מחלקות',
+ Status: 'מצב',
+ Source_Code: 'קוד מקור'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/hr.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/hr.js.svn-base
new file mode 100644
index 0000000..193e31a
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/hr.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['hr'] = {
+ Strong: 'Podebljano',
+ Emphasis: 'Naglašeno',
+ Superscript: 'Iznad',
+ Subscript: 'Ispod',
+ Ordered_List: 'Pobrojana lista',
+ Unordered_List: 'Nepobrojana lista',
+ Indent: 'Uvuci',
+ Outdent: 'Izvuci',
+ Undo: 'Poništi promjenu',
+ Redo: 'Ponovno promjeni',
+ Link: 'Hiperveza',
+ Unlink: 'Ukloni hipervezu',
+ Image: 'Slika',
+ Table: 'Tablica',
+ HTML: 'HTML',
+ Paragraph: 'Paragraf',
+ Heading_1: 'Naslov 1',
+ Heading_2: 'Naslov 2',
+ Heading_3: 'Naslov 3',
+ Heading_4: 'Naslov 4',
+ Heading_5: 'Naslov 5',
+ Heading_6: 'Naslov 6',
+ Preformatted: 'Unaprijed formatirano',
+ Blockquote: 'Citat',
+ Table_Header: 'Zaglavlje tablice',
+ URL: 'URL',
+ Title: 'Naslov',
+ Alternative_Text: 'Alternativni tekst',
+ Caption: 'Zaglavlje',
+ Summary: 'Sažetak',
+ Number_Of_Rows: 'Broj redova',
+ Number_Of_Cols: 'Broj kolona',
+ Submit: 'Snimi',
+ Cancel: 'Odustani',
+ Choose: 'Izaberi',
+ Preview: 'Pregled',
+ Paste_From_Word: 'Zalijepi iz Word-a',
+ Tools: 'Alati',
+ Containers: 'Kontejneri',
+ Classes: 'Klase',
+ Status: 'Status',
+ Source_Code: 'Izvorni kod'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/hu.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/hu.js.svn-base
new file mode 100644
index 0000000..a8cdbc6
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/hu.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['hu'] = {
+ Strong: 'Félkövér',
+ Emphasis: 'Kiemelt',
+ Superscript: 'Felső index',
+ Subscript: 'Alsó index',
+ Ordered_List: 'Rendezett lista',
+ Unordered_List: 'Rendezetlen lista',
+ Indent: 'Bekezdés',
+ Outdent: 'Bekezdés törlése',
+ Undo: 'Visszavon',
+ Redo: 'Visszaállít',
+ Link: 'Link',
+ Unlink: 'Link törlése',
+ Image: 'Kép',
+ Table: 'Tábla',
+ HTML: 'HTML',
+ Paragraph: 'Bekezdés',
+ Heading_1: 'Címsor 1',
+ Heading_2: 'Címsor 2',
+ Heading_3: 'Címsor 3',
+ Heading_4: 'Címsor 4',
+ Heading_5: 'Címsor 5',
+ Heading_6: 'Címsor 6',
+ Preformatted: 'Előformázott',
+ Blockquote: 'Idézet',
+ Table_Header: 'Tábla Fejléc',
+ URL: 'Webcím',
+ Title: 'Megnevezés',
+ Alternative_Text: 'Alternatív szöveg',
+ Caption: 'Fejléc',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Sorok száma',
+ Number_Of_Cols: 'Oszlopok száma',
+ Submit: 'Elküld',
+ Cancel: 'Mégsem',
+ Choose: 'Választ',
+ Preview: 'Előnézet',
+ Paste_From_Word: 'Másolás Word-ból',
+ Tools: 'Eszközök',
+ Containers: 'Tartalmak',
+ Classes: 'Osztályok',
+ Status: 'Állapot',
+ Source_Code: 'Forráskód'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/it.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/it.js.svn-base
new file mode 100644
index 0000000..ca632a9
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/it.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['it'] = {
+ Strong: 'Grassetto',
+ Emphasis: 'Corsetto',
+ Superscript: 'Apice',
+ Subscript: 'Pedice',
+ Ordered_List: 'Lista Ordinata',
+ Unordered_List: 'Lista Puntata',
+ Indent: 'Indenta',
+ Outdent: 'Caccia',
+ Undo: 'Indietro',
+ Redo: 'Avanti',
+ Link: 'Inserisci Link',
+ Unlink: 'Togli Link',
+ Image: 'Inserisci Immagine',
+ Table: 'Inserisci Tabella',
+ HTML: 'HTML',
+ Paragraph: 'Paragrafo',
+ Heading_1: 'Heading 1',
+ Heading_2: 'Heading 2',
+ Heading_3: 'Heading 3',
+ Heading_4: 'Heading 4',
+ Heading_5: 'Heading 5',
+ Heading_6: 'Heading 6',
+ Preformatted: 'Preformattato',
+ Blockquote: 'Blockquote',
+ Table_Header: 'Header Tabella',
+ URL: 'Indirizzo',
+ Title: 'Titolo',
+ Alternative_Text: 'Testo Alternativo',
+ Caption: 'Caption',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Numero di Righe',
+ Number_Of_Cols: 'Numero di Colonne',
+ Submit: 'Invia',
+ Cancel: 'Cancella',
+ Choose: 'Scegli',
+ Preview: 'Anteprima',
+ Paste_From_Word: 'Incolla',
+ Tools: 'Tools',
+ Containers: 'Contenitori',
+ Classes: 'Classi',
+ Status: 'Stato',
+ Source_Code: 'Codice Sorgente'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/ja.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/ja.js.svn-base
new file mode 100644
index 0000000..c9b6410
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/ja.js.svn-base
@@ -0,0 +1,44 @@
+WYMeditor.STRINGS['ja'] = {
+ Strong: '強調<strong>',
+ Emphasis: '強調<em>',
+ Superscript: '上付き',
+ Subscript: '下付き',
+ Ordered_List: '番号付きリスト',
+ Unordered_List: '番号無リスト',
+ Indent: 'インデントを増やす',
+ Outdent: 'インデントを減らす',
+ Undo: '元に戻す',
+ Redo: 'やり直す',
+ Link: 'リンク',
+ Unlink: 'リンク取消',
+ Image: '画像',
+ Table: 'テーブル',
+ HTML: 'HTML',
+ Paragraph: '段落',
+ Heading_1: '見出し 1',
+ Heading_2: '見出し 2',
+ Heading_3: '見出し 3',
+ Heading_4: '見出し 4',
+ Heading_5: '見出し 5',
+ Heading_6: '見出し 6',
+ Preformatted: '整形済みテキスト',
+ Blockquote: '引用文',
+ Table_Header: '表見出し',
+ URL: 'URL',
+ Title: 'タイトル',
+ Alternative_Text: '代替テキスト',
+ Caption: 'キャプション',
+ Summary: 'サマリー',
+ Number_Of_Rows: '行数',
+ Number_Of_Cols: '列数',
+ Submit: '送信',
+ Cancel: 'キャンセル',
+ Choose: '選択',
+ Preview: 'プレビュー',
+ Paste_From_Word: '貼り付け',
+ Tools: 'ツール',
+ Containers: 'コンテナ',
+ Classes: 'クラス',
+ Status: 'ステータス',
+ Source_Code: 'ソースコード'
+};
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/nb.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/nb.js.svn-base
new file mode 100644
index 0000000..7573b78
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/nb.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nb'] = {
+ Strong: 'Fet',
+ Emphasis: 'Uthevet',
+ Superscript: 'Opphøyet',
+ Subscript: 'Nedsenket',
+ Ordered_List: 'Nummerert liste',
+ Unordered_List: 'Punktliste',
+ Indent: 'Rykk inn',
+ Outdent: 'Rykk ut',
+ Undo: 'Angre',
+ Redo: 'Gjenta',
+ Link: 'Lenke',
+ Unlink: 'Ta bort lenken',
+ Image: 'Bilde',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Avsnitt',
+ Heading_1: 'Overskrift 1',
+ Heading_2: 'Overskrift 2',
+ Heading_3: 'Overskrift 3',
+ Heading_4: 'Overskrift 4',
+ Heading_5: 'Overskrift 5',
+ Heading_6: 'Overskrift 6',
+ Preformatted: 'Preformatert',
+ Blockquote: 'Sitat',
+ Table_Header: 'Tabelloverskrift',
+ URL: 'URL',
+ Title: 'Tittel',
+ Alternative_Text: 'Alternativ tekst',
+ Caption: 'Overskrift',
+ Summary: 'Sammendrag',
+ Number_Of_Rows: 'Antall rader',
+ Number_Of_Cols: 'Antall kolonner',
+ Submit: 'Ok',
+ Cancel: 'Avbryt',
+ Choose: 'Velg',
+ Preview: 'Forhåndsvis',
+ Paste_From_Word: 'Lim inn fra Word',
+ Tools: 'Verktøy',
+ Containers: 'Formatering',
+ Classes: 'Klasser',
+ Status: 'Status',
+ Source_Code: 'Kildekode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/nl.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/nl.js.svn-base
new file mode 100644
index 0000000..cdfa21c
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/nl.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nl'] = {
+ Strong: 'Sterk benadrukken',
+ Emphasis: 'Benadrukken',
+ Superscript: 'Bovenschrift',
+ Subscript: 'Onderschrift',
+ Ordered_List: 'Geordende lijst',
+ Unordered_List: 'Ongeordende lijst',
+ Indent: 'Inspringen',
+ Outdent: 'Terugspringen',
+ Undo: 'Ongedaan maken',
+ Redo: 'Opnieuw uitvoeren',
+ Link: 'Linken',
+ Unlink: 'Ontlinken',
+ Image: 'Afbeelding',
+ Table: 'Tabel',
+ HTML: 'HTML',
+ Paragraph: 'Paragraaf',
+ Heading_1: 'Kop 1',
+ Heading_2: 'Kop 2',
+ Heading_3: 'Kop 3',
+ Heading_4: 'Kop 4',
+ Heading_5: 'Kop 5',
+ Heading_6: 'Kop 6',
+ Preformatted: 'Voorgeformatteerd',
+ Blockquote: 'Citaat',
+ Table_Header: 'Tabel-kop',
+ URL: 'URL',
+ Title: 'Titel',
+ Alternative_Text: 'Alternatieve tekst',
+ Caption: 'Bijschrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Aantal rijen',
+ Number_Of_Cols: 'Aantal kolommen',
+ Submit: 'Versturen',
+ Cancel: 'Annuleren',
+ Choose: 'Kiezen',
+ Preview: 'Voorbeeld bekijken',
+ Paste_From_Word: 'Plakken uit Word',
+ Tools: 'Hulpmiddelen',
+ Containers: 'Teksttypes',
+ Classes: 'Klassen',
+ Status: 'Status',
+ Source_Code: 'Broncode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/nn.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/nn.js.svn-base
new file mode 100644
index 0000000..51cec2b
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/nn.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nn'] = {
+ Strong: 'Feit',
+ Emphasis: 'Utheva',
+ Superscript: 'Opphøgd',
+ Subscript: 'Nedsenka',
+ Ordered_List: 'Nummerert liste',
+ Unordered_List: 'Punktliste',
+ Indent: 'Rykk inn',
+ Outdent: 'Rykk ut',
+ Undo: 'Angre',
+ Redo: 'Gjentaka',
+ Link: 'Lenkje',
+ Unlink: 'Ta bort lenkja',
+ Image: 'Bilete',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Avsnitt',
+ Heading_1: 'Overskrift 1',
+ Heading_2: 'Overskrift 2',
+ Heading_3: 'Overskrift 3',
+ Heading_4: 'Overskrift 4',
+ Heading_5: 'Overskrift 5',
+ Heading_6: 'Overskrift 6',
+ Preformatted: 'Preformatert',
+ Blockquote: 'Sitat',
+ Table_Header: 'Tabelloverskrift',
+ URL: 'URL',
+ Title: 'Tittel',
+ Alternative_Text: 'Alternativ tekst',
+ Caption: 'Overskrift',
+ Summary: 'Samandrag',
+ Number_Of_Rows: 'Tal på rader',
+ Number_Of_Cols: 'Tal på kolonnar',
+ Submit: 'Ok',
+ Cancel: 'Avbryt',
+ Choose: 'Vel',
+ Preview: 'Førehandsvis',
+ Paste_From_Word: 'Lim inn frå Word',
+ Tools: 'Verkty',
+ Containers: 'Formatering',
+ Classes: 'Klassar',
+ Status: 'Status',
+ Source_Code: 'Kjeldekode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/pl.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/pl.js.svn-base
new file mode 100644
index 0000000..d6c0471
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/pl.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pl'] = {
+ Strong: 'Nacisk',
+ Emphasis: 'Emfaza',
+ Superscript: 'Indeks górny',
+ Subscript: 'Indeks dolny',
+ Ordered_List: 'Lista numerowana',
+ Unordered_List: 'Lista wypunktowana',
+ Indent: 'Zwiększ wcięcie',
+ Outdent: 'Zmniejsz wcięcie',
+ Undo: 'Cofnij',
+ Redo: 'Ponów',
+ Link: 'Wstaw link',
+ Unlink: 'Usuń link',
+ Image: 'Obraz',
+ Table: 'Tabela',
+ HTML: 'Źródło HTML',
+ Paragraph: 'Akapit',
+ Heading_1: 'Nagłówek 1',
+ Heading_2: 'Nagłówek 2',
+ Heading_3: 'Nagłówek 3',
+ Heading_4: 'Nagłówek 4',
+ Heading_5: 'Nagłówek 5',
+ Heading_6: 'Nagłówek 6',
+ Preformatted: 'Preformatowany',
+ Blockquote: 'Cytat blokowy',
+ Table_Header: 'Nagłówek tabeli',
+ URL: 'URL',
+ Title: 'Tytuł',
+ Alternative_Text: 'Tekst alternatywny',
+ Caption: 'Tytuł tabeli',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Liczba wierszy',
+ Number_Of_Cols: 'Liczba kolumn',
+ Submit: 'Wyślij',
+ Cancel: 'Anuluj',
+ Choose: 'Wybierz',
+ Preview: 'Podgląd',
+ Paste_From_Word: 'Wklej z Worda',
+ Tools: 'Narzędzia',
+ Containers: 'Format',
+ Classes: 'Styl',
+ Status: 'Status',
+ Source_Code: 'Kod źródłowy'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/pt-br.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/pt-br.js.svn-base
new file mode 100644
index 0000000..2ec18fe
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/pt-br.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pt-br'] = {
+ Strong: 'Resaltar',
+ Emphasis: 'Enfatizar',
+ Superscript: 'Sobre escrito',
+ Subscript: 'Sub escrito ',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista desordenada',
+ Indent: 'Indentado',
+ Outdent: 'Desidentar',
+ Undo: 'Desfazer',
+ Redo: 'Refazer',
+ Link: 'Link',
+ Unlink: 'Remover Link',
+ Image: 'Imagem',
+ Table: 'Tabela',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Preformatado',
+ Blockquote: 'Citação',
+ Table_Header: 'Título de tabela',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Legenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de linhas',
+ Number_Of_Cols: 'Número de colunas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Selecionar',
+ Preview: 'Previsualizar',
+ Paste_From_Word: 'Copiar do Word',
+ Tools: 'Ferramentas',
+ Containers: 'Conteneiners',
+ Classes: 'Classes',
+ Status: 'Estado',
+ Source_Code: 'Código fonte'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/pt.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/pt.js.svn-base
new file mode 100644
index 0000000..a3d1a17
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/pt.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pt'] = {
+ Strong: 'Negrito',
+ Emphasis: 'Itálico',
+ Superscript: 'Sobrescrito',
+ Subscript: 'Subsescrito',
+ Ordered_List: 'Lista Numerada',
+ Unordered_List: 'Lista Marcada',
+ Indent: 'Aumentar Indentaçã',
+ Outdent: 'Diminuir Indentaçã',
+ Undo: 'Desfazer',
+ Redo: 'Restaurar',
+ Link: 'Link',
+ Unlink: 'Tirar link',
+ Image: 'Imagem',
+ Table: 'Tabela',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Pré-formatado',
+ Blockquote: 'Citação',
+ Table_Header: 'Cabeçalho Tabela',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto Alterativo',
+ Caption: 'Título Tabela',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de Linhas',
+ Number_Of_Cols: 'Número de Colunas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Escolha',
+ Preview: 'Prever',
+ Paste_From_Word: 'Colar do Word',
+ Tools: 'Ferramentas',
+ Containers: 'Containers',
+ Classes: 'Classes',
+ Status: 'Status',
+ Source_Code: 'Código Fonte'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/ru.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/ru.js.svn-base
new file mode 100644
index 0000000..7895f8d
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/ru.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['ru'] = {
+ Strong: 'Жирный',
+ Emphasis: 'Наклонный',
+ Superscript: 'Надстрочный',
+ Subscript: 'Подстрочный',
+ Ordered_List: 'Нумерованый список',
+ Unordered_List: 'Ненумерованый список',
+ Indent: 'Увеличить отступ',
+ Outdent: 'Уменьшить отступ',
+ Undo: 'Отменить',
+ Redo: 'Повторить',
+ Link: 'Ссылка',
+ Unlink: 'Удалить ссылку',
+ Image: 'Изображение',
+ Table: 'Таблица',
+ HTML: 'Править HTML',
+ Paragraph: 'Параграф',
+ Heading_1: 'Заголовок 1',
+ Heading_2: 'Заголовок 2',
+ Heading_3: 'Заголовок 3',
+ Heading_4: 'Заголовок 4',
+ Heading_5: 'Заголовок 5',
+ Heading_6: 'Заголовок 6',
+ Preformatted: 'Preformatted',
+ Blockquote: 'Цитата',
+ Table_Header: 'Заголовок таблицы',
+ URL: 'URL',
+ Title: 'Заголовок',
+ Alternative_Text: 'Альтернативный текст',
+ Caption: 'Надпись',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Кол-во строк',
+ Number_Of_Cols: 'Кол-во столбцов',
+ Submit: 'Отправить',
+ Cancel: 'Отмена',
+ Choose: 'Выбор',
+ Preview: 'Просмотр',
+ Paste_From_Word: 'Вставить из Word',
+ Tools: 'Инструменты',
+ Containers: 'Контейнеры',
+ Classes: 'Классы',
+ Status: 'Статус',
+ Source_Code: 'Исходный код'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/sv.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/sv.js.svn-base
new file mode 100644
index 0000000..6347956
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/sv.js.svn-base
@@ -0,0 +1,46 @@
+WYMeditor.STRINGS['sv'] = {
+ Strong: 'Viktigt',
+ Emphasis: 'Betoning',
+ Superscript: 'Upphöjt',
+ Subscript: 'Nedsänkt',
+ Ordered_List: 'Nummerlista',
+ Unordered_List: 'Punktlista',
+ Indent: 'Indrag',
+ Outdent: 'Utdrag',
+ Undo: 'Ångra',
+ Redo: 'Gör om',
+ Link: 'Länk',
+ Unlink: 'Ta bort länk',
+ Image: 'Bild',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Paragraf',
+ Heading_1: 'Rubrik 1',
+ Heading_2: 'Rubrik 2',
+ Heading_3: 'Rubrik 3',
+ Heading_4: 'Rubrik 4',
+ Heading_5: 'Rubrik 5',
+ Heading_6: 'Rubrik 6',
+ Preformatted: 'Förformaterad',
+ Blockquote: 'Blockcitat',
+ Table_Header: 'Tabellrubrik',
+ URL: 'URL',
+ Title: 'Titel',
+ Relationship: 'Relation',
+ Alternative_Text: 'Alternativ text',
+ Caption: 'Överskrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Antal rader',
+ Number_Of_Cols: 'Antal kolumner',
+ Submit: 'Skicka',
+ Cancel: 'Avbryt',
+ Choose: 'Välj',
+ Preview: 'Förhandsgranska',
+ Paste_From_Word: 'Klistra in från Word',
+ Tools: 'Verktyg',
+ Containers: 'Formatering',
+ Classes: 'Klasser',
+ Status: 'Status',
+ Source_Code: 'Källkod'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/tr.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/tr.js.svn-base
new file mode 100644
index 0000000..d26f0ff
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/tr.js.svn-base
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['tr'] = {
+ Strong: 'Kalın',
+ Emphasis: 'Vurgu',
+ Superscript: 'Superscript',
+ Subscript: 'Subscript',
+ Ordered_List: 'Sıralı List',
+ Unordered_List: 'Sırasız List',
+ Indent: 'İçerlek',
+ Outdent: 'Çıkıntılı',
+ Undo: 'Geri Al',
+ Redo: 'Yinele',
+ Link: 'Bağlantı',
+ Unlink: 'Bağlantıyı Kaldır',
+ Image: 'İmaj',
+ Table: 'Tablo',
+ HTML: 'HTML',
+ Paragraph: 'Parağraf',
+ Heading_1: 'Başlık 1',
+ Heading_2: 'Başlık 2',
+ Heading_3: 'Başlık 3',
+ Heading_4: 'Başlık 4',
+ Heading_5: 'Başlık 5',
+ Heading_6: 'Başlık 6',
+ Preformatted: 'Önceden Formatlı',
+ Blockquote: 'Alıntı',
+ Table_Header: 'Tablo Başlığı',
+ URL: 'URL',
+ Title: 'Başlık',
+ Alternative_Text: 'Alternatif Metin',
+ Caption: 'Etiket',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Satır sayısı',
+ Number_Of_Cols: 'Sütun sayısı',
+ Submit: 'Gönder',
+ Cancel: 'İptal',
+ Choose: 'Seç',
+ Preview: 'Önizleme',
+ Paste_From_Word: 'Wordden yapıştır',
+ Tools: 'Araçlar',
+ Containers: 'Kapsayıcılar',
+ Classes: 'Sınıflar',
+ Status: 'Durum',
+ Source_Code: 'Kaynak Kodu'
+};
+
diff --git a/studio/static/js/wymeditor/lang/.svn/text-base/zh_cn.js.svn-base b/studio/static/js/wymeditor/lang/.svn/text-base/zh_cn.js.svn-base
new file mode 100644
index 0000000..72f5aaf
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/.svn/text-base/zh_cn.js.svn-base
@@ -0,0 +1,47 @@
+WYMeditor.STRINGS['zh_cn'] = {
+ Strong: '加粗',
+ Emphasis: '斜体',
+ Superscript: '上标',
+ Subscript: '下标',
+ Ordered_List: '有序列表',
+ Unordered_List: '无序列表',
+ Indent: '增加缩进',
+ Outdent: '减少缩进',
+ Undo: '撤消',
+ Redo: '重做',
+ Link: '链接',
+ Unlink: '取消链接',
+ Image: '图片',
+ Table: '表格',
+ HTML: 'HTML源代码',
+ Paragraph: '段落',
+ Heading_1: '标题 1',
+ Heading_2: '标题 2',
+ Heading_3: '标题 3',
+ Heading_4: '标题 4',
+ Heading_5: '标题 5',
+ Heading_6: '标题 6',
+ Preformatted: '原始文本',
+ Blockquote: '引语',
+ Table_Header: '表头',
+ URL: '地址',
+ Title: '提示文字',
+ Alternative_Text: '失效文字',
+ Caption: '标题',
+ Summary: 'Summary',
+ Number_Of_Rows: '行数',
+ Number_Of_Cols: '列数',
+ Submit: '提交',
+ Cancel: '放弃',
+ Choose: '选择',
+ Preview: '预览',
+ Paste_From_Word: '从Word粘贴纯文本',
+ Tools: '工具',
+ Containers: '容器',
+ Classes: '预定义样式',
+ Status: '状态',
+ Source_Code: '源代码',
+ Attachment: '附件',
+ NewParagraph: '新段落'
+};
+
diff --git a/studio/static/js/wymeditor/lang/bg.js b/studio/static/js/wymeditor/lang/bg.js
new file mode 100644
index 0000000..576bca5
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/bg.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['bg'] = {
+ Strong: 'Получер',
+ Emphasis: 'Курсив',
+ Superscript: 'Горен индекс',
+ Subscript: 'Долен индекс',
+ Ordered_List: 'Подреден списък',
+ Unordered_List: 'Неподреден списък',
+ Indent: 'Блок навътре',
+ Outdent: 'Блок навън',
+ Undo: 'Стъпка назад',
+ Redo: 'Стъпка напред',
+ Link: 'Създай хипервръзка',
+ Unlink: 'Премахни хипервръзката',
+ Image: 'Изображение',
+ Table: 'Таблица',
+ HTML: 'HTML',
+ Paragraph: 'Абзац',
+ Heading_1: 'Заглавие 1',
+ Heading_2: 'Заглавие 2',
+ Heading_3: 'Заглавие 3',
+ Heading_4: 'Заглавие 4',
+ Heading_5: 'Заглавие 5',
+ Heading_6: 'Заглавие 6',
+ Preformatted: 'Преформатиран',
+ Blockquote: 'Цитат',
+ Table_Header: 'Заглавие на таблицата',
+ URL: 'URL',
+ Title: 'Заглавие',
+ Alternative_Text: 'Алтернативен текст',
+ Caption: 'Етикет',
+ Summary: 'Общо',
+ Number_Of_Rows: 'Брой редове',
+ Number_Of_Cols: 'Брой колони',
+ Submit: 'Изпрати',
+ Cancel: 'Отмени',
+ Choose: 'Затвори',
+ Preview: 'Предварителен преглед',
+ Paste_From_Word: 'Вмъкни от MS WORD',
+ Tools: 'Инструменти',
+ Containers: 'Контейнери',
+ Classes: 'Класове',
+ Status: 'Статус',
+ Source_Code: 'Източник, код'
+};
+
diff --git a/studio/static/js/wymeditor/lang/ca.js b/studio/static/js/wymeditor/lang/ca.js
new file mode 100644
index 0000000..c342406
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/ca.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['ca'] = {
+ Strong: 'Ressaltar',
+ Emphasis: 'Emfatitzar',
+ Superscript: 'Superindex',
+ Subscript: 'Subindex',
+ Ordered_List: 'Llistat ordenat',
+ Unordered_List: 'Llistat sense ordenar',
+ Indent: 'Indentat',
+ Outdent: 'Sense indentar',
+ Undo: 'Desfer',
+ Redo: 'Refer',
+ Link: 'Enllaçar',
+ Unlink: 'Eliminar enllaç',
+ Image: 'Imatge',
+ Table: 'Taula',
+ HTML: 'HTML',
+ Paragraph: 'Paràgraf',
+ Heading_1: 'Capçalera 1',
+ Heading_2: 'Capçalera 2',
+ Heading_3: 'Capçalera 3',
+ Heading_4: 'Capçalera 4',
+ Heading_5: 'Capçalera 5',
+ Heading_6: 'Capçalera 6',
+ Preformatted: 'Pre-formatejat',
+ Blockquote: 'Cita',
+ Table_Header: 'Capçalera de la taula',
+ URL: 'URL',
+ Title: 'Títol',
+ Alternative_Text: 'Text alternatiu',
+ Caption: 'Llegenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Nombre de files',
+ Number_Of_Cols: 'Nombre de columnes',
+ Submit: 'Enviar',
+ Cancel: 'Cancel·lar',
+ Choose: 'Triar',
+ Preview: 'Vista prèvia',
+ Paste_From_Word: 'Pegar des de Word',
+ Tools: 'Eines',
+ Containers: 'Contenidors',
+ Classes: 'Classes',
+ Status: 'Estat',
+ Source_Code: 'Codi font'
+};
+
diff --git a/studio/static/js/wymeditor/lang/cs.js b/studio/static/js/wymeditor/lang/cs.js
new file mode 100644
index 0000000..3939d71
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/cs.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['cs'] = {
+ Strong: 'Tučné',
+ Emphasis: 'Kurzíva',
+ Superscript: 'Horní index',
+ Subscript: 'Dolní index',
+ Ordered_List: 'Číslovaný seznam',
+ Unordered_List: 'Nečíslovaný seznam',
+ Indent: 'Zvětšit odsazení',
+ Outdent: 'Zmenšit odsazení',
+ Undo: 'Zpět',
+ Redo: 'Znovu',
+ Link: 'Vytvořit odkaz',
+ Unlink: 'Zrušit odkaz',
+ Image: 'Obrázek',
+ Table: 'Tabulka',
+ HTML: 'HTML',
+ Paragraph: 'Odstavec',
+ Heading_1: 'Nadpis 1. úrovně',
+ Heading_2: 'Nadpis 2. úrovně',
+ Heading_3: 'Nadpis 3. úrovně',
+ Heading_4: 'Nadpis 4. úrovně',
+ Heading_5: 'Nadpis 5. úrovně',
+ Heading_6: 'Nadpis 6. úrovně',
+ Preformatted: 'Předformátovaný text',
+ Blockquote: 'Citace',
+ Table_Header: 'Hlavičková buňka tabulky',
+ URL: 'Adresa',
+ Title: 'Text po najetí myší',
+ Alternative_Text: 'Text pro případ nezobrazení obrázku',
+ Caption: 'Titulek tabulky',
+ Summary: 'Shrnutí obsahu',
+ Number_Of_Rows: 'Počet řádek',
+ Number_Of_Cols: 'Počet sloupců',
+ Submit: 'Vytvořit',
+ Cancel: 'Zrušit',
+ Choose: 'Vybrat',
+ Preview: 'Náhled',
+ Paste_From_Word: 'Vložit z Wordu',
+ Tools: 'Nástroje',
+ Containers: 'Typy obsahu',
+ Classes: 'Třídy',
+ Status: 'Stav',
+ Source_Code: 'Zdrojový kód'
+};
+
diff --git a/studio/static/js/wymeditor/lang/cy.js b/studio/static/js/wymeditor/lang/cy.js
new file mode 100644
index 0000000..7d15b79
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/cy.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['cy'] = {
+ Strong: 'Bras',
+ Emphasis: 'Italig',
+ Superscript: 'Uwchsgript',
+ Subscript: 'Is-sgript',
+ Ordered_List: 'Rhestr mewn Trefn',
+ Unordered_List: 'Pwyntiau Bwled',
+ Indent: 'Mewnoli',
+ Outdent: 'Alloli',
+ Undo: 'Dadwneud',
+ Redo: 'Ailwneud',
+ Link: 'Cysylltu',
+ Unlink: 'Datgysylltu',
+ Image: 'Delwedd',
+ Table: 'Tabl',
+ HTML: 'HTML',
+ Paragraph: 'Paragraff',
+ Heading_1: 'Pennawd 1',
+ Heading_2: 'Pennawd 2',
+ Heading_3: 'Pennawd 3',
+ Heading_4: 'Pennawd 4',
+ Heading_5: 'Pennawd 5',
+ Heading_6: 'Pennawd 6',
+ Preformatted: 'Rhagfformat',
+ Blockquote: 'Bloc Dyfyniad',
+ Table_Header: 'Pennyn Tabl',
+ URL: 'URL',
+ Title: 'Teitl',
+ Alternative_Text: 'Testun Amgen',
+ Caption: 'Pennawd',
+ Summary: 'Crynodeb',
+ Number_Of_Rows: 'Nifer y rhesi',
+ Number_Of_Cols: 'Nifer y colofnau',
+ Submit: 'Anfon',
+ Cancel: 'Diddymu',
+ Choose: 'Dewis',
+ Preview: 'Rhagolwg',
+ Paste_From_Word: 'Gludo o Word',
+ Tools: 'Offer',
+ Containers: 'Cynhwysyddion',
+ Classes: 'Dosbarthiadau',
+ Status: 'Statws',
+ Source_Code: 'Cod ffynhonnell'
+};
+
diff --git a/studio/static/js/wymeditor/lang/de.js b/studio/static/js/wymeditor/lang/de.js
new file mode 100644
index 0000000..a1e01e1
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/de.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['de'] = {
+ Strong: 'Fett',
+ Emphasis: 'Kursiv',
+ Superscript: 'Text hochstellen',
+ Subscript: 'Text tiefstellen',
+ Ordered_List: 'Geordnete Liste einfügen',
+ Unordered_List: 'Ungeordnete Liste einfügen',
+ Indent: 'Einzug erhöhen',
+ Outdent: 'Einzug vermindern',
+ Undo: 'Befehle rückgängig machen',
+ Redo: 'Befehle wiederherstellen',
+ Link: 'Hyperlink einfügen',
+ Unlink: 'Hyperlink entfernen',
+ Image: 'Bild einfügen',
+ Table: 'Tabelle einfügen',
+ HTML: 'HTML anzeigen/verstecken',
+ Paragraph: 'Absatz',
+ Heading_1: 'Überschrift 1',
+ Heading_2: 'Überschrift 2',
+ Heading_3: 'Überschrift 3',
+ Heading_4: 'Überschrift 4',
+ Heading_5: 'Überschrift 5',
+ Heading_6: 'Überschrift 6',
+ Preformatted: 'Vorformatiert',
+ Blockquote: 'Zitat',
+ Table_Header: 'Tabellenüberschrift',
+ URL: 'URL',
+ Title: 'Titel',
+ Alternative_Text: 'Alternativer Text',
+ Caption: 'Tabellenüberschrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Anzahl Zeilen',
+ Number_Of_Cols: 'Anzahl Spalten',
+ Submit: 'Absenden',
+ Cancel: 'Abbrechen',
+ Choose: 'Auswählen',
+ Preview: 'Vorschau',
+ Paste_From_Word: 'Aus Word einfügen',
+ Tools: 'Werkzeuge',
+ Containers: 'Inhaltstyp',
+ Classes: 'Klassen',
+ Status: 'Status',
+ Source_Code: 'Quellcode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/en.js b/studio/static/js/wymeditor/lang/en.js
new file mode 100644
index 0000000..dc7eb21
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/en.js
@@ -0,0 +1,46 @@
+WYMeditor.STRINGS['en'] = {
+ Strong: 'Strong',
+ Emphasis: 'Emphasis',
+ Superscript: 'Superscript',
+ Subscript: 'Subscript',
+ Ordered_List: 'Ordered List',
+ Unordered_List: 'Unordered List',
+ Indent: 'Indent',
+ Outdent: 'Outdent',
+ Undo: 'Undo',
+ Redo: 'Redo',
+ Link: 'Link',
+ Unlink: 'Unlink',
+ Image: 'Image',
+ Table: 'Table',
+ HTML: 'HTML',
+ Paragraph: 'Paragraph',
+ Heading_1: 'Heading 1',
+ Heading_2: 'Heading 2',
+ Heading_3: 'Heading 3',
+ Heading_4: 'Heading 4',
+ Heading_5: 'Heading 5',
+ Heading_6: 'Heading 6',
+ Preformatted: 'Preformatted',
+ Blockquote: 'Blockquote',
+ Table_Header: 'Table Header',
+ URL: 'URL',
+ Title: 'Title',
+ Relationship: 'Relationship',
+ Alternative_Text: 'Alternative text',
+ Caption: 'Caption',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Number of rows',
+ Number_Of_Cols: 'Number of cols',
+ Submit: 'Submit',
+ Cancel: 'Cancel',
+ Choose: 'Choose',
+ Preview: 'Preview',
+ Paste_From_Word: 'Paste from Word',
+ Tools: 'Tools',
+ Containers: 'Containers',
+ Classes: 'Classes',
+ Status: 'Status',
+ Source_Code: 'Source code'
+};
+
diff --git a/studio/static/js/wymeditor/lang/es.js b/studio/static/js/wymeditor/lang/es.js
new file mode 100644
index 0000000..cdb03c1
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/es.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['es'] = {
+ Strong: 'Resaltar',
+ Emphasis: 'Enfatizar',
+ Superscript: 'Superindice',
+ Subscript: 'Subindice',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista sin ordenar',
+ Indent: 'Indentado',
+ Outdent: 'Sin indentar',
+ Undo: 'Deshacer',
+ Redo: 'Rehacer',
+ Link: 'Enlazar',
+ Unlink: 'Eliminar enlace',
+ Image: 'Imagen',
+ Table: 'Tabla',
+ HTML: 'HTML',
+ Paragraph: 'Párrafo',
+ Heading_1: 'Cabecera 1',
+ Heading_2: 'Cabecera 2',
+ Heading_3: 'Cabecera 3',
+ Heading_4: 'Cabecera 4',
+ Heading_5: 'Cabecera 5',
+ Heading_6: 'Cabecera 6',
+ Preformatted: 'Preformateado',
+ Blockquote: 'Cita',
+ Table_Header: 'Cabecera de la tabla',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Leyenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de filas',
+ Number_Of_Cols: 'Número de columnas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Seleccionar',
+ Preview: 'Vista previa',
+ Paste_From_Word: 'Pegar desde Word',
+ Tools: 'Herramientas',
+ Containers: 'Contenedores',
+ Classes: 'Clases',
+ Status: 'Estado',
+ Source_Code: 'Código fuente'
+};
+
diff --git a/studio/static/js/wymeditor/lang/fa.js b/studio/static/js/wymeditor/lang/fa.js
new file mode 100644
index 0000000..9d70fcb
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/fa.js
@@ -0,0 +1,46 @@
+//Translation To Persian: Ghassem Tofighi (http://ght.ir)
+WYMeditor.STRINGS['fa'] = {
+ Strong: 'پررنگ',//Strong
+ Emphasis: 'ایتالیک',//Emphasis
+ Superscript: 'بالانويس‌ ',//Superscript
+ Subscript: 'زيرنويس‌',//Subscript
+ Ordered_List: 'لیست مرتب',//Ordered List
+ Unordered_List: 'لیست نامرتب',//Unordered List
+ Indent: 'افزودن دندانه',//Indent
+ Outdent: 'کاهش دندانه',//Outdent
+ Undo: 'واگردانی',//Undo
+ Redo: 'تکرار',//Redo
+ Link: 'ساختن پیوند',//Link
+ Unlink: 'برداشتن پیوند',//Unlink
+ Image: 'تصویر',//Image
+ Table: 'جدول',//Table
+ HTML: 'HTML',//HTML
+ Paragraph: 'پاراگراف',//Paragraph
+ Heading_1: 'سرتیتر ۱',//Heading 1
+ Heading_2: 'سرتیتر ۲',//Heading 2
+ Heading_3: 'سرتیتر ۳',//Heading 3
+ Heading_4: 'سرتیتر ۴',//Heading 4
+ Heading_5: 'سرتیتر ۵',//Heading 5
+ Heading_6: 'سرتیتر ۶',//Heading 6
+ Preformatted: 'قالب آماده',//Preformatted
+ Blockquote: 'نقل قول',//Blockquote
+ Table_Header: 'سرجدول',//Table Header
+ URL: 'آدرس اینترنتی',//URL
+ Title: 'عنوان',//Title
+ Alternative_Text: 'متن جایگزین',//Alternative text
+ Caption: 'عنوان',//Caption
+ Summary: 'Summary',
+ Number_Of_Rows: 'تعداد سطرها',//Number of rows
+ Number_Of_Cols: 'تعداد ستون‌ها',//Number of cols
+ Submit: 'فرستادن',//Submit
+ Cancel: 'لغو',//Cancel
+ Choose: 'انتخاب',//Choose
+ Preview: 'پیش‌نمایش',//Preview
+ Paste_From_Word: 'انتقال از ورد',//Paste from Word
+ Tools: 'ابزار',//Tools
+ Containers: '‌قالب‌ها',//Containers
+ Classes: 'کلاس‌ها',//Classes
+ Status: 'وضعیت',//Status
+ Source_Code: 'کد مبدأ'//Source code
+};
+
diff --git a/studio/static/js/wymeditor/lang/fi.js b/studio/static/js/wymeditor/lang/fi.js
new file mode 100644
index 0000000..fe1eab4
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/fi.js
@@ -0,0 +1,44 @@
+WYMeditor.STRINGS['fi'] = {
+ Strong: 'Lihavoitu',
+ Emphasis: 'Korostus',
+ Superscript: 'Yläindeksi',
+ Subscript: 'Alaindeksi',
+ Ordered_List: 'Numeroitu lista',
+ Unordered_List: 'Luettelomerkit',
+ Indent: 'Suurenna sisennystä',
+ Outdent: 'Pienennä sisennystä',
+ Undo: 'Kumoa',
+ Redo: 'Toista',
+ Link: 'Linkitä',
+ Unlink: 'Poista linkitys',
+ Image: 'Kuva',
+ Table: 'Taulukko',
+ HTML: 'HTML',
+ Paragraph: 'Kappale',
+ Heading_1: 'Otsikko 1',
+ Heading_2: 'Otsikko 2',
+ Heading_3: 'Otsikko 3',
+ Heading_4: 'Otsikko 4',
+ Heading_5: 'Otsikko 5',
+ Heading_6: 'Otsikko 6',
+ Preformatted: 'Esimuotoilu',
+ Blockquote: 'Sitaatti',
+ Table_Header: 'Taulukon otsikko',
+ URL: 'URL',
+ Title: 'Otsikko',
+ Alternative_Text: 'Vaihtoehtoinen teksti',
+ Caption: 'Kuvateksti',
+ Summary: 'Yhteenveto',
+ Number_Of_Rows: 'Rivien määrä',
+ Number_Of_Cols: 'Palstojen määrä',
+ Submit: 'Lähetä',
+ Cancel: 'Peruuta',
+ Choose: 'Valitse',
+ Preview: 'Esikatsele',
+ Paste_From_Word: 'Tuo Wordista',
+ Tools: 'Työkalut',
+ Containers: 'Muotoilut',
+ Classes: 'Luokat',
+ Status: 'Tila',
+ Source_Code: 'Lähdekoodi'
+};
diff --git a/studio/static/js/wymeditor/lang/fr.js b/studio/static/js/wymeditor/lang/fr.js
new file mode 100644
index 0000000..9b6deb9
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/fr.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['fr'] = {
+ Strong: 'Mise en évidence',
+ Emphasis: 'Emphase',
+ Superscript: 'Exposant',
+ Subscript: 'Indice',
+ Ordered_List: 'Liste Ordonnée',
+ Unordered_List: 'Liste Non-Ordonnée',
+ Indent: 'Imbriqué',
+ Outdent: 'Non-imbriqué',
+ Undo: 'Annuler',
+ Redo: 'Rétablir',
+ Link: 'Lien',
+ Unlink: 'Supprimer le Lien',
+ Image: 'Image',
+ Table: 'Tableau',
+ HTML: 'HTML',
+ Paragraph: 'Paragraphe',
+ Heading_1: 'Titre 1',
+ Heading_2: 'Titre 2',
+ Heading_3: 'Titre 3',
+ Heading_4: 'Titre 4',
+ Heading_5: 'Titre 5',
+ Heading_6: 'Titre 6',
+ Preformatted: 'Pré-formatté',
+ Blockquote: 'Citation',
+ Table_Header: 'Cellule de titre',
+ URL: 'URL',
+ Title: 'Titre',
+ Alternative_Text: 'Texte alternatif',
+ Caption: 'Légende',
+ Summary: 'Résumé',
+ Number_Of_Rows: 'Nombre de lignes',
+ Number_Of_Cols: 'Nombre de colonnes',
+ Submit: 'Envoyer',
+ Cancel: 'Annuler',
+ Choose: 'Choisir',
+ Preview: 'Prévisualisation',
+ Paste_From_Word: 'Copier depuis Word',
+ Tools: 'Outils',
+ Containers: 'Type de texte',
+ Classes: 'Type de contenu',
+ Status: 'Infos',
+ Source_Code: 'Code source'
+};
+
diff --git a/studio/static/js/wymeditor/lang/gl.js b/studio/static/js/wymeditor/lang/gl.js
new file mode 100644
index 0000000..d4786b8
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/gl.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['gl'] = {
+ Strong: 'Moita énfase',
+ Emphasis: 'Énfase',
+ Superscript: 'Superíndice',
+ Subscript: 'Subíndice',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista sen ordenar',
+ Indent: 'Aniñar',
+ Outdent: 'Desaniñar',
+ Undo: 'Desfacer',
+ Redo: 'Refacer',
+ Link: 'Ligazón',
+ Unlink: 'Desligar',
+ Image: 'Imaxe',
+ Table: 'Táboa',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Preformatado',
+ Blockquote: 'Cita en parágrafo',
+ Table_Header: 'Cabeceira da táboa',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Título',
+ Summary: 'Resumo',
+ Number_Of_Rows: 'Número de filas',
+ Number_Of_Cols: 'Número de columnas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Escoller',
+ Preview: 'Previsualizar',
+ Paste_From_Word: 'Colar dende Word',
+ Tools: 'Ferramentas',
+ Containers: 'Contenedores',
+ Classes: 'Clases',
+ Status: 'Estado',
+ Source_Code: 'Código fonte'
+};
+
diff --git a/studio/static/js/wymeditor/lang/he.js b/studio/static/js/wymeditor/lang/he.js
new file mode 100644
index 0000000..97c9675
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/he.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['he'] = {
+ Strong: 'חזק',
+ Emphasis: 'מובלט',
+ Superscript: 'כתב עילי',
+ Subscript: 'כתב תחתי',
+ Ordered_List: 'רשימה ממוספרת',
+ Unordered_List: 'רשימה לא ממוספרת',
+ Indent: 'הזחה פנימה',
+ Outdent: 'הזחה החוצה',
+ Undo: 'בטל פעולה',
+ Redo: 'בצע מחדש פעולה',
+ Link: 'קישור',
+ Unlink: 'בטל קישור',
+ Image: 'תמונה',
+ Table: 'טבלה',
+ HTML: 'קוד HTML',
+ Paragraph: 'פסקה',
+ Heading_1: 'כותרת 1 ; תג &lt;h1&gt;',
+ Heading_2: 'כותרת 2 ; תג &lt;h2&gt;',
+ Heading_3: 'כותרת 3 ; תג &lt;h3&gt;',
+ Heading_4: 'כותרת 4 ; תג &lt;h4&gt;',
+ Heading_5: 'כותרת 5 ; תג &lt;h5&gt;',
+ Heading_6: 'כותרת 6 ; תג &lt;h6&gt;',
+ Preformatted: 'משמר רווחים',
+ Blockquote: 'ציטוט',
+ Table_Header: 'כותרת טבלה',
+ URL: 'קישור (URL)',
+ Title: 'כותרת',
+ Alternative_Text: 'טקסט חלופי',
+ Caption: 'כותרת',
+ Summary: 'סיכום',
+ Number_Of_Rows: 'מספר שורות',
+ Number_Of_Cols: 'מספר טורים',
+ Submit: 'שלח',
+ Cancel: 'בטל',
+ Choose: 'בחר',
+ Preview: 'תצוגה מקדימה',
+ Paste_From_Word: 'העתק מ-Word',
+ Tools: 'כלים',
+ Containers: 'מיכלים',
+ Classes: 'מחלקות',
+ Status: 'מצב',
+ Source_Code: 'קוד מקור'
+};
+
diff --git a/studio/static/js/wymeditor/lang/hr.js b/studio/static/js/wymeditor/lang/hr.js
new file mode 100644
index 0000000..193e31a
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/hr.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['hr'] = {
+ Strong: 'Podebljano',
+ Emphasis: 'Naglašeno',
+ Superscript: 'Iznad',
+ Subscript: 'Ispod',
+ Ordered_List: 'Pobrojana lista',
+ Unordered_List: 'Nepobrojana lista',
+ Indent: 'Uvuci',
+ Outdent: 'Izvuci',
+ Undo: 'Poništi promjenu',
+ Redo: 'Ponovno promjeni',
+ Link: 'Hiperveza',
+ Unlink: 'Ukloni hipervezu',
+ Image: 'Slika',
+ Table: 'Tablica',
+ HTML: 'HTML',
+ Paragraph: 'Paragraf',
+ Heading_1: 'Naslov 1',
+ Heading_2: 'Naslov 2',
+ Heading_3: 'Naslov 3',
+ Heading_4: 'Naslov 4',
+ Heading_5: 'Naslov 5',
+ Heading_6: 'Naslov 6',
+ Preformatted: 'Unaprijed formatirano',
+ Blockquote: 'Citat',
+ Table_Header: 'Zaglavlje tablice',
+ URL: 'URL',
+ Title: 'Naslov',
+ Alternative_Text: 'Alternativni tekst',
+ Caption: 'Zaglavlje',
+ Summary: 'Sažetak',
+ Number_Of_Rows: 'Broj redova',
+ Number_Of_Cols: 'Broj kolona',
+ Submit: 'Snimi',
+ Cancel: 'Odustani',
+ Choose: 'Izaberi',
+ Preview: 'Pregled',
+ Paste_From_Word: 'Zalijepi iz Word-a',
+ Tools: 'Alati',
+ Containers: 'Kontejneri',
+ Classes: 'Klase',
+ Status: 'Status',
+ Source_Code: 'Izvorni kod'
+};
+
diff --git a/studio/static/js/wymeditor/lang/hu.js b/studio/static/js/wymeditor/lang/hu.js
new file mode 100644
index 0000000..a8cdbc6
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/hu.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['hu'] = {
+ Strong: 'Félkövér',
+ Emphasis: 'Kiemelt',
+ Superscript: 'Felső index',
+ Subscript: 'Alsó index',
+ Ordered_List: 'Rendezett lista',
+ Unordered_List: 'Rendezetlen lista',
+ Indent: 'Bekezdés',
+ Outdent: 'Bekezdés törlése',
+ Undo: 'Visszavon',
+ Redo: 'Visszaállít',
+ Link: 'Link',
+ Unlink: 'Link törlése',
+ Image: 'Kép',
+ Table: 'Tábla',
+ HTML: 'HTML',
+ Paragraph: 'Bekezdés',
+ Heading_1: 'Címsor 1',
+ Heading_2: 'Címsor 2',
+ Heading_3: 'Címsor 3',
+ Heading_4: 'Címsor 4',
+ Heading_5: 'Címsor 5',
+ Heading_6: 'Címsor 6',
+ Preformatted: 'Előformázott',
+ Blockquote: 'Idézet',
+ Table_Header: 'Tábla Fejléc',
+ URL: 'Webcím',
+ Title: 'Megnevezés',
+ Alternative_Text: 'Alternatív szöveg',
+ Caption: 'Fejléc',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Sorok száma',
+ Number_Of_Cols: 'Oszlopok száma',
+ Submit: 'Elküld',
+ Cancel: 'Mégsem',
+ Choose: 'Választ',
+ Preview: 'Előnézet',
+ Paste_From_Word: 'Másolás Word-ból',
+ Tools: 'Eszközök',
+ Containers: 'Tartalmak',
+ Classes: 'Osztályok',
+ Status: 'Állapot',
+ Source_Code: 'Forráskód'
+};
+
diff --git a/studio/static/js/wymeditor/lang/it.js b/studio/static/js/wymeditor/lang/it.js
new file mode 100644
index 0000000..ca632a9
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/it.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['it'] = {
+ Strong: 'Grassetto',
+ Emphasis: 'Corsetto',
+ Superscript: 'Apice',
+ Subscript: 'Pedice',
+ Ordered_List: 'Lista Ordinata',
+ Unordered_List: 'Lista Puntata',
+ Indent: 'Indenta',
+ Outdent: 'Caccia',
+ Undo: 'Indietro',
+ Redo: 'Avanti',
+ Link: 'Inserisci Link',
+ Unlink: 'Togli Link',
+ Image: 'Inserisci Immagine',
+ Table: 'Inserisci Tabella',
+ HTML: 'HTML',
+ Paragraph: 'Paragrafo',
+ Heading_1: 'Heading 1',
+ Heading_2: 'Heading 2',
+ Heading_3: 'Heading 3',
+ Heading_4: 'Heading 4',
+ Heading_5: 'Heading 5',
+ Heading_6: 'Heading 6',
+ Preformatted: 'Preformattato',
+ Blockquote: 'Blockquote',
+ Table_Header: 'Header Tabella',
+ URL: 'Indirizzo',
+ Title: 'Titolo',
+ Alternative_Text: 'Testo Alternativo',
+ Caption: 'Caption',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Numero di Righe',
+ Number_Of_Cols: 'Numero di Colonne',
+ Submit: 'Invia',
+ Cancel: 'Cancella',
+ Choose: 'Scegli',
+ Preview: 'Anteprima',
+ Paste_From_Word: 'Incolla',
+ Tools: 'Tools',
+ Containers: 'Contenitori',
+ Classes: 'Classi',
+ Status: 'Stato',
+ Source_Code: 'Codice Sorgente'
+};
+
diff --git a/studio/static/js/wymeditor/lang/ja.js b/studio/static/js/wymeditor/lang/ja.js
new file mode 100644
index 0000000..c9b6410
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/ja.js
@@ -0,0 +1,44 @@
+WYMeditor.STRINGS['ja'] = {
+ Strong: '強調<strong>',
+ Emphasis: '強調<em>',
+ Superscript: '上付き',
+ Subscript: '下付き',
+ Ordered_List: '番号付きリスト',
+ Unordered_List: '番号無リスト',
+ Indent: 'インデントを増やす',
+ Outdent: 'インデントを減らす',
+ Undo: '元に戻す',
+ Redo: 'やり直す',
+ Link: 'リンク',
+ Unlink: 'リンク取消',
+ Image: '画像',
+ Table: 'テーブル',
+ HTML: 'HTML',
+ Paragraph: '段落',
+ Heading_1: '見出し 1',
+ Heading_2: '見出し 2',
+ Heading_3: '見出し 3',
+ Heading_4: '見出し 4',
+ Heading_5: '見出し 5',
+ Heading_6: '見出し 6',
+ Preformatted: '整形済みテキスト',
+ Blockquote: '引用文',
+ Table_Header: '表見出し',
+ URL: 'URL',
+ Title: 'タイトル',
+ Alternative_Text: '代替テキスト',
+ Caption: 'キャプション',
+ Summary: 'サマリー',
+ Number_Of_Rows: '行数',
+ Number_Of_Cols: '列数',
+ Submit: '送信',
+ Cancel: 'キャンセル',
+ Choose: '選択',
+ Preview: 'プレビュー',
+ Paste_From_Word: '貼り付け',
+ Tools: 'ツール',
+ Containers: 'コンテナ',
+ Classes: 'クラス',
+ Status: 'ステータス',
+ Source_Code: 'ソースコード'
+};
diff --git a/studio/static/js/wymeditor/lang/nb.js b/studio/static/js/wymeditor/lang/nb.js
new file mode 100644
index 0000000..7573b78
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/nb.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nb'] = {
+ Strong: 'Fet',
+ Emphasis: 'Uthevet',
+ Superscript: 'Opphøyet',
+ Subscript: 'Nedsenket',
+ Ordered_List: 'Nummerert liste',
+ Unordered_List: 'Punktliste',
+ Indent: 'Rykk inn',
+ Outdent: 'Rykk ut',
+ Undo: 'Angre',
+ Redo: 'Gjenta',
+ Link: 'Lenke',
+ Unlink: 'Ta bort lenken',
+ Image: 'Bilde',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Avsnitt',
+ Heading_1: 'Overskrift 1',
+ Heading_2: 'Overskrift 2',
+ Heading_3: 'Overskrift 3',
+ Heading_4: 'Overskrift 4',
+ Heading_5: 'Overskrift 5',
+ Heading_6: 'Overskrift 6',
+ Preformatted: 'Preformatert',
+ Blockquote: 'Sitat',
+ Table_Header: 'Tabelloverskrift',
+ URL: 'URL',
+ Title: 'Tittel',
+ Alternative_Text: 'Alternativ tekst',
+ Caption: 'Overskrift',
+ Summary: 'Sammendrag',
+ Number_Of_Rows: 'Antall rader',
+ Number_Of_Cols: 'Antall kolonner',
+ Submit: 'Ok',
+ Cancel: 'Avbryt',
+ Choose: 'Velg',
+ Preview: 'Forhåndsvis',
+ Paste_From_Word: 'Lim inn fra Word',
+ Tools: 'Verktøy',
+ Containers: 'Formatering',
+ Classes: 'Klasser',
+ Status: 'Status',
+ Source_Code: 'Kildekode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/nl.js b/studio/static/js/wymeditor/lang/nl.js
new file mode 100644
index 0000000..cdfa21c
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/nl.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nl'] = {
+ Strong: 'Sterk benadrukken',
+ Emphasis: 'Benadrukken',
+ Superscript: 'Bovenschrift',
+ Subscript: 'Onderschrift',
+ Ordered_List: 'Geordende lijst',
+ Unordered_List: 'Ongeordende lijst',
+ Indent: 'Inspringen',
+ Outdent: 'Terugspringen',
+ Undo: 'Ongedaan maken',
+ Redo: 'Opnieuw uitvoeren',
+ Link: 'Linken',
+ Unlink: 'Ontlinken',
+ Image: 'Afbeelding',
+ Table: 'Tabel',
+ HTML: 'HTML',
+ Paragraph: 'Paragraaf',
+ Heading_1: 'Kop 1',
+ Heading_2: 'Kop 2',
+ Heading_3: 'Kop 3',
+ Heading_4: 'Kop 4',
+ Heading_5: 'Kop 5',
+ Heading_6: 'Kop 6',
+ Preformatted: 'Voorgeformatteerd',
+ Blockquote: 'Citaat',
+ Table_Header: 'Tabel-kop',
+ URL: 'URL',
+ Title: 'Titel',
+ Alternative_Text: 'Alternatieve tekst',
+ Caption: 'Bijschrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Aantal rijen',
+ Number_Of_Cols: 'Aantal kolommen',
+ Submit: 'Versturen',
+ Cancel: 'Annuleren',
+ Choose: 'Kiezen',
+ Preview: 'Voorbeeld bekijken',
+ Paste_From_Word: 'Plakken uit Word',
+ Tools: 'Hulpmiddelen',
+ Containers: 'Teksttypes',
+ Classes: 'Klassen',
+ Status: 'Status',
+ Source_Code: 'Broncode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/nn.js b/studio/static/js/wymeditor/lang/nn.js
new file mode 100644
index 0000000..51cec2b
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/nn.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['nn'] = {
+ Strong: 'Feit',
+ Emphasis: 'Utheva',
+ Superscript: 'Opphøgd',
+ Subscript: 'Nedsenka',
+ Ordered_List: 'Nummerert liste',
+ Unordered_List: 'Punktliste',
+ Indent: 'Rykk inn',
+ Outdent: 'Rykk ut',
+ Undo: 'Angre',
+ Redo: 'Gjentaka',
+ Link: 'Lenkje',
+ Unlink: 'Ta bort lenkja',
+ Image: 'Bilete',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Avsnitt',
+ Heading_1: 'Overskrift 1',
+ Heading_2: 'Overskrift 2',
+ Heading_3: 'Overskrift 3',
+ Heading_4: 'Overskrift 4',
+ Heading_5: 'Overskrift 5',
+ Heading_6: 'Overskrift 6',
+ Preformatted: 'Preformatert',
+ Blockquote: 'Sitat',
+ Table_Header: 'Tabelloverskrift',
+ URL: 'URL',
+ Title: 'Tittel',
+ Alternative_Text: 'Alternativ tekst',
+ Caption: 'Overskrift',
+ Summary: 'Samandrag',
+ Number_Of_Rows: 'Tal på rader',
+ Number_Of_Cols: 'Tal på kolonnar',
+ Submit: 'Ok',
+ Cancel: 'Avbryt',
+ Choose: 'Vel',
+ Preview: 'Førehandsvis',
+ Paste_From_Word: 'Lim inn frå Word',
+ Tools: 'Verkty',
+ Containers: 'Formatering',
+ Classes: 'Klassar',
+ Status: 'Status',
+ Source_Code: 'Kjeldekode'
+};
+
diff --git a/studio/static/js/wymeditor/lang/pl.js b/studio/static/js/wymeditor/lang/pl.js
new file mode 100644
index 0000000..d6c0471
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/pl.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pl'] = {
+ Strong: 'Nacisk',
+ Emphasis: 'Emfaza',
+ Superscript: 'Indeks górny',
+ Subscript: 'Indeks dolny',
+ Ordered_List: 'Lista numerowana',
+ Unordered_List: 'Lista wypunktowana',
+ Indent: 'Zwiększ wcięcie',
+ Outdent: 'Zmniejsz wcięcie',
+ Undo: 'Cofnij',
+ Redo: 'Ponów',
+ Link: 'Wstaw link',
+ Unlink: 'Usuń link',
+ Image: 'Obraz',
+ Table: 'Tabela',
+ HTML: 'Źródło HTML',
+ Paragraph: 'Akapit',
+ Heading_1: 'Nagłówek 1',
+ Heading_2: 'Nagłówek 2',
+ Heading_3: 'Nagłówek 3',
+ Heading_4: 'Nagłówek 4',
+ Heading_5: 'Nagłówek 5',
+ Heading_6: 'Nagłówek 6',
+ Preformatted: 'Preformatowany',
+ Blockquote: 'Cytat blokowy',
+ Table_Header: 'Nagłówek tabeli',
+ URL: 'URL',
+ Title: 'Tytuł',
+ Alternative_Text: 'Tekst alternatywny',
+ Caption: 'Tytuł tabeli',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Liczba wierszy',
+ Number_Of_Cols: 'Liczba kolumn',
+ Submit: 'Wyślij',
+ Cancel: 'Anuluj',
+ Choose: 'Wybierz',
+ Preview: 'Podgląd',
+ Paste_From_Word: 'Wklej z Worda',
+ Tools: 'Narzędzia',
+ Containers: 'Format',
+ Classes: 'Styl',
+ Status: 'Status',
+ Source_Code: 'Kod źródłowy'
+};
+
diff --git a/studio/static/js/wymeditor/lang/pt-br.js b/studio/static/js/wymeditor/lang/pt-br.js
new file mode 100644
index 0000000..2ec18fe
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/pt-br.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pt-br'] = {
+ Strong: 'Resaltar',
+ Emphasis: 'Enfatizar',
+ Superscript: 'Sobre escrito',
+ Subscript: 'Sub escrito ',
+ Ordered_List: 'Lista ordenada',
+ Unordered_List: 'Lista desordenada',
+ Indent: 'Indentado',
+ Outdent: 'Desidentar',
+ Undo: 'Desfazer',
+ Redo: 'Refazer',
+ Link: 'Link',
+ Unlink: 'Remover Link',
+ Image: 'Imagem',
+ Table: 'Tabela',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Preformatado',
+ Blockquote: 'Citação',
+ Table_Header: 'Título de tabela',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto alternativo',
+ Caption: 'Legenda',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de linhas',
+ Number_Of_Cols: 'Número de colunas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Selecionar',
+ Preview: 'Previsualizar',
+ Paste_From_Word: 'Copiar do Word',
+ Tools: 'Ferramentas',
+ Containers: 'Conteneiners',
+ Classes: 'Classes',
+ Status: 'Estado',
+ Source_Code: 'Código fonte'
+};
+
diff --git a/studio/static/js/wymeditor/lang/pt.js b/studio/static/js/wymeditor/lang/pt.js
new file mode 100644
index 0000000..a3d1a17
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/pt.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['pt'] = {
+ Strong: 'Negrito',
+ Emphasis: 'Itálico',
+ Superscript: 'Sobrescrito',
+ Subscript: 'Subsescrito',
+ Ordered_List: 'Lista Numerada',
+ Unordered_List: 'Lista Marcada',
+ Indent: 'Aumentar Indentaçã',
+ Outdent: 'Diminuir Indentaçã',
+ Undo: 'Desfazer',
+ Redo: 'Restaurar',
+ Link: 'Link',
+ Unlink: 'Tirar link',
+ Image: 'Imagem',
+ Table: 'Tabela',
+ HTML: 'HTML',
+ Paragraph: 'Parágrafo',
+ Heading_1: 'Título 1',
+ Heading_2: 'Título 2',
+ Heading_3: 'Título 3',
+ Heading_4: 'Título 4',
+ Heading_5: 'Título 5',
+ Heading_6: 'Título 6',
+ Preformatted: 'Pré-formatado',
+ Blockquote: 'Citação',
+ Table_Header: 'Cabeçalho Tabela',
+ URL: 'URL',
+ Title: 'Título',
+ Alternative_Text: 'Texto Alterativo',
+ Caption: 'Título Tabela',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Número de Linhas',
+ Number_Of_Cols: 'Número de Colunas',
+ Submit: 'Enviar',
+ Cancel: 'Cancelar',
+ Choose: 'Escolha',
+ Preview: 'Prever',
+ Paste_From_Word: 'Colar do Word',
+ Tools: 'Ferramentas',
+ Containers: 'Containers',
+ Classes: 'Classes',
+ Status: 'Status',
+ Source_Code: 'Código Fonte'
+};
+
diff --git a/studio/static/js/wymeditor/lang/ru.js b/studio/static/js/wymeditor/lang/ru.js
new file mode 100644
index 0000000..7895f8d
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/ru.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['ru'] = {
+ Strong: 'Жирный',
+ Emphasis: 'Наклонный',
+ Superscript: 'Надстрочный',
+ Subscript: 'Подстрочный',
+ Ordered_List: 'Нумерованый список',
+ Unordered_List: 'Ненумерованый список',
+ Indent: 'Увеличить отступ',
+ Outdent: 'Уменьшить отступ',
+ Undo: 'Отменить',
+ Redo: 'Повторить',
+ Link: 'Ссылка',
+ Unlink: 'Удалить ссылку',
+ Image: 'Изображение',
+ Table: 'Таблица',
+ HTML: 'Править HTML',
+ Paragraph: 'Параграф',
+ Heading_1: 'Заголовок 1',
+ Heading_2: 'Заголовок 2',
+ Heading_3: 'Заголовок 3',
+ Heading_4: 'Заголовок 4',
+ Heading_5: 'Заголовок 5',
+ Heading_6: 'Заголовок 6',
+ Preformatted: 'Preformatted',
+ Blockquote: 'Цитата',
+ Table_Header: 'Заголовок таблицы',
+ URL: 'URL',
+ Title: 'Заголовок',
+ Alternative_Text: 'Альтернативный текст',
+ Caption: 'Надпись',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Кол-во строк',
+ Number_Of_Cols: 'Кол-во столбцов',
+ Submit: 'Отправить',
+ Cancel: 'Отмена',
+ Choose: 'Выбор',
+ Preview: 'Просмотр',
+ Paste_From_Word: 'Вставить из Word',
+ Tools: 'Инструменты',
+ Containers: 'Контейнеры',
+ Classes: 'Классы',
+ Status: 'Статус',
+ Source_Code: 'Исходный код'
+};
+
diff --git a/studio/static/js/wymeditor/lang/sv.js b/studio/static/js/wymeditor/lang/sv.js
new file mode 100644
index 0000000..6347956
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/sv.js
@@ -0,0 +1,46 @@
+WYMeditor.STRINGS['sv'] = {
+ Strong: 'Viktigt',
+ Emphasis: 'Betoning',
+ Superscript: 'Upphöjt',
+ Subscript: 'Nedsänkt',
+ Ordered_List: 'Nummerlista',
+ Unordered_List: 'Punktlista',
+ Indent: 'Indrag',
+ Outdent: 'Utdrag',
+ Undo: 'Ångra',
+ Redo: 'Gör om',
+ Link: 'Länk',
+ Unlink: 'Ta bort länk',
+ Image: 'Bild',
+ Table: 'Tabell',
+ HTML: 'HTML',
+ Paragraph: 'Paragraf',
+ Heading_1: 'Rubrik 1',
+ Heading_2: 'Rubrik 2',
+ Heading_3: 'Rubrik 3',
+ Heading_4: 'Rubrik 4',
+ Heading_5: 'Rubrik 5',
+ Heading_6: 'Rubrik 6',
+ Preformatted: 'Förformaterad',
+ Blockquote: 'Blockcitat',
+ Table_Header: 'Tabellrubrik',
+ URL: 'URL',
+ Title: 'Titel',
+ Relationship: 'Relation',
+ Alternative_Text: 'Alternativ text',
+ Caption: 'Överskrift',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Antal rader',
+ Number_Of_Cols: 'Antal kolumner',
+ Submit: 'Skicka',
+ Cancel: 'Avbryt',
+ Choose: 'Välj',
+ Preview: 'Förhandsgranska',
+ Paste_From_Word: 'Klistra in från Word',
+ Tools: 'Verktyg',
+ Containers: 'Formatering',
+ Classes: 'Klasser',
+ Status: 'Status',
+ Source_Code: 'Källkod'
+};
+
diff --git a/studio/static/js/wymeditor/lang/tr.js b/studio/static/js/wymeditor/lang/tr.js
new file mode 100644
index 0000000..d26f0ff
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/tr.js
@@ -0,0 +1,45 @@
+WYMeditor.STRINGS['tr'] = {
+ Strong: 'Kalın',
+ Emphasis: 'Vurgu',
+ Superscript: 'Superscript',
+ Subscript: 'Subscript',
+ Ordered_List: 'Sıralı List',
+ Unordered_List: 'Sırasız List',
+ Indent: 'İçerlek',
+ Outdent: 'Çıkıntılı',
+ Undo: 'Geri Al',
+ Redo: 'Yinele',
+ Link: 'Bağlantı',
+ Unlink: 'Bağlantıyı Kaldır',
+ Image: 'İmaj',
+ Table: 'Tablo',
+ HTML: 'HTML',
+ Paragraph: 'Parağraf',
+ Heading_1: 'Başlık 1',
+ Heading_2: 'Başlık 2',
+ Heading_3: 'Başlık 3',
+ Heading_4: 'Başlık 4',
+ Heading_5: 'Başlık 5',
+ Heading_6: 'Başlık 6',
+ Preformatted: 'Önceden Formatlı',
+ Blockquote: 'Alıntı',
+ Table_Header: 'Tablo Başlığı',
+ URL: 'URL',
+ Title: 'Başlık',
+ Alternative_Text: 'Alternatif Metin',
+ Caption: 'Etiket',
+ Summary: 'Summary',
+ Number_Of_Rows: 'Satır sayısı',
+ Number_Of_Cols: 'Sütun sayısı',
+ Submit: 'Gönder',
+ Cancel: 'İptal',
+ Choose: 'Seç',
+ Preview: 'Önizleme',
+ Paste_From_Word: 'Wordden yapıştır',
+ Tools: 'Araçlar',
+ Containers: 'Kapsayıcılar',
+ Classes: 'Sınıflar',
+ Status: 'Durum',
+ Source_Code: 'Kaynak Kodu'
+};
+
diff --git a/studio/static/js/wymeditor/lang/zh_cn.js b/studio/static/js/wymeditor/lang/zh_cn.js
new file mode 100644
index 0000000..72f5aaf
--- /dev/null
+++ b/studio/static/js/wymeditor/lang/zh_cn.js
@@ -0,0 +1,47 @@
+WYMeditor.STRINGS['zh_cn'] = {
+ Strong: '加粗',
+ Emphasis: '斜体',
+ Superscript: '上标',
+ Subscript: '下标',
+ Ordered_List: '有序列表',
+ Unordered_List: '无序列表',
+ Indent: '增加缩进',
+ Outdent: '减少缩进',
+ Undo: '撤消',
+ Redo: '重做',
+ Link: '链接',
+ Unlink: '取消链接',
+ Image: '图片',
+ Table: '表格',
+ HTML: 'HTML源代码',
+ Paragraph: '段落',
+ Heading_1: '标题 1',
+ Heading_2: '标题 2',
+ Heading_3: '标题 3',
+ Heading_4: '标题 4',
+ Heading_5: '标题 5',
+ Heading_6: '标题 6',
+ Preformatted: '原始文本',
+ Blockquote: '引语',
+ Table_Header: '表头',
+ URL: '地址',
+ Title: '提示文字',
+ Alternative_Text: '失效文字',
+ Caption: '标题',
+ Summary: 'Summary',
+ Number_Of_Rows: '行数',
+ Number_Of_Cols: '列数',
+ Submit: '提交',
+ Cancel: '放弃',
+ Choose: '选择',
+ Preview: '预览',
+ Paste_From_Word: '从Word粘贴纯文本',
+ Tools: '工具',
+ Containers: '容器',
+ Classes: '预定义样式',
+ Status: '状态',
+ Source_Code: '源代码',
+ Attachment: '附件',
+ NewParagraph: '新段落'
+};
+
diff --git a/studio/static/js/wymeditor/plugins/.svn/entries b/studio/static/js/wymeditor/plugins/.svn/entries
new file mode 100644
index 0000000..e6a5e96
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/.svn/entries
@@ -0,0 +1,43 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/plugins
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2010-04-11T19:53:07.572530Z
+659
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+resizable
+dir
+
+fullscreen
+dir
+
+hovertools
+dir
+
+tidy
+dir
+
+embed
+dir
+
diff --git a/studio/static/js/wymeditor/plugins/embed/.svn/entries b/studio/static/js/wymeditor/plugins/embed/.svn/entries
new file mode 100644
index 0000000..20f177e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/embed/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/plugins/embed
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2010-04-11T12:17:24.397707Z
+655
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+jquery.wymeditor.embed.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+9a97b387093aa3d7406470f11d31a9c9
+2010-04-11T12:17:24.397707Z
+655
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1517
+
diff --git a/studio/static/js/wymeditor/plugins/embed/.svn/text-base/jquery.wymeditor.embed.js.svn-base b/studio/static/js/wymeditor/plugins/embed/.svn/text-base/jquery.wymeditor.embed.js.svn-base
new file mode 100644
index 0000000..e553c3e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/embed/.svn/text-base/jquery.wymeditor.embed.js.svn-base
@@ -0,0 +1,52 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.embed.js
+ * Experimental embed plugin
+ *
+ * File Authors:
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+/*
+ * ISSUES:
+ * - The closing object tag seems to be stripped out...
+ */
+(function() {
+ if (WYMeditor && WYMeditor.XhtmlValidator['_tags']['param']['attributes']) {
+
+ WYMeditor.XhtmlValidator['_tags']["embed"] = {
+ "attributes":[
+ "allowscriptaccess",
+ "allowfullscreen",
+ "height",
+ "src",
+ "type",
+ "width"
+ ]
+ };
+
+ WYMeditor.XhtmlValidator['_tags']['param']['attributes'] = {
+ '0':'name',
+ '1':'type',
+ 'valuetype':/^(data|ref|object)$/,
+ '2':'valuetype',
+ '3':'value'
+ };
+
+ var XhtmlSaxListener = WYMeditor.XhtmlSaxListener;
+ WYMeditor.XhtmlSaxListener = function () {
+ var listener = XhtmlSaxListener.call(this);
+ listener.block_tags.push('embed');
+ return listener;
+ };
+ WYMeditor.XhtmlSaxListener.prototype = XhtmlSaxListener.prototype;
+ }
+})();
diff --git a/studio/static/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js b/studio/static/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js
new file mode 100644
index 0000000..e553c3e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js
@@ -0,0 +1,52 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.embed.js
+ * Experimental embed plugin
+ *
+ * File Authors:
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ */
+
+/*
+ * ISSUES:
+ * - The closing object tag seems to be stripped out...
+ */
+(function() {
+ if (WYMeditor && WYMeditor.XhtmlValidator['_tags']['param']['attributes']) {
+
+ WYMeditor.XhtmlValidator['_tags']["embed"] = {
+ "attributes":[
+ "allowscriptaccess",
+ "allowfullscreen",
+ "height",
+ "src",
+ "type",
+ "width"
+ ]
+ };
+
+ WYMeditor.XhtmlValidator['_tags']['param']['attributes'] = {
+ '0':'name',
+ '1':'type',
+ 'valuetype':/^(data|ref|object)$/,
+ '2':'valuetype',
+ '3':'value'
+ };
+
+ var XhtmlSaxListener = WYMeditor.XhtmlSaxListener;
+ WYMeditor.XhtmlSaxListener = function () {
+ var listener = XhtmlSaxListener.call(this);
+ listener.block_tags.push('embed');
+ return listener;
+ };
+ WYMeditor.XhtmlSaxListener.prototype = XhtmlSaxListener.prototype;
+ }
+})();
diff --git a/studio/static/js/wymeditor/plugins/fullscreen/.svn/entries b/studio/static/js/wymeditor/plugins/fullscreen/.svn/entries
new file mode 100644
index 0000000..c18dcf7
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/fullscreen/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/plugins/fullscreen
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2010-04-11T19:53:07.572530Z
+659
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+jquery.wymeditor.fullscreen.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+503112698b04abb0774b8697d6309576
+2010-04-11T19:53:07.572530Z
+659
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4383
+
+icon_fullscreen.gif
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+f443297ce16c5a70b10874e2a3e90b0a
+2009-02-07T19:29:43.660231Z
+576
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+509
+
diff --git a/studio/static/js/wymeditor/plugins/fullscreen/.svn/prop-base/icon_fullscreen.gif.svn-base b/studio/static/js/wymeditor/plugins/fullscreen/.svn/prop-base/icon_fullscreen.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/fullscreen/.svn/prop-base/icon_fullscreen.gif.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-base b/studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-base
new file mode 100644
index 0000000..d2a8b0a
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/jquery.wymeditor.fullscreen.js.svn-base b/studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/jquery.wymeditor.fullscreen.js.svn-base
new file mode 100644
index 0000000..275c816
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/fullscreen/.svn/text-base/jquery.wymeditor.fullscreen.js.svn-base
@@ -0,0 +1,127 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.fullscreen.js
+ * Fullscreen plugin for WYMeditor
+ *
+ * File Authors:
+ * Luis Santos (luis.santos a-t openquest dotpt)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ * Gerd Riesselmann (gerd a-t gyro-php dot org) : Fixed issue with new skin layout
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.fullscreen = function() {
+ var wym = this,
+ $box = jQuery(this._box),
+ $iframe = jQuery(this._iframe),
+ $overlay = null,
+ $window = jQuery(window),
+
+ editorMargin = 15; // Margin from window (without padding)
+
+
+ //construct the button's html
+ var html = "<li class='wym_tools_fullscreen'>"
+ + "<a name='Fullscreen' href='#'"
+ + " style='background-image:"
+ + " url(" + wym._options.basePath +"plugins/fullscreen/icon_fullscreen.gif)'>"
+ + "Fullscreen"
+ + "</a></li>";
+
+ //add the button to the tools box
+ $box.find(wym._options.toolsSelector + wym._options.toolsListSelector)
+ .append(html);
+
+ function resize () {
+ // Calculate margins
+ var uiHeight = $box.outerHeight(true)
+ - $iframe.outerHeight(true),
+ editorPadding = $box.outerWidth() - $box.width(),
+
+ // Calculate heights
+ screenHeight = $window.height(),
+ iframeHeight = (screenHeight
+ - uiHeight
+ - (editorMargin * 2)) + 'px',
+
+ // Calculate witdths
+ screenWidth = $window.width(),
+ boxWidth = (screenWidth
+ - editorPadding
+ - (editorMargin * 2)) + 'px';
+
+ $box.css('width', boxWidth);
+ $iframe.css('height', iframeHeight);
+ $overlay.css({
+ 'height': screenHeight + 'px',
+ 'width': screenWidth + 'px'
+ });
+ };
+
+ //handle click event
+ $box.find('li.wym_tools_fullscreen a').click(function() {
+ if ($box.css('position') != 'fixed') {
+ // Store previous inline styles
+ $box.data('wym-inline-css', $box.attr('style'));
+ $iframe.data('wym-inline-css', $iframe.attr('style'));
+
+ // Create overlay
+ $overlay = jQuery('<div id="wym-fullscreen-overlay"></div>')
+ .appendTo('body').css({
+ 'position': 'fixed',
+ 'background-color': 'rgb(0, 0, 0)',
+ 'opacity': '0.75',
+ 'z-index': '98',
+ 'top': '0px',
+ 'left': '0px'
+ });
+
+ // Possition the editor
+ $box.css({
+ 'position': 'fixed',
+ 'z-index': '99',
+ 'top': editorMargin + 'px',
+ 'left': editorMargin + 'px'
+ });
+
+ // Bind event listeners
+ $window.bind('resize', resize);
+ $box.find('li.wym_tools_html a').bind('click', resize);
+
+ // Force resize
+ resize();
+ } else {
+ // Unbind event listeners
+ $window.unbind('resize', resize);
+ $box.find('li.wym_tools_html a').unbind('click', resize);
+
+ // Remove inline styles
+ $box.css({
+ 'position': 'static',
+ 'z-index': '',
+ 'width': '',
+ 'top': '',
+ 'left': ''
+ });
+ $iframe.css('height', '');
+
+ // Remove overlay
+ $overlay.remove();
+ $overlay = null;
+
+ // Retore previous inline styles
+ $box.attr('style', $box.data('wym-inline-css'));
+ $iframe.attr('style', $iframe.data('wym-inline-css'));
+ }
+
+ return false;
+ });
+};
diff --git a/studio/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif b/studio/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif
new file mode 100644
index 0000000..d2a8b0a
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif
Binary files differ
diff --git a/studio/static/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js b/studio/static/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js
new file mode 100644
index 0000000..275c816
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js
@@ -0,0 +1,127 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.fullscreen.js
+ * Fullscreen plugin for WYMeditor
+ *
+ * File Authors:
+ * Luis Santos (luis.santos a-t openquest dotpt)
+ * Jonatan Lundin (jonatan.lundin a-t gmail dotcom)
+ * Gerd Riesselmann (gerd a-t gyro-php dot org) : Fixed issue with new skin layout
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.fullscreen = function() {
+ var wym = this,
+ $box = jQuery(this._box),
+ $iframe = jQuery(this._iframe),
+ $overlay = null,
+ $window = jQuery(window),
+
+ editorMargin = 15; // Margin from window (without padding)
+
+
+ //construct the button's html
+ var html = "<li class='wym_tools_fullscreen'>"
+ + "<a name='Fullscreen' href='#'"
+ + " style='background-image:"
+ + " url(" + wym._options.basePath +"plugins/fullscreen/icon_fullscreen.gif)'>"
+ + "Fullscreen"
+ + "</a></li>";
+
+ //add the button to the tools box
+ $box.find(wym._options.toolsSelector + wym._options.toolsListSelector)
+ .append(html);
+
+ function resize () {
+ // Calculate margins
+ var uiHeight = $box.outerHeight(true)
+ - $iframe.outerHeight(true),
+ editorPadding = $box.outerWidth() - $box.width(),
+
+ // Calculate heights
+ screenHeight = $window.height(),
+ iframeHeight = (screenHeight
+ - uiHeight
+ - (editorMargin * 2)) + 'px',
+
+ // Calculate witdths
+ screenWidth = $window.width(),
+ boxWidth = (screenWidth
+ - editorPadding
+ - (editorMargin * 2)) + 'px';
+
+ $box.css('width', boxWidth);
+ $iframe.css('height', iframeHeight);
+ $overlay.css({
+ 'height': screenHeight + 'px',
+ 'width': screenWidth + 'px'
+ });
+ };
+
+ //handle click event
+ $box.find('li.wym_tools_fullscreen a').click(function() {
+ if ($box.css('position') != 'fixed') {
+ // Store previous inline styles
+ $box.data('wym-inline-css', $box.attr('style'));
+ $iframe.data('wym-inline-css', $iframe.attr('style'));
+
+ // Create overlay
+ $overlay = jQuery('<div id="wym-fullscreen-overlay"></div>')
+ .appendTo('body').css({
+ 'position': 'fixed',
+ 'background-color': 'rgb(0, 0, 0)',
+ 'opacity': '0.75',
+ 'z-index': '98',
+ 'top': '0px',
+ 'left': '0px'
+ });
+
+ // Possition the editor
+ $box.css({
+ 'position': 'fixed',
+ 'z-index': '99',
+ 'top': editorMargin + 'px',
+ 'left': editorMargin + 'px'
+ });
+
+ // Bind event listeners
+ $window.bind('resize', resize);
+ $box.find('li.wym_tools_html a').bind('click', resize);
+
+ // Force resize
+ resize();
+ } else {
+ // Unbind event listeners
+ $window.unbind('resize', resize);
+ $box.find('li.wym_tools_html a').unbind('click', resize);
+
+ // Remove inline styles
+ $box.css({
+ 'position': 'static',
+ 'z-index': '',
+ 'width': '',
+ 'top': '',
+ 'left': ''
+ });
+ $iframe.css('height', '');
+
+ // Remove overlay
+ $overlay.remove();
+ $overlay = null;
+
+ // Retore previous inline styles
+ $box.attr('style', $box.data('wym-inline-css'));
+ $iframe.attr('style', $iframe.data('wym-inline-css'));
+ }
+
+ return false;
+ });
+};
diff --git a/studio/static/js/wymeditor/plugins/hovertools/.svn/entries b/studio/static/js/wymeditor/plugins/hovertools/.svn/entries
new file mode 100644
index 0000000..dcb85a5
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/hovertools/.svn/entries
@@ -0,0 +1,62 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/plugins/hovertools
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+jquery.wymeditor.hovertools.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+9128791ae94a2438f6f6f751204e9827
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1644
+
diff --git a/studio/static/js/wymeditor/plugins/hovertools/.svn/text-base/jquery.wymeditor.hovertools.js.svn-base b/studio/static/js/wymeditor/plugins/hovertools/.svn/text-base/jquery.wymeditor.hovertools.js.svn-base
new file mode 100644
index 0000000..2c71ba5
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/hovertools/.svn/text-base/jquery.wymeditor.hovertools.js.svn-base
@@ -0,0 +1,57 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.hovertools.js
+ * hovertools plugin for WYMeditor
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.hovertools = function() {
+
+ var wym = this;
+
+ //bind events on buttons
+ jQuery(this._box).find(this._options.toolSelector).hover(
+ function() {
+ wym.status(jQuery(this).html());
+ },
+ function() {
+ wym.status('&nbsp;');
+ }
+ );
+
+ //classes: add/remove a style attr to matching elems
+ //while mouseover/mouseout
+ jQuery(this._box).find(this._options.classSelector).hover(
+ function() {
+ var aClasses = eval(wym._options.classesItems);
+ var sName = jQuery(this).attr(WYMeditor.NAME);
+ var oClass = WYMeditor.Helper.findByName(aClasses, sName);
+
+ if(oClass){
+ jqexpr = oClass.expr;
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ if(!jQuery.browser.msie)
+ jQuery(wym._doc).find(jqexpr).css('background-color','#cfc');
+ }
+ },
+ function() {
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ if(!jQuery.browser.msie)
+ jQuery(wym._doc).find('*').removeAttr('style');
+ }
+ );
+
+};
diff --git a/studio/static/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js b/studio/static/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js
new file mode 100644
index 0000000..2c71ba5
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js
@@ -0,0 +1,57 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.hovertools.js
+ * hovertools plugin for WYMeditor
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.hovertools = function() {
+
+ var wym = this;
+
+ //bind events on buttons
+ jQuery(this._box).find(this._options.toolSelector).hover(
+ function() {
+ wym.status(jQuery(this).html());
+ },
+ function() {
+ wym.status('&nbsp;');
+ }
+ );
+
+ //classes: add/remove a style attr to matching elems
+ //while mouseover/mouseout
+ jQuery(this._box).find(this._options.classSelector).hover(
+ function() {
+ var aClasses = eval(wym._options.classesItems);
+ var sName = jQuery(this).attr(WYMeditor.NAME);
+ var oClass = WYMeditor.Helper.findByName(aClasses, sName);
+
+ if(oClass){
+ jqexpr = oClass.expr;
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ if(!jQuery.browser.msie)
+ jQuery(wym._doc).find(jqexpr).css('background-color','#cfc');
+ }
+ },
+ function() {
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ if(!jQuery.browser.msie)
+ jQuery(wym._doc).find('*').removeAttr('style');
+ }
+ );
+
+};
diff --git a/studio/static/js/wymeditor/plugins/resizable/.svn/entries b/studio/static/js/wymeditor/plugins/resizable/.svn/entries
new file mode 100644
index 0000000..8afaf68
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/resizable/.svn/entries
@@ -0,0 +1,96 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/plugins/resizable
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-04-28T19:42:37.044706Z
+614
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+jquery.wymeditor.resizable.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+dfe19ff3f55ea32c465ecd64c8b4b8bc
+2009-04-28T19:42:37.044706Z
+614
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2913
+
+readme.txt
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+779875a11fe8526a6d246829afda4f1c
+2008-07-05T12:52:55.176122Z
+510
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+4273
+
diff --git a/studio/static/js/wymeditor/plugins/resizable/.svn/text-base/jquery.wymeditor.resizable.js.svn-base b/studio/static/js/wymeditor/plugins/resizable/.svn/text-base/jquery.wymeditor.resizable.js.svn-base
new file mode 100644
index 0000000..1ba2d2e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/resizable/.svn/text-base/jquery.wymeditor.resizable.js.svn-base
@@ -0,0 +1,91 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.resizable.js
+ * resize plugin for WYMeditor
+ *
+ * File Authors:
+ * Peter Eschler (peschler _at_ gmail.com)
+ * Jean-Francois Hovinne - http://www.hovinne.com/
+ *
+ * Version:
+ * 0.4
+ *
+ * Changelog:
+ *
+ * 0.4
+ * - Removed UI and UI.resizable scripts loading - see #167 (jfh).
+ *
+ * 0.3
+ * - Added 'iframeOriginalSize' and removed 'ui.instance' calls (jfh).
+ *
+ * 0.2
+ * - Added full support for all jQueryUI resizable plugin options.
+ * - Refactored and documented code.
+ * 0.1
+ * - Initial release.
+ */
+
+/**
+ * The resizable plugin makes the wymeditor box vertically resizable.
+ * It it based on the ui.resizable.js plugin of the jQuery UI library.
+ *
+ * The WYMeditor resizable plugin supports all parameters of the jQueryUI
+ * resizable plugin. The parameters are passed like this:
+ *
+ * wym.resizable({ handles: "s,e",
+ * maxHeight: 600 });
+ *
+ * DEPENDENCIES: jQuery UI, jQuery UI resizable
+ *
+ * @param options options for the plugin
+ */
+WYMeditor.editor.prototype.resizable = function(options) {
+
+ var wym = this;
+ var iframe = jQuery(wym._box).find('iframe');
+ var iframeOriginalSize = {};
+
+ // Define some default options
+ var default_options = {
+ start: function(e, ui) {
+ iframeOriginalSize = {
+ width: jQuery(iframe).width(),
+ height: jQuery(iframe).height()
+ }
+ },
+
+ // resize is called by the jQuery resizable plugin whenever the
+ // client area was resized.
+ resize: function(e, ui) {
+ var diff = ui.size.height - ui.originalSize.height;
+ jQuery(iframe).height( iframeOriginalSize.height + diff );
+
+ // If the plugin has horizontal resizing disabled we need to
+ // adjust the "width" attribute of the area css, because the
+ // resizing will set a fixed width (which breaks liquid layout
+ // of the wymeditor area).
+ if( !ui.options.handles['w'] && !ui.options.handles['e'] ) {
+ ui.size.width = "inherit";
+ }
+ },
+ handles: "s,e,se",
+ minHeight: 250,
+ maxHeight: 600
+ };
+
+ // Merge given options with default options. Given options override
+ // default ones.
+ var final_options = jQuery.extend(default_options, options);
+
+ if(jQuery.isFunction( jQuery.fn.resizable )) jQuery(wym._box).resizable(final_options);
+ else WYMeditor.console.error('Oops, jQuery UI.resizable unavailable.');
+
+};
diff --git a/studio/static/js/wymeditor/plugins/resizable/.svn/text-base/readme.txt.svn-base b/studio/static/js/wymeditor/plugins/resizable/.svn/text-base/readme.txt.svn-base
new file mode 100644
index 0000000..2a0444e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/resizable/.svn/text-base/readme.txt.svn-base
@@ -0,0 +1,124 @@
+
+
+resizable plugin for WYMeditor
+##############################
+
+The ``resizable`` plugin for WYMeditor_ enables vertical resizing of the
+editor area. The plugin is based on the jQuery UI library.
+
+Requirements
+============
+The following packages are required for using the WYMeditor ``resizable``
+plugin:
+
+* jQuery (tested with jQuery ``jquery-1.2.4a.js`` from ``jquery.ui`` package)
+* WYMeditor SVN trunk (Revision: 482)
+* jQuery-UI (tested with ``jquery.ui-1.5b2``)
+
+It should be possible to use this plugin with ``WYMeditor-0.4`` but I have not
+tried.
+
+Download
+========
+You can download the WYMeditor ``resizable`` plugin here:
+
+* wymeditor-resizable-plugin-0.2.tgz_
+* wymeditor-resizable-plugin-0.1.tgz_
+
+See the Changelog_ for more infos about the releases.
+
+.. _wymeditor-resizable-plugin-0.2.tgz: http://pyjax.net/download/wymeditor-resizable-plugin-0.2.tgz
+.. _wymeditor-resizable-plugin-0.1.tgz: http://pyjax.net/download/wymeditor-resizable-plugin-0.1.tgz
+
+Installation
+============
+Just extract the downloaded archive into your WYMeditor's ``plugin``
+directory.
+
+Usage
+=====
+For general instructions on WYMeditor plugins please refer to the `WYMeditor
+plugin page`_.
+
+To use the ``resizable`` plugin simply include the plugin's JavaScript file in
+your code. You **do not** need to include the jQuery UI files - this is done
+automatically by the plugin (see `Internals`_)::
+
+ <script type="text/javascript"
+ src="/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js">
+ </script>
+
+Make sure to adjust the ``src`` attribute to your needs, then initialize the
+plugin in WYMeditor's ``postInit`` function::
+
+ wymeditor({postInit: function(wym) {
+ wym.hovertools(); // other plugins...
+ wym.resizable({handles: "s,e",
+ maxHeight: 600});
+ }
+ })
+
+The ``resizable`` plugin takes exactly one parameter, which is an object literal
+containing the options of the plugin. The WYMeditor ``resizable`` plugin
+supports all options of the jQuery UI ``resizable`` plugin. These are the
+default values used by the plugin::
+
+ handles: "s,e,se",
+ minHeight: 250,
+ maxHeight: 600
+
+See the `jQuery UI resizable plugin docs`_ for a list of all options.
+
+That's it! You are now able to resize the WYMeditor vertically, horizontally or
+both, depending on your options.
+
+.. _jQuery UI resizable plugin docs: http://docs.jquery.com/UI/Resizables
+
+Internals
+=========
+The plugin takes care of loading the necessary jQuery UI files (``base`` and
+``resizable``) from the same path the jQuery library was loaded. Here's how
+it's done::
+
+ // Get the jQuery path from the editor, stripping away the jQuery file.
+ // see http://www.oreilly.com/catalog/regex/chapter/ch04.html
+ // The match result array contains the path and the filename.
+ var jQueryPath = wym.computeJqueryPath().match(/^(.*)\/(.*)$/)[1];
+
+ // Make an array of the external JavaScript files required by the plugin.
+ var jQueryPlugins = [jQueryPath + '/ui.base.js',
+ jQueryPath + '/ui.resizable.js'];
+
+ // First get the jQuery UI base file
+ $.getScript(jQueryPlugins[0]);
+
+ // Get the jQuery UI resizeable plugin and then init the wymeditor resizable
+ // plugin. It is import to do the initialisation after loading the
+ // necessary jQuery UI files has finished, otherwise the "resizable" method
+ // would not be available.
+ $.getScript(jQueryPlugins[1], function() {
+ jQuery(wym._box).resizable(final_options);
+ });
+
+An alternative approach would be to use an AJAX queue when getting the script
+files to ensure that all jQuery files are loaded before the initialisation code
+of the plugin is executed. There is an `jQuery AJAX queue plugin`_ which does
+that.
+
+.. _jQuery AJAX queue plugin: http://plugins.jquery.com/project/ajaxqueue
+
+Changelog
+=========
+
+0.2
+---
+- Added full support for all jQuery UI resizable plugin options.
+- Refactored and documented code.
+- Now contains a packed version (775 bytes).
+
+0.1
+---
+- Initial release.
+
+.. _WYMeditor: http://www.wymeditor.org/
+.. _WYMeditor plugin page: http://trac.wymeditor.org/trac/wiki/0.4/Plugins
diff --git a/studio/static/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js b/studio/static/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js
new file mode 100644
index 0000000..1ba2d2e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js
@@ -0,0 +1,91 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.resizable.js
+ * resize plugin for WYMeditor
+ *
+ * File Authors:
+ * Peter Eschler (peschler _at_ gmail.com)
+ * Jean-Francois Hovinne - http://www.hovinne.com/
+ *
+ * Version:
+ * 0.4
+ *
+ * Changelog:
+ *
+ * 0.4
+ * - Removed UI and UI.resizable scripts loading - see #167 (jfh).
+ *
+ * 0.3
+ * - Added 'iframeOriginalSize' and removed 'ui.instance' calls (jfh).
+ *
+ * 0.2
+ * - Added full support for all jQueryUI resizable plugin options.
+ * - Refactored and documented code.
+ * 0.1
+ * - Initial release.
+ */
+
+/**
+ * The resizable plugin makes the wymeditor box vertically resizable.
+ * It it based on the ui.resizable.js plugin of the jQuery UI library.
+ *
+ * The WYMeditor resizable plugin supports all parameters of the jQueryUI
+ * resizable plugin. The parameters are passed like this:
+ *
+ * wym.resizable({ handles: "s,e",
+ * maxHeight: 600 });
+ *
+ * DEPENDENCIES: jQuery UI, jQuery UI resizable
+ *
+ * @param options options for the plugin
+ */
+WYMeditor.editor.prototype.resizable = function(options) {
+
+ var wym = this;
+ var iframe = jQuery(wym._box).find('iframe');
+ var iframeOriginalSize = {};
+
+ // Define some default options
+ var default_options = {
+ start: function(e, ui) {
+ iframeOriginalSize = {
+ width: jQuery(iframe).width(),
+ height: jQuery(iframe).height()
+ }
+ },
+
+ // resize is called by the jQuery resizable plugin whenever the
+ // client area was resized.
+ resize: function(e, ui) {
+ var diff = ui.size.height - ui.originalSize.height;
+ jQuery(iframe).height( iframeOriginalSize.height + diff );
+
+ // If the plugin has horizontal resizing disabled we need to
+ // adjust the "width" attribute of the area css, because the
+ // resizing will set a fixed width (which breaks liquid layout
+ // of the wymeditor area).
+ if( !ui.options.handles['w'] && !ui.options.handles['e'] ) {
+ ui.size.width = "inherit";
+ }
+ },
+ handles: "s,e,se",
+ minHeight: 250,
+ maxHeight: 600
+ };
+
+ // Merge given options with default options. Given options override
+ // default ones.
+ var final_options = jQuery.extend(default_options, options);
+
+ if(jQuery.isFunction( jQuery.fn.resizable )) jQuery(wym._box).resizable(final_options);
+ else WYMeditor.console.error('Oops, jQuery UI.resizable unavailable.');
+
+};
diff --git a/studio/static/js/wymeditor/plugins/resizable/readme.txt b/studio/static/js/wymeditor/plugins/resizable/readme.txt
new file mode 100644
index 0000000..2a0444e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/resizable/readme.txt
@@ -0,0 +1,124 @@
+
+
+resizable plugin for WYMeditor
+##############################
+
+The ``resizable`` plugin for WYMeditor_ enables vertical resizing of the
+editor area. The plugin is based on the jQuery UI library.
+
+Requirements
+============
+The following packages are required for using the WYMeditor ``resizable``
+plugin:
+
+* jQuery (tested with jQuery ``jquery-1.2.4a.js`` from ``jquery.ui`` package)
+* WYMeditor SVN trunk (Revision: 482)
+* jQuery-UI (tested with ``jquery.ui-1.5b2``)
+
+It should be possible to use this plugin with ``WYMeditor-0.4`` but I have not
+tried.
+
+Download
+========
+You can download the WYMeditor ``resizable`` plugin here:
+
+* wymeditor-resizable-plugin-0.2.tgz_
+* wymeditor-resizable-plugin-0.1.tgz_
+
+See the Changelog_ for more infos about the releases.
+
+.. _wymeditor-resizable-plugin-0.2.tgz: http://pyjax.net/download/wymeditor-resizable-plugin-0.2.tgz
+.. _wymeditor-resizable-plugin-0.1.tgz: http://pyjax.net/download/wymeditor-resizable-plugin-0.1.tgz
+
+Installation
+============
+Just extract the downloaded archive into your WYMeditor's ``plugin``
+directory.
+
+Usage
+=====
+For general instructions on WYMeditor plugins please refer to the `WYMeditor
+plugin page`_.
+
+To use the ``resizable`` plugin simply include the plugin's JavaScript file in
+your code. You **do not** need to include the jQuery UI files - this is done
+automatically by the plugin (see `Internals`_)::
+
+ <script type="text/javascript"
+ src="/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js">
+ </script>
+
+Make sure to adjust the ``src`` attribute to your needs, then initialize the
+plugin in WYMeditor's ``postInit`` function::
+
+ wymeditor({postInit: function(wym) {
+ wym.hovertools(); // other plugins...
+ wym.resizable({handles: "s,e",
+ maxHeight: 600});
+ }
+ })
+
+The ``resizable`` plugin takes exactly one parameter, which is an object literal
+containing the options of the plugin. The WYMeditor ``resizable`` plugin
+supports all options of the jQuery UI ``resizable`` plugin. These are the
+default values used by the plugin::
+
+ handles: "s,e,se",
+ minHeight: 250,
+ maxHeight: 600
+
+See the `jQuery UI resizable plugin docs`_ for a list of all options.
+
+That's it! You are now able to resize the WYMeditor vertically, horizontally or
+both, depending on your options.
+
+.. _jQuery UI resizable plugin docs: http://docs.jquery.com/UI/Resizables
+
+Internals
+=========
+The plugin takes care of loading the necessary jQuery UI files (``base`` and
+``resizable``) from the same path the jQuery library was loaded. Here's how
+it's done::
+
+ // Get the jQuery path from the editor, stripping away the jQuery file.
+ // see http://www.oreilly.com/catalog/regex/chapter/ch04.html
+ // The match result array contains the path and the filename.
+ var jQueryPath = wym.computeJqueryPath().match(/^(.*)\/(.*)$/)[1];
+
+ // Make an array of the external JavaScript files required by the plugin.
+ var jQueryPlugins = [jQueryPath + '/ui.base.js',
+ jQueryPath + '/ui.resizable.js'];
+
+ // First get the jQuery UI base file
+ $.getScript(jQueryPlugins[0]);
+
+ // Get the jQuery UI resizeable plugin and then init the wymeditor resizable
+ // plugin. It is import to do the initialisation after loading the
+ // necessary jQuery UI files has finished, otherwise the "resizable" method
+ // would not be available.
+ $.getScript(jQueryPlugins[1], function() {
+ jQuery(wym._box).resizable(final_options);
+ });
+
+An alternative approach would be to use an AJAX queue when getting the script
+files to ensure that all jQuery files are loaded before the initialisation code
+of the plugin is executed. There is an `jQuery AJAX queue plugin`_ which does
+that.
+
+.. _jQuery AJAX queue plugin: http://plugins.jquery.com/project/ajaxqueue
+
+Changelog
+=========
+
+0.2
+---
+- Added full support for all jQuery UI resizable plugin options.
+- Refactored and documented code.
+- Now contains a packed version (775 bytes).
+
+0.1
+---
+- Initial release.
+
+.. _WYMeditor: http://www.wymeditor.org/
+.. _WYMeditor plugin page: http://trac.wymeditor.org/trac/wiki/0.4/Plugins
diff --git a/studio/static/js/wymeditor/plugins/tidy/.svn/entries b/studio/static/js/wymeditor/plugins/tidy/.svn/entries
new file mode 100644
index 0000000..3acea50
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/.svn/entries
@@ -0,0 +1,164 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/plugins/tidy
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2010-04-11T19:34:57.530630Z
+658
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+tidy.php
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+3355fbc23378052db8213dbdcd4fe31f
+2007-04-26T12:45:23.848206Z
+245
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1052
+
+jquery.wymeditor.tidy.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+3a2dee9a16df26d6d1b36886f77880c8
+2010-04-11T19:34:57.530630Z
+658
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2157
+
+wand.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+22d8038ebf5ac63b0062ebf361e8261c
+2007-04-23T11:42:06.279227Z
+233
+d.reszka
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+715
+
+README
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+b31aaf6b3928608a98b2028c2d9f523e
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+607
+
diff --git a/studio/static/js/wymeditor/plugins/tidy/.svn/prop-base/wand.png.svn-base b/studio/static/js/wymeditor/plugins/tidy/.svn/prop-base/wand.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/.svn/prop-base/wand.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/README.svn-base b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/README.svn-base
new file mode 100644
index 0000000..acc7ffd
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/README.svn-base
@@ -0,0 +1,19 @@
+WYMeditor : what you see is What You Mean web-based editor
+Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+Dual licensed under the MIT (MIT-license.txt)
+and GPL (GPL-license.txt) licenses.
+
+For further information visit:
+ http://www.wymeditor.org/
+
+File Name:
+ README - HTML Tidy plugin for WYMeditor
+
+File Authors:
+ Jean-François Hovinne (jf.hovinne a-t wymeditor dotorg)
+
+Credits:
+ 'HTML Tidy' by Dave Ragget - http://tidy.sourceforge.net/
+ Icon 'wand' by Mark James - http://famfamfam.com/
+
+WYMeditor documentation is available online at http://www.wymeditor.org/
diff --git a/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/jquery.wymeditor.tidy.js.svn-base b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/jquery.wymeditor.tidy.js.svn-base
new file mode 100644
index 0000000..bf30c4c
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/jquery.wymeditor.tidy.js.svn-base
@@ -0,0 +1,82 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.tidy.js
+ * HTML Tidy plugin for WYMeditor
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.tidy = function(options) {
+ var tidy = new WymTidy(options, this);
+ return(tidy);
+};
+
+//WymTidy constructor
+function WymTidy(options, wym) {
+
+ options = jQuery.extend({
+
+ sUrl: wym._options.basePath + "plugins/tidy/tidy.php",
+ sButtonHtml: "<li class='wym_tools_tidy'>"
+ + "<a name='CleanUp' href='#'"
+ + " style='background-image:"
+ + " url(" + wym._options.basePath + "plugins/tidy/wand.png)'>"
+ + "Clean up HTML"
+ + "</a></li>",
+
+ sButtonSelector: "li.wym_tools_tidy a"
+
+ }, options);
+
+ this._options = options;
+ this._wym = wym;
+
+};
+
+//WymTidy initialization
+WymTidy.prototype.init = function() {
+
+ var tidy = this;
+
+ jQuery(this._wym._box).find(
+ this._wym._options.toolsSelector + this._wym._options.toolsListSelector)
+ .append(this._options.sButtonHtml);
+
+ //handle click event
+ jQuery(this._wym._box).find(this._options.sButtonSelector).click(function() {
+ tidy.cleanup();
+ return(false);
+ });
+
+};
+
+//WymTidy cleanup
+WymTidy.prototype.cleanup = function() {
+
+ var wym = this._wym;
+ var html = "<html><body>" + wym.xhtml() + "</body></html>";
+
+ jQuery.post(this._options.sUrl, { html: html}, function(data) {
+
+ if(data.length > 0 && data != '0') {
+ if(data.indexOf("<?php") == 0) {
+ wym.status("Ooops... Is PHP installed?");
+ } else {
+ wym.html(data);
+ wym.status("HTML has been cleaned up.");
+ }
+ } else {
+ wym.status("An error occurred.");
+ }
+ });
+};
diff --git a/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/tidy.php.svn-base b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/tidy.php.svn-base
new file mode 100644
index 0000000..23b9bec
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/tidy.php.svn-base
@@ -0,0 +1,36 @@
+<?php
+
+if (get_magic_quotes_gpc()) $html = stripslashes($_REQUEST['html']);
+else $html = $_REQUEST['html'];
+
+if(strlen($html) > 0) {
+
+ // Specify configuration
+ $config = array(
+ 'bare' => true,
+ 'clean' => true,
+ 'doctype' => 'strict',
+ 'drop-empty-paras' => true,
+ 'drop-font-tags' => true,
+ 'drop-proprietary-attributes' => true,
+ 'enclose-block-text' => true,
+ 'indent' => false,
+ 'join-classes' => true,
+ 'join-styles' => true,
+ 'logical-emphasis' => true,
+ 'output-xhtml' => true,
+ 'show-body-only' => true,
+ 'wrap' => 0);
+
+ // Tidy
+ $tidy = new tidy;
+ $tidy->parseString($html, $config, 'utf8');
+ $tidy->cleanRepair();
+
+ // Output
+ echo $tidy;
+} else {
+
+echo ('0');
+}
+?>
diff --git a/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-base b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-base
new file mode 100644
index 0000000..bb55eea
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/plugins/tidy/README b/studio/static/js/wymeditor/plugins/tidy/README
new file mode 100644
index 0000000..acc7ffd
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/README
@@ -0,0 +1,19 @@
+WYMeditor : what you see is What You Mean web-based editor
+Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+Dual licensed under the MIT (MIT-license.txt)
+and GPL (GPL-license.txt) licenses.
+
+For further information visit:
+ http://www.wymeditor.org/
+
+File Name:
+ README - HTML Tidy plugin for WYMeditor
+
+File Authors:
+ Jean-François Hovinne (jf.hovinne a-t wymeditor dotorg)
+
+Credits:
+ 'HTML Tidy' by Dave Ragget - http://tidy.sourceforge.net/
+ Icon 'wand' by Mark James - http://famfamfam.com/
+
+WYMeditor documentation is available online at http://www.wymeditor.org/
diff --git a/studio/static/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js b/studio/static/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js
new file mode 100644
index 0000000..bf30c4c
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js
@@ -0,0 +1,82 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * jquery.wymeditor.tidy.js
+ * HTML Tidy plugin for WYMeditor
+ *
+ * File Authors:
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ */
+
+//Extend WYMeditor
+WYMeditor.editor.prototype.tidy = function(options) {
+ var tidy = new WymTidy(options, this);
+ return(tidy);
+};
+
+//WymTidy constructor
+function WymTidy(options, wym) {
+
+ options = jQuery.extend({
+
+ sUrl: wym._options.basePath + "plugins/tidy/tidy.php",
+ sButtonHtml: "<li class='wym_tools_tidy'>"
+ + "<a name='CleanUp' href='#'"
+ + " style='background-image:"
+ + " url(" + wym._options.basePath + "plugins/tidy/wand.png)'>"
+ + "Clean up HTML"
+ + "</a></li>",
+
+ sButtonSelector: "li.wym_tools_tidy a"
+
+ }, options);
+
+ this._options = options;
+ this._wym = wym;
+
+};
+
+//WymTidy initialization
+WymTidy.prototype.init = function() {
+
+ var tidy = this;
+
+ jQuery(this._wym._box).find(
+ this._wym._options.toolsSelector + this._wym._options.toolsListSelector)
+ .append(this._options.sButtonHtml);
+
+ //handle click event
+ jQuery(this._wym._box).find(this._options.sButtonSelector).click(function() {
+ tidy.cleanup();
+ return(false);
+ });
+
+};
+
+//WymTidy cleanup
+WymTidy.prototype.cleanup = function() {
+
+ var wym = this._wym;
+ var html = "<html><body>" + wym.xhtml() + "</body></html>";
+
+ jQuery.post(this._options.sUrl, { html: html}, function(data) {
+
+ if(data.length > 0 && data != '0') {
+ if(data.indexOf("<?php") == 0) {
+ wym.status("Ooops... Is PHP installed?");
+ } else {
+ wym.html(data);
+ wym.status("HTML has been cleaned up.");
+ }
+ } else {
+ wym.status("An error occurred.");
+ }
+ });
+};
diff --git a/studio/static/js/wymeditor/plugins/tidy/tidy.php b/studio/static/js/wymeditor/plugins/tidy/tidy.php
new file mode 100644
index 0000000..23b9bec
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/tidy.php
@@ -0,0 +1,36 @@
+<?php
+
+if (get_magic_quotes_gpc()) $html = stripslashes($_REQUEST['html']);
+else $html = $_REQUEST['html'];
+
+if(strlen($html) > 0) {
+
+ // Specify configuration
+ $config = array(
+ 'bare' => true,
+ 'clean' => true,
+ 'doctype' => 'strict',
+ 'drop-empty-paras' => true,
+ 'drop-font-tags' => true,
+ 'drop-proprietary-attributes' => true,
+ 'enclose-block-text' => true,
+ 'indent' => false,
+ 'join-classes' => true,
+ 'join-styles' => true,
+ 'logical-emphasis' => true,
+ 'output-xhtml' => true,
+ 'show-body-only' => true,
+ 'wrap' => 0);
+
+ // Tidy
+ $tidy = new tidy;
+ $tidy->parseString($html, $config, 'utf8');
+ $tidy->cleanRepair();
+
+ // Output
+ echo $tidy;
+} else {
+
+echo ('0');
+}
+?>
diff --git a/studio/static/js/wymeditor/plugins/tidy/wand.png b/studio/static/js/wymeditor/plugins/tidy/wand.png
new file mode 100644
index 0000000..bb55eea
--- /dev/null
+++ b/studio/static/js/wymeditor/plugins/tidy/wand.png
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/.svn/entries b/studio/static/js/wymeditor/skins/.svn/entries
new file mode 100644
index 0000000..81655ae
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/.svn/entries
@@ -0,0 +1,77 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2010-06-20T14:29:05.352766Z
+675
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+default
+dir
+
+silver
+dir
+
+compact
+dir
+
+minimal
+dir
+
+twopanels
+dir
+
+wymeditor_icon.png
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+d43650efc0228099352fc9998573dbb6
+2007-08-22T15:06:37.289602Z
+413
+d.reszka
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1028
+
diff --git a/studio/static/js/wymeditor/skins/.svn/prop-base/wymeditor_icon.png.svn-base b/studio/static/js/wymeditor/skins/.svn/prop-base/wymeditor_icon.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/.svn/prop-base/wymeditor_icon.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-base b/studio/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-base
new file mode 100644
index 0000000..d4fc155
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/compact/.svn/entries b/studio/static/js/wymeditor/skins/compact/.svn/entries
new file mode 100644
index 0000000..4218693
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins/compact
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+skin.js
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+eb8726f426d41a0cb87a3a6831cf2848
+2009-04-20T15:16:38.336253Z
+595
+totoro
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1155
+
+skin.css
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+85d1cb084ccc301c9306974311a86df7
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7937
+
+icons.png
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+45a781288dc799f892fa517355ff80b6
+2009-04-18T11:14:41.598474Z
+592
+totoro
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3651
+
diff --git a/studio/static/js/wymeditor/skins/compact/.svn/prop-base/icons.png.svn-base b/studio/static/js/wymeditor/skins/compact/.svn/prop-base/icons.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/.svn/prop-base/icons.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-base b/studio/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-base
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.css.svn-base b/studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..4a6a0c6
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.css.svn-base
@@ -0,0 +1,134 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_compact p, .wym_skin_compact h2, .wym_skin_compact h3,
+ .wym_skin_compact ul, .wym_skin_compact li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_compact .wym_area_left { display: none; }
+ .wym_skin_compact .wym_area_right { display: none; }
+
+
+/*TYPO*/
+ .wym_skin_compact { font-size: 10px; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_compact h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_compact h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_compact li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_compact { border: 1px solid gray; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_compact:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_compact { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_compact .wym_html { width: 98%;}
+ .wym_skin_compact .wym_html textarea { font-size: 120%; width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_compact .wym_iframe { width: 98%;}
+ .wym_skin_compact .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_compact .wym_area_left { width: 100px; float: left;}
+ .wym_skin_compact .wym_area_right { width: 150px; float: right;}
+ .wym_skin_compact .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_compact .wym_area_main { height: 1%;}
+ * html .wym_skin_compact .wym_area_top { height: 1%;}
+ *+html .wym_skin_compact .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_compact .wym_section { margin-bottom: 5px; }
+ .wym_skin_compact .wym_section h2,
+ .wym_skin_compact .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_compact .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_compact .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_compact .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_compact .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_compact .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_compact .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_compact .wym_panel { }
+ .wym_skin_compact .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_compact .wym_dropdown h2 { display: block; }
+ .wym_skin_compact .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_compact .wym_dropdown:hover ul,
+ .wym_skin_compact .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_compact .wym_buttons li { float:left;}
+ .wym_skin_compact .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_compact .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_compact .wym_section h2 { background: #f0f0f0; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_compact .wym_section h2 span { color: gray;}
+ .wym_skin_compact .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_compact .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_compact .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_compact .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.js.svn-base b/studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..cfb7cc1
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/.svn/text-base/skin.js.svn-base
@@ -0,0 +1,35 @@
+WYMeditor.SKINS['compact'] = {
+
+ init: function(wym) {
+
+ //move the containers panel to the top area
+ jQuery(wym._options.containersSelector + ', '
+ + wym._options.classesSelector, wym._box)
+ .appendTo( jQuery("div.wym_area_top", wym._box) )
+ .addClass("wym_dropdown")
+ .css({"margin-right": "10px", "width": "120px", "float": "left"});
+
+ //render following sections as buttons
+ jQuery(wym._options.toolsSelector, wym._box)
+ .addClass("wym_buttons")
+ .css({"margin-right": "10px", "float": "left"});
+
+ //make hover work under IE < 7
+ jQuery(".wym_section", wym._box).hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+
+ var postInit = wym._options.postInit;
+ wym._options.postInit = function(wym) {
+
+ if(postInit) postInit.call(wym, wym);
+ var rule = {
+ name: 'body',
+ css: 'background-color: #f0f0f0;'
+ };
+ wym.addCssRule( wym._doc.styleSheets[0], rule);
+ };
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/compact/icons.png b/studio/static/js/wymeditor/skins/compact/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/icons.png
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/compact/skin.css b/studio/static/js/wymeditor/skins/compact/skin.css
new file mode 100644
index 0000000..4a6a0c6
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/skin.css
@@ -0,0 +1,134 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_compact p, .wym_skin_compact h2, .wym_skin_compact h3,
+ .wym_skin_compact ul, .wym_skin_compact li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_compact .wym_area_left { display: none; }
+ .wym_skin_compact .wym_area_right { display: none; }
+
+
+/*TYPO*/
+ .wym_skin_compact { font-size: 10px; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_compact h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_compact h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_compact li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_compact { border: 1px solid gray; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_compact:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_compact { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_compact .wym_html { width: 98%;}
+ .wym_skin_compact .wym_html textarea { font-size: 120%; width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_compact .wym_iframe { width: 98%;}
+ .wym_skin_compact .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_compact .wym_area_left { width: 100px; float: left;}
+ .wym_skin_compact .wym_area_right { width: 150px; float: right;}
+ .wym_skin_compact .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_compact .wym_area_main { height: 1%;}
+ * html .wym_skin_compact .wym_area_top { height: 1%;}
+ *+html .wym_skin_compact .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_compact .wym_section { margin-bottom: 5px; }
+ .wym_skin_compact .wym_section h2,
+ .wym_skin_compact .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_compact .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_compact .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_compact .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_compact .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_compact .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_compact .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_compact .wym_panel { }
+ .wym_skin_compact .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_compact .wym_dropdown h2 { display: block; }
+ .wym_skin_compact .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_compact .wym_dropdown:hover ul,
+ .wym_skin_compact .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_compact .wym_buttons li { float:left;}
+ .wym_skin_compact .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_compact .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_compact .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_compact .wym_section h2 { background: #f0f0f0; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_compact .wym_section h2 span { color: gray;}
+ .wym_skin_compact .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_compact .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_compact .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_compact .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/studio/static/js/wymeditor/skins/compact/skin.js b/studio/static/js/wymeditor/skins/compact/skin.js
new file mode 100644
index 0000000..cfb7cc1
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/compact/skin.js
@@ -0,0 +1,35 @@
+WYMeditor.SKINS['compact'] = {
+
+ init: function(wym) {
+
+ //move the containers panel to the top area
+ jQuery(wym._options.containersSelector + ', '
+ + wym._options.classesSelector, wym._box)
+ .appendTo( jQuery("div.wym_area_top", wym._box) )
+ .addClass("wym_dropdown")
+ .css({"margin-right": "10px", "width": "120px", "float": "left"});
+
+ //render following sections as buttons
+ jQuery(wym._options.toolsSelector, wym._box)
+ .addClass("wym_buttons")
+ .css({"margin-right": "10px", "float": "left"});
+
+ //make hover work under IE < 7
+ jQuery(".wym_section", wym._box).hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+
+ var postInit = wym._options.postInit;
+ wym._options.postInit = function(wym) {
+
+ if(postInit) postInit.call(wym, wym);
+ var rule = {
+ name: 'body',
+ css: 'background-color: #f0f0f0;'
+ };
+ wym.addCssRule( wym._doc.styleSheets[0], rule);
+ };
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/default/.svn/entries b/studio/static/js/wymeditor/skins/default/.svn/entries
new file mode 100644
index 0000000..3893f6d
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins/default
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2010-06-20T14:29:05.352766Z
+675
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+skin.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+cbb79789e5a95dc8cbdcff294dd67ea0
+2010-06-20T14:29:05.352766Z
+675
+mr_lundis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1386
+
+skin.css
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+c8b6d612f048cbf5c1b8a54f816dc5db
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+7890
+
+icons.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+45a781288dc799f892fa517355ff80b6
+2007-03-06T13:53:36.153298Z
+149
+d.reszka
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3651
+
diff --git a/studio/static/js/wymeditor/skins/default/.svn/prop-base/icons.png.svn-base b/studio/static/js/wymeditor/skins/default/.svn/prop-base/icons.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/.svn/prop-base/icons.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-base b/studio/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-base
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/default/.svn/text-base/skin.css.svn-base b/studio/static/js/wymeditor/skins/default/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..eb4680f
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/.svn/text-base/skin.css.svn-base
@@ -0,0 +1,133 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * skin.css
+ * main stylesheet for the default WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_default p, .wym_skin_default h2, .wym_skin_default h3,
+ .wym_skin_default ul, .wym_skin_default li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_default .wym_area_left { display: none; }
+ .wym_skin_default .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_default { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_default h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_default h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_default li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_default { border: 1px solid gray; background: #f2f2f2; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_default:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_default { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_default .wym_html { width: 98%;}
+ .wym_skin_default .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_default .wym_iframe { width: 98%;}
+ .wym_skin_default .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_default .wym_area_left { width: 150px; float: left;}
+ .wym_skin_default .wym_area_right { width: 150px; float: right;}
+ .wym_skin_default .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_default .wym_area_main { height: 1%;}
+ * html .wym_skin_default .wym_area_top { height: 1%;}
+ *+html .wym_skin_default .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_default .wym_section { margin-bottom: 5px; }
+ .wym_skin_default .wym_section h2,
+ .wym_skin_default .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_default .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_default .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_default .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_default .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_default .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_default .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_default .wym_panel { }
+ .wym_skin_default .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_default .wym_dropdown h2 { display: block; }
+ .wym_skin_default .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_default .wym_dropdown:hover ul,
+ .wym_skin_default .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_default .wym_buttons li { float:left;}
+ .wym_skin_default .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_default .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_default .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_default .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_default .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_default .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_default .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_default .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_default .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_default .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_default .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_default .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_default .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_default .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_default .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_default .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_default .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_default .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_default .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_default .wym_section h2 { background: #ddd; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_default .wym_section h2 span { color: gray;}
+ .wym_skin_default .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_default .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_default .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_default .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/studio/static/js/wymeditor/skins/default/.svn/text-base/skin.js.svn-base b/studio/static/js/wymeditor/skins/default/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..5f6d97e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/.svn/text-base/skin.js.svn-base
@@ -0,0 +1,40 @@
+WYMeditor.SKINS['default'] = {
+
+ init: function(wym) {
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .addClass("wym_dropdown")
+ .find(WYMeditor.H2)
+ .append("<span> ></span>");
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "155px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/default/icons.png b/studio/static/js/wymeditor/skins/default/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/icons.png
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/default/skin.css b/studio/static/js/wymeditor/skins/default/skin.css
new file mode 100644
index 0000000..eb4680f
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/skin.css
@@ -0,0 +1,133 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * skin.css
+ * main stylesheet for the default WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_default p, .wym_skin_default h2, .wym_skin_default h3,
+ .wym_skin_default ul, .wym_skin_default li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_default .wym_area_left { display: none; }
+ .wym_skin_default .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_default { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_default h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_default h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_default li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_default { border: 1px solid gray; background: #f2f2f2; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_default:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_default { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_default .wym_html { width: 98%;}
+ .wym_skin_default .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_default .wym_iframe { width: 98%;}
+ .wym_skin_default .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_default .wym_area_left { width: 150px; float: left;}
+ .wym_skin_default .wym_area_right { width: 150px; float: right;}
+ .wym_skin_default .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_default .wym_area_main { height: 1%;}
+ * html .wym_skin_default .wym_area_top { height: 1%;}
+ *+html .wym_skin_default .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_default .wym_section { margin-bottom: 5px; }
+ .wym_skin_default .wym_section h2,
+ .wym_skin_default .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_default .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_default .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_default .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_default .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_default .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_default .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_default .wym_panel { }
+ .wym_skin_default .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_default .wym_dropdown h2 { display: block; }
+ .wym_skin_default .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_default .wym_dropdown:hover ul,
+ .wym_skin_default .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_default .wym_buttons li { float:left;}
+ .wym_skin_default .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_default .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_default .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_default .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_default .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_default .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_default .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_default .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_default .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_default .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_default .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_default .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_default .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_default .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_default .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_default .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_default .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_default .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_default .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_default .wym_section h2 { background: #ddd; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_default .wym_section h2 span { color: gray;}
+ .wym_skin_default .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_default .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_default .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_default .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/studio/static/js/wymeditor/skins/default/skin.js b/studio/static/js/wymeditor/skins/default/skin.js
new file mode 100644
index 0000000..5f6d97e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/default/skin.js
@@ -0,0 +1,40 @@
+WYMeditor.SKINS['default'] = {
+
+ init: function(wym) {
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .addClass("wym_dropdown")
+ .find(WYMeditor.H2)
+ .append("<span> ></span>");
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "155px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/minimal/.svn/entries b/studio/static/js/wymeditor/skins/minimal/.svn/entries
new file mode 100644
index 0000000..fb50d46
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/.svn/entries
@@ -0,0 +1,99 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins/minimal
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+skin.js
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+4f493f73bfb815d0dfff9c39d15a14e8
+2008-06-02T20:02:11.870601Z
+505
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+868
+
+images
+dir
+
+skin.css
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+b24eafed148e80826464943a977ba2ad
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2743
+
diff --git a/studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.css.svn-base b/studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..cea8d84
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.css.svn-base
@@ -0,0 +1,131 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * skin.css
+ * main stylesheet for the minimal WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne
+ * Scott Lewis (see Silver skin)
+*/
+
+/* Set iframe */
+.wym_skin_minimal div.wym_iframe iframe {
+ width: 90%;
+ height: 200px;
+}
+
+/* Hide h2 by default */
+.wym_skin_minimal h2 {
+ display: none;
+}
+
+/* Show specific h2 */
+.wym_skin_minimal div.wym_tools h2,
+.wym_skin_minimal div.wym_containers h2,
+.wym_skin_minimal div.wym_classes h2 {
+ display: block;
+}
+
+.wym_skin_minimal div.wym_section ul {
+ margin: 0;
+}
+
+.wym_skin_minimal div.wym_section ul li {
+ float: left;
+ list-style-type: none;
+ margin-right: 5px;
+}
+
+.wym_skin_minimal div.wym_area_top,
+.wym_skin_minimal div.wym_area_right,
+.wym_skin_minimal div.wym_containers,
+.wym_skin_minimal div.wym_classes {
+ float: left;
+}
+
+.wym_skin_minimal div.wym_area_main {
+ clear: both;
+}
+
+.wym_skin_minimal div.wym_html {
+ width: 90%;
+}
+
+.wym_skin_minimal textarea.wym_html_val {
+ width: 100%;
+ height: 100px;
+}
+
+/* DROPDOWNS (see Silver skin) */
+.wym_skin_minimal div.wym_dropdown {
+ cursor: pointer;
+ margin: 0px 4px 10px 0px;
+ padding: 0px;
+ z-index: 1001;
+ display: block;
+}
+
+.wym_skin_minimal div.wym_dropdown ul {
+ display: none;
+ width: 124px;
+ padding: 0px;
+ margin: 0px;
+ list-style-type: none;
+ list-style-image: none;
+ z-index: 1002;
+ position: absolute;
+ border-top: 1px solid #AAA;
+}
+
+.wym_skin_minimal div.wym_dropdown ul li {
+ width: 146px;
+ height: 20px;
+ padding: 0px;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #EEE;
+ list-style-image: none;
+}
+
+.wym_skin_minimal div.wym_dropdown h2 {
+ width: 138px;
+ height: 16px;
+ color: #000;
+ background-image: url(images/bg.selector.silver.gif);
+ background-position: 0px -18px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ font-weight: bold;
+ padding: 2px 0px 0px 10px;
+ margin: 0px;
+}
+
+.wym_skin_minimal div.wym_dropdown a {
+ text-decoration: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ padding: 5px 0px 0px 10px;
+ display: block;
+ width: 136px;
+ height: 15px;
+ color: #000;
+ text-align: left;
+ margin-left: 0px;
+}
+
+.wym_skin_minimal div.wym_dropdown a:hover {
+ background: #BBB;
+ border-bottom: none;
+}
diff --git a/studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.js.svn-base b/studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..af29ed4
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/.svn/text-base/skin.js.svn-base
@@ -0,0 +1,30 @@
+jQuery.fn.selectify = function() {
+ return this.each(function() {
+ jQuery(this).hover(
+ function() {
+ jQuery("h2", this).css("background-position", "0px -18px");
+ jQuery("ul", this).fadeIn("fast");
+ },
+ function() {
+ jQuery("h2", this).css("background-position", "");
+ jQuery("ul", this).fadeOut("fast");
+ }
+ );
+ });
+};
+
+WYMeditor.SKINS['minimal'] = {
+ //placeholder for the skin JS, if needed
+
+ //init the skin
+ //wym is the WYMeditor.editor instance
+ init: function(wym) {
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.toolsSelector + ', ' + wym._options.containersSelector + ', ' + wym._options.classesSelector)
+ .addClass("wym_dropdown")
+ .selectify();
+
+
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/entries b/studio/static/js/wymeditor/skins/minimal/images/.svn/entries
new file mode 100644
index 0000000..ff30362
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/entries
@@ -0,0 +1,164 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins/minimal/images
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2008-06-02T20:02:11.870601Z
+505
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+icons.silver.gif
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+3d55143203f242061d02ed4387e3c498
+2008-06-02T20:02:11.870601Z
+505
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15382
+
+bg.header.gif
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+4871b677b0af34f02d3a51046dd51f20
+2008-06-02T20:02:11.870601Z
+505
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+781
+
+bg.wymeditor.png
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+dae577218f4bdd6f59197e3d8c8c9ea6
+2008-06-02T20:02:11.870601Z
+505
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+498
+
+bg.selector.silver.gif
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+1101554f412121db5ad6157f366515e3
+2008-06-02T20:02:11.870601Z
+505
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1621
+
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.header.gif.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.header.gif.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.selector.silver.gif.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.selector.silver.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.selector.silver.gif.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.wymeditor.png.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.wymeditor.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/icons.silver.gif.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/prop-base/icons.silver.gif.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.selector.silver.gif.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.selector.silver.gif.svn-base
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.selector.silver.gif.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-base b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/images/bg.header.gif b/studio/static/js/wymeditor/skins/minimal/images/bg.header.gif
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/bg.header.gif
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gif b/studio/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gif
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gif
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/images/bg.wymeditor.png b/studio/static/js/wymeditor/skins/minimal/images/bg.wymeditor.png
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/bg.wymeditor.png
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/images/icons.silver.gif b/studio/static/js/wymeditor/skins/minimal/images/icons.silver.gif
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/images/icons.silver.gif
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/minimal/skin.css b/studio/static/js/wymeditor/skins/minimal/skin.css
new file mode 100644
index 0000000..cea8d84
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/skin.css
@@ -0,0 +1,131 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * skin.css
+ * main stylesheet for the minimal WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Jean-Francois Hovinne
+ * Scott Lewis (see Silver skin)
+*/
+
+/* Set iframe */
+.wym_skin_minimal div.wym_iframe iframe {
+ width: 90%;
+ height: 200px;
+}
+
+/* Hide h2 by default */
+.wym_skin_minimal h2 {
+ display: none;
+}
+
+/* Show specific h2 */
+.wym_skin_minimal div.wym_tools h2,
+.wym_skin_minimal div.wym_containers h2,
+.wym_skin_minimal div.wym_classes h2 {
+ display: block;
+}
+
+.wym_skin_minimal div.wym_section ul {
+ margin: 0;
+}
+
+.wym_skin_minimal div.wym_section ul li {
+ float: left;
+ list-style-type: none;
+ margin-right: 5px;
+}
+
+.wym_skin_minimal div.wym_area_top,
+.wym_skin_minimal div.wym_area_right,
+.wym_skin_minimal div.wym_containers,
+.wym_skin_minimal div.wym_classes {
+ float: left;
+}
+
+.wym_skin_minimal div.wym_area_main {
+ clear: both;
+}
+
+.wym_skin_minimal div.wym_html {
+ width: 90%;
+}
+
+.wym_skin_minimal textarea.wym_html_val {
+ width: 100%;
+ height: 100px;
+}
+
+/* DROPDOWNS (see Silver skin) */
+.wym_skin_minimal div.wym_dropdown {
+ cursor: pointer;
+ margin: 0px 4px 10px 0px;
+ padding: 0px;
+ z-index: 1001;
+ display: block;
+}
+
+.wym_skin_minimal div.wym_dropdown ul {
+ display: none;
+ width: 124px;
+ padding: 0px;
+ margin: 0px;
+ list-style-type: none;
+ list-style-image: none;
+ z-index: 1002;
+ position: absolute;
+ border-top: 1px solid #AAA;
+}
+
+.wym_skin_minimal div.wym_dropdown ul li {
+ width: 146px;
+ height: 20px;
+ padding: 0px;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #EEE;
+ list-style-image: none;
+}
+
+.wym_skin_minimal div.wym_dropdown h2 {
+ width: 138px;
+ height: 16px;
+ color: #000;
+ background-image: url(images/bg.selector.silver.gif);
+ background-position: 0px -18px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ font-weight: bold;
+ padding: 2px 0px 0px 10px;
+ margin: 0px;
+}
+
+.wym_skin_minimal div.wym_dropdown a {
+ text-decoration: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ padding: 5px 0px 0px 10px;
+ display: block;
+ width: 136px;
+ height: 15px;
+ color: #000;
+ text-align: left;
+ margin-left: 0px;
+}
+
+.wym_skin_minimal div.wym_dropdown a:hover {
+ background: #BBB;
+ border-bottom: none;
+}
diff --git a/studio/static/js/wymeditor/skins/minimal/skin.js b/studio/static/js/wymeditor/skins/minimal/skin.js
new file mode 100644
index 0000000..af29ed4
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/minimal/skin.js
@@ -0,0 +1,30 @@
+jQuery.fn.selectify = function() {
+ return this.each(function() {
+ jQuery(this).hover(
+ function() {
+ jQuery("h2", this).css("background-position", "0px -18px");
+ jQuery("ul", this).fadeIn("fast");
+ },
+ function() {
+ jQuery("h2", this).css("background-position", "");
+ jQuery("ul", this).fadeOut("fast");
+ }
+ );
+ });
+};
+
+WYMeditor.SKINS['minimal'] = {
+ //placeholder for the skin JS, if needed
+
+ //init the skin
+ //wym is the WYMeditor.editor instance
+ init: function(wym) {
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.toolsSelector + ', ' + wym._options.containersSelector + ', ' + wym._options.classesSelector)
+ .addClass("wym_dropdown")
+ .selectify();
+
+
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/silver/.svn/entries b/studio/static/js/wymeditor/skins/silver/.svn/entries
new file mode 100644
index 0000000..2969c6b
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/.svn/entries
@@ -0,0 +1,167 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins/silver
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+skin.js
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+7f55ea883250c072ce72cf60c2d77064
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+2024
+
+images
+dir
+
+COPYING
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+d32239bcb673463ab874e80d47fae504
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+35147
+
+skin.css
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+4af1f9844350d87d34bfeeca2702ea0c
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+13023
+
+README
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+649465d1c305caedb34adc02b8cc0ea8
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+725
+
diff --git a/studio/static/js/wymeditor/skins/silver/.svn/prop-base/COPYING.svn-base b/studio/static/js/wymeditor/skins/silver/.svn/prop-base/COPYING.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/.svn/prop-base/COPYING.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/studio/static/js/wymeditor/skins/silver/.svn/prop-base/README.svn-base b/studio/static/js/wymeditor/skins/silver/.svn/prop-base/README.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/.svn/prop-base/README.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/studio/static/js/wymeditor/skins/silver/.svn/text-base/COPYING.svn-base b/studio/static/js/wymeditor/skins/silver/.svn/text-base/COPYING.svn-base
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/.svn/text-base/COPYING.svn-base
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/studio/static/js/wymeditor/skins/silver/.svn/text-base/README.svn-base b/studio/static/js/wymeditor/skins/silver/.svn/text-base/README.svn-base
new file mode 100644
index 0000000..130dc46
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/.svn/text-base/README.svn-base
@@ -0,0 +1,27 @@
+/**
+* @version Alpha 0.1 2008-05-10 23:28:43 $
+* @package Silver skin for WYMeditor
+* @copyright Copyright (C) 2008 Scott Edwin Lewis. All rights reserved.
+* @license GNU/GPL, see COPYING
+* Silver skin for WYMeditor is free software and is licensed under the
+* GNU General Public License. See COPYING for copyright notices and details.
+*/
+
+Adds custom buttons and color palette to the WYMeditor XHTML Editor.
+
+INSTALLATION:
+
+1. Copy the entire /silver/ directory to /wymeditor/skins/
+2. Initialize the WYMeditor 'skin' option as below:
+
+<script type="text/javascript">
+jQuery(function() {
+
+ jQuery('.wymeditor').wymeditor({
+ skin: 'silver'
+ });
+
+});
+</script>
+
+That's it. You're done.
diff --git a/studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.css.svn-base b/studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..8284d81
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.css.svn-base
@@ -0,0 +1,297 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the default WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Scott Edwin Lewis
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_silver p, .wym_skin_silver h2, .wym_skin_silver h3,
+ .wym_skin_silver ul, .wym_skin_silver li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_silver .wym_area_left { display: none; }
+ .wym_skin_silver .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_silver { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_silver h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_silver h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_silver li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_silver { border: 1px solid gray; background: #f2f2f2; padding: 0px; margin: 0px;}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_silver:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_silver { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_silver .wym_html { width: 98%;}
+ .wym_skin_silver .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_silver .wym_iframe { width: 98%;}
+ .wym_skin_silver .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_silver .wym_area_left { width: 150px; float: left;}
+ .wym_skin_silver .wym_area_right { width: 150px; float: right;}
+ .wym_skin_silver .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_silver .wym_area_main { height: 1%;}
+ * html .wym_skin_silver .wym_area_top { height: 1%;}
+ *+html .wym_skin_silver .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_silver .wym_section { margin-bottom: 5px; }
+ .wym_skin_silver .wym_section h2,
+ .wym_skin_silver .wym_section h3 { padding: 1px 3px; margin: 0; cursor: pointer; }
+ .wym_skin_silver .wym_section a { padding: 5px 0px 0px 10px; display: block; text-decoration: none; color: black; }
+ .wym_skin_silver .wym_section a:hover { /*background-color: #DDD;*/}
+ /*hide section titles by default*/
+ .wym_skin_silver .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_silver .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_silver .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; padding: 0px; }
+ * html .wym_skin_silver .wym_section ul { height: 1%;}
+ .wym_skin_silver .wym_section li {}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_silver .wym_panel { }
+ .wym_skin_silver .wym_panel h2 { display: block; font-size: 11px; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_silver .wym_dropdown h2 { display: block; font-size: 11px;}
+ .wym_skin_silver .wym_dropdown ul { position: absolute; background: white; padding: 0px;}
+ .wym_skin_silver .wym_dropdown:hover ul,
+ .wym_skin_silver .wym_dropdown.hover ul { cursor: pointer;}
+ .wym_skin_silver .wym_dropdown ul li a {/*border-bottom: 1px solid #AAA;*/}
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_silver .wym_buttons li { float:left;}
+ .wym_skin_silver .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px; text-decoration: none !important; border: 1px solid #666; }
+ .wym_skin_silver .wym_buttons a:hover { text-decoration: none !important; border: 1px solid #000;}
+ /*image replacements*/
+ .wym_skin_silver .wym_buttons li a { background: url(images/icons.silver.gif) no-repeat; text-indent: -9999px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_strong a { background-position: 0 -384px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_emphasis a { background-position: 0 -24px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_superscript a { background-position: 0 -432px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_subscript a { background-position: 0 -456px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_indent a { background-position: 0 -600px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_outdent a { background-position: 0 -624px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_undo a { background-position: 0 -504px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_redo a { background-position: 0 -528px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_image a { background-position: 0 -120px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_html a { background-position: 0 -192px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_gadget a { background-position: 0 -576px;}
+
+ .wym_skin_silver .wym_buttons li.wym_tools_strong a:hover { background-position: -24px -384px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_emphasis a:hover { background-position: -24px -24px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_superscript a:hover { background-position: -24px -432px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_subscript a:hover { background-position: -24px -456px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_ordered_list a:hover { background-position: -24px -48px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unordered_list a:hover{ background-position: -24px -72px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_indent a:hover { background-position: -24px -600px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_outdent a:hover { background-position: -24px -624px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_undo a:hover { background-position: -24px -504px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_redo a:hover { background-position: -24px -528px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_link a:hover { background-position: -24px -96px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unlink a:hover { background-position: -24px -168px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_image a:hover { background-position: -24px -120px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_table a:hover { background-position: -24px -144px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_paste a:hover { background-position: -24px -552px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_html a:hover { background-position: -24px -192px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_preview a:hover { background-position: -24px -408px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_gadget a:hover { background-position: -24px -576px;}
+
+/*DECORATION*/
+ .wym_skin_silver .wym_section h2 { background: #ddd; border: none;}
+ .wym_skin_silver .wym_section h2 span { color: gray;}
+ .wym_skin_silver .wym_panel { padding: 0; border: solid gray; border-width: 0px;}
+ .wym_skin_silver .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_silver .wym_dropdown { padding: 0; border: none; }
+ .wym_skin_silver .wym_dropdown ul { border: none; margin-left: -1px; padding: 0px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link
+ {
+ text-indent: -9999px;
+ float: right;
+ display: block;
+ width: 50px;
+ height: 15px;
+ background: url(../wymeditor_icon.png);
+ background-position: 1px 1px;
+ background-repeat: no-repeat;
+ overflow: hidden;
+ text-decoration: none;
+ padding: 1px !important;
+ border: 1px solid #333 !important;
+ background-color: #FFF !important;
+ }
+
+.wym_box
+{
+ padding: 0px !important;
+ margin: 0px;
+}
+.wym_inner
+{
+ border-left: 1px solid #FFF;
+ border-top: 1px solid #FFF;
+ border-right: 1px solid #FFF;
+ border-bottom: 1px solid #FFF;
+ padding: 5px;
+ background-color: #B8C1C4;
+ height: auto;
+}
+
+.clear {clear: both;}
+
+div.wym_dropdown
+{
+ cursor: pointer;
+ width: 138px !important;
+ margin: 0px 4px 10px 0px !important;
+ padding: 0px;
+ z-index: 1001;
+ display: block;
+ border: 1px solid red;
+}
+
+div.wym_dropdown ul
+{
+ display: none;
+ width: 124px;
+ padding: 0px !important;
+ margin: 0px !important;
+ list-style-type: none;
+ list-style-image: none;
+ z-index: 1002;
+ position: absolute;
+ border-top: 1px solid #AAA;
+}
+
+div.wym_dropdown ul li
+{
+ width: 146px;
+ height: 20px;
+ padding: 0px !important;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #DDD;
+ list-style-image: none;
+}
+
+div.wym_dropdown h2
+{
+ width: 138px;
+ height: 16px;
+ color: #000 !important;
+ background-image: url(images/bg.selector.silver.gif) !important;
+ background-position: 0px -18px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px !important;
+ font-weight: bold !important;
+ padding: 2px 0px 0px 10px !important;
+ margin: 0px !important;
+}
+
+.wym_skin_silver .wym_panel h2
+{
+ width: 138px;
+ height: 16px;
+ color: #000 !important;
+ background-image: url(images/bg.header.gif) !important;
+ background-position: 0px 0px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px !important;
+ font-weight: bold !important;
+ padding: 2px 0px 0px 10px !important;
+ margin: 0px !important;
+}
+
+.wym_skin_silver .wym_panel ul
+{
+ margin-top: 0px !important;
+}
+
+.wym_skin_silver .wym_panel ul li
+{
+ width: 146px;
+ height: 20px;
+ padding: 0px !important;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #DDD;
+ list-style-image: none;
+}
+
+.wym_skin_silver .wym_panel a,
+div.wym_dropdown a
+{
+ text-decoration: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ padding: 5px 0px 0px 10px !important;
+ display: block;
+ width: 136px;
+ height: 15px;
+ color: #000;
+ text-align: left !important;
+ margin-left: 0px !important;
+}
+
+div.wym_dropdown a:hover,
+.wym_skin_silver .wym_panel a:hover
+{
+ background: #BBB;
+ border-bottom: none !important;
+}
diff --git a/studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.js.svn-base b/studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..948ed91
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/.svn/text-base/skin.js.svn-base
@@ -0,0 +1,61 @@
+/* This file is part of the Silver skin for WYMeditor by Scott Edwin Lewis */
+
+jQuery.fn.selectify = function() {
+ return this.each(function() {
+ jQuery(this).hover(
+ function() {
+ jQuery("h2", this).css("background-position", "0px -18px");
+ jQuery("ul", this).fadeIn("fast");
+ },
+ function() {
+ jQuery("h2", this).css("background-position", "");
+ jQuery("ul", this).fadeOut("fast");
+ }
+ );
+ });
+};
+
+WYMeditor.SKINS['silver'] = {
+
+ init: function(wym) {
+
+ //add some elements to improve the rendering
+ jQuery(wym._box)
+ .append('<div class="clear"></div>')
+ .wrapInner('<div class="wym_inner"></div>');
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .addClass("wym_dropdown")
+ .selectify();
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "155px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/silver/COPYING b/studio/static/js/wymeditor/skins/silver/COPYING
new file mode 100755
index 0000000..94a9ed0
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/studio/static/js/wymeditor/skins/silver/README b/studio/static/js/wymeditor/skins/silver/README
new file mode 100755
index 0000000..130dc46
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/README
@@ -0,0 +1,27 @@
+/**
+* @version Alpha 0.1 2008-05-10 23:28:43 $
+* @package Silver skin for WYMeditor
+* @copyright Copyright (C) 2008 Scott Edwin Lewis. All rights reserved.
+* @license GNU/GPL, see COPYING
+* Silver skin for WYMeditor is free software and is licensed under the
+* GNU General Public License. See COPYING for copyright notices and details.
+*/
+
+Adds custom buttons and color palette to the WYMeditor XHTML Editor.
+
+INSTALLATION:
+
+1. Copy the entire /silver/ directory to /wymeditor/skins/
+2. Initialize the WYMeditor 'skin' option as below:
+
+<script type="text/javascript">
+jQuery(function() {
+
+ jQuery('.wymeditor').wymeditor({
+ skin: 'silver'
+ });
+
+});
+</script>
+
+That's it. You're done.
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/entries b/studio/static/js/wymeditor/skins/silver/images/.svn/entries
new file mode 100644
index 0000000..d70fd13
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/entries
@@ -0,0 +1,164 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins/silver/images
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+icons.silver.gif
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+3d55143203f242061d02ed4387e3c498
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+15382
+
+bg.header.gif
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+4871b677b0af34f02d3a51046dd51f20
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+781
+
+bg.wymeditor.png
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+dae577218f4bdd6f59197e3d8c8c9ea6
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+498
+
+bg.selector.silver.gif
+file
+
+
+
+
+2011-07-13T16:45:39.000000Z
+1101554f412121db5ad6157f366515e3
+2008-05-12T20:05:54.287558Z
+494
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1621
+
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.header.gif.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.header.gif.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.selector.silver.gif.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.selector.silver.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.selector.silver.gif.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.wymeditor.png.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.wymeditor.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/icons.silver.gif.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/prop-base/icons.silver.gif.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.selector.silver.gif.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.selector.silver.gif.svn-base
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.selector.silver.gif.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-base b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/images/bg.header.gif b/studio/static/js/wymeditor/skins/silver/images/bg.header.gif
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/bg.header.gif
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/images/bg.selector.silver.gif b/studio/static/js/wymeditor/skins/silver/images/bg.selector.silver.gif
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/bg.selector.silver.gif
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/images/bg.wymeditor.png b/studio/static/js/wymeditor/skins/silver/images/bg.wymeditor.png
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/bg.wymeditor.png
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/images/icons.silver.gif b/studio/static/js/wymeditor/skins/silver/images/icons.silver.gif
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/images/icons.silver.gif
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/silver/skin.css b/studio/static/js/wymeditor/skins/silver/skin.css
new file mode 100644
index 0000000..56e3248
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/skin.css
@@ -0,0 +1,297 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the default WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Scott Edwin Lewis
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_silver p, .wym_skin_silver h2, .wym_skin_silver h3,
+ .wym_skin_silver ul, .wym_skin_silver li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_silver .wym_area_left { display: none; }
+ .wym_skin_silver .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_silver { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_silver h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_silver h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_silver li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_silver { border: 1px solid gray; background: #E8E8E8; padding: 0px; margin: 0px;}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_silver:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_silver { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_silver .wym_html { width: 98%;}
+ .wym_skin_silver .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_silver .wym_iframe { width: 98%;}
+ .wym_skin_silver .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_silver .wym_area_left { width: 150px; float: left;}
+ .wym_skin_silver .wym_area_right { width: 150px; float: right;}
+ .wym_skin_silver .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_silver .wym_area_main { height: 1%;}
+ * html .wym_skin_silver .wym_area_top { height: 1%;}
+ *+html .wym_skin_silver .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_silver .wym_section { margin-bottom: 5px; }
+ .wym_skin_silver .wym_section h2,
+ .wym_skin_silver .wym_section h3 { padding: 1px 3px; margin: 0; cursor: pointer; }
+ .wym_skin_silver .wym_section a { padding: 5px 0px 0px 10px; display: block; text-decoration: none; color: black; }
+ .wym_skin_silver .wym_section a:hover { /*background-color: #DDD;*/}
+ /*hide section titles by default*/
+ .wym_skin_silver .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_silver .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_silver .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; padding: 0px; }
+ * html .wym_skin_silver .wym_section ul { height: 1%;}
+ .wym_skin_silver .wym_section li {}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_silver .wym_panel { }
+ .wym_skin_silver .wym_panel h2 { display: block; font-size: 11px; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_silver .wym_dropdown h2 { display: block; font-size: 11px;}
+ .wym_skin_silver .wym_dropdown ul { position: absolute; background: white; padding: 0px;}
+ .wym_skin_silver .wym_dropdown:hover ul,
+ .wym_skin_silver .wym_dropdown.hover ul { cursor: pointer;}
+ .wym_skin_silver .wym_dropdown ul li a {/*border-bottom: 1px solid #AAA;*/}
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_silver .wym_buttons li { float:left;}
+ .wym_skin_silver .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px; text-decoration: none !important; border: 1px solid #666; }
+ .wym_skin_silver .wym_buttons a:hover { text-decoration: none !important; border: 1px solid #000;}
+ /*image replacements*/
+ .wym_skin_silver .wym_buttons li a { background: url(images/icons.silver.gif) no-repeat; text-indent: -9999px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_strong a { background-position: 0 -384px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_emphasis a { background-position: 0 -24px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_superscript a { background-position: 0 -432px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_subscript a { background-position: 0 -456px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_indent a { background-position: 0 -600px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_outdent a { background-position: 0 -624px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_undo a { background-position: 0 -504px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_redo a { background-position: 0 -528px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_image a { background-position: 0 -120px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_html a { background-position: 0 -192px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_gadget a { background-position: 0 -576px;}
+
+ .wym_skin_silver .wym_buttons li.wym_tools_strong a:hover { background-position: -24px -384px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_emphasis a:hover { background-position: -24px -24px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_superscript a:hover { background-position: -24px -432px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_subscript a:hover { background-position: -24px -456px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_ordered_list a:hover { background-position: -24px -48px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unordered_list a:hover{ background-position: -24px -72px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_indent a:hover { background-position: -24px -600px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_outdent a:hover { background-position: -24px -624px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_undo a:hover { background-position: -24px -504px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_redo a:hover { background-position: -24px -528px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_link a:hover { background-position: -24px -96px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_unlink a:hover { background-position: -24px -168px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_image a:hover { background-position: -24px -120px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_table a:hover { background-position: -24px -144px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_paste a:hover { background-position: -24px -552px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_html a:hover { background-position: -24px -192px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_preview a:hover { background-position: -24px -408px;}
+ .wym_skin_silver .wym_buttons li.wym_tools_gadget a:hover { background-position: -24px -576px;}
+
+/*DECORATION*/
+ .wym_skin_silver .wym_section h2 { background: #ddd; border: none;}
+ .wym_skin_silver .wym_section h2 span { color: gray;}
+ .wym_skin_silver .wym_panel { padding: 0; border: solid gray; border-width: 0px;}
+ .wym_skin_silver .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_silver .wym_dropdown { padding: 0; border: none; }
+ .wym_skin_silver .wym_dropdown ul { border: none; margin-left: -1px; padding: 0px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link
+ {
+ text-indent: -9999px;
+ float: right;
+ display: block;
+ width: 50px;
+ height: 15px;
+ background: url(../wymeditor_icon.png);
+ background-position: 1px 1px;
+ background-repeat: no-repeat;
+ overflow: hidden;
+ text-decoration: none;
+ padding: 1px !important;
+ border: 1px solid #333 !important;
+ background-color: #FFF !important;
+ }
+
+.wym_box
+{
+ padding: 0px !important;
+ margin: 0px;
+}
+.wym_inner
+{
+ border-left: 1px solid #FFF;
+ border-top: 1px solid #FFF;
+ border-right: 1px solid #FFF;
+ border-bottom: 1px solid #FFF;
+ padding: 5px;
+ background-color: #E8E8E8;
+ height: auto;
+}
+
+.clear {clear: both;}
+
+div.wym_dropdown
+{
+ cursor: pointer;
+ width: 138px !important;
+ margin: 0px 4px 10px 0px !important;
+ padding: 0px;
+ z-index: 1001;
+ display: block;
+ border: 1px solid red;
+}
+
+div.wym_dropdown ul
+{
+ display: none;
+ width: 124px;
+ padding: 0px !important;
+ margin: 0px !important;
+ list-style-type: none;
+ list-style-image: none;
+ z-index: 1002;
+ position: absolute;
+ border-top: 1px solid #AAA;
+}
+
+div.wym_dropdown ul li
+{
+ width: 146px;
+ height: 20px;
+ padding: 0px !important;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #DDD;
+ list-style-image: none;
+}
+
+div.wym_dropdown h2
+{
+ width: 138px;
+ height: 16px;
+ color: #000 !important;
+ background-image: url(images/bg.selector.silver.gif) !important;
+ background-position: 0px -18px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px !important;
+ font-weight: bold !important;
+ padding: 2px 0px 0px 10px !important;
+ margin: 0px !important;
+}
+
+.wym_skin_silver .wym_panel h2
+{
+ width: 138px;
+ height: 16px;
+ color: #000 !important;
+ background-image: url(images/bg.header.gif) !important;
+ background-position: 0px 0px;
+ background-repeat: no-repeat;
+ border: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px !important;
+ font-weight: bold !important;
+ padding: 2px 0px 0px 10px !important;
+ margin: 0px !important;
+}
+
+.wym_skin_silver .wym_panel ul
+{
+ margin-top: 0px !important;
+}
+
+.wym_skin_silver .wym_panel ul li
+{
+ width: 146px;
+ height: 20px;
+ padding: 0px !important;
+ margin: 0px;
+ border: 1px solid #777;
+ border-top: none;
+ background: #DDD;
+ list-style-image: none;
+}
+
+.wym_skin_silver .wym_panel a,
+div.wym_dropdown a
+{
+ text-decoration: none;
+ font-family: "Trebuchet MS", Verdana, Arial, Helvetica, Sanserif;
+ font-size: 12px;
+ padding: 5px 0px 0px 10px !important;
+ display: block;
+ width: 136px;
+ height: 15px;
+ color: #000;
+ text-align: left !important;
+ margin-left: 0px !important;
+}
+
+div.wym_dropdown a:hover,
+.wym_skin_silver .wym_panel a:hover
+{
+ background: #BBB;
+ border-bottom: none !important;
+}
diff --git a/studio/static/js/wymeditor/skins/silver/skin.js b/studio/static/js/wymeditor/skins/silver/skin.js
new file mode 100644
index 0000000..948ed91
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/silver/skin.js
@@ -0,0 +1,61 @@
+/* This file is part of the Silver skin for WYMeditor by Scott Edwin Lewis */
+
+jQuery.fn.selectify = function() {
+ return this.each(function() {
+ jQuery(this).hover(
+ function() {
+ jQuery("h2", this).css("background-position", "0px -18px");
+ jQuery("ul", this).fadeIn("fast");
+ },
+ function() {
+ jQuery("h2", this).css("background-position", "");
+ jQuery("ul", this).fadeOut("fast");
+ }
+ );
+ });
+};
+
+WYMeditor.SKINS['silver'] = {
+
+ init: function(wym) {
+
+ //add some elements to improve the rendering
+ jQuery(wym._box)
+ .append('<div class="clear"></div>')
+ .wrapInner('<div class="wym_inner"></div>');
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ //render following sections as dropdown menus
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .addClass("wym_dropdown")
+ .selectify();
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "155px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/twopanels/.svn/entries b/studio/static/js/wymeditor/skins/twopanels/.svn/entries
new file mode 100644
index 0000000..a477f85
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/.svn/entries
@@ -0,0 +1,130 @@
+10
+
+dir
+677
+svn://svn.wymeditor.org/wymeditor/trunk/src/wymeditor/skins/twopanels
+svn://svn.wymeditor.org/wymeditor
+
+
+
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+89e89e35-0a13-0410-8f61-920bba073fa9
+
+skin.js
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+d47d82f6cda558d258263f3949d815f1
+2008-05-30T19:59:08.978756Z
+502
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+1380
+
+skin.css
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+5da884f153705d38e473d5cf4bdc7deb
+2009-05-27T19:20:55.910061Z
+632
+jf.hovinne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+8045
+
+icons.png
+file
+
+
+
+
+2011-07-13T16:45:40.000000Z
+45a781288dc799f892fa517355ff80b6
+2008-05-30T19:59:08.978756Z
+502
+jf.hovinne
+has-props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+3651
+
diff --git a/studio/static/js/wymeditor/skins/twopanels/.svn/prop-base/icons.png.svn-base b/studio/static/js/wymeditor/skins/twopanels/.svn/prop-base/icons.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/.svn/prop-base/icons.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-base b/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-base
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-base
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.css.svn-base b/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..7e6b8fd
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.css.svn-base
@@ -0,0 +1,134 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jean-Francois Hovinne
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_twopanels p, .wym_skin_twopanels h2, .wym_skin_twopanels h3,
+ .wym_skin_twopanels ul, .wym_skin_twopanels li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_twopanels .wym_area_left { display: block; }
+ .wym_skin_twopanels .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_twopanels { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_twopanels h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_twopanels h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_twopanels li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_twopanels { border: 1px solid gray; background: #f2f2f2; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_twopanels:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_twopanels { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_twopanels .wym_html { width: 98%;}
+ .wym_skin_twopanels .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_twopanels .wym_iframe { width: 98%;}
+ .wym_skin_twopanels .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_twopanels .wym_area_left { width: 100px; float: left;}
+ .wym_skin_twopanels .wym_area_right { width: 150px; float: right;}
+ .wym_skin_twopanels .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_twopanels .wym_area_main { height: 1%;}
+ * html .wym_skin_twopanels .wym_area_top { height: 1%;}
+ *+html .wym_skin_twopanels .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_twopanels .wym_section { margin-bottom: 5px; }
+ .wym_skin_twopanels .wym_section h2,
+ .wym_skin_twopanels .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_twopanels .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_twopanels .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_twopanels .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_twopanels .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_twopanels .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_twopanels .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_twopanels .wym_panel { }
+ .wym_skin_twopanels .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_twopanels .wym_dropdown h2 { display: block; }
+ .wym_skin_twopanels .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_twopanels .wym_dropdown:hover ul,
+ .wym_skin_twopanels .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_twopanels .wym_buttons li { float:left;}
+ .wym_skin_twopanels .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_twopanels .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_twopanels .wym_section h2 { background: #ddd; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_twopanels .wym_section h2 span { color: gray;}
+ .wym_skin_twopanels .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_twopanels .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_twopanels .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_twopanels .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.js.svn-base b/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..e82efc5
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.js.svn-base
@@ -0,0 +1,39 @@
+WYMeditor.SKINS['twopanels'] = {
+
+ init: function(wym) {
+
+ //move the containers panel to the left area
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .appendTo("div.wym_area_left");
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector + ', '
+ + wym._options.containersSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "115px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/twopanels/icons.png b/studio/static/js/wymeditor/skins/twopanels/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/icons.png
Binary files differ
diff --git a/studio/static/js/wymeditor/skins/twopanels/skin.css b/studio/static/js/wymeditor/skins/twopanels/skin.css
new file mode 100644
index 0000000..7e6b8fd
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/skin.css
@@ -0,0 +1,134 @@
+/*
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File Name:
+ * screen.css
+ * main stylesheet for the WYMeditor skin
+ * See the documentation for more info.
+ *
+ * File Authors:
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jean-Francois Hovinne
+*/
+
+/*TRYING TO RESET STYLES THAT MAY INTERFERE WITH WYMEDITOR*/
+ .wym_skin_twopanels p, .wym_skin_twopanels h2, .wym_skin_twopanels h3,
+ .wym_skin_twopanels ul, .wym_skin_twopanels li { background: transparent url(); margin: 0; padding: 0; border-width:0; list-style: none; }
+
+
+/*HIDDEN BY DEFAULT*/
+ .wym_skin_twopanels .wym_area_left { display: block; }
+ .wym_skin_twopanels .wym_area_right { display: block; }
+
+
+/*TYPO*/
+ .wym_skin_twopanels { font-size: 62.5%; font-family: Verdana, Arial, sans-serif; }
+ .wym_skin_twopanels h2 { font-size: 110%; /* = 11px */}
+ .wym_skin_twopanels h3 { font-size: 100%; /* = 10px */}
+ .wym_skin_twopanels li { font-size: 100%; /* = 10px */}
+
+
+/*WYM_BOX*/
+ .wym_skin_twopanels { border: 1px solid gray; background: #f2f2f2; padding: 5px}
+
+ /*auto-clear the wym_box*/
+ .wym_skin_twopanels:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_twopanels { height: 1%;}
+
+
+/*WYM_HTML*/
+ .wym_skin_twopanels .wym_html { width: 98%;}
+ .wym_skin_twopanels .wym_html textarea { width: 100%; height: 200px; border: 1px solid gray; background: white; }
+
+
+/*WYM_IFRAME*/
+ .wym_skin_twopanels .wym_iframe { width: 98%;}
+ .wym_skin_twopanels .wym_iframe iframe { width: 100%; height: 200px; border: 1px solid gray; background: white }
+
+
+/*AREAS*/
+ .wym_skin_twopanels .wym_area_left { width: 100px; float: left;}
+ .wym_skin_twopanels .wym_area_right { width: 150px; float: right;}
+ .wym_skin_twopanels .wym_area_bottom { height: 1%; clear: both;}
+ * html .wym_skin_twopanels .wym_area_main { height: 1%;}
+ * html .wym_skin_twopanels .wym_area_top { height: 1%;}
+ *+html .wym_skin_twopanels .wym_area_top { height: 1%;}
+
+/*SECTIONS SYSTEM*/
+
+ /*common defaults for all sections*/
+ .wym_skin_twopanels .wym_section { margin-bottom: 5px; }
+ .wym_skin_twopanels .wym_section h2,
+ .wym_skin_twopanels .wym_section h3 { padding: 1px 3px; margin: 0; }
+ .wym_skin_twopanels .wym_section a { padding: 0 3px; display: block; text-decoration: none; color: black; }
+ .wym_skin_twopanels .wym_section a:hover { background-color: yellow; }
+ /*hide section titles by default*/
+ .wym_skin_twopanels .wym_section h2 { display: none; }
+ /*disable any margin-collapse*/
+ .wym_skin_twopanels .wym_section { padding-top: 1px; padding-bottom: 1px; }
+ /*auto-clear sections*/
+ .wym_skin_twopanels .wym_section ul:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ * html .wym_skin_twopanels .wym_section ul { height: 1%;}
+
+ /*option: add this class to a section to make it render as a panel*/
+ .wym_skin_twopanels .wym_panel { }
+ .wym_skin_twopanels .wym_panel h2 { display: block; }
+
+ /*option: add this class to a section to make it render as a dropdown menu*/
+ .wym_skin_twopanels .wym_dropdown h2 { display: block; }
+ .wym_skin_twopanels .wym_dropdown ul { display: none; position: absolute; background: white; }
+ .wym_skin_twopanels .wym_dropdown:hover ul,
+ .wym_skin_twopanels .wym_dropdown.hover ul { display: block; }
+
+ /*option: add this class to a section to make its elements render buttons (icons are only available for the wym_tools section for now)*/
+ .wym_skin_twopanels .wym_buttons li { float:left;}
+ .wym_skin_twopanels .wym_buttons a { width: 20px; height: 20px; overflow: hidden; padding: 2px }
+ /*image replacements*/
+ .wym_skin_twopanels .wym_buttons li a { background: url(icons.png) no-repeat; text-indent: -9999px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_strong a { background-position: 0 -382px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_emphasis a { background-position: 0 -22px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_superscript a { background-position: 0 -430px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_subscript a { background-position: 0 -454px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_ordered_list a { background-position: 0 -48px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_unordered_list a{ background-position: 0 -72px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_indent a { background-position: 0 -574px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_outdent a { background-position: 0 -598px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_undo a { background-position: 0 -502px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_redo a { background-position: 0 -526px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_link a { background-position: 0 -96px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_unlink a { background-position: 0 -168px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_image a { background-position: 0 -121px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_table a { background-position: 0 -144px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_paste a { background-position: 0 -552px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_html a { background-position: 0 -193px;}
+ .wym_skin_twopanels .wym_buttons li.wym_tools_preview a { background-position: 0 -408px;}
+
+/*DECORATION*/
+ .wym_skin_twopanels .wym_section h2 { background: #ddd; border: solid gray; border-width: 0 0 1px;}
+ .wym_skin_twopanels .wym_section h2 span { color: gray;}
+ .wym_skin_twopanels .wym_panel { padding: 0; border: solid gray; border-width: 1px; background: white;}
+ .wym_skin_twopanels .wym_panel ul { margin: 2px 0 5px; }
+ .wym_skin_twopanels .wym_dropdown { padding: 0; border: solid gray; border-width: 1px 1px 0 1px; }
+ .wym_skin_twopanels .wym_dropdown ul { border: solid gray; border-width: 0 1px 1px 1px; margin-left: -1px; padding: 5px 10px 5px 3px;}
+
+/*DIALOGS*/
+ .wym_dialog div.row { margin-bottom: 5px;}
+ .wym_dialog div.row input { margin-right: 5px;}
+ .wym_dialog div.row label { float: left; width: 150px; display: block; text-align: right; margin-right: 10px; }
+ .wym_dialog div.row-indent { padding-left: 160px; }
+ /*autoclearing*/
+ .wym_dialog div.row:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+ .wym_dialog div.row { display: inline-block; }
+ /* Hides from IE-mac \*/
+ * html .wym_dialog div.row { height: 1%; }
+ .wym_dialog div.row { display: block; }
+ /* End hide from IE-mac */
+
+/*WYMEDITOR_LINK*/
+ a.wym_wymeditor_link { text-indent: -9999px; float: right; display: block; width: 50px; height: 15px; background: url(../wymeditor_icon.png); overflow: hidden; text-decoration: none; }
diff --git a/studio/static/js/wymeditor/skins/twopanels/skin.js b/studio/static/js/wymeditor/skins/twopanels/skin.js
new file mode 100644
index 0000000..e82efc5
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/twopanels/skin.js
@@ -0,0 +1,39 @@
+WYMeditor.SKINS['twopanels'] = {
+
+ init: function(wym) {
+
+ //move the containers panel to the left area
+ jQuery(wym._box).find(wym._options.containersSelector)
+ .appendTo("div.wym_area_left");
+
+ //render following sections as panels
+ jQuery(wym._box).find(wym._options.classesSelector + ', '
+ + wym._options.containersSelector)
+ .addClass("wym_panel");
+
+ //render following sections as buttons
+ jQuery(wym._box).find(wym._options.toolsSelector)
+ .addClass("wym_buttons");
+
+ // auto add some margin to the main area sides if left area
+ // or right area are not empty (if they contain sections)
+ jQuery(wym._box).find("div.wym_area_right ul")
+ .parents("div.wym_area_right").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-right": "155px"});
+
+ jQuery(wym._box).find("div.wym_area_left ul")
+ .parents("div.wym_area_left").show()
+ .parents(wym._options.boxSelector)
+ .find("div.wym_area_main")
+ .css({"margin-left": "115px"});
+
+ //make hover work under IE < 7
+ jQuery(wym._box).find(".wym_section").hover(function(){
+ jQuery(this).addClass("hover");
+ },function(){
+ jQuery(this).removeClass("hover");
+ });
+ }
+};
diff --git a/studio/static/js/wymeditor/skins/wymeditor_icon.png b/studio/static/js/wymeditor/skins/wymeditor_icon.png
new file mode 100644
index 0000000..d4fc155
--- /dev/null
+++ b/studio/static/js/wymeditor/skins/wymeditor_icon.png
Binary files differ
diff --git a/studio/static/static b/studio/static/static
new file mode 120000
index 0000000..945c9b4
--- /dev/null
+++ b/studio/static/static
@@ -0,0 +1 @@
+. \ No newline at end of file
diff --git a/studio/studio.py b/studio/studio.py
new file mode 100644
index 0000000..9740b55
--- /dev/null
+++ b/studio/studio.py
@@ -0,0 +1,124 @@
+import os
+import sys
+from flaskext.genshi import Genshi, render_response
+from flask import Flask,request,url_for,redirect
+
+studio = Flask(__name__)
+studio.debug = True
+genshi = Genshi(studio)
+
+def shutdown_server():
+ func = request.environ.get('werkzeug.server.shutdown')
+ if func is None:
+ raise RuntimeError('Not running with the Werkzeug Server')
+ func()
+
+def list_files(directory):
+ files=os.listdir(directory)
+ print "showing %s" % directory
+ return files
+
+def identify(filename):
+ icon = 'document-generic.png'
+ mode = ''
+ directory=os.path.dirname(filename)
+ icon = 'document-generic.png'
+ href = '/edit/%s' % filename
+ if filename.endswith('.py'):
+ icon = 'text-x-python.png'
+ mode = 'python'
+ if filename.endswith('.html'):
+ icon = 'text-uri-list.png'
+ mode = 'html'
+ if filename.endswith('.css'):
+ icon = 'text-uri-list.png'
+ mode = 'css'
+ if filename.endswith('.js'):
+ icon = 'text-uri-list.png'
+ mode = 'javascript'
+ if os.path.isdir(filename):
+ icon = 'folder.png'
+ href = '/files/%s' % filename
+ if filename.endswith('.xo'):
+ href = '#'
+ return icon,mode,href
+
+@studio.route('/')
+def index():
+ port = request.environ.get('SERVER_PORT')
+ return render_response('index.html', dict(name="WebSDK Activity",
+ port=port))
+
+@studio.route('/edit/')
+@studio.route('/edit/<path:filename>')
+def edit(filename="activity.py"):
+ icon, mode, href = identify(filename)
+ content = open(filename).read().decode('utf-8')
+ tmpl = 'editor.html'
+ directory=os.path.dirname(filename)
+ return render_response(tmpl, dict(content=content, icon=icon,basename=os.path.basename(filename),
+ filename=filename, absdir=os.path.normpath(directory), mode=mode, directory=directory))
+
+@studio.route('/save', methods=['POST'])
+def save():
+ filename = request.form['filename']
+ f=open(filename,"wb")
+ content = request.form['content']
+ content = content.replace('\r\n', '\n').replace('\r', '\n') # HACK - Ace seems to be confused about newlines
+ f.write(content.encode('utf-8'))
+ print "saving content: %s" % filename
+ f.close()
+ directory = os.path.dirname(filename)
+ return redirect(url_for('browse', directory=directory))
+
+@studio.route('/files/')
+@studio.route('/files/<path:directory>')
+def browse(directory="."):
+ filelist = list_files(directory)
+ files = []
+ if not os.path.abspath(directory)==os.path.abspath("."):
+ files.append( { 'name': '..',
+ 'icon': 'folder.png',
+ 'href': '/files/%s' % os.path.join(directory,"..") })
+ for filename in sorted(filelist):
+ icon, mode, href = identify(directory + "/" + filename)
+ if filename.startswith('.'): #hidden files
+ continue
+ if filename.endswith('.pyc'): #lets avoid confusion
+ continue
+ files.append( { 'name': filename,
+ 'icon': icon,
+ 'href': href } )
+ return render_response('filer.html', dict(files=files, absdir=os.path.normpath(directory)))
+
+@studio.route('/delete/<path:filename>')
+def delete(filename):
+ os.unlink(filename)
+ directory = os.path.dirname(filename)
+ return redirect(url_for('browse', directory=directory))
+
+@studio.route('/shutdown')
+def shutdown():
+ shutdown_server()
+ return 'Goodbye'
+
+def vsplit(frame1='/files/studio', frame2='/files/studio/templates'):
+ return render_response('split-view.html', dict(frame1=frame1, frame2=frame2))
+
+@studio.route('/split')
+def split():
+ return vsplit()
+
+@studio.route('/debug')
+def debug():
+ raise Warning("Welcome to the debugger. Note an interactive interpreter is available at each line. You may raise an exception at any time in your controller to examine its environment.")
+
+if __name__=="__main__":
+ try:
+ port=int(sys.argv[1])
+ except IndexError:
+ port=5000
+ import webbrowser
+ webbrowser.open("http://localhost:%s/" % port)
+ #studio.run(port=port) # for local only
+ studio.run(host='0.0.0.0', port=port) # open for all
diff --git a/studio/templates/editor.html b/studio/templates/editor.html
new file mode 100644
index 0000000..88e45cb
--- /dev/null
+++ b/studio/templates/editor.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/">
+<head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>Editor</title>
+ <link rel="stylesheet" href="/static/css/main.css" type="text/css" />
+ <link rel="stylesheet" href="/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css" />
+ <script src="/static/js/jquery-1.6.2.js" type="text/javascript"></script>
+ <script src="/static/js/jquery-ui-1.8.16.sugar.min.js" type="text/javascript"></script>
+ <script src="/static/js/websdk-1.js" type="text/javascript"></script>
+<!-- <script src="js/jquery-plugins/json.js" type="text/javascript"></script>
+ <script src="js/jquery-plugins/jquery.hotkeys-0.7.8.js" type="text/javascript"></script>
+ <script src="js/jquery-plugins/jquery.intercept.js" type="text/javascript"></script> -->
+</head>
+<body>
+<script src="/static/js/ace/ace-uncompressed.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/ace/theme-twilight.js" type="text/javascript" charset="utf-8"></script>
+<script py:if="mode" src="/static/js/ace/mode-${mode}.js" type="text/javascript" charset="utf-8"></script>
+<pre id="editor">$content</pre>
+<div id="editor-sidebar" class="bling">
+<div class="file">
+ <img src="/static/icons/${icon}" /><br/>
+ ${absdir}/<br/>
+ <strong>${basename}</strong><br/>
+</div>
+<hr />
+<form action="/save" method="post">
+<input type="hidden" name="filename" id="filename" value="$filename" />
+<input type="hidden" name="directory" id="directory" value="$directory" />
+<input type="hidden" name="content" id="content" value="ñññ" />
+<input type="button" class="btn" value="Browse" onclick='location.href="/files/$directory"' />
+<br/>
+<input type="submit" value="Save file" /><br/>
+<input type="button" class="btn" value="Delete file" onclick='location.href="/delete/${filename}"' />
+<!-- save a magic cookie for security here TODO -->
+<input type="button" class="btn" value="Return" onclick='top.location.href="/"' />
+<!--input type="button" class="btn" value="edit wysiwyg" onclick='top.location.href=top.location.href+"&amp;editor=wysiwyg"' /-->
+</form>
+</div>
+<script>
+window.onload = function() {
+ var aceEditor = ace.edit("editor");
+ aceEditor.setTheme("ace/theme/twilight");
+ document.getElementById('editor').style.fontSize='14px';
+ aceEditor.getSession().setUseSoftTabs(true);
+ // we can assume we're on unix
+ aceEditor.getSession().setNewLineMode("unix");
+
+ if ("${mode}"!="") {
+ var thisMode = require("ace/mode/${mode}").Mode;
+ aceEditor.getSession().setMode(new thisMode());
+ }
+
+ function dispatch() {
+ var c = $('#content');
+ c.val(aceEditor.getSession().getValue());
+ }
+ dispatch()
+
+ aceEditor.getSession().on('change', dispatch);
+ $('.bling').slideDown("slow");
+};
+</script>
+</body>
+</html>
diff --git a/studio/templates/filer.html b/studio/templates/filer.html
new file mode 100644
index 0000000..5ca87e7
--- /dev/null
+++ b/studio/templates/filer.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/">
+<head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>Filer</title>
+ <link rel="stylesheet" href="/static/css/main.css" type="text/css" />
+ <link rel="stylesheet" href="/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css" />
+<script src="/static/js/jquery-1.6.2.js" type="text/javascript"></script>
+<script src="/static/js/jquery-ui-1.8.16.sugar.min.js" type="text/javascript"></script>
+<script src="/static/js/jquery.corner.js" type="text/javascript"></script>
+<script src="/static/js/websdk-1.js" type="text/javascript"></script>
+<!-- <script src="/static/js/jquery.plugin.svgimg.js" type="text/javascript"></script>
+ <script src="js/jquery-plugins/json.js" type="text/javascript"></script>
+ <script src="js/jquery-plugins/jquery.hotkeys-0.7.8.js" type="text/javascript"></script>
+ <script src="js/jquery-plugins/jquery.intercept.js" type="text/javascript"></script> -->
+ <style type="text/css">
+ .demoHeaders { margin-top: 2em; }
+ #dialog_link {padding: .4em 1em .4em 20px;text-decoration: none;position: relative;}
+ #dialog_link span.ui-icon {margin: 0 5px 0 0;position: absolute;left: .2em;top: 50%;margin-top: -8px;}
+ ul#icons {margin: 0; padding: 0;}
+ ul#icons li {margin: 2px; position: relative; padding: 4px 0; cursor: pointer; float: left; list-style: none;}
+ ul#icons span.ui-icon {float: left; margin: 0 4px;}
+ </style>
+</head>
+<body>
+ <div style="height:24px;" id="filer-header">
+ <ul id="icons" style="float:left" class="ui-widget ui-helper-clearfix">
+ <li onclick="top.location='/'" class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-w"><span class="ui-icon ui-icon-circle-arrow-w"></span></li>
+ </ul>
+ <div style="margin-left:15px; padding-top:7px">
+ ${absdir}/
+ </div>
+ <!-- ul id="icons" class="ui-widget ui-helper-clearfix">
+ <li class="ui-state-default ui-corner-all" title=".ui-icon-home"><span class="ui-icon ui-icon-home"></span></li>
+ </ul-->
+ </div>
+ <div id="filer" class="bling">
+ <ul>
+ <li py:for="file in files">
+ <a href="${file.href}" class="file">
+ <img src="/static/icons/${file.icon}" style="border:none;"/>
+ <br/>${file.name}
+ </a></li>
+</ul>
+ </div>
+<script>
+window.onload = function() {
+ $('.file').corner("15px");
+ $('.bling').show("slow");
+ /*if ($.browser.mozilla) {
+ $('#filer img').hide();
+ $('#filer span').show();
+ }*/
+ $( "input:button" ).button();
+};
+</script>
+</body>
+</html>
diff --git a/studio/templates/index.html b/studio/templates/index.html
new file mode 100644
index 0000000..1dd74e5
--- /dev/null
+++ b/studio/templates/index.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html lang="en-US">
+<head>
+ <meta charset="UTF-8" />
+ <title></title>
+ <link rel="stylesheet" href="/static/css/main.css" type="text/css" />
+ <link rel="stylesheet" href="/static/css/sugar-theme/jquery-ui-1.8.16.sugar.css" />
+ <script src="/static/js/jquery-1.6.2.js" type="text/javascript"></script>
+ <script src="/static/js/jquery-ui-1.8.16.sugar.min.js" type="text/javascript"></script>
+ <script src="/static/js/jquery.corner.js" type="text/javascript"></script>
+ <script src="/static/js/websdk-1.js" type="text/javascript"></script>
+</head>
+<body>
+ <div id="content" class="bling">
+ <header><h1>WebSDK Studio <span id="beta">Tech Preview 2</span><br /><em style="font-size: 10pt;"> "craftsmanship" </em></h1></header>
+ <section>
+ <div style="float:left; width:60%;">
+ <div id="accordion">
+ <div>
+ <h3><a href="#">Start with your Logic</a></h3>
+ <div style="text-align:right"><div style="float:left; padding-right: 15px;"><img src="/static/images/logo-flask-s.png" /></div>
+ <p>You can build Sugar Activities using standard web technologies with WebSDK.
+ The controller is where the logic of your Activity is expressed.
+ At the core of WebSDK there is Flask: a microframework built with good intentions.</p>
+ <p>The controller is implemented in the <a href="/edit/studio/studio.py">studio.py</a> file.
+ </p>
+ <input type="button"
+ value="Quick Start" onclick='location.href="/static/doc/flask-docs/quickstart.html#quickstart"' />
+ <input type="button"
+ value="Documentation Index" onclick='location.href="/static/doc/flask-docs/index.html"' />
+ <input type="button"
+ value="API" onclick='location.href="/static/doc/flask-docs/api.html#api"' />
+ </div>
+ </div>
+ <div>
+ <h3><a href="#">Build your User Experience</a></h3>
+ <div style="text-align:right"><div style="float:left; padding-right: 15px;"><img src="/static/images/logo-jqueryui.gif" /></div>
+ <p>Animation, advanced effects and widgets, built using HTML, CSS and Javascript, that you can use to build highly interactive Sugar Activities.</p>
+ <p>The initial screen for an activity is in <a href="/edit/studio/templates/index.html">index.html</a>. You can browse the <a href="/files/studio/templates">templates</a> directory for other views.</p>
+ <input type="button"
+ value="Sugar Web Widgets" onclick='location.href="/static/doc/sugar-theme.html"' />
+ </div>
+ <div>
+ <h3><a href="#">Deeper in Sugar</a></h3>
+ <div style="text-align:right"><div style="float:left; padding-right: 15px;"><img src="/static/images/logo-sugar-trans.png" /></div>
+ <p>This is where you set up your toolbars. If you need further system interaction at a lower level you'll start here.</p>
+ <p><a href="/edit/activity.py">activity.py</a> contains the browser loader for Sugar.</p>
+ <input type="button"
+ value="Make Your Own Sugar Activities" onclick='location.href="static/doc/myosa/ch000_table_of_contents.xhtml"' />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="accordion2" style="width:38%; float:right">
+ <div>
+ <h3><a href="#">${name}</a></h3>
+ <div>
+ <div id="filer-header" style="padding-top: 5px; padding-bottom: 5px">
+ Frequent actions
+ </div>
+ <div id="filer" style="display:inline;">
+ <ul>
+ <li>
+ <a href="/edit/studio/studio.py" class="file">
+ <img src="/static/icons/text-x-python.png" />
+ <br />studio.py
+ </a>
+ </li>
+ <li>
+ <a href="/edit/studio/templates/index.html" class="file">
+ <img src="/static/icons/text-uri-list.png" />
+ <br />index.html
+ </a>
+ </li>
+ <li>
+ <a href="/files/studio/templates" class="file">
+ <img src="/static/icons/folder.png" />
+ <br />templates
+ </a>
+ </li>
+ <li>
+ <a href="/files/studio/static" class="file">
+ <img src="/static/icons/folder.png" />
+ <br />static
+ </a>
+ </li>
+ <li>
+ <a href="/edit/./activity.py" class="file">
+ <img src="/static/icons/text-x-python.png" />
+ <br />activity.py
+ </a>
+ </li>
+ <li>
+ <a href="/edit/studio/static/css/main.css" class="file">
+ <img src="/static/icons/text-uri-list.png" />
+ <br />main.css
+ </a>
+ </li>
+ </ul>
+ <div style="float:both; width:100%">
+ <p style="font-size:8pt;">Running on port ${port}.</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" value="Split view" onclick='location.href="/split"' />
+ </div>
+ </div>
+ </section>
+ </div>
+ <script>
+ window.onload = function() {
+ $('#content').fadeIn(700);
+ $('.file').corner("15px");
+ $('#filer-header').corner("15px");
+ $('.bling').show("slow");
+ /* if ($.browser.mozilla) {
+ $('#filer img').hide();
+ $('#filer span').show();
+ } This was SVG workaround but is broken in older
+ gecko anyway*/
+ $('#beta').delay(1500).fadeIn("slow");
+ $("#accordion").accordion({ header: "h3" });
+ $("#accordion2").accordion({ header: "h3" });
+ }
+ </script>
+</body>
+</html>
diff --git a/studio/templates/skel.html b/studio/templates/skel.html
new file mode 100644
index 0000000..447ae4b
--- /dev/null
+++ b/studio/templates/skel.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html lang="en-US">
+<head>
+ <meta charset="UTF-8" />
+ <title></title>
+ <link rel="stylesheet" href="/static/css/main.css" type="text/css" />
+ <link rel="stylesheet" href="/static/css/sugar-theme/jquery-ui-1.8.15.custom.css" />
+ <script src="/static/js/jquery-1.6.2.js" type="text/javascript"></script>
+ <script src="/static/js/jquery-ui-1.8.15.custom.min.js" type="text/javascript"></script>
+ <script src="/static/js/websdk-1.js" type="text/javascript"></script>
+</head>
+<body>
+ <div id="content" class="bling">
+ </div>
+ <script>
+ window.onload = function() {
+ $('#content').fadeIn(700);
+ }
+ </script>
+</body>
+</html>
diff --git a/studio/templates/split-view.html b/studio/templates/split-view.html
new file mode 100644
index 0000000..e23b696
--- /dev/null
+++ b/studio/templates/split-view.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>basic frameset</title>
+</head>
+
+<frameset rows="50%, *">
+ <frame src="${frame1}" />
+ <frame src="${frame2}" />
+</frameset>
+</html>
diff --git a/studio/templates/wysiwyg-editor.html b/studio/templates/wysiwyg-editor.html
new file mode 100644
index 0000000..3f162c9
--- /dev/null
+++ b/studio/templates/wysiwyg-editor.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/">
+<head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>Editor</title>
+ <link rel="stylesheet" href="/www/css/main.css" type="text/css" />
+<script src="/www/js/jquery-1.6.2.js" type="text/javascript"></script>
+<!-- <script src="js/jquery-plugins/json.js" type="text/javascript"></script>
+ <script src="js/jquery-plugins/jquery.hotkeys-0.7.8.js" type="text/javascript"></script>
+ <script src="js/jquery-plugins/jquery.intercept.js" type="text/javascript"></script> -->
+</head>
+<body>
+<script type="text/javascript" src="/www/js/wymeditor/jquery.wymeditor.pack.js"></script>
+ <script type="text/javascript" src="/www/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js"></script>
+<form action="/save" method="post">
+<div id="editor">
+<textarea class="wymeditor">$content</textarea>
+</div>
+<div id="editor-sidebar" class="bling">
+<div class="file">
+ <img src="/www/icons/${icon}" /><br/>
+ ${absdir}/<br/>
+ <strong>${basename}</strong><br/>
+</div>
+<hr />
+<input type="hidden" name="filename" id="filename" value="$filename" />
+<input type="hidden" name="directory" id="directory" value="$directory" />
+<input type="button" class="btn" value="Browse directory" onclick='location.href="/browse?directory=${directory}"' />
+<br/>
+<input type="submit" class="btn" value="Save file" /><br/>
+<input type="button" class="btn" value="Delete file" onclick='location.href="/delete?filename=${filename}"' />
+<!-- save a magic cookie for security here TODO -->
+<input type="button" class="btn" value="Return to main" onclick='top.location.href="/www/index.html"' />
+</div>
+</form>
+<script>
+jQuery(function() {
+ jQuery(".wymeditor").wymeditor({
+ logoHtml: '',
+ skin: 'silver',
+ stylesheet: 'main.css',
+ postInit: function(wym) {
+ wym.fullscreen();
+ }
+ });
+});
+window.onload = function() {
+ $('.bling').slideDown("slow");
+};
+</script>
+</body>
+</html>
diff --git a/websdk/browser.py b/websdk/browser.py
new file mode 100644
index 0000000..e6ad639
--- /dev/null
+++ b/websdk/browser.py
@@ -0,0 +1,59 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+import time
+import logging
+from gettext import gettext as _
+
+import gobject
+import gtk
+import hulahop
+import xpcom
+from xpcom.nsError import *
+from xpcom import components
+from xpcom.components import interfaces
+from hulahop.webview import WebView
+
+from sugar.datastore import datastore
+from sugar import profile
+from sugar import env
+from sugar.activity import activity
+from sugar.graphics import style
+
+_ZOOM_AMOUNT = 0.1
+
+class Browser(WebView):
+ def __init__(self):
+ WebView.__init__(self)
+
+ def do_setup(self):
+ WebView.do_setup(self)
+
+ def zoom_in(self):
+ contentViewer = self.doc_shell.queryInterface( \
+ interfaces.nsIDocShell).contentViewer
+ if contentViewer is not None:
+ markupDocumentViewer = contentViewer.queryInterface( \
+ interfaces.nsIMarkupDocumentViewer)
+ markupDocumentViewer.fullZoom += _ZOOM_AMOUNT
+
+ def zoom_out(self):
+ contentViewer = self.doc_shell.queryInterface( \
+ interfaces.nsIDocShell).contentViewer
+ if contentViewer is not None:
+ markupDocumentViewer = contentViewer.queryInterface( \
+ interfaces.nsIMarkupDocumentViewer)
+ markupDocumentViewer.fullZoom -= _ZOOM_AMOUNT
+
diff --git a/websdk/inspector.py b/websdk/inspector.py
new file mode 100644
index 0000000..df3db38
--- /dev/null
+++ b/websdk/inspector.py
@@ -0,0 +1,79 @@
+# Copyright (C) 2008 Jan Alonzo <jmalonzo@unpluggable.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import gtk
+import webkit
+
+class Inspector (gtk.Window):
+ def __init__ (self, inspector):
+ """initialize the WebInspector class"""
+ gtk.Window.__init__(self)
+ self.set_default_size(600, 480)
+
+ self._web_inspector = inspector
+
+ self._web_inspector.connect("inspect-web-view",
+ self._inspect_web_view_cb)
+ self._web_inspector.connect("show-window",
+ self._show_window_cb)
+ self._web_inspector.connect("attach-window",
+ self._attach_window_cb)
+ self._web_inspector.connect("detach-window",
+ self._detach_window_cb)
+ self._web_inspector.connect("close-window",
+ self._close_window_cb)
+ self._web_inspector.connect("finished",
+ self._finished_cb)
+
+ self.connect("delete-event", self._close_window_cb)
+
+ def _inspect_web_view_cb (self, inspector, web_view):
+ """Called when the 'inspect' menu item is activated"""
+ scrolled_window = gtk.ScrolledWindow()
+ scrolled_window.props.hscrollbar_policy = gtk.POLICY_AUTOMATIC
+ scrolled_window.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
+ webview = webkit.WebView()
+ scrolled_window.add(webview)
+ scrolled_window.show_all()
+
+ self.add(scrolled_window)
+ return webview
+
+ def _show_window_cb (self, inspector):
+ """Called when the inspector window should be displayed"""
+ self.present()
+ return True
+
+ def _attach_window_cb (self, inspector):
+ """Called when the inspector should displayed in the same
+ window as the WebView being inspected
+ """
+ return False
+
+ def _detach_window_cb (self, inspector):
+ """Called when the inspector should appear in a separate window"""
+ return False
+
+ def _close_window_cb (self, inspector, view):
+ """Called when the inspector window should be closed"""
+ self.hide()
+ return True
+
+ def _finished_cb (self, inspector):
+ """Called when inspection is done"""
+ self._web_inspector = 0
+ self.destroy()
+ return False
diff --git a/websdk/skel.py b/websdk/skel.py
new file mode 100644
index 0000000..9336a07
--- /dev/null
+++ b/websdk/skel.py
@@ -0,0 +1,30 @@
+import os
+import sys
+from flask import Flask
+from flaskext.genshi import Genshi, render_response
+from werkzeug.utils import redirect
+from flask import request,url_for
+
+app = Flask(__name__)
+app.debug = True
+genshi = Genshi(app)
+
+def shutdown_server():
+ func = request.environ.get('werkzeug.server.shutdown')
+ if func is None:
+ raise RuntimeError('Not running with the Werkzeug Server')
+ func()
+
+@app.route('/')
+def index():
+ return render_response('index.html')
+
+@app.route('/shutdown')
+def shutdown():
+ shutdown_server()
+ return 'Server shutting down...'
+
+if __name__=="__main__":
+ port=int(sys.argv[1])
+ app.run(port=port)
+ #or app.run(host='0.0.0.0')